From b42c0d847a785487f3222b0d5360746d25d3209c Mon Sep 17 00:00:00 2001 From: Joe Crayne Date: Thu, 14 Nov 2019 16:45:14 -0500 Subject: Cv25519 encryption. --- Data/OpenPGP.hs | 77 +++++++----- Data/OpenPGP/Util/Cv25519.hs | 231 ++++++++++++++++++++++++++++++++++ Data/OpenPGP/Util/DecryptSecretKey.hs | 27 ++-- Data/OpenPGP/Util/Ed25519.hs | 4 +- 4 files changed, 287 insertions(+), 52 deletions(-) create mode 100644 Data/OpenPGP/Util/Cv25519.hs (limited to 'Data') diff --git a/Data/OpenPGP.hs b/Data/OpenPGP.hs index 45ca27e..17a6927 100644 --- a/Data/OpenPGP.hs +++ b/Data/OpenPGP.hs @@ -66,7 +66,10 @@ module Data.OpenPGP ( secret_key_fields, eccOID, encode_public_key_material, - decode_public_key_material + decode_public_key_material, + getEllipticCurvePublicKey, + encodeOID, + hashLen ) where import Control.Applicative @@ -361,7 +364,8 @@ secret_key_fields ELGAMAL = ['x'] secret_key_fields DSA = ['x'] secret_key_fields ECDSA = ['d'] secret_key_fields Ed25519 = ['d'] -secret_key_fields alg = error ("Unkown secret fields for "++show alg) -- Nothing in the spec. Maybe empty +secret_key_fields ECC = ['d'] +secret_key_fields alg = error ("Unknown secret fields for "++show alg) -- Nothing in the spec. Maybe empty (!) :: (HasCallStack, Show k, Eq k) => [(k,v)] -> k -> v (!) xs k = case lookup k xs of @@ -425,16 +429,21 @@ eccOID PublicKeyPacket { key = k } = lookup 'c' k >>= \(MPI oid) -> Just (snd $ eccOID SecretKeyPacket { key = k } = lookup 'c' k >>= \(MPI oid) -> Just (snd $ putBigNum oid) eccOID _ = Nothing +encodeOID :: MPI -> B.ByteString +encodeOID c = + let (bitlen,oid) = B.splitAt 2 (encode c) + len16 = decode bitlen :: Word16 + (fullbytes,rembits) = len16 `quotRem` 8 + len8 = fromIntegral (fullbytes + if rembits/=0 then 1 else 0) :: Word8 + in len8 `B.cons` oid + encode_public_key_material :: Packet -> [B.ByteString] encode_public_key_material k | key_algorithm k `elem` [ECDSA,Ed25519,ECC] = do -- http://tools.ietf.org/html/rfc6637 c <- maybeToList $ lookup 'c' (key k) MPI l <- maybeToList $ lookup 'l' (key k) MPI flag <- maybeToList $ lookup 'f' (key k) - let (bitlen,oid) = B.splitAt 2 (encode c) - len16 = decode bitlen :: Word16 - (fullbytes,rembits) = len16 `quotRem` 8 - len8 = fromIntegral (fullbytes + if rembits/=0 then 1 else 0) :: Word8 + let oid = encodeOID c eccstuff = case lookup 'e' (key k) of Just (MPI stuff) -> encode (fromIntegral stuff :: Word32) Nothing -> B.empty @@ -442,39 +451,45 @@ encode_public_key_material k | key_algorithm k `elem` [ECDSA,Ed25519,ECC] = do 0x40 -> do MPI n <- maybeToList $ lookup 'n' (key k) let xy = flag*(4^l) + n - [ len8 `B.cons` oid, encode (MPI xy), eccstuff ] + [ oid, encode (MPI xy), eccstuff ] _ -> do MPI x <- maybeToList $ lookup 'x' (key k) MPI y <- maybeToList $ lookup 'y' (key k) let xy = flag*(4^l) + x*(2^l) + y - [ len8 `B.cons` oid, encode (MPI xy), eccstuff ] + [ oid, encode (MPI xy), eccstuff ] encode_public_key_material k = map (encode . (key k !)) (public_key_fields $ key_algorithm k) -decode_public_key_material :: KeyAlgorithm -> Get [(Char,MPI)] -decode_public_key_material algorithm | algorithm `elem` [ECDSA, Ed25519] = do - -- http://tools.ietf.org/html/rfc6637 (9) Algorithm-Specific Fields for ECDSA keys - oidlen <- get :: Get Word8 - oidbytes <- getSomeByteString (fromIntegral oidlen) - let mpiFromBytes bytes = MPI (getBigNum $ B.toStrict bytes) - oid = mpiFromBytes oidbytes +getEllipticCurvePublicKey :: Get [(Char,MPI)] +getEllipticCurvePublicKey = do MPI fxy <- get let integerBytesize i = fromIntegral $ LZ.length (encode (MPI i)) - 2 width = ( integerBytesize fxy - 1 ) `div` 2 l = width*8 (flag,xy) = fxy `quotRem` (256^(2*width)) return $ case flag of - 0x40 -> [('c',oid), ('l',MPI l), ('n',MPI xy), ('f',MPI flag)] + 0x40 -> [('l',MPI l), ('n',MPI xy), ('f',MPI flag)] _ -> let (x,y) = xy `quotRem` (256^width) -- (fx,y) = xy `quotRem` (256^width) -- (flag,x) = fx `quotRem` (256^width) - in [('c',oid), ('l',MPI l), ('x',MPI x), ('y',MPI y), ('f',MPI flag)] -decode_public_key_material ECC = do - -- http://tools.ietf.org/html/rfc6637 (9) Algorithm-Specific Fields for ECDH keys: + in [('l',MPI l), ('x',MPI x), ('y',MPI y), ('f',MPI flag)] + +getOID :: Get MPI +getOID = do oidlen <- get :: Get Word8 oidbytes <- getSomeByteString (fromIntegral oidlen) let mpiFromBytes bytes = MPI (getBigNum $ B.toStrict bytes) oid = mpiFromBytes oidbytes - MPI fxy <- get + return oid + +decode_public_key_material :: KeyAlgorithm -> Get [(Char,MPI)] +decode_public_key_material algorithm | algorithm `elem` [ECDSA, Ed25519] = do + -- http://tools.ietf.org/html/rfc6637 (9) Algorithm-Specific Fields for ECDSA keys + oid <- getOID + fmap (('c',oid) :) getEllipticCurvePublicKey +decode_public_key_material ECC = do + -- http://tools.ietf.org/html/rfc6637 (9) Algorithm-Specific Fields for ECDH keys: + oid <- getOID + result <- getEllipticCurvePublicKey eccstuff <- get :: Get Word32 {- eccstuff is 4 one-byte fields: flen <- get :: Get Word8 @@ -482,17 +497,7 @@ decode_public_key_material ECC = do hashid <- get :: Get Word8 algoid <- get :: Get Word8 -} - let integerBytesize i = fromIntegral $ LZ.length (encode (MPI i)) - 2 - width = ( integerBytesize fxy - 1 ) `div` 2 - l = width*8 - (flag,xy) = fxy `quotRem` (256^(2*width)) - result = case flag of - 0x40 -> [('c',oid), ('l',MPI l), ('n',MPI xy), ('f',MPI flag)] - _ -> let (x,y) = xy `quotRem` (256^width) - -- (fx,y) = xy `quotRem` (256^width) - -- (flag,x) = fx `quotRem` (256^width) - in [('c',oid), ('l',MPI l), ('x',MPI x), ('y',MPI y), ('f',MPI flag)] - return $ result ++ [('e',MPI (fromIntegral eccstuff))] + return $ ('c', oid) : result ++ [('e',MPI (fromIntegral eccstuff))] decode_public_key_material algorithm = mapM (\f -> fmap ((,)f) get) (public_key_fields algorithm) put_packet :: Packet -> (B.ByteString, Word8) @@ -907,6 +912,16 @@ infiniHashes hsh s = LZ.fromChunks (hs 0) data HashAlgorithm = MD5 | SHA1 | RIPEMD160 | SHA256 | SHA384 | SHA512 | SHA224 | HashAlgorithm Word8 deriving (Show, Read, Eq, Ord) +hashLen :: HashAlgorithm -> Int +hashLen MD5 = 16 +hashLen SHA1 = 20 +hashLen RIPEMD160 = 20 +hashLen SHA256 = 32 +hashLen SHA384 = 48 +hashLen SHA512 = 64 +hashLen SHA224 = 28 +hashLen (HashAlgorithm _) = 0 + instance Enum HashAlgorithm where toEnum 01 = MD5 toEnum 02 = SHA1 diff --git a/Data/OpenPGP/Util/Cv25519.hs b/Data/OpenPGP/Util/Cv25519.hs new file mode 100644 index 0000000..aef3521 --- /dev/null +++ b/Data/OpenPGP/Util/Cv25519.hs @@ -0,0 +1,231 @@ +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE QuasiQuotes #-} +module Data.OpenPGP.Util.Cv25519 where + +import Control.Arrow +import Control.Monad +import Data.Binary +import Data.Binary.Get +import Data.ByteString (ByteString) +import Data.Bits +import qualified Data.ByteArray as BA +import qualified Data.ByteString.Char8 as B8 +import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as BL +import Data.Char +import Numeric +import Data.Int + +import Data.OpenPGP.Internal +import Data.OpenPGP.Util +import Data.OpenPGP.Util.Base +import Data.OpenPGP as OpenPGP +import Crypto.Cipher.SBox +import Data.OpenPGP.Util.Ed25519 (zeroExtend,zeroPad) +import qualified Crypto.PubKey.Curve25519 as Cv25519 +import Crypto.Error +import Crypto.Cipher.AES +import Crypto.Cipher.Types +import Data.OpenPGP.Util.DecryptSecretKey -- (withS2K, simpleUnCFB, Enciphered(..)) + +import Crypto.JOSE.AESKW + +oid_cv25519 = 0x2B060104019755010501 + +getEphemeralKey :: OpenPGP.Packet -> Maybe ([(Char,MPI)],BL.ByteString) +getEphemeralKey AsymmetricSessionKeyPacket + { version = 3 + , key_algorithm = ECC + , encrypted_data = dta } = do + -- Algorithm-Specific Fields for ECDH encryption: + -- + -- * MPI of an EC point representing an ephemeral public key. + -- + -- * a one-octet size, followed by a symmetric key encoded using the + -- method described in Section 13.5. + (b,_,d) <- either (const Nothing) Just $ runGetOrFail getEllipticCurvePublicKey dta + (sz,m) <- BL.uncons b + guard $ BL.length m == fromIntegral sz + return (d,m) +getEphemeralKey _ = Nothing + +-- The value "m" in the above formulas is derived from the session key +-- as follows. First, the session key is prefixed with a one-octet +-- algorithm identifier that specifies the symmetric encryption +-- algorithm used to encrypt the following Symmetrically Encrypted Data +-- Packet. Then a two-octet checksum is appended, which is equal to the +-- sum of the preceding session key octets, not including the algorithm +-- identifier, modulo 65536. This value is then encoded as described in +-- PKCS#1 block encoding EME-PKCS1-v1_5 in Section 7.2.1 of [RFC3447] to +-- form the "m" value used in the formulas above. See Section 14.1 of +-- this document for notes on OpenPGP's use of PKCS#1. + +privateCv25519Key :: OpenPGP.Packet -> Maybe Cv25519.SecretKey +privateCv25519Key k@SecretKeyPacket { key_algorithm = ECC, symmetric_algorithm = Unencrypted } = do + guard $ oid_cv25519 == keyParam 'c' k + case Cv25519.secretKey $ zeroExtend 32 $ integerToLE (keyParam 'd' k) of + CryptoPassed cv25519sec -> Just cv25519sec + CryptoFailed err -> Nothing + +hexify = map toUpper . hexString . BS.unpack + + + +hexString :: [Word8] -> String +hexString = foldr (pad `oo` showHex) "" + where + pad s | odd $ length s = '0':s + | otherwise = s + + oo :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c + oo = (.) . (.) + +cv25519Key :: [(Char,MPI)] -> Maybe Cv25519.PublicKey +cv25519Key k = do + MPI flag <- lookup 'f' k + n <- case flag of + 0x40 -> zeroPad 32 . integerToBS . (\(MPI n)-> n) <$> lookup 'n' k + -- TODO: The following was based on Ed25519. Verify that it is correct for Cv25519. + _ -> do MPI y <- lookup 'y' k + MPI x <- lookup 'x' k + let ybs = zeroExtend 32 $ integerToLE y + lb = BS.last ybs + return $ if x < 0 then BS.take 31 ybs `BS.snoc` (lb .|. 0x80) + else BS.take 31 ybs `BS.snoc` (lb .&. 0x7F) + maybeCryptoError $ Cv25519.publicKey n + +kdfParams :: OpenPGP.Packet -> (OpenPGP.HashAlgorithm, OpenPGP.SymmetricAlgorithm) +kdfParams k = toEnum *** toEnum $ divMod e 256 + where + e = 0x0FFFF .&. (fromIntegral $ keyParam 'e' k) + -- flen <- get :: Get Word8 -- always 3 (length of following bytes) + -- one <- get :: Get Word8 -- always 0x01 (reserved) + -- hashid <- get :: Get Word8 -- HashAlgorithm + -- algoid <- get :: Get Word8 -- SymmetricAlgorithm + +data SomeKeyCipher = forall c. BlockCipher128 c => SomeKeyCipher c + +someAES128 :: AES128 -> SomeKeyCipher +someAES192 :: AES192 -> SomeKeyCipher +someAES256 :: AES256 -> SomeKeyCipher +someAES128 = SomeKeyCipher +someAES192 = SomeKeyCipher +someAES256 = SomeKeyCipher + +keyCipher :: OpenPGP.SymmetricAlgorithm -> BS.ByteString -> Maybe SomeKeyCipher +keyCipher OpenPGP.AES128 key = someAES128 <$> maybeCryptoError (cipherInit key) +keyCipher OpenPGP.AES192 key = someAES192 <$> maybeCryptoError (cipherInit key) +keyCipher OpenPGP.AES256 key = someAES256 <$> maybeCryptoError (cipherInit key) +keyCipher _ _ = Nothing + +keyCipherSize OpenPGP.AES128 = cipherKeySize (undefined :: AES128) +keyCipherSize OpenPGP.AES192 = cipherKeySize (undefined :: AES192) +keyCipherSize OpenPGP.AES256 = cipherKeySize (undefined :: AES256) + + +kdfParamBytes :: OpenPGP.Packet -> BL.ByteString +kdfParamBytes k = BL.fromChunks + [ BL.toStrict $ encodeOID (MPI $ keyParam 'c' k) -- curve_OID_len || curve_OID + , BS.singleton $ fromIntegral $ fromEnum $ key_algorithm k -- public_key_alg_ID + , BL.toStrict $ encode (fromIntegral (keyParam 'e' k) :: Word32) -- 03 || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap + , B8.pack "Anonymous Sender " + , let Fingerprint fp = fingerprint k in fp + ] + +-- The Concatenation Key Derivation Function (Approved Alternative 1) [SP800-56A] +kdf :: OpenPGP.HashAlgorithm -> Cv25519.DhSecret -> Int -> BL.ByteString -> Maybe BL.ByteString +kdf hsh z keybytelen otherinfo + | reps > 2^32 - 1 = Nothing + -- XXX: I don't understand /max_hash_inputlen/. + -- + -- max_hash_inputlen: an integer that indicates the maximum length (in + -- bits) of the bit string(s) input to the hash function. + -- + -- | 8 * (BS.length zo) > max_hash_inputlen - 32 = Nothing + | otherwise = Just derivedKeyingMaterial + where + keydatalen = 8 * fromIntegral keybytelen :: Int64 + hashlen = 8 * fromIntegral (hashLen hsh) :: Int64 + reps = fromIntegral $ (keydatalen + hashlen - 1) `div` hashlen + counter = 0x00000001 :: Word32 + zo = BL.fromStrict (BA.convert z) <> otherinfo + hashes = [ hashBySymbol hsh (encode (i::Word32) <> zo) + | i <- [1 .. reps] ] -- Compute Hash i = H(counter || Z || OtherInfo). + -- Let Hhash be set to Hash[reps] if (keydatalen / hashlen) is an integer; otherwise, let Hhash + -- be set to the (keydatalen mod hashlen) leftmost bits of Hash[reps]. + hhash = case keydatalen `mod` hashlen of + 0 -> last hashes + r -> BS.take (fromIntegral $ (r + 7) `div` 8) $ last hashes -- TODO: Zero out the 8 - (r `mod` 8) last bits? + derivedKeyingMaterial = BL.fromChunks $ init hashes ++ [ hhash ] + + +-- The input to the key wrapping method is the value "m" derived from +-- the session key, as described in Section 5.1, "Public-Key Encrypted +-- Session Key Packets (Tag 1)", except that the PKCS #1.5 padding step +-- is omitted. The result is padded using the method described in +-- [PKCS5] to the 8-byte granularity. For example, the following +-- AES-256 session key, in which 32 octets are denoted from k0 to k31, +-- is composed to form the following 40 octet sequence: +-- +-- 09 k0 k1 ... k31 c0 c1 05 05 05 05 05 +-- +-- The octets c0 and c1 above denote the checksum. This encoding allows +-- the sender to obfuscate the size of the symmetric encryption key used +-- to encrypt the data. For example, assuming that an AES algorithm is +-- used for the session key, the sender MAY use 21, 13, and 5 bytes of +-- padding for AES-128, AES-192, and AES-256, respectively, to provide +-- the same number of octets, 40 total, as an input to the key wrapping +-- method. +-- +-- From Section 5.1, "Public-Key Encrypted Session Key Packets (Tag 1)" +-- +-- The value "m" in the above formulas is derived from the session key +-- as follows. First, the session key is prefixed with a one-octet +-- algorithm identifier that specifies the symmetric encryption +-- algorithm used to encrypt the following Symmetrically Encrypted Data +-- Packet. Then a two-octet checksum is appended, which is equal to the +-- sum of the preceding session key octets, not including the algorithm +-- identifier, modulo 65536. This value is then encoded as described in +-- PKCS#1 block encoding EME-PKCS1-v1_5 in Section 7.2.1 of [RFC3447] to +-- form the "m" value used in the formulas above. See Section 14.1 of +-- this document for notes on OpenPGP's use of PKCS#1. +decodeEncryptedKey :: ByteString -> Maybe (SymmetricAlgorithm, ByteString) +decodeEncryptedKey m = do + (algb,ks) <- BS.uncons m + let alg = toEnum $ fromIntegral algb :: OpenPGP.SymmetricAlgorithm + sz <- case keyCipherSize alg of + KeySizeFixed n -> Just n + _ -> Nothing + let (key,macbs) = BS.splitAt sz ks + (macb,trail) = BS.splitAt 2 macbs + mac = decode $ BL.fromStrict macb :: Word16 + chk = sum $ map fromIntegral $ BS.unpack key + guard $ chk == mac + Just (alg, key) + +decryptMessage :: Packet -- ^ local secret key (ecdh cv25519) + -> Packet -- ^ ephemeral remote public key (ecdh cv25519) and encrypted symmetric key. + -> Packet -- ^ symmetrically encrypted data packet + -> Maybe [Packet] +decryptMessage ecdhkey asym encdta = do + (pubk,m) <- getEphemeralKey asym + pub25519 <- cv25519Key pubk + sec25519 <- privateCv25519Key ecdhkey + let shared = Cv25519.dh pub25519 sec25519 + (hsh, alg) = kdfParams ecdhkey + miv = let sz = case keyCipherSize alg of + KeySizeFixed n -> n + KeySizeEnum ns -> head ns + KeySizeRange mn mx -> mn + in kdf hsh shared sz (kdfParamBytes ecdhkey) + (alg,k) <- do + iv <- BL.toStrict <$> miv + SomeKeyCipher c <- keyCipher alg iv + m' <- aesKeyUnwrap c (BL.toStrict m) :: Maybe BS.ByteString + decodeEncryptedKey m' + withS2K' alg Nothing (BL.fromStrict k) $ \cipher -> do + let blksize = blockSize cipher + b0 = simpleUnCFB cipher nullIV (encrypted_data encdta) + b1 = BL.drop (2 + fromIntegral blksize) b0 + (_,_, Message ps) <- either (const Nothing) Just $ decodeOrFail b1 + return ps diff --git a/Data/OpenPGP/Util/DecryptSecretKey.hs b/Data/OpenPGP/Util/DecryptSecretKey.hs index 1188f3e..a637b29 100644 --- a/Data/OpenPGP/Util/DecryptSecretKey.hs +++ b/Data/OpenPGP/Util/DecryptSecretKey.hs @@ -90,7 +90,7 @@ decryptSecretKey pass k@(OpenPGP.SecretKeyPacket { | OpenPGP.s2k_useage k == 254 = (20, sha1 . toStrictBS) | otherwise = (2, toStrictBS . encode . checksum . toStrictBS) -- Words16s are written as 2 bytes in big-endian (network) order - decd = withS2K simpleUnCFB salgo s2k (toLazyBS pass) (EncipheredWithIV encd) + decd = withS2K simpleUnCFB salgo (Just s2k) (toLazyBS pass) (EncipheredWithIV encd) #if defined(VERSION_cryptonite) sha1 x = Bytes.convert (hash x :: Digest SHA1) @@ -122,7 +122,7 @@ maybeGet g bs = unsafePerformIO $ withS2K :: (forall k. (Vincent.BlockCipher k) => k -> Vincent.IV k -> LZ.ByteString -> LZ.ByteString) -> OpenPGP.SymmetricAlgorithm - -> OpenPGP.S2K + -> Maybe OpenPGP.S2K -> LZ.ByteString -> Enciphered -> LZ.ByteString withS2K codec OpenPGP.AES128 s2k s = withIV $ codec (string2key s2k s :: Vincent.AES128) withS2K codec OpenPGP.AES192 s2k s = withIV $ codec (string2key s2k s :: Vincent.AES192) @@ -131,7 +131,7 @@ withS2K codec OpenPGP.Blowfish s2k s = withIV $ codec (string2key s2k s :: Vince withS2K codec OpenPGP.CAST5 s2k s = withIV $ codec (string2key s2k s :: ThomasToVincent CAST5_128) withS2K codec algo _ _ = error $ "Unsupported symmetric algorithm : " ++ show algo ++ " in Data.OpenPGP.CryptoAPI.withS2K" -withS2K' :: OpenPGP.SymmetricAlgorithm -> OpenPGP.S2K -> LZ.ByteString +withS2K' :: OpenPGP.SymmetricAlgorithm -> Maybe OpenPGP.S2K -> LZ.ByteString -> (forall b. Vincent.BlockCipher b => b -> x) -> x withS2K' OpenPGP.AES128 s2k s f = f (string2key s2k s :: Vincent.AES128) withS2K' OpenPGP.AES192 s2k s f = f (string2key s2k s :: Vincent.AES192) @@ -169,27 +169,16 @@ padThenUnpad k f s = dropPadEnd (f padded) padAmount = blksize - (LZ.length s `mod` blksize) blksize = fromIntegral $ Vincent.blockSize k -{- -Data/OpenPGP/Util/DecryptSecretKey.hs:172:20: - Couldn't match expected type ‘k’ - with actual type ‘cryptonite-0.15:Crypto.Error.Types.CryptoFailable - cipher0’ - ‘k’ is a rigid type variable bound by - the type signature for - string2key :: Vincent.BlockCipher k => - OpenPGP.S2K -> LZ.ByteString -> k - at Data/OpenPGP/Util/DecryptSecretKey.hs:171:15 --} -string2key :: (Vincent.BlockCipher k) => OpenPGP.S2K -> LZ.ByteString -> k -string2key s2k s = cipher +string2key :: (Vincent.BlockCipher k) => Maybe OpenPGP.S2K -> LZ.ByteString -> k +string2key ms2k s = cipher where #if defined(VERSION_cryptonite) CryptoPassed cipher = Vincent.cipherInit k - k = toStrictBS $ LZ.take ksize $ OpenPGP.string2key hashBySymbol s2k s + k = toStrictBS $ LZ.take ksize $ maybe s (\s2k -> OpenPGP.string2key hashBySymbol s2k s) ms2k #else cipher = Vincent.cipherInit k Right k = Vincent.makeKey $ toStrictBS $ - LZ.take ksize $ OpenPGP.string2key hashBySymbol s2k s + LZ.take ksize $ maybe s (\s2k -> OpenPGP.string2key hashBySymbol s2k s) ms2k #endif ksize = case Vincent.cipherKeySize cipher of Vincent.KeySizeFixed n -> fromIntegral n @@ -217,7 +206,7 @@ encryptSecretKey passphrase s2k salgo plain = do maybeToList $ lookup f (OpenPGP.key plain) chk = LZ.fromChunks [ chkF material ] decd = LZ.append material chk - encd g = fst $ withS2K' salgo s2k (toLazyBS passphrase) (simpleCFB g) decd + encd g = fst $ withS2K' salgo (Just s2k) (toLazyBS passphrase) (simpleCFB g) decd -- If the string-to-key usage octet is zero or 255, then a two-octet -- checksum of the plaintext of the algorithm-specific portion (sum diff --git a/Data/OpenPGP/Util/Ed25519.hs b/Data/OpenPGP/Util/Ed25519.hs index 7504e7e..67eeba3 100644 --- a/Data/OpenPGP/Util/Ed25519.hs +++ b/Data/OpenPGP/Util/Ed25519.hs @@ -43,8 +43,8 @@ ed25519Key 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 .|. 1) - else BS.take 31 ybs `BS.snoc` (lb .&. 0xFE) + 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 -- cgit v1.2.3