diff options
author | Stephen Paul Weber <singpolyma@singpolyma.net> | 2012-04-25 11:11:09 -0500 |
---|---|---|
committer | Stephen Paul Weber <singpolyma@singpolyma.net> | 2012-04-25 11:11:09 -0500 |
commit | da82b6a356e6a1571047fdea15d26ec10c869fa4 (patch) | |
tree | 84876a89db1a41a81b36ab17b85b9e1dfeda15af /Data | |
parent | 7de451e7d9761126da49cc71ef1fe6eed728ccb4 (diff) |
Make SignaturePacket opaque, emit trailer
Instead of the Put instance emitting the actual packet header, it emits
the start of the trailer data (which is the same bytes as the packet
header).
SignaturePacket is opaque and there is a smart constructor,
signaturePacket, that takes all the data *except* the trailer and
auto-generates the trailer, making sure the trailer is always valid, so
that the above becomes possible.
WARNING: SignaturePacket is not *fully* opaque. You *may* still update
fields directly using record syntax (on an already constructed packet).
This may be useful, but if any of the values that make up the trailer
are changed this will MAKE THE PACKET INVALID. This trade-off is deemed
acceptable for now, but may change in the future. Any fields that do not
affect the trailer (unhashed subpackets, etc) may be safely updated in
this way. Other fields MUST be updated by constructing a new
SignaturePacket with the smart constructor.
This usage is exemplefied by Data.OpenPGP.Crypto
The major upside of this is that it is now possible to re-emit
unmodified (or even modified, if only fields not in the trailer are
modified) SignaturePackets without invalidating the signature.
Closes #11
Diffstat (limited to 'Data')
-rw-r--r-- | Data/OpenPGP.hs | 79 | ||||
-rw-r--r-- | Data/OpenPGP/Crypto.hs | 43 |
2 files changed, 87 insertions, 35 deletions
diff --git a/Data/OpenPGP.hs b/Data/OpenPGP.hs index be50d1a..d950570 100644 --- a/Data/OpenPGP.hs +++ b/Data/OpenPGP.hs | |||
@@ -3,7 +3,47 @@ | |||
3 | -- The recommended way to import this module is: | 3 | -- The recommended way to import this module is: |
4 | -- | 4 | -- |
5 | -- > import qualified Data.OpenPGP as OpenPGP | 5 | -- > import qualified Data.OpenPGP as OpenPGP |
6 | module Data.OpenPGP (Message(..), Packet(..), SignatureSubpacket(..), HashAlgorithm(..), KeyAlgorithm(..), CompressionAlgorithm(..), MPI(..), fingerprint_material, signatures_and_data, signature_issuer, calculate_signature_trailer, decode_s2k_count, encode_s2k_count) where | 6 | module Data.OpenPGP ( |
7 | Packet(OnePassSignaturePacket, PublicKeyPacket, SecretKeyPacket, CompressedDataPacket, LiteralDataPacket, UserIDPacket, UnsupportedPacket), | ||
8 | compression_algorithm, | ||
9 | content, | ||
10 | encrypted_data, | ||
11 | filename, | ||
12 | format, | ||
13 | hash_algorithm, | ||
14 | hashed_subpackets, | ||
15 | hash_head, | ||
16 | key, | ||
17 | key_algorithm, | ||
18 | key_id, | ||
19 | message, | ||
20 | nested, | ||
21 | private_hash, | ||
22 | s2k_count, | ||
23 | s2k_hash_algorithm, | ||
24 | s2k_salt, | ||
25 | s2k_type, | ||
26 | s2k_useage, | ||
27 | signature, | ||
28 | signature_type, | ||
29 | symmetric_type, | ||
30 | timestamp, | ||
31 | trailer, | ||
32 | unhashed_subpackets, | ||
33 | version, | ||
34 | isSignaturePacket, | ||
35 | signaturePacket, | ||
36 | Message(..), | ||
37 | SignatureSubpacket(..), | ||
38 | HashAlgorithm(..), | ||
39 | KeyAlgorithm(..), | ||
40 | CompressionAlgorithm(..), | ||
41 | MPI(..), | ||
42 | fingerprint_material, | ||
43 | signatures_and_data, | ||
44 | signature_issuer, | ||
45 | decode_s2k_count, encode_s2k_count | ||
46 | ) where | ||
7 | 47 | ||
8 | import Control.Monad | 48 | import Control.Monad |
9 | import Data.Bits | 49 | import Data.Bits |
@@ -181,22 +221,16 @@ calculate_signature_trailer p = | |||
181 | 221 | ||
182 | put_packet :: (Num a) => Packet -> (LZ.ByteString, a) | 222 | put_packet :: (Num a) => Packet -> (LZ.ByteString, a) |
183 | put_packet (SignaturePacket { version = 4, | 223 | put_packet (SignaturePacket { version = 4, |
184 | signature_type = signature_type, | ||
185 | key_algorithm = key_algorithm, | ||
186 | hash_algorithm = hash_algorithm, | ||
187 | hashed_subpackets = hashed_subpackets, | ||
188 | unhashed_subpackets = unhashed_subpackets, | 224 | unhashed_subpackets = unhashed_subpackets, |
189 | hash_head = hash_head, | 225 | hash_head = hash_head, |
190 | signature = signature }) = | 226 | signature = signature, |
191 | (LZ.concat [ LZ.singleton 4, encode signature_type, | 227 | trailer = trailer }) = |
192 | encode key_algorithm, encode hash_algorithm, | 228 | (LZ.concat [ trailer_top, |
193 | encode (fromIntegral $ LZ.length hashed :: Word16), | ||
194 | hashed, | ||
195 | encode (fromIntegral $ LZ.length unhashed :: Word16), | 229 | encode (fromIntegral $ LZ.length unhashed :: Word16), |
196 | unhashed, | 230 | unhashed, |
197 | encode hash_head, encode signature ], 2) | 231 | encode hash_head, encode signature ], 2) |
198 | where | 232 | where |
199 | hashed = LZ.concat $ map encode hashed_subpackets | 233 | trailer_top = LZ.reverse $ LZ.drop 6 $ LZ.reverse trailer |
200 | unhashed = LZ.concat $ map encode unhashed_subpackets | 234 | unhashed = LZ.concat $ map encode unhashed_subpackets |
201 | put_packet (OnePassSignaturePacket { version = version, | 235 | put_packet (OnePassSignaturePacket { version = version, |
202 | signature_type = signature_type, | 236 | signature_type = signature_type, |
@@ -522,10 +556,8 @@ signatures_and_data :: Message -> ([Packet], [Packet]) | |||
522 | signatures_and_data (Message ((CompressedDataPacket {message = m}):_)) = | 556 | signatures_and_data (Message ((CompressedDataPacket {message = m}):_)) = |
523 | signatures_and_data m | 557 | signatures_and_data m |
524 | signatures_and_data (Message lst) = | 558 | signatures_and_data (Message lst) = |
525 | (filter isSig lst, filter isDta lst) | 559 | (filter isSignaturePacket lst, filter isDta lst) |
526 | where | 560 | where |
527 | isSig (SignaturePacket {}) = True | ||
528 | isSig _ = False | ||
529 | isDta (LiteralDataPacket {}) = True | 561 | isDta (LiteralDataPacket {}) = True |
530 | isDta _ = False | 562 | isDta _ = False |
531 | 563 | ||
@@ -631,3 +663,22 @@ encode_s2k_count iterations | |||
631 | encode_s2k_count' count c | 663 | encode_s2k_count' count c |
632 | | count < 32 = (count, c) | 664 | | count < 32 = (count, c) |
633 | | otherwise = encode_s2k_count' (count `shiftR` 1) (c+1) | 665 | | otherwise = encode_s2k_count' (count `shiftR` 1) (c+1) |
666 | |||
667 | -- SignaturePacket smart constructor | ||
668 | signaturePacket :: Word8 -> Word8 -> KeyAlgorithm -> HashAlgorithm -> [SignatureSubpacket] -> [SignatureSubpacket] -> Word16 -> MPI -> Packet | ||
669 | signaturePacket version signature_type key_algorithm hash_algorithm hashed_subpackets unhashed_subpackets hash_head signature = | ||
670 | let p = SignaturePacket { | ||
671 | version = version, | ||
672 | signature_type = signature_type, | ||
673 | key_algorithm = key_algorithm, | ||
674 | hash_algorithm = hash_algorithm, | ||
675 | hashed_subpackets = hashed_subpackets, | ||
676 | unhashed_subpackets = unhashed_subpackets, | ||
677 | hash_head = hash_head, | ||
678 | signature = signature, | ||
679 | trailer = undefined | ||
680 | } in p { trailer = calculate_signature_trailer p } | ||
681 | |||
682 | isSignaturePacket :: Packet -> Bool | ||
683 | isSignaturePacket (SignaturePacket {}) = True | ||
684 | isSignaturePacket _ = False | ||
diff --git a/Data/OpenPGP/Crypto.hs b/Data/OpenPGP/Crypto.hs index b34c395..173fe08 100644 --- a/Data/OpenPGP/Crypto.hs +++ b/Data/OpenPGP/Crypto.hs | |||
@@ -106,6 +106,8 @@ sign :: OpenPGP.Message -- ^ SecretKeys, one of which will be used | |||
106 | -> Integer -- ^ Timestamp for signature (unless sig supplied) | 106 | -> Integer -- ^ Timestamp for signature (unless sig supplied) |
107 | -> OpenPGP.Packet | 107 | -> OpenPGP.Packet |
108 | sign keys message hsh keyid timestamp = | 108 | sign keys message hsh keyid timestamp = |
109 | -- WARNING: this style of update is unsafe on most fields | ||
110 | -- it is safe on signature and hash_head, though | ||
109 | sig { | 111 | sig { |
110 | OpenPGP.signature = OpenPGP.MPI $ toNum final, | 112 | OpenPGP.signature = OpenPGP.MPI $ toNum final, |
111 | OpenPGP.hash_head = toNum $ take 2 final | 113 | OpenPGP.hash_head = toNum $ take 2 final |
@@ -124,32 +126,34 @@ sign keys message hsh keyid timestamp = | |||
124 | LZ.fromString firstUserID | 126 | LZ.fromString firstUserID |
125 | ] | 127 | ] |
126 | } `LZ.append` OpenPGP.trailer sig | 128 | } `LZ.append` OpenPGP.trailer sig |
127 | -- Always force key and hash algorithm | 129 | sig = (findSigOrDefault (find OpenPGP.isSignaturePacket m)) |
128 | sig = let s = (findSigOrDefault (find isSignature m)) { | ||
129 | OpenPGP.key_algorithm = OpenPGP.RSA, | ||
130 | OpenPGP.hash_algorithm = hsh | ||
131 | } in s { OpenPGP.trailer = OpenPGP.calculate_signature_trailer s } | ||
132 | 130 | ||
133 | -- Either a SignaturePacket was found, or we need to make one | 131 | -- Either a SignaturePacket was found, or we need to make one |
134 | findSigOrDefault (Just s) = s | 132 | findSigOrDefault (Just s) = OpenPGP.signaturePacket |
135 | findSigOrDefault Nothing = OpenPGP.SignaturePacket { | 133 | (OpenPGP.version s) |
136 | OpenPGP.version = 4, | 134 | (OpenPGP.signature_type s) |
137 | OpenPGP.key_algorithm = undefined, | 135 | OpenPGP.RSA -- force key and hash algorithm |
138 | OpenPGP.hash_algorithm = undefined, | 136 | hsh |
139 | OpenPGP.signature_type = defaultStype, | 137 | (OpenPGP.hashed_subpackets s) |
140 | OpenPGP.hashed_subpackets = [ | 138 | (OpenPGP.unhashed_subpackets s) |
139 | (OpenPGP.hash_head s) | ||
140 | (OpenPGP.signature s) | ||
141 | findSigOrDefault Nothing = OpenPGP.signaturePacket | ||
142 | 4 | ||
143 | defaultStype | ||
144 | OpenPGP.RSA | ||
145 | hsh | ||
146 | ([ | ||
141 | -- Do we really need to pass in timestamp just for the default? | 147 | -- Do we really need to pass in timestamp just for the default? |
142 | OpenPGP.SignatureCreationTimePacket $ fromIntegral timestamp, | 148 | OpenPGP.SignatureCreationTimePacket $ fromIntegral timestamp, |
143 | OpenPGP.IssuerPacket keyid' | 149 | OpenPGP.IssuerPacket keyid' |
144 | ] ++ (case signOver of | 150 | ] ++ (case signOver of |
145 | OpenPGP.LiteralDataPacket {} -> [] | 151 | OpenPGP.LiteralDataPacket {} -> [] |
146 | _ -> [] -- TODO: OpenPGP.KeyFlagsPacket [0x01, 0x02] | 152 | _ -> [] -- TODO: OpenPGP.KeyFlagsPacket [0x01, 0x02] |
147 | ), | 153 | )) |
148 | OpenPGP.unhashed_subpackets = [], | 154 | [] |
149 | OpenPGP.signature = undefined, | 155 | undefined |
150 | OpenPGP.trailer = undefined, | 156 | undefined |
151 | OpenPGP.hash_head = undefined | ||
152 | } | ||
153 | 157 | ||
154 | keyid' = reverse $ take 16 $ reverse $ fingerprint k | 158 | keyid' = reverse $ take 16 $ reverse $ fingerprint k |
155 | Just k = find_key keys keyid | 159 | Just k = find_key keys keyid |
@@ -169,8 +173,5 @@ sign keys message hsh keyid timestamp = | |||
169 | isSignable (OpenPGP.SecretKeyPacket {}) = True | 173 | isSignable (OpenPGP.SecretKeyPacket {}) = True |
170 | isSignable _ = False | 174 | isSignable _ = False |
171 | 175 | ||
172 | isSignature (OpenPGP.SignaturePacket {}) = True | ||
173 | isSignature _ = False | ||
174 | |||
175 | isUserID (OpenPGP.UserIDPacket {}) = True | 176 | isUserID (OpenPGP.UserIDPacket {}) = True |
176 | isUserID _ = False | 177 | isUserID _ = False |