diff options
-rw-r--r-- | Arbitrary.patch | 8 | ||||
-rw-r--r-- | Data/OpenPGP.hs | 86 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | tests/suite.hs | 5 |
4 files changed, 57 insertions, 44 deletions
diff --git a/Arbitrary.patch b/Arbitrary.patch index 641609f..2d14399 100644 --- a/Arbitrary.patch +++ b/Arbitrary.patch | |||
@@ -24,6 +24,14 @@ | |||
24 | 2 -> do x1 <- arbitrary | 24 | 2 -> do x1 <- arbitrary |
25 | x2 <- arbitrary | 25 | x2 <- arbitrary |
26 | x3 <- arbitrary | 26 | x3 <- arbitrary |
27 | @@ -88,5 +93,5 @@ | ||
28 | x2 <- arbitrary | ||
29 | - x3 <- arbitrary | ||
30 | + x3 <- fmap decode_s2k_count arbitrary | ||
31 | return (IteratedSaltedS2K x1 x2 x3) | ||
32 | - 3 -> do x1 <- arbitrary | ||
33 | + 3 -> do x1 <- suchThat arbitrary (`notElem` [0,1,3]) | ||
34 | x2 <- arbitrary | ||
27 | @@ -73,7 +72,7 @@ | 35 | @@ -73,7 +72,7 @@ |
28 | 4 -> return SHA384 | 36 | 4 -> return SHA384 |
29 | 5 -> return SHA512 | 37 | 5 -> return SHA512 |
diff --git a/Data/OpenPGP.hs b/Data/OpenPGP.hs index 4dad404..5e62d1a 100644 --- a/Data/OpenPGP.hs +++ b/Data/OpenPGP.hs | |||
@@ -34,14 +34,11 @@ module Data.OpenPGP ( | |||
34 | message, | 34 | message, |
35 | nested, | 35 | nested, |
36 | private_hash, | 36 | private_hash, |
37 | s2k_count, | ||
38 | s2k_hash_algorithm, | ||
39 | s2k_salt, | ||
40 | s2k_type, | ||
41 | s2k_useage, | 37 | s2k_useage, |
38 | s2k, | ||
42 | signature, | 39 | signature, |
43 | signature_type, | 40 | signature_type, |
44 | symmetric_type, | 41 | symmetric_algorithm, |
45 | timestamp, | 42 | timestamp, |
46 | trailer, | 43 | trailer, |
47 | unhashed_subpackets, | 44 | unhashed_subpackets, |
@@ -51,6 +48,7 @@ module Data.OpenPGP ( | |||
51 | signaturePacket, | 48 | signaturePacket, |
52 | Message(..), | 49 | Message(..), |
53 | SignatureSubpacket(..), | 50 | SignatureSubpacket(..), |
51 | S2K(..), | ||
54 | HashAlgorithm(..), | 52 | HashAlgorithm(..), |
55 | KeyAlgorithm(..), | 53 | KeyAlgorithm(..), |
56 | SymmetricAlgorithm(..), | 54 | SymmetricAlgorithm(..), |
@@ -211,12 +209,9 @@ data Packet = | |||
211 | timestamp::Word32, | 209 | timestamp::Word32, |
212 | key_algorithm::KeyAlgorithm, | 210 | key_algorithm::KeyAlgorithm, |
213 | key::[(Char,MPI)], | 211 | key::[(Char,MPI)], |
214 | s2k_useage::Word8, -- ^ determines if the 'Maybe's are 'Just' or 'Nothing' | 212 | s2k_useage::Word8, |
215 | symmetric_type::Maybe Word8, | 213 | s2k::Maybe S2K, |
216 | s2k_type::Maybe Word8, | 214 | symmetric_algorithm::SymmetricAlgorithm, |
217 | s2k_hash_algorithm::Maybe HashAlgorithm, | ||
218 | s2k_salt::Maybe Word64, | ||
219 | s2k_count::Maybe Word32, | ||
220 | encrypted_data::B.ByteString, | 215 | encrypted_data::B.ByteString, |
221 | private_hash::Maybe B.ByteString, -- ^ the hash may be in the encrypted data | 216 | private_hash::Maybe B.ByteString, -- ^ the hash may be in the encrypted data |
222 | is_subkey::Bool | 217 | is_subkey::Bool |
@@ -436,23 +431,16 @@ put_packet (OnePassSignaturePacket { version = version, | |||
436 | ], 4) | 431 | ], 4) |
437 | put_packet (SecretKeyPacket { version = version, timestamp = timestamp, | 432 | put_packet (SecretKeyPacket { version = version, timestamp = timestamp, |
438 | key_algorithm = algorithm, key = key, | 433 | key_algorithm = algorithm, key = key, |
439 | s2k_useage = s2k_useage, | 434 | s2k_useage = s2k_useage, s2k = s2k, |
440 | symmetric_type = symmetric_type, | 435 | symmetric_algorithm = symmetric_algorithm, |
441 | s2k_type = s2k_type, | ||
442 | s2k_hash_algorithm = s2k_hash_algo, | ||
443 | s2k_salt = s2k_salt, | ||
444 | s2k_count = s2k_count, | ||
445 | encrypted_data = encrypted_data, | 436 | encrypted_data = encrypted_data, |
446 | is_subkey = is_subkey }) = | 437 | is_subkey = is_subkey }) = |
447 | (B.concat $ [p, encode s2k_useage] ++ | 438 | (B.concat $ p : |
448 | (if s2k_useage `elem` [255, 254] then | 439 | (case s2k of |
449 | [encode $ fromJust symmetric_type, encode s2k_t, | 440 | Just s2k -> [encode s2k_useage, encode symmetric_algorithm, encode s2k] |
450 | encode $ fromJust s2k_hash_algo] ++ | 441 | Nothing -> [encode symmetric_algorithm] |
451 | (if s2k_t `elem` [1,3] then [encode $ fromJust s2k_salt] else []) ++ | 442 | ) ++ |
452 | if s2k_t == 3 then | 443 | (if symmetric_algorithm /= Unencrypted then |
453 | [encode $ encode_s2k_count $ fromJust s2k_count] else [] | ||
454 | else []) ++ | ||
455 | (if s2k_useage > 0 then | ||
456 | [encrypted_data] | 444 | [encrypted_data] |
457 | else s ++ | 445 | else s ++ |
458 | -- XXX: Checksum is part of encrypted_data for V4 ONLY | 446 | -- XXX: Checksum is part of encrypted_data for V4 ONLY |
@@ -464,7 +452,6 @@ put_packet (SecretKeyPacket { version = version, timestamp = timestamp, | |||
464 | (0::Integer) (B.concat s) :: Word16)]), | 452 | (0::Integer) (B.concat s) :: Word16)]), |
465 | if is_subkey then 7 else 5) | 453 | if is_subkey then 7 else 5) |
466 | where | 454 | where |
467 | (Just s2k_t) = s2k_type | ||
468 | p = fst (put_packet $ | 455 | p = fst (put_packet $ |
469 | PublicKeyPacket version timestamp algorithm key False Nothing) | 456 | PublicKeyPacket version timestamp algorithm key False Nothing) |
470 | s = map (encode . (key !)) (secret_key_fields algorithm) | 457 | s = map (encode . (key !)) (secret_key_fields algorithm) |
@@ -592,31 +579,22 @@ parse_packet 5 = do | |||
592 | }) <- parse_packet 6 | 579 | }) <- parse_packet 6 |
593 | s2k_useage <- get :: Get Word8 | 580 | s2k_useage <- get :: Get Word8 |
594 | let k = SecretKeyPacket version timestamp algorithm key s2k_useage | 581 | let k = SecretKeyPacket version timestamp algorithm key s2k_useage |
595 | k' <- case s2k_useage of | 582 | (symmetric_algorithm, s2k) <- case () of |
596 | _ | s2k_useage `elem` [255, 254] -> do | 583 | _ | s2k_useage `elem` [255, 254] -> (,) <$> get <*> fmap Just get |
597 | symmetric_type <- get | ||
598 | s2k_type <- get | ||
599 | s2k_hash_algorithm <- get | ||
600 | s2k_salt <- if s2k_type `elem` [1, 3] then get | ||
601 | else return undefined | ||
602 | s2k_count <- if s2k_type == 3 then fmap decode_s2k_count get else | ||
603 | return undefined | ||
604 | return (k (Just symmetric_type) (Just s2k_type) | ||
605 | (Just s2k_hash_algorithm) (Just s2k_salt) (Just s2k_count)) | ||
606 | _ | s2k_useage > 0 -> | 584 | _ | s2k_useage > 0 -> |
607 | -- s2k_useage is symmetric_type in this case | 585 | -- s2k_useage is symmetric_type in this case |
608 | return (k (Just s2k_useage) Nothing Nothing Nothing Nothing) | 586 | return (decode $ encode s2k_useage, Just $ SimpleS2K MD5) |
609 | _ -> | 587 | _ -> |
610 | return (k Nothing Nothing Nothing Nothing Nothing) | 588 | return (Unencrypted, Nothing) |
611 | if s2k_useage > 0 then do { | 589 | if symmetric_algorithm /= Unencrypted then do { |
612 | encrypted <- getRemainingByteString; | 590 | encrypted <- getRemainingByteString; |
613 | return (k' encrypted Nothing False) | 591 | return (k s2k symmetric_algorithm encrypted Nothing False) |
614 | } else do | 592 | } else do |
615 | key <- foldM (\m f -> do | 593 | key <- foldM (\m f -> do |
616 | mpi <- get :: Get MPI | 594 | mpi <- get :: Get MPI |
617 | return $ (f,mpi):m) key (secret_key_fields algorithm) | 595 | return $ (f,mpi):m) key (secret_key_fields algorithm) |
618 | private_hash <- getRemainingByteString | 596 | private_hash <- getRemainingByteString |
619 | return ((k' B.empty (Just private_hash) False) {key = key}) | 597 | return ((k s2k symmetric_algorithm B.empty (Just private_hash) False) {key = key}) |
620 | -- PublicKeyPacket, http://tools.ietf.org/html/rfc4880#section-5.5.2 | 598 | -- PublicKeyPacket, http://tools.ietf.org/html/rfc4880#section-5.5.2 |
621 | parse_packet 6 = do | 599 | parse_packet 6 = do |
622 | version <- get :: Get Word8 | 600 | version <- get :: Get Word8 |
@@ -718,6 +696,28 @@ enum_to_word8 = fromIntegral . fromEnum | |||
718 | enum_from_word8 :: (Enum a) => Word8 -> a | 696 | enum_from_word8 :: (Enum a) => Word8 -> a |
719 | enum_from_word8 = toEnum . fromIntegral | 697 | enum_from_word8 = toEnum . fromIntegral |
720 | 698 | ||
699 | data S2K = | ||
700 | SimpleS2K HashAlgorithm | | ||
701 | SaltedS2K HashAlgorithm Word64 | | ||
702 | IteratedSaltedS2K HashAlgorithm Word64 Word32 | | ||
703 | S2K Word8 B.ByteString | ||
704 | deriving (Show, Read, Eq) | ||
705 | |||
706 | instance BINARY_CLASS S2K where | ||
707 | put (SimpleS2K halgo) = put (0::Word8) >> put halgo | ||
708 | put (SaltedS2K halgo salt) = put (1::Word8) >> put halgo >> put salt | ||
709 | put (IteratedSaltedS2K halgo salt count) = put (3::Word8) >> put halgo | ||
710 | >> put salt >> put (encode_s2k_count count) | ||
711 | put (S2K t body) = put t >> putSomeByteString body | ||
712 | |||
713 | get = do | ||
714 | t <- get :: Get Word8 | ||
715 | case t of | ||
716 | 0 -> SimpleS2K <$> get | ||
717 | 1 -> SaltedS2K <$> get <*> get | ||
718 | 3 -> IteratedSaltedS2K <$> get <*> get <*> (decode_s2k_count <$> get) | ||
719 | _ -> S2K t <$> getRemainingByteString | ||
720 | |||
721 | data HashAlgorithm = MD5 | SHA1 | RIPEMD160 | SHA256 | SHA384 | SHA512 | SHA224 | HashAlgorithm Word8 | 721 | data HashAlgorithm = MD5 | SHA1 | RIPEMD160 | SHA256 | SHA384 | SHA512 | SHA224 | HashAlgorithm Word8 |
722 | deriving (Show, Read, Eq) | 722 | deriving (Show, Read, Eq) |
723 | 723 | ||
@@ -23,7 +23,7 @@ tests/suite: tests/suite.hs Data/OpenPGP.hs Data/OpenPGP/Internal.hs Data/OpenPG | |||
23 | ghc --make $(GHCFLAGS) -o $@ $^ | 23 | ghc --make $(GHCFLAGS) -o $@ $^ |
24 | 24 | ||
25 | Data/OpenPGP/Arbitrary.hs: Data/OpenPGP.hs Arbitrary.patch | 25 | Data/OpenPGP/Arbitrary.hs: Data/OpenPGP.hs Arbitrary.patch |
26 | derive -d Arbitrary -m Data.OpenPGP.Arbitrary -iData.OpenPGP -iTest.QuickCheck -iTest.QuickCheck.Instances -iNumeric -iData.Char -iData.Word -o $@ Data/OpenPGP.hs | 26 | derive -d Arbitrary -m Data.OpenPGP.Arbitrary -iData.OpenPGP -iData.OpenPGP.Internal -iTest.QuickCheck -iTest.QuickCheck.Instances -iNumeric -iData.Char -iData.Word -o $@ Data/OpenPGP.hs |
27 | patch $@ Arbitrary.patch | 27 | patch $@ Arbitrary.patch |
28 | 28 | ||
29 | report.html: tests/suite.hs Data/OpenPGP.hs Data/OpenPGP/Internal.hs | 29 | report.html: tests/suite.hs Data/OpenPGP.hs Data/OpenPGP/Internal.hs |
diff --git a/tests/suite.hs b/tests/suite.hs index 3094214..feb5fe6 100644 --- a/tests/suite.hs +++ b/tests/suite.hs | |||
@@ -44,6 +44,10 @@ prop_MPI_serialization_loop :: OpenPGP.MPI -> Bool | |||
44 | prop_MPI_serialization_loop mpi = | 44 | prop_MPI_serialization_loop mpi = |
45 | mpi == decode' (encode mpi) | 45 | mpi == decode' (encode mpi) |
46 | 46 | ||
47 | prop_S2K_serialization_loop :: OpenPGP.S2K -> Bool | ||
48 | prop_S2K_serialization_loop s2k = | ||
49 | s2k == decode' (encode s2k) | ||
50 | |||
47 | prop_SignatureSubpacket_serialization_loop :: OpenPGP.SignatureSubpacket -> Bool | 51 | prop_SignatureSubpacket_serialization_loop :: OpenPGP.SignatureSubpacket -> Bool |
48 | prop_SignatureSubpacket_serialization_loop packet = | 52 | prop_SignatureSubpacket_serialization_loop packet = |
49 | packet == decode' (encode packet) | 53 | packet == decode' (encode packet) |
@@ -141,6 +145,7 @@ tests = | |||
141 | testCase "uncompressed-ops-dsa-sha384.txt.gpg" (testSerialization "uncompressed-ops-dsa-sha384.txt.gpg"), | 145 | testCase "uncompressed-ops-dsa-sha384.txt.gpg" (testSerialization "uncompressed-ops-dsa-sha384.txt.gpg"), |
142 | testCase "uncompressed-ops-rsa.gpg" (testSerialization "uncompressed-ops-rsa.gpg"), | 146 | testCase "uncompressed-ops-rsa.gpg" (testSerialization "uncompressed-ops-rsa.gpg"), |
143 | testProperty "MPI encode/decode" prop_MPI_serialization_loop, | 147 | testProperty "MPI encode/decode" prop_MPI_serialization_loop, |
148 | testProperty "S2K encode/decode" prop_S2K_serialization_loop, | ||
144 | testProperty "SignatureSubpacket encode/decode" prop_SignatureSubpacket_serialization_loop | 149 | testProperty "SignatureSubpacket encode/decode" prop_SignatureSubpacket_serialization_loop |
145 | ], | 150 | ], |
146 | testGroup "S2K count" [ | 151 | testGroup "S2K count" [ |