module Data.OpenPGP.Util.Ed25519 where import Control.Monad import Crypto.Error import qualified Crypto.PubKey.Ed25519 as Ed25519 import Data.Bits import qualified Data.ByteArray as BA import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL import qualified Data.OpenPGP as OpenPGP import Data.OpenPGP (MPI(..)) import Data.OpenPGP.Internal import Data.OpenPGP.Util.Base -- import Crypto.ECC.Edwards25519 oid_ed25516 = 0x2B06010401DA470F01 zeroExtend :: Int -> BS.ByteString -> BS.ByteString zeroExtend n bs = case compare (BS.length bs) n of GT -> BS.take n bs EQ -> bs LT -> bs <> BS.replicate (n - BS.length bs) 0 zeroPad :: Int -> BS.ByteString -> BS.ByteString zeroPad n bs = case compare (BS.length bs) n of GT -> BS.take n bs EQ -> bs LT -> BS.replicate (n - BS.length bs) 0 <> bs ed25519Key :: OpenPGP.Packet -> Maybe Ed25519.PublicKey ed25519Key k = let n = case keyParam 'f' k of 0x40 -> zeroPad 32 $ integerToBS $ keyParam 'n' k _ -> -- From Bernstein's "High-speed high-security signatures" -- -- An element (x, y) ∈ E is encoded as a b-bit string (x, y), namely the (b − -- 1)- bit encoding of y followed by a sign bit; the sign bit is 1 iff x is -- negative. This encoding immediately determines y, and it determines x via -- the equation x = ± √(y² − 1)/(dy² + 1). let y = keyParam 'y' k x = keyParam 'x' k ybs = zeroExtend 32 $ integerToLE y lb = BS.last ybs in if x < 0 then BS.take 31 ybs `BS.snoc` (lb .|. 0x80) else BS.take 31 ybs `BS.snoc` (lb .&. 0x7F) in case Ed25519.publicKey n of CryptoPassed ed25519 -> Just ed25519 CryptoFailed _ -> Nothing ed25519sig :: OpenPGP.Packet -> Maybe Ed25519.Signature ed25519sig sig = let [MPI r,MPI s] = OpenPGP.signature sig rbs = zeroPad 32 $ integerToBS r sbs = zeroPad 32 $ integerToBS s in case Ed25519.signature (rbs <> sbs) of CryptoPassed sig25519 -> Just sig25519 CryptoFailed _ -> Nothing privateEd25519Key :: OpenPGP.Packet -> Ed25519.SecretKey privateEd25519Key k = case Ed25519.secretKey $ zeroExtend 32 $ integerToLE (keyParam 'd' k) of CryptoPassed ed25519sec -> ed25519sec CryptoFailed err -> error $ "Ed25519.secretKey: " ++ show err ed25519Verify :: OpenPGP.Packet -> BS.ByteString -> OpenPGP.Packet -> Maybe Bool ed25519Verify sig over k = do let hashbs = hashBySymbol (OpenPGP.hash_algorithm sig) $ BL.fromChunks [over] guard $ oid_ed25516 == keyParam 'c' k -- Only Ed25519 curve. ek <- ed25519Key k esig <- ed25519sig sig Just $ Ed25519.verify ek hashbs esig ed25519Sign :: OpenPGP.Packet -> OpenPGP.HashAlgorithm -> BS.ByteString -> [Integer] ed25519Sign k hsh dta = [ getBigNum rbs, getBigNum sbs ] where hashbs = hashBySymbol hsh $ BL.fromChunks [dta] sec = privateEd25519Key k Just pub = ed25519Key k sig = Ed25519.sign sec pub hashbs (rbs,sbs) = BS.splitAt 32 $ BA.convert sig importSecretEd25519 :: Ed25519.SecretKey -> [(Char,MPI)] importSecretEd25519 k = [ ('c', MPI oid_ed25516) , ('l', MPI 128) , ('n', MPI pub) , ('f', MPI 0x40) , ('d', MPI sec) ] where pub = getBigNum $ BA.convert $ Ed25519.toPublic k sec = getBigNumLE $ BA.convert k importPublicEd25519 :: Ed25519.PublicKey -> [(Char,MPI)] importPublicEd25519 k = [ ('c', MPI oid_ed25516) , ('l', MPI 128) , ('n', MPI pub) , ('f', MPI 0x40) ] where pub = getBigNum $ BA.convert k