summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2013-12-14 22:34:26 -0500
committerjoe <joe@jerkface.net>2013-12-14 22:34:26 -0500
commitce0d32ef83ccf15198bdd5248faa02abbcf2f769 (patch)
tree96155973d7af58bbb79a8f67a21f646af46cb47f
New package openpgp-util as alternative to OpenPGP-CryptoAPI.
-rw-r--r--Data/OpenPGP/Util.hs12
-rw-r--r--Data/OpenPGP/Util/DecryptSecretKey.hs134
-rw-r--r--Data/OpenPGP/Util/Fingerprint.hs33
-rw-r--r--Data/OpenPGP/Util/Sign.hs213
-rw-r--r--Data/OpenPGP/Util/Verify.hs115
5 files changed, 507 insertions, 0 deletions
diff --git a/Data/OpenPGP/Util.hs b/Data/OpenPGP/Util.hs
new file mode 100644
index 0000000..c675ec2
--- /dev/null
+++ b/Data/OpenPGP/Util.hs
@@ -0,0 +1,12 @@
1module Data.OpenPGP.Util
2 ( fingerprint
3 , decryptSecretKey
4 , verify
5 , pgpSign
6 ) where
7
8import Data.OpenPGP.Util.Fingerprint
9import Data.OpenPGP.Util.Sign
10import Data.OpenPGP.Util.Verify
11import Data.OpenPGP.Util.DecryptSecretKey
12
diff --git a/Data/OpenPGP/Util/DecryptSecretKey.hs b/Data/OpenPGP/Util/DecryptSecretKey.hs
new file mode 100644
index 0000000..0ba89d2
--- /dev/null
+++ b/Data/OpenPGP/Util/DecryptSecretKey.hs
@@ -0,0 +1,134 @@
1module Data.OpenPGP.Util.DecryptSecretKey where
2
3import qualified Data.OpenPGP as OpenPGP
4import qualified Data.ByteString as BS
5import qualified Data.ByteString.Lazy as LZ
6import Data.Word (Word16,Word8)
7import Data.Char (toUpper)
8import Control.Monad (foldM)
9import Numeric (showHex)
10import Data.Binary (get,Binary,Get)
11import Data.Binary.Get (runGetOrFail)
12import qualified Data.Serialize as Serialize
13import Control.Applicative ( (<$>) )
14
15import Crypto.Hash.MD5 as MD5
16import Crypto.Hash.SHA1 as SHA1
17import Crypto.Hash.SHA256 as SHA256
18import Crypto.Hash.SHA384 as SHA384
19import Crypto.Hash.SHA512 as SHA512
20import Crypto.Hash.SHA224 as SHA224
21import Crypto.Hash.RIPEMD160 as RIPEMD160
22
23import qualified Crypto.Cipher.AES as Vincent
24import qualified Crypto.Cipher.Blowfish as Vincent
25
26import qualified Crypto.Cipher.Types as Vincent
27import qualified Data.Byteable as Vincent
28
29import Crypto.Cipher.Cast5 (CAST5_128)
30import Crypto.Cipher.ThomasToVincent
31
32
33hashBySymbol OpenPGP.MD5 = MD5.hashlazy
34hashBySymbol OpenPGP.SHA1 = SHA1.hashlazy
35hashBySymbol OpenPGP.SHA256 = SHA256.hashlazy
36hashBySymbol OpenPGP.SHA384 = SHA384.hashlazy
37hashBySymbol OpenPGP.SHA512 = SHA512.hashlazy
38hashBySymbol OpenPGP.SHA224 = SHA224.hashlazy
39hashBySymbol OpenPGP.RIPEMD160 = RIPEMD160.hashlazy
40
41
42
43data Enciphered =
44 EncipheredWithIV !LZ.ByteString -- initial vector is appended to front of ByteString
45 | EncipheredZeroIV !LZ.ByteString -- initial vector is zero, ByteString contains only the block
46
47withIV :: (Vincent.BlockCipher k) => (Vincent.IV k -> LZ.ByteString -> LZ.ByteString) -> Enciphered -> LZ.ByteString
48withIV f (EncipheredWithIV s) = f iv bs
49 where
50 Just iv = Vincent.makeIV (toStrictBS ivbs)
51 (ivbs,bs) = LZ.splitAt (fromIntegral ivlen) s
52 ivlen = Vincent.byteableLength z
53 z = Vincent.nullIV
54 _ = Vincent.constEqBytes z iv
55withIV f (EncipheredZeroIV s) = f Vincent.nullIV s
56
57decryptSecretKey ::
58 BS.ByteString -- ^ Passphrase
59 -> OpenPGP.Packet -- ^ Encrypted SecretKeyPacket
60 -> Maybe OpenPGP.Packet -- ^ Decrypted SecretKeyPacket
61decryptSecretKey pass k@(OpenPGP.SecretKeyPacket {
62 OpenPGP.version = 4, OpenPGP.key_algorithm = kalgo,
63 OpenPGP.s2k = s2k, OpenPGP.symmetric_algorithm = salgo,
64 OpenPGP.key = existing, OpenPGP.encrypted_data = encd
65 }) | chkF material == toStrictBS chk =
66 fmap (\m -> k {
67 OpenPGP.s2k_useage = 0,
68 OpenPGP.symmetric_algorithm = OpenPGP.Unencrypted,
69 OpenPGP.encrypted_data = LZ.empty,
70 OpenPGP.key = m
71 }) parseMaterial
72 | otherwise = Nothing
73 where
74 parseMaterial = maybeGet
75 (foldM (\m f -> do {mpi <- get; return $ (f,mpi):m}) existing
76 (OpenPGP.secret_key_fields kalgo)) material
77 (material, chk) = LZ.splitAt (LZ.length decd - chkSize) decd
78 (chkSize, chkF)
79 | OpenPGP.s2k_useage k == 254 = (20, SHA1.hash . toStrictBS)
80 | otherwise = (2, Serialize.encode . checksum . toStrictBS)
81 decd = string2sdecrypt salgo s2k (toLazyBS pass) (EncipheredWithIV encd)
82
83 checksum :: BS.ByteString -> Word16
84 checksum key = fromIntegral $
85 BS.foldl' (\x y -> x + fromIntegral y) (0::Integer) key `mod` 65536
86
87 maybeGet :: (Binary a) => Get a -> LZ.ByteString -> Maybe a
88 maybeGet g bs = (\(_,_,x) -> x) <$> hush (runGetOrFail g bs)
89
90 hush :: Either a b -> Maybe b
91 hush (Left _) = Nothing
92 hush (Right x) = Just x
93
94
95decryptSecretKey _ _ = Nothing
96
97toStrictBS :: LZ.ByteString -> BS.ByteString
98toStrictBS = BS.concat . LZ.toChunks
99
100toLazyBS :: BS.ByteString -> LZ.ByteString
101toLazyBS = LZ.fromChunks . (:[])
102
103
104
105string2sdecrypt :: OpenPGP.SymmetricAlgorithm -> OpenPGP.S2K -> LZ.ByteString -> Enciphered -> LZ.ByteString
106string2sdecrypt OpenPGP.AES128 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES128)
107string2sdecrypt OpenPGP.AES192 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES192)
108string2sdecrypt OpenPGP.AES256 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES256)
109string2sdecrypt OpenPGP.Blowfish s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.Blowfish128)
110string2sdecrypt OpenPGP.CAST5 s2k s = withIV $ simpleUnCFB (string2key s2k s :: ThomasToVincent CAST5_128)
111string2sdecrypt algo _ _ = error $ "Unsupported symmetric algorithm : " ++ show algo ++ " in Data.OpenPGP.CryptoAPI.string2sdecrypt"
112
113simpleUnCFB :: (Vincent.BlockCipher k) => k -> Vincent.IV k -> LZ.ByteString -> LZ.ByteString
114simpleUnCFB k iv = padThenUnpad k (toLazyBS . Vincent.cfbDecrypt k iv . toStrictBS)
115 where
116 padThenUnpad :: (Vincent.BlockCipher k) => k -> (LZ.ByteString -> LZ.ByteString) -> LZ.ByteString -> LZ.ByteString
117 padThenUnpad k f s = dropPadEnd (f padded)
118 where
119 dropPadEnd s = LZ.take (LZ.length s - padAmount) s
120 padded = s `LZ.append` LZ.replicate padAmount 0
121 padAmount = blksize - (LZ.length s `mod` blksize)
122 blksize = fromIntegral $ Vincent.blockSize k
123
124string2key :: (Vincent.BlockCipher k) => OpenPGP.S2K -> LZ.ByteString -> k
125string2key s2k s = cipher
126 where
127 cipher = Vincent.cipherInit k
128 Right k = Vincent.makeKey $ toStrictBS $
129 LZ.take ksize $ OpenPGP.string2key hashBySymbol s2k s
130 ksize = case Vincent.cipherKeySize cipher of
131 Vincent.KeySizeFixed n -> fromIntegral n
132 Vincent.KeySizeEnum xs -> error $ "Unknown key size in string2key"
133 Vincent.KeySizeRange min max -> error $ "Unknown key size range in string2key"
134
diff --git a/Data/OpenPGP/Util/Fingerprint.hs b/Data/OpenPGP/Util/Fingerprint.hs
new file mode 100644
index 0000000..538688b
--- /dev/null
+++ b/Data/OpenPGP/Util/Fingerprint.hs
@@ -0,0 +1,33 @@
1module Data.OpenPGP.Util.Fingerprint (fingerprint) where
2
3import qualified Data.OpenPGP as OpenPGP
4import qualified Data.ByteString as BS
5import qualified Data.ByteString.Lazy as LZ
6import Data.Char (toUpper)
7import Data.Word (Word8)
8import Numeric (showHex)
9
10import Crypto.Hash.MD5 as MD5
11import Crypto.Hash.SHA1 as SHA1
12
13-- | Generate a key fingerprint from a PublicKeyPacket or SecretKeyPacket
14-- <http://tools.ietf.org/html/rfc4880#section-12.2>
15fingerprint :: OpenPGP.Packet -> String
16fingerprint p
17 | OpenPGP.version p == 4 = hexify $ SHA1.hashlazy material
18 | OpenPGP.version p `elem` [2, 3] = hexify $ MD5.hashlazy material
19 | otherwise = error "Unsupported Packet version or type in fingerprint"
20 where
21 material = LZ.concat $ OpenPGP.fingerprint_material p
22
23 hexify = map toUpper . hexString . BS.unpack
24
25 hexString :: [Word8] -> String
26 hexString = foldr (pad `oo` showHex) ""
27 where
28 pad s | odd $ length s = '0':s
29 | otherwise = s
30
31 oo :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
32 oo = (.) . (.)
33
diff --git a/Data/OpenPGP/Util/Sign.hs b/Data/OpenPGP/Util/Sign.hs
new file mode 100644
index 0000000..ef7d16b
--- /dev/null
+++ b/Data/OpenPGP/Util/Sign.hs
@@ -0,0 +1,213 @@
1{-# LANGUAGE ScopedTypeVariables #-}
2module Data.OpenPGP.Util.Sign where
3
4import qualified Data.OpenPGP as OpenPGP
5import Data.Maybe
6import Data.Binary (encode)
7import qualified Data.ByteString as BS
8import qualified Data.ByteString.Lazy as LZ
9import Data.Bits ( (.|.), shiftL )
10import Control.Applicative ( (<$>) )
11import Data.Time.Clock.POSIX
12import Control.Exception as Exception (IOException(..),catch)
13
14import Data.OpenPGP.Util.Fingerprint (fingerprint)
15
16import qualified Crypto.Random as Vincent
17import qualified Crypto.PubKey.DSA as Vincent.DSA
18import qualified Crypto.PubKey.RSA as Vincent.RSA
19import qualified Crypto.PubKey.RSA.PKCS15 as Vincent.RSA
20import Crypto.PubKey.HashDescr as Vincent
21
22import Crypto.Hash.MD5 as MD5
23import Crypto.Hash.SHA1 as SHA1
24import Crypto.Hash.SHA256 as SHA256
25import Crypto.Hash.SHA384 as SHA384
26import Crypto.Hash.SHA512 as SHA512
27import Crypto.Hash.SHA224 as SHA224
28import Crypto.Hash.RIPEMD160 as RIPEMD160
29
30hashAlgoDesc OpenPGP.MD5 = Vincent.hashDescrMD5
31hashAlgoDesc OpenPGP.SHA1 = Vincent.hashDescrSHA1
32hashAlgoDesc OpenPGP.RIPEMD160 = Vincent.hashDescrRIPEMD160
33hashAlgoDesc OpenPGP.SHA256 = Vincent.hashDescrSHA256
34hashAlgoDesc OpenPGP.SHA384 = Vincent.hashDescrSHA384
35hashAlgoDesc OpenPGP.SHA512 = Vincent.hashDescrSHA512
36hashAlgoDesc OpenPGP.SHA224 = Vincent.hashDescrSHA224
37hashAlgoDesc _ =
38 error "Unsupported HashAlgorithm in hashAlgoDesc"
39
40find_key :: OpenPGP.Message -> String -> Maybe OpenPGP.Packet
41find_key = OpenPGP.find_key fingerprint
42
43
44privateDSAkey :: OpenPGP.Packet -> Vincent.DSA.PrivateKey
45privateDSAkey k = Vincent.DSA.PrivateKey
46 (Vincent.DSA.Params (keyParam 'p' k) (keyParam 'g' k) (keyParam 'q' k))
47 (keyParam 'x' k)
48privateRSAkey :: OpenPGP.Packet -> Vincent.RSA.PrivateKey
49privateRSAkey k =
50 -- Invert p and q because u is pinv not qinv
51 Vincent.RSA.PrivateKey pubkey d q p
52 (d `mod` (q-1))
53 (d `mod` (p-1))
54 (keyParam 'u' k)
55 where
56 d = keyParam 'd' k
57 p = keyParam 'p' k
58 q = keyParam 'q' k
59 pubkey = rsaKey k
60
61rsaKey :: OpenPGP.Packet -> Vincent.RSA.PublicKey
62rsaKey k =
63 Vincent.RSA.PublicKey (integerBytesize n) n (keyParam 'e' k)
64 where
65 n = keyParam 'n' k
66
67integerBytesize :: Integer -> Int
68integerBytesize i = fromIntegral $ LZ.length (encode (OpenPGP.MPI i)) - 2
69
70
71toStrictBS :: LZ.ByteString -> BS.ByteString
72toStrictBS = BS.concat . LZ.toChunks
73
74toLazyBS :: BS.ByteString -> LZ.ByteString
75toLazyBS = LZ.fromChunks . (:[])
76
77
78keyParam :: Char -> OpenPGP.Packet -> Integer
79keyParam c k = fromJustMPI $ lookup c (OpenPGP.key k)
80fromJustMPI :: Maybe OpenPGP.MPI -> Integer
81fromJustMPI (Just (OpenPGP.MPI x)) = x
82fromJustMPI _ = error "Not a Just MPI, Data.OpenPGP.CryptoAPI"
83
84hashBySymbol OpenPGP.MD5 = MD5.hashlazy
85hashBySymbol OpenPGP.SHA1 = SHA1.hashlazy
86hashBySymbol OpenPGP.SHA256 = SHA256.hashlazy
87hashBySymbol OpenPGP.SHA384 = SHA384.hashlazy
88hashBySymbol OpenPGP.SHA512 = SHA512.hashlazy
89hashBySymbol OpenPGP.SHA224 = SHA224.hashlazy
90hashBySymbol OpenPGP.RIPEMD160 = RIPEMD160.hashlazy
91
92
93
94
95-- | Make a signature
96--
97-- In order to set more options on a signature, pass in a signature packet.
98-- Operation is unsafe in that it silently re-uses "random" bytes when
99-- entropy runs out. Use pgpSign for a safer interface.
100unsafeSign :: (Vincent.CPRG g) => -- CryptoRandomGen g) =>
101 OpenPGP.Message -- ^ SecretKeys, one of which will be used
102 -> OpenPGP.SignatureOver -- ^ Data to sign, and optional signature packet
103 -> OpenPGP.HashAlgorithm -- ^ HashAlgorithm to use in signature
104 -> String -- ^ KeyID of key to choose
105 -> Integer -- ^ Timestamp for signature (unless sig supplied)
106 -> g -- ^ Random number generator
107 -> (OpenPGP.SignatureOver, g)
108unsafeSign keys over hsh keyid timestamp g = (over {OpenPGP.signatures_over = [sig]}, g')
109 where
110 (final, g') = case OpenPGP.key_algorithm sig of
111 OpenPGP.DSA -> ([dsaR, dsaS], dsaG)
112 kalgo | kalgo `elem` [OpenPGP.RSA,OpenPGP.RSA_S] -> ([toNum rsaFinal], g)
113 | otherwise ->
114 error ("Unsupported key algorithm " ++ show kalgo ++ "in sign")
115 (Vincent.DSA.Signature dsaR dsaS,dsaG) = let k' = privateDSAkey k in
116 Vincent.DSA.sign g k' (dsaTruncate k' . bhash) dta
117 (Right rsaFinal,_) = Vincent.RSA.signSafer g desc (privateRSAkey k) dta
118 dsaTruncate (Vincent.DSA.PrivateKey (Vincent.DSA.Params _ _ q) _) = BS.take (integerBytesize q)
119 dta = toStrictBS $ encode over `LZ.append` OpenPGP.trailer sig
120 sig = findSigOrDefault (listToMaybe $ OpenPGP.signatures_over over)
121 -- padding = emsa_pkcs1_v1_5_hash_padding hsh
122 desc = hashAlgoDesc hsh
123 bhash = hashBySymbol hsh . toLazyBS
124 toNum = BS.foldl (\a b -> a `shiftL` 8 .|. fromIntegral b) 0
125 Just k = find_key keys keyid
126
127 -- Either a SignaturePacket was found, or we need to make one
128 findSigOrDefault (Just s) = OpenPGP.signaturePacket
129 (OpenPGP.version s)
130 (OpenPGP.signature_type s)
131 (OpenPGP.key_algorithm k) -- force to algo of key
132 hsh -- force hash algorithm
133 (OpenPGP.hashed_subpackets s)
134 (OpenPGP.unhashed_subpackets s)
135 (OpenPGP.hash_head s)
136 (map OpenPGP.MPI final)
137 findSigOrDefault Nothing = OpenPGP.signaturePacket
138 4
139 defaultStype
140 (OpenPGP.key_algorithm k) -- force to algo of key
141 hsh
142 ([
143 -- Do we really need to pass in timestamp just for the default?
144 OpenPGP.SignatureCreationTimePacket $ fromIntegral timestamp,
145 OpenPGP.IssuerPacket $ fingerprint k
146 ] ++ (case over of
147 OpenPGP.KeySignature {} -> [OpenPGP.KeyFlagsPacket {
148 OpenPGP.certify_keys = True,
149 OpenPGP.sign_data = True,
150 OpenPGP.encrypt_communication = False,
151 OpenPGP.encrypt_storage = False,
152 OpenPGP.split_key = False,
153 OpenPGP.authentication = False,
154 OpenPGP.group_key = False
155 }]
156 _ -> []
157 ))
158 []
159 0 -- TODO
160 (map OpenPGP.MPI final)
161
162 defaultStype = case over of
163 OpenPGP.DataSignature ld _
164 | OpenPGP.format ld == 'b' -> 0x00
165 | otherwise -> 0x01
166 OpenPGP.KeySignature {} -> 0x1F
167 OpenPGP.SubkeySignature {} -> 0x18
168 OpenPGP.CertificationSignature {} -> 0x13
169
170
171
172now = floor <$> Data.Time.Clock.POSIX.getPOSIXTime
173
174stampit timestamp sig = sig { OpenPGP.hashed_subpackets = hashed' }
175 where
176 hashed_stamps = filter isStamp (OpenPGP.hashed_subpackets sig)
177 unhashed_stamps = filter isStamp (OpenPGP.unhashed_subpackets sig)
178 hashed' = case hashed_stamps ++ unhashed_stamps of
179 [] -> OpenPGP.SignatureCreationTimePacket (fromIntegral timestamp)
180 : OpenPGP.hashed_subpackets sig
181 _ -> OpenPGP.hashed_subpackets sig
182 isStamp (OpenPGP.SignatureCreationTimePacket {}) = True
183 isStamp _ = False
184
185-- | Make a signature
186--
187-- In order to set more options on a signature, pass in a signature packet.
188pgpSign ::
189 OpenPGP.Message -- ^ SecretKeys, one of which will be used
190 -> OpenPGP.SignatureOver -- ^ Data to sign, and optional signature packet
191 -> OpenPGP.HashAlgorithm -- ^ HashAlgorithm to use in signature
192 -> String -- ^ KeyID of key to choose
193 -> IO (Maybe OpenPGP.SignatureOver)
194pgpSign seckeys dta hash_algo keyid =
195 handleIO_ (return Nothing) $ do
196 timestamp <- now
197 -- g <- Thomas.newGenIO :: IO Thomas.SystemRandom
198 g <- fmap Vincent.cprgCreate $ Vincent.createEntropyPool
199 let _ = g :: Vincent.SystemRNG
200 let sigs = map (stampit timestamp) $ OpenPGP.signatures_over dta
201 dta' = dta { OpenPGP.signatures_over = sigs }
202 let (r,g') = unsafeSign seckeys dta' hash_algo keyid timestamp g
203 return (Just r)
204
205catchIO_ :: IO a -> IO a -> IO a
206catchIO_ a h = Exception.catch a (\(_ :: IOException) -> h)
207
208catchIO :: IO a -> (IOException -> IO a) -> IO a
209catchIO body handler = Exception.catch body handler
210
211handleIO_ = flip catchIO_
212handleIO = flip catchIO
213
diff --git a/Data/OpenPGP/Util/Verify.hs b/Data/OpenPGP/Util/Verify.hs
new file mode 100644
index 0000000..137c00f
--- /dev/null
+++ b/Data/OpenPGP/Util/Verify.hs
@@ -0,0 +1,115 @@
1module Data.OpenPGP.Util.Verify where
2
3import qualified Data.OpenPGP as OpenPGP
4import Data.Maybe
5import Data.Binary (encode)
6import Control.Monad
7import qualified Data.ByteString as BS
8import qualified Data.ByteString.Lazy as LZ
9import Data.Monoid ( (<>) )
10
11import Data.OpenPGP.Util.Fingerprint (fingerprint)
12
13import qualified Crypto.PubKey.DSA as Vincent.DSA
14import qualified Crypto.PubKey.RSA as Vincent.RSA
15import qualified Crypto.PubKey.RSA.PKCS15 as Vincent.RSA
16import Crypto.PubKey.HashDescr as Vincent
17
18import Crypto.Hash.MD5 as MD5
19import Crypto.Hash.SHA1 as SHA1
20import Crypto.Hash.SHA256 as SHA256
21import Crypto.Hash.SHA384 as SHA384
22import Crypto.Hash.SHA512 as SHA512
23import Crypto.Hash.SHA224 as SHA224
24import Crypto.Hash.RIPEMD160 as RIPEMD160
25
26hashBySymbol OpenPGP.MD5 = MD5.hashlazy
27hashBySymbol OpenPGP.SHA1 = SHA1.hashlazy
28hashBySymbol OpenPGP.SHA256 = SHA256.hashlazy
29hashBySymbol OpenPGP.SHA384 = SHA384.hashlazy
30hashBySymbol OpenPGP.SHA512 = SHA512.hashlazy
31hashBySymbol OpenPGP.SHA224 = SHA224.hashlazy
32hashBySymbol OpenPGP.RIPEMD160 = RIPEMD160.hashlazy
33
34
35toStrictBS :: LZ.ByteString -> BS.ByteString
36toStrictBS = BS.concat . LZ.toChunks
37
38toLazyBS :: BS.ByteString -> LZ.ByteString
39toLazyBS = LZ.fromChunks . (:[])
40
41hush :: Either a b -> Maybe b
42hush (Left _) = Nothing
43hush (Right x) = Just x
44
45fromJustMPI :: Maybe OpenPGP.MPI -> Integer
46fromJustMPI (Just (OpenPGP.MPI x)) = x
47fromJustMPI _ = error "Not a Just MPI, Data.OpenPGP.CryptoAPI"
48
49
50
51find_key :: OpenPGP.Message -> String -> Maybe OpenPGP.Packet
52find_key = OpenPGP.find_key fingerprint
53
54integerBytesize :: Integer -> Int
55integerBytesize i = fromIntegral $ LZ.length (encode (OpenPGP.MPI i)) - 2
56
57dsaKey :: OpenPGP.Packet -> Vincent.DSA.PublicKey
58dsaKey k = Vincent.DSA.PublicKey
59 (Vincent.DSA.Params (keyParam 'p' k) (keyParam 'g' k) (keyParam 'q' k))
60 (keyParam 'y' k)
61
62rsaKey :: OpenPGP.Packet -> Vincent.RSA.PublicKey
63rsaKey k =
64 Vincent.RSA.PublicKey (integerBytesize n) n (keyParam 'e' k)
65 where
66 n = keyParam 'n' k
67
68
69keyParam :: Char -> OpenPGP.Packet -> Integer
70keyParam c k = fromJustMPI $ lookup c (OpenPGP.key k)
71
72
73-- | Verify a message signature
74verify ::
75 OpenPGP.Message -- ^ Keys that may have made the signature
76 -> OpenPGP.SignatureOver -- ^ Signatures to verify
77 -> OpenPGP.SignatureOver -- ^ Will only contain signatures that passed
78verify keys over =
79 over {OpenPGP.signatures_over = mapMaybe (uncurry $ verifyOne keys) sigs}
80 where
81 sigs = map (\s -> (s, toStrictBS $ encode over `LZ.append` OpenPGP.trailer s))
82 (OpenPGP.signatures_over over)
83
84verifyOne :: OpenPGP.Message -> OpenPGP.Packet -> BS.ByteString -> Maybe OpenPGP.Packet
85verifyOne keys sig over = fmap (const sig) $ maybeKey >>= verification >>= guard
86 where
87 verification = case OpenPGP.key_algorithm sig of
88 OpenPGP.DSA -> dsaVerify
89 alg | alg `elem` [OpenPGP.RSA,OpenPGP.RSA_S] -> rsaVerify
90 | otherwise -> const Nothing
91 dsaVerify k = let k' = dsaKey k in
92 Just $ Vincent.DSA.verify (dsaTruncate k' . bhash) k' dsaSig over
93 rsaVerify k = Just $ Vincent.RSA.verify desc (rsaKey k) over rsaSig
94 [rsaSig] = map (toStrictBS . LZ.drop 2 . encode) (OpenPGP.signature sig)
95 dsaSig = let [OpenPGP.MPI r, OpenPGP.MPI s] = OpenPGP.signature sig in
96 Vincent.DSA.Signature r s
97 dsaTruncate (Vincent.DSA.PublicKey (Vincent.DSA.Params _ _ q) _) = BS.take (integerBytesize q)
98 bhash = hashBySymbol hash_algo . toLazyBS
99 desc = hashAlgoDesc hash_algo
100 hash_algo = OpenPGP.hash_algorithm sig
101 maybeKey = OpenPGP.signature_issuer sig >>= find_key keys
102
103-- http://tools.ietf.org/html/rfc3447#page-43
104-- http://tools.ietf.org/html/rfc4880#section-5.2.2
105hashAlgoDesc OpenPGP.MD5 = Vincent.hashDescrMD5
106hashAlgoDesc OpenPGP.SHA1 = Vincent.hashDescrSHA1
107hashAlgoDesc OpenPGP.RIPEMD160 = Vincent.hashDescrRIPEMD160
108hashAlgoDesc OpenPGP.SHA256 = Vincent.hashDescrSHA256
109hashAlgoDesc OpenPGP.SHA384 = Vincent.hashDescrSHA384
110hashAlgoDesc OpenPGP.SHA512 = Vincent.hashDescrSHA512
111hashAlgoDesc OpenPGP.SHA224 = Vincent.hashDescrSHA224
112hashAlgoDesc _ =
113 error "Unsupported HashAlgorithm in hashAlgoDesc"
114
115