summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2020-05-10 14:05:42 -0400
committerJoe Crayne <joe@jerkface.net>2020-05-19 11:59:10 -0400
commitcee4108007b2a384f54b4a8943a612fdc3c9f260 (patch)
tree3f2f476b6bd5af89da0014d97baa39ca67cea7b9
parent4bfc6b028527f43d9ae48d24dc2afb5f0db7ad1e (diff)
Parse v5 secret key packets (draft-ietf-openpgp-rfc4880bis-09).
-rw-r--r--Data/OpenPGP.hs96
-rw-r--r--Data/OpenPGP/Util/Gen.hs1
2 files changed, 80 insertions, 17 deletions
diff --git a/Data/OpenPGP.hs b/Data/OpenPGP.hs
index 16fcd1e..598fe75 100644
--- a/Data/OpenPGP.hs
+++ b/Data/OpenPGP.hs
@@ -39,6 +39,7 @@ module Data.OpenPGP (
39 s2k, 39 s2k,
40 signature, 40 signature,
41 signature_type, 41 signature_type,
42 aead_algorithm,
42 symmetric_algorithm, 43 symmetric_algorithm,
43 timestamp, 44 timestamp,
44 trailer, 45 trailer,
@@ -53,6 +54,7 @@ module Data.OpenPGP (
53 string2key, 54 string2key,
54 HashAlgorithm(..), 55 HashAlgorithm(..),
55 KeyAlgorithm(..), 56 KeyAlgorithm(..),
57 AEADAlgorithm(..),
56 SymmetricAlgorithm(..), 58 SymmetricAlgorithm(..),
57 CompressionAlgorithm(..), 59 CompressionAlgorithm(..),
58 RevocationCode(..), 60 RevocationCode(..),
@@ -245,6 +247,7 @@ data Packet =
245 key :: [(Char,MPI)], 247 key :: [(Char,MPI)],
246 s2k_useage :: Word8, 248 s2k_useage :: Word8,
247 s2k :: S2K, -- ^ This is meaningless if symmetric_algorithm == Unencrypted 249 s2k :: S2K, -- ^ This is meaningless if symmetric_algorithm == Unencrypted
250 aead_algorithm :: Maybe AEADAlgorithm,
248 symmetric_algorithm :: SymmetricAlgorithm, 251 symmetric_algorithm :: SymmetricAlgorithm,
249 encrypted_data :: B.ByteString, 252 encrypted_data :: B.ByteString,
250 is_subkey :: Bool 253 is_subkey :: Bool
@@ -588,13 +591,14 @@ put_packet (SecretKeyPacket { version = version, timestamp = timestamp,
588put_packet p@(PublicKeyPacket { version = v, timestamp = timestamp, 591put_packet p@(PublicKeyPacket { version = v, timestamp = timestamp,
589 key_algorithm = algorithm, key = key, 592 key_algorithm = algorithm, key = key,
590 is_subkey = is_subkey }) 593 is_subkey = is_subkey })
591 | v == 3 = 594 = case v of
595 3 ->
592 final (B.concat $ [ 596 final (B.concat $ [
593 B.singleton 3, encode timestamp, 597 B.singleton 3, encode timestamp,
594 encode v3_days, 598 encode v3_days,
595 encode algorithm 599 encode algorithm
596 ] ++ material) 600 ] ++ material)
597 | v == 4 = 601 _ ->
598 final (B.concat $ [ 602 final (B.concat $ [
599 B.singleton 4, encode timestamp, encode algorithm 603 B.singleton 4, encode timestamp, encode algorithm
600 ] ++ material) 604 ] ++ material)
@@ -757,24 +761,66 @@ parse_packet 5 = do
757 }) <- parse_packet 6 761 }) <- parse_packet 6
758 s2k_useage <- get :: Get Word8 762 s2k_useage <- get :: Get Word8
759 let k = SecretKeyPacket version timestamp algorithm key s2k_useage 763 let k = SecretKeyPacket version timestamp algorithm key s2k_useage
760 (symmetric_algorithm, s2k) <- case () of 764 optlen1 <- if version == 5
761 _ | s2k_useage `elem` [255, 254] -> (,) <$> get <*> get 765 then do
762 _ | s2k_useage > 0 -> 766 len <- get
767 return $ isolate (fromIntegral (len :: Word8))
768 else return id
769 ((symmetric_algorithm, aead, s2k),iv) <- optlen1 $ do
770 symdata <- case () of
771 _ | s2k_useage `elem` [255, 254] -> do
772 sym <- get
773 s2k <- get
774 return (sym,Nothing,s2k)
775 _ | s2k_useage == 253 -> do
776 sym <- get
777 aead <- get :: Get AEADAlgorithm
778 s2k <- get
779 return (sym,Just aead,s2k)
780 _ | s2k_useage > 0 -> do
763 -- s2k_useage is symmetric_type in this case 781 -- s2k_useage is symmetric_type in this case
764 (,) <$> localGet get (encode s2k_useage) <*> pure (SimpleS2K MD5) 782 sym <- localGet get (encode s2k_useage)
783 return $ (sym, Nothing, SimpleS2K MD5)
765 _ -> 784 _ ->
766 return (Unencrypted, S2K 100 B.empty) 785 return (Unencrypted, Nothing, S2K 100 B.empty)
786 iv <- if version==5 then getRemainingByteString
787 else return mempty -- For now, empty. It will be read later.
788 return (symdata,iv)
789 let -- We make an isolate5 utility to avoid running isolate for v4 keys.
790 -- The reason for this is because apparently, Data.Serialize.isolate
791 -- documents the unfortunate requirement:
792 -- "The action is required to consume all the bytes that it is isolated to."
793 -- This prevents using maxBound or some other large default isolation.
794 isolate5 | version == 5 = isolate
795 | otherwise = const id
796 optlen2 <- if version == 5
797 then fromIntegral <$> (get :: Get Word32)
798 else return 0 -- Value is not used.
767 if symmetric_algorithm /= Unencrypted then do { 799 if symmetric_algorithm /= Unencrypted then do {
768 encrypted <- getRemainingByteString; 800 isolate5 optlen2 $ do
769 return (k s2k symmetric_algorithm encrypted False) 801 encrypted <- getRemainingByteString;
802 return (k s2k aead symmetric_algorithm (iv <> encrypted) False)
770 } else do 803 } else do
771 skey <- foldM (\m f -> do 804 (skey,skeybs) <- isolate5 optlen2 $ case known_secret_key_fields algorithm of
772 mpi <- get :: Get MPI 805 Just fs -> do
773 return $ (f,mpi):m) [] (secret_key_fields algorithm) 806 skey <- foldM (\m f -> do
774 chk <- get 807 mpi <- get :: Get MPI
775 when (checksum (B.concat $ map (encode . snd) skey) /= chk) $ 808 return $ (f,mpi):m)
776 fail "Checksum verification failed for unencrypted secret key" 809 []
777 return ((k s2k symmetric_algorithm B.empty False) {key = key ++ skey}) 810 fs
811 return (skey, B.concat $ map (encode . snd) skey)
812 Nothing -> do
813 keybs <- getRemainingByteString
814 return ([('R', MPI $ getBigNum $ toStrictBS keybs)], keybs)
815 let (sumcnt0, csum) = checksumForKey s2k_useage
816 sumcnt = fromIntegral sumcnt0 :: Int
817 -- Note: Spec draft-ietf-openpgp-rfc4880bis-09.txt is unclear.
818 -- I think checksum should be excluded from the optlen2 count
819 -- when s2k_useage is 0 so I am excluding it.
820 chk <- getByteString (fromIntegral sumcnt)
821 when (csum skeybs /= chk) $
822 fail "Checksum verification failed for unencrypted secret key"
823 return ((k s2k aead symmetric_algorithm B.empty False) {key = key ++ skey})
778-- PublicKeyPacket, http://tools.ietf.org/html/rfc4880#section-5.5.2 824-- PublicKeyPacket, http://tools.ietf.org/html/rfc4880#section-5.5.2
779parse_packet 6 = do 825parse_packet 6 = do
780 version <- get :: Get Word8 826 version <- get :: Get Word8
@@ -1047,6 +1093,22 @@ instance BINARY_CLASS SymmetricAlgorithm where
1047 put = put . enum_to_word8 1093 put = put . enum_to_word8
1048 get = fmap enum_from_word8 get 1094 get = fmap enum_from_word8 get
1049 1095
1096data AEADAlgorithm = EAX | OCB | AEADAlgorithm Word8
1097 deriving (Show, Read, Eq, Ord)
1098
1099instance Enum AEADAlgorithm where
1100 toEnum 1 = EAX
1101 toEnum 2 = OCB
1102 toEnum x = AEADAlgorithm (fromIntegral x)
1103 fromEnum EAX = 1
1104 fromEnum OCB = 2
1105 fromEnum (AEADAlgorithm x) = fromIntegral x
1106
1107instance BINARY_CLASS AEADAlgorithm where
1108 put = put . enum_to_word8
1109 get = fmap enum_from_word8 get
1110
1111
1050data CompressionAlgorithm = Uncompressed | ZIP | ZLIB | BZip2 | CompressionAlgorithm Word8 1112data CompressionAlgorithm = Uncompressed | ZIP | ZLIB | BZip2 | CompressionAlgorithm Word8
1051 deriving (Show, Read, Eq) 1113 deriving (Show, Read, Eq)
1052 1114
@@ -1109,7 +1171,7 @@ data SignatureOver =
1109-- To get the signed-over bytes 1171-- To get the signed-over bytes
1110instance BINARY_CLASS SignatureOver where 1172instance BINARY_CLASS SignatureOver where
1111 put (DataSignature (LiteralDataPacket {content = c}) _) = 1173 put (DataSignature (LiteralDataPacket {content = c}) _) =
1112 putSomeByteString c 1174 putSomeByteString c -- TODO: This is incorrect for v5 signatures. See section 5.10.
1113 put (KeySignature k _) = mapM_ putSomeByteString (fingerprint_material k) 1175 put (KeySignature k _) = mapM_ putSomeByteString (fingerprint_material k)
1114 put (SubkeySignature k s _) = mapM_ (mapM_ putSomeByteString) 1176 put (SubkeySignature k s _) = mapM_ (mapM_ putSomeByteString)
1115 [fingerprint_material k, fingerprint_material s] 1177 [fingerprint_material k, fingerprint_material s]
diff --git a/Data/OpenPGP/Util/Gen.hs b/Data/OpenPGP/Util/Gen.hs
index 713e909..4e629cd 100644
--- a/Data/OpenPGP/Util/Gen.hs
+++ b/Data/OpenPGP/Util/Gen.hs
@@ -143,6 +143,7 @@ buildPacket alg stamp fields =
143 key = fields :: [(Char,MPI)], 143 key = fields :: [(Char,MPI)],
144 s2k_useage = 0 :: Word8, 144 s2k_useage = 0 :: Word8,
145 s2k = S2K 100 B.empty :: S2K, -- Unencrypted so meaningless 145 s2k = S2K 100 B.empty :: S2K, -- Unencrypted so meaningless
146 aead_algorithm = Nothing :: Maybe AEADAlgorithm,
146 symmetric_algorithm = Unencrypted :: SymmetricAlgorithm, 147 symmetric_algorithm = Unencrypted :: SymmetricAlgorithm,
147 encrypted_data = B.empty :: B.ByteString, 148 encrypted_data = B.empty :: B.ByteString,
148 is_subkey = True :: Bool 149 is_subkey = True :: Bool