summaryrefslogtreecommitdiff
path: root/Data
diff options
context:
space:
mode:
authorStephen Paul Weber <singpolyma@singpolyma.net>2012-04-25 11:11:09 -0500
committerStephen Paul Weber <singpolyma@singpolyma.net>2012-04-25 11:11:09 -0500
commitda82b6a356e6a1571047fdea15d26ec10c869fa4 (patch)
tree84876a89db1a41a81b36ab17b85b9e1dfeda15af /Data
parent7de451e7d9761126da49cc71ef1fe6eed728ccb4 (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.hs79
-rw-r--r--Data/OpenPGP/Crypto.hs43
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
6module 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 6module 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
8import Control.Monad 48import Control.Monad
9import Data.Bits 49import Data.Bits
@@ -181,22 +221,16 @@ calculate_signature_trailer p =
181 221
182put_packet :: (Num a) => Packet -> (LZ.ByteString, a) 222put_packet :: (Num a) => Packet -> (LZ.ByteString, a)
183put_packet (SignaturePacket { version = 4, 223put_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
201put_packet (OnePassSignaturePacket { version = version, 235put_packet (OnePassSignaturePacket { version = version,
202 signature_type = signature_type, 236 signature_type = signature_type,
@@ -522,10 +556,8 @@ signatures_and_data :: Message -> ([Packet], [Packet])
522signatures_and_data (Message ((CompressedDataPacket {message = m}):_)) = 556signatures_and_data (Message ((CompressedDataPacket {message = m}):_)) =
523 signatures_and_data m 557 signatures_and_data m
524signatures_and_data (Message lst) = 558signatures_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
668signaturePacket :: Word8 -> Word8 -> KeyAlgorithm -> HashAlgorithm -> [SignatureSubpacket] -> [SignatureSubpacket] -> Word16 -> MPI -> Packet
669signaturePacket 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
682isSignaturePacket :: Packet -> Bool
683isSignaturePacket (SignaturePacket {}) = True
684isSignaturePacket _ = 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
108sign keys message hsh keyid timestamp = 108sign 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