summaryrefslogtreecommitdiff
path: root/Data
diff options
context:
space:
mode:
authorStephen Paul Weber <singpolyma@singpolyma.net>2012-04-25 17:19:07 -0500
committerStephen Paul Weber <singpolyma@singpolyma.net>2012-04-25 17:19:07 -0500
commit6b743222684f2b8151dfbdef42f0dc890e590c41 (patch)
tree77b5ae031d2b863f03399ba9a5b56ba4478d2ab2 /Data
parent7b3232778f284dd4dd3a6f3287bcbe1fbe10b010 (diff)
Split OpenPGP.Crypto out into a seperate package
Diffstat (limited to 'Data')
-rw-r--r--Data/OpenPGP/Crypto.hs183
1 files changed, 0 insertions, 183 deletions
diff --git a/Data/OpenPGP/Crypto.hs b/Data/OpenPGP/Crypto.hs
deleted file mode 100644
index 54fb81e..0000000
--- a/Data/OpenPGP/Crypto.hs
+++ /dev/null
@@ -1,183 +0,0 @@
1-- | This is a wrapper around <http://hackage.haskell.org/package/Crypto>
2-- that currently does fingerprint generation and signature verification.
3--
4-- The recommended way to import this module is:
5--
6-- > import qualified Data.OpenPGP.Crypto as OpenPGP
7module Data.OpenPGP.Crypto (sign, verify, fingerprint) where
8
9import Numeric
10import Data.Word
11import Data.Char
12import Data.List (find)
13import Data.Map ((!))
14import qualified Data.ByteString.Lazy as LZ
15import qualified Data.ByteString.Lazy.UTF8 as LZ (fromString)
16
17import Data.Binary
18import Codec.Utils (fromOctets)
19import qualified Codec.Encryption.RSA as RSA
20import qualified Data.Digest.MD5 as MD5
21import qualified Data.Digest.SHA1 as SHA1
22import qualified Data.Digest.SHA256 as SHA256
23import qualified Data.Digest.SHA384 as SHA384
24import qualified Data.Digest.SHA512 as SHA512
25
26import qualified Data.OpenPGP as OpenPGP
27
28-- | Generate a key fingerprint from a PublicKeyPacket or SecretKeyPacket
29-- <http://tools.ietf.org/html/rfc4880#section-12.2>
30fingerprint :: OpenPGP.Packet -> String
31fingerprint p
32 | OpenPGP.version p == 4 =
33 map toUpper $ (`showHex` "") $ SHA1.toInteger $ SHA1.hash $
34 LZ.unpack (LZ.concat (OpenPGP.fingerprint_material p))
35 | OpenPGP.version p `elem` [2, 3] =
36 map toUpper $ foldr (pad `oo` showHex) "" $
37 MD5.hash $ LZ.unpack (LZ.concat (OpenPGP.fingerprint_material p))
38 | otherwise = error "Unsupported Packet version or type in fingerprint"
39 where
40 oo = (.) . (.)
41 pad s | odd $ length s = '0':s
42 | otherwise = s
43
44find_key :: OpenPGP.Message -> String -> Maybe OpenPGP.Packet
45find_key (OpenPGP.Message (x@(OpenPGP.PublicKeyPacket {}):xs)) keyid =
46 find_key_ x xs keyid
47find_key (OpenPGP.Message (x@(OpenPGP.SecretKeyPacket {}):xs)) keyid =
48 find_key_ x xs keyid
49find_key (OpenPGP.Message (_:xs)) keyid =
50 find_key (OpenPGP.Message xs) keyid
51find_key _ _ = Nothing
52
53find_key_ :: OpenPGP.Packet -> [OpenPGP.Packet] -> String -> Maybe OpenPGP.Packet
54find_key_ x xs keyid
55 | thisid == keyid = Just x
56 | otherwise = find_key (OpenPGP.Message xs) keyid
57 where
58 thisid = reverse $ take (length keyid) (reverse (fingerprint x))
59
60keyfield_as_octets :: OpenPGP.Packet -> Char -> [Word8]
61keyfield_as_octets k f =
62 LZ.unpack $ LZ.drop 2 (encode (k' ! f))
63 where k' = OpenPGP.key k
64
65-- http://tools.ietf.org/html/rfc3447#page-43
66emsa_pkcs1_v1_5_hash_padding :: OpenPGP.HashAlgorithm -> [Word8]
67emsa_pkcs1_v1_5_hash_padding OpenPGP.MD5 = [0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10]
68emsa_pkcs1_v1_5_hash_padding OpenPGP.SHA1 = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14]
69emsa_pkcs1_v1_5_hash_padding OpenPGP.SHA256 = [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20]
70emsa_pkcs1_v1_5_hash_padding OpenPGP.SHA384 = [0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30]
71emsa_pkcs1_v1_5_hash_padding OpenPGP.SHA512 = [0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40]
72emsa_pkcs1_v1_5_hash_padding _ =
73 error "Unsupported HashAlgorithm in emsa_pkcs1_v1_5_hash_padding."
74
75hash :: OpenPGP.HashAlgorithm -> [Word8] -> [Word8]
76hash OpenPGP.MD5 = MD5.hash
77hash OpenPGP.SHA1 = drop 2 . LZ.unpack . encode . OpenPGP.MPI . SHA1.toInteger . SHA1.hash
78hash OpenPGP.SHA256 = SHA256.hash
79hash OpenPGP.SHA384 = SHA384.hash
80hash OpenPGP.SHA512 = SHA512.hash
81hash _ = error "Unsupported HashAlgorithm in hash."
82
83emsa_pkcs1_v1_5_encode :: [Word8] -> Int -> OpenPGP.HashAlgorithm -> [Word8]
84emsa_pkcs1_v1_5_encode m emLen algo =
85 [0, 1] ++ replicate (emLen - length t - 3) 0xff ++ [0] ++ t
86 where t = emsa_pkcs1_v1_5_hash_padding algo ++ hash algo m
87
88-- | Verify a message signature. Only supports RSA keys for now.
89verify :: OpenPGP.Message -- ^ Keys that may have made the signature
90 -> OpenPGP.Message -- ^ LiteralData message to verify
91 -> Int -- ^ Index of signature to verify (0th, 1st, etc)
92 -> Bool
93verify keys message sigidx =
94 encoded == RSA.encrypt (n, e) raw_sig
95 where
96 raw_sig = LZ.unpack $ LZ.drop 2 $ encode (OpenPGP.signature sig)
97 encoded = emsa_pkcs1_v1_5_encode signature_over
98 (length n) (OpenPGP.hash_algorithm sig)
99 signature_over = LZ.unpack $ dta `LZ.append` OpenPGP.trailer sig
100 (n, e) = (keyfield_as_octets k 'n', keyfield_as_octets k 'e')
101 Just k = find_key keys issuer
102 Just issuer = OpenPGP.signature_issuer sig
103 sig = sigs !! sigidx
104 (sigs, (OpenPGP.LiteralDataPacket {OpenPGP.content = dta}):_) =
105 OpenPGP.signatures_and_data message
106
107-- | Sign data or key/userID pair. Only supports RSA keys for now.
108sign :: OpenPGP.Message -- ^ SecretKeys, one of which will be used
109 -> OpenPGP.Message -- ^ Message containing data or key to sign, and optional signature packet
110 -> OpenPGP.HashAlgorithm -- ^ HashAlgorithm to use in signature
111 -> String -- ^ KeyID of key to choose or @[]@ for first
112 -> Integer -- ^ Timestamp for signature (unless sig supplied)
113 -> OpenPGP.Packet
114sign keys message hsh keyid timestamp =
115 -- WARNING: this style of update is unsafe on most fields
116 -- it is safe on signature and hash_head, though
117 sig {
118 OpenPGP.signature = OpenPGP.MPI $ toNum final,
119 OpenPGP.hash_head = toNum $ take 2 final
120 }
121 where
122 -- toNum has explicit param so that it can remain polymorphic
123 toNum l = fromOctets (256::Integer) l
124 final = dropWhile (==0) $ RSA.decrypt (n, d) encoded
125 encoded = emsa_pkcs1_v1_5_encode dta (length n) hsh
126 (n, d) = (keyfield_as_octets k 'n', keyfield_as_octets k 'd')
127 dta = LZ.unpack $ case signOver of {
128 OpenPGP.LiteralDataPacket {OpenPGP.content = c} -> c;
129 _ -> LZ.concat $ OpenPGP.fingerprint_material signOver ++ [
130 LZ.singleton 0xB4,
131 encode (fromIntegral (length firstUserID) :: Word32),
132 LZ.fromString firstUserID
133 ]
134 } `LZ.append` OpenPGP.trailer sig
135 sig = findSigOrDefault (find OpenPGP.isSignaturePacket m)
136
137 -- Either a SignaturePacket was found, or we need to make one
138 findSigOrDefault (Just s) = OpenPGP.signaturePacket
139 (OpenPGP.version s)
140 (OpenPGP.signature_type s)
141 OpenPGP.RSA -- force key and hash algorithm
142 hsh
143 (OpenPGP.hashed_subpackets s)
144 (OpenPGP.unhashed_subpackets s)
145 (OpenPGP.hash_head s)
146 (OpenPGP.signature s)
147 findSigOrDefault Nothing = OpenPGP.signaturePacket
148 4
149 defaultStype
150 OpenPGP.RSA
151 hsh
152 ([
153 -- Do we really need to pass in timestamp just for the default?
154 OpenPGP.SignatureCreationTimePacket $ fromIntegral timestamp,
155 OpenPGP.IssuerPacket keyid'
156 ] ++ (case signOver of
157 OpenPGP.LiteralDataPacket {} -> []
158 _ -> [] -- TODO: OpenPGP.KeyFlagsPacket [0x01, 0x02]
159 ))
160 []
161 undefined
162 undefined
163
164 keyid' = reverse $ take 16 $ reverse $ fingerprint k
165 Just k = find_key keys keyid
166
167 Just (OpenPGP.UserIDPacket firstUserID) = find isUserID m
168
169 defaultStype = case signOver of
170 OpenPGP.LiteralDataPacket {OpenPGP.format = f} ->
171 if f == 'b' then 0x00 else 0x01
172 _ -> 0x13
173
174 Just signOver = find isSignable m
175 OpenPGP.Message m = message
176
177 isSignable (OpenPGP.LiteralDataPacket {}) = True
178 isSignable (OpenPGP.PublicKeyPacket {}) = True
179 isSignable (OpenPGP.SecretKeyPacket {}) = True
180 isSignable _ = False
181
182 isUserID (OpenPGP.UserIDPacket {}) = True
183 isUserID _ = False