diff options
author | joe <joe@jerkface.net> | 2016-08-25 00:11:46 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2019-07-01 09:19:55 -0400 |
commit | dbab71d60e31e27e4331aa6b2c15d2fa9b78fbd8 (patch) | |
tree | 8b214350ea6785b2d23795694d38e66b34344bd1 | |
parent | a86fa24e7e78ae880f62f3910b02601fb8abf8be (diff) |
encryptSecretKey
-rw-r--r-- | Data/OpenPGP/Util.hs | 1 | ||||
-rw-r--r-- | Data/OpenPGP/Util/DecryptSecretKey.hs | 102 |
2 files changed, 87 insertions, 16 deletions
diff --git a/Data/OpenPGP/Util.hs b/Data/OpenPGP/Util.hs index 8a1a449..19d98ec 100644 --- a/Data/OpenPGP/Util.hs +++ b/Data/OpenPGP/Util.hs | |||
@@ -1,6 +1,7 @@ | |||
1 | module Data.OpenPGP.Util | 1 | module Data.OpenPGP.Util |
2 | ( fingerprint | 2 | ( fingerprint |
3 | , decryptSecretKey | 3 | , decryptSecretKey |
4 | , encryptSecretKey | ||
4 | , verify | 5 | , verify |
5 | , pgpSign | 6 | , pgpSign |
6 | , GenerateKeyParams(..) | 7 | , GenerateKeyParams(..) |
diff --git a/Data/OpenPGP/Util/DecryptSecretKey.hs b/Data/OpenPGP/Util/DecryptSecretKey.hs index 6bfc2af..11627d5 100644 --- a/Data/OpenPGP/Util/DecryptSecretKey.hs +++ b/Data/OpenPGP/Util/DecryptSecretKey.hs | |||
@@ -1,12 +1,15 @@ | |||
1 | {-# LANGUAGE ScopedTypeVariables #-} | ||
1 | {-# LANGUAGE CPP #-} | 2 | {-# LANGUAGE CPP #-} |
3 | {-# LANGUAGE Rank2Types #-} | ||
2 | module Data.OpenPGP.Util.DecryptSecretKey where | 4 | module Data.OpenPGP.Util.DecryptSecretKey where |
3 | 5 | ||
4 | import qualified Data.OpenPGP as OpenPGP | 6 | import qualified Data.OpenPGP as OpenPGP |
5 | import qualified Data.ByteString as BS | 7 | import qualified Data.ByteString as BS |
6 | import qualified Data.ByteString.Lazy as LZ | 8 | import qualified Data.ByteString.Lazy as LZ |
7 | import Data.Word (Word16) | 9 | import Data.Word (Word16) |
10 | import Data.Maybe | ||
8 | import Control.Monad (foldM) | 11 | import Control.Monad (foldM) |
9 | import Data.Binary (get,Binary,Get,encode) | 12 | import Data.Binary (get,Binary,Get,encode,put) |
10 | #if MIN_VERSION_binary(0,6,4) | 13 | #if MIN_VERSION_binary(0,6,4) |
11 | import Data.Binary.Get (runGetOrFail) | 14 | import Data.Binary.Get (runGetOrFail) |
12 | #else | 15 | #else |
@@ -14,6 +17,8 @@ import Control.Exception as Exception (handle,ErrorCall(..)) | |||
14 | import System.IO.Unsafe | 17 | import System.IO.Unsafe |
15 | import Data.Binary.Get (runGet) | 18 | import Data.Binary.Get (runGet) |
16 | #endif | 19 | #endif |
20 | import Control.Exception as Exception (IOException(..),catch) | ||
21 | import Data.Binary.Put (runPut) | ||
17 | import Control.Applicative ( (<$>) ) | 22 | import Control.Applicative ( (<$>) ) |
18 | 23 | ||
19 | import Crypto.Hash.SHA1 as SHA1 | 24 | import Crypto.Hash.SHA1 as SHA1 |
@@ -24,6 +29,8 @@ import qualified Crypto.Cipher.Blowfish as Vincent | |||
24 | import qualified Crypto.Cipher.Types as Vincent | 29 | import qualified Crypto.Cipher.Types as Vincent |
25 | import qualified Data.Byteable as Vincent | 30 | import qualified Data.Byteable as Vincent |
26 | 31 | ||
32 | import qualified Crypto.Random as Vincent | ||
33 | |||
27 | import Crypto.Cipher.Cast5 (CAST5_128) | 34 | import Crypto.Cipher.Cast5 (CAST5_128) |
28 | import Crypto.Cipher.ThomasToVincent | 35 | import Crypto.Cipher.ThomasToVincent |
29 | import Data.OpenPGP.Util.Base (toStrictBS,toLazyBS,hashBySymbol) | 36 | import Data.OpenPGP.Util.Base (toStrictBS,toLazyBS,hashBySymbol) |
@@ -70,14 +77,14 @@ decryptSecretKey pass k@(OpenPGP.SecretKeyPacket { | |||
70 | | OpenPGP.s2k_useage k == 254 = (20, SHA1.hash . toStrictBS) | 77 | | OpenPGP.s2k_useage k == 254 = (20, SHA1.hash . toStrictBS) |
71 | | otherwise = (2, toStrictBS . encode . checksum . toStrictBS) | 78 | | otherwise = (2, toStrictBS . encode . checksum . toStrictBS) |
72 | -- Words16s are written as 2 bytes in big-endian (network) order | 79 | -- Words16s are written as 2 bytes in big-endian (network) order |
73 | decd = string2sdecrypt salgo s2k (toLazyBS pass) (EncipheredWithIV encd) | 80 | decd = withS2K simpleUnCFB salgo s2k (toLazyBS pass) (EncipheredWithIV encd) |
74 | |||
75 | checksum :: BS.ByteString -> Word16 | ||
76 | checksum key = fromIntegral $ | ||
77 | BS.foldl' (\x y -> x + fromIntegral y) (0::Integer) key `mod` 65536 | ||
78 | 81 | ||
79 | decryptSecretKey _ _ = Nothing | 82 | decryptSecretKey _ _ = Nothing |
80 | 83 | ||
84 | checksum :: BS.ByteString -> Word16 | ||
85 | checksum key = fromIntegral $ | ||
86 | BS.foldl' (\x y -> x + fromIntegral y) (0::Integer) key `mod` 65536 | ||
87 | |||
81 | 88 | ||
82 | #if MIN_VERSION_binary(0,6,4) | 89 | #if MIN_VERSION_binary(0,6,4) |
83 | maybeGet :: (Binary a) => Get a -> LZ.ByteString -> Maybe a | 90 | maybeGet :: (Binary a) => Get a -> LZ.ByteString -> Maybe a |
@@ -94,20 +101,45 @@ maybeGet g bs = unsafePerformIO $ | |||
94 | 101 | ||
95 | 102 | ||
96 | 103 | ||
97 | string2sdecrypt :: OpenPGP.SymmetricAlgorithm -> OpenPGP.S2K -> LZ.ByteString -> Enciphered -> LZ.ByteString | 104 | withS2K :: (forall k. (Vincent.BlockCipher k) => k -> Vincent.IV k -> LZ.ByteString -> LZ.ByteString) |
98 | string2sdecrypt OpenPGP.AES128 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES128) | 105 | -> OpenPGP.SymmetricAlgorithm |
99 | string2sdecrypt OpenPGP.AES192 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES192) | 106 | -> OpenPGP.S2K |
100 | string2sdecrypt OpenPGP.AES256 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES256) | 107 | -> LZ.ByteString -> Enciphered -> LZ.ByteString |
101 | string2sdecrypt OpenPGP.Blowfish s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.Blowfish128) | 108 | withS2K codec OpenPGP.AES128 s2k s = withIV $ codec (string2key s2k s :: Vincent.AES128) |
102 | string2sdecrypt OpenPGP.CAST5 s2k s = withIV $ simpleUnCFB (string2key s2k s :: ThomasToVincent CAST5_128) | 109 | withS2K codec OpenPGP.AES192 s2k s = withIV $ codec (string2key s2k s :: Vincent.AES192) |
103 | string2sdecrypt algo _ _ = error $ "Unsupported symmetric algorithm : " ++ show algo ++ " in Data.OpenPGP.CryptoAPI.string2sdecrypt" | 110 | withS2K codec OpenPGP.AES256 s2k s = withIV $ codec (string2key s2k s :: Vincent.AES256) |
111 | withS2K codec OpenPGP.Blowfish s2k s = withIV $ codec (string2key s2k s :: Vincent.Blowfish128) | ||
112 | withS2K codec OpenPGP.CAST5 s2k s = withIV $ codec (string2key s2k s :: ThomasToVincent CAST5_128) | ||
113 | withS2K codec algo _ _ = error $ "Unsupported symmetric algorithm : " ++ show algo ++ " in Data.OpenPGP.CryptoAPI.withS2K" | ||
104 | 114 | ||
115 | withS2K' :: OpenPGP.SymmetricAlgorithm -> OpenPGP.S2K -> LZ.ByteString | ||
116 | -> (forall b. Vincent.BlockCipher b => b -> x) -> x | ||
117 | withS2K' OpenPGP.AES128 s2k s f = f (string2key s2k s :: Vincent.AES128) | ||
118 | withS2K' OpenPGP.AES192 s2k s f = f (string2key s2k s :: Vincent.AES192) | ||
119 | withS2K' OpenPGP.AES256 s2k s f = f (string2key s2k s :: Vincent.AES256) | ||
120 | withS2K' OpenPGP.Blowfish s2k s f = f (string2key s2k s :: Vincent.Blowfish128) | ||
121 | withS2K' OpenPGP.CAST5 s2k s f = f (string2key s2k s :: ThomasToVincent CAST5_128) | ||
122 | |||
123 | -- decryption codec for withS2K | ||
105 | simpleUnCFB :: (Vincent.BlockCipher k) => k -> Vincent.IV k -> LZ.ByteString -> LZ.ByteString | 124 | simpleUnCFB :: (Vincent.BlockCipher k) => k -> Vincent.IV k -> LZ.ByteString -> LZ.ByteString |
106 | simpleUnCFB k iv = padThenUnpad k (toLazyBS . Vincent.cfbDecrypt k iv . toStrictBS) | 125 | simpleUnCFB k iv = padThenUnpad k (toLazyBS . Vincent.cfbDecrypt k iv . toStrictBS) |
126 | |||
127 | simpleCFB :: (Vincent.BlockCipher k, Vincent.CPRG g) => g -> k -> LZ.ByteString -> (LZ.ByteString, g) | ||
128 | simpleCFB g k bs = ( padThenUnpad k (LZ.fromChunks . (ivbs:) . (:[]) . Vincent.cfbEncrypt k iv . toStrictBS) bs | ||
129 | , g' ) | ||
130 | where | ||
131 | Just iv = Vincent.makeIV ivbs | ||
132 | (ivbs,g') = Vincent.cprgGenerate ivlen g | ||
133 | ivlen = Vincent.byteableLength z | ||
134 | z = Vincent.nullIV | ||
135 | _ = Vincent.constEqBytes z iv | ||
136 | |||
137 | -- Apply a function f to a zero-padded bytestring s to a multiple | ||
138 | -- of the blocksize for cyper k. | ||
139 | -- Then drop the same number of bytes from the result of f. | ||
140 | padThenUnpad :: (Vincent.BlockCipher k) => k -> (LZ.ByteString -> LZ.ByteString) -> LZ.ByteString -> LZ.ByteString | ||
141 | padThenUnpad k f s = dropPadEnd (f padded) | ||
107 | where | 142 | where |
108 | padThenUnpad :: (Vincent.BlockCipher k) => k -> (LZ.ByteString -> LZ.ByteString) -> LZ.ByteString -> LZ.ByteString | ||
109 | padThenUnpad k f s = dropPadEnd (f padded) | ||
110 | where | ||
111 | dropPadEnd s = LZ.take (LZ.length s - padAmount) s | 143 | dropPadEnd s = LZ.take (LZ.length s - padAmount) s |
112 | padded = s `LZ.append` LZ.replicate padAmount 0 | 144 | padded = s `LZ.append` LZ.replicate padAmount 0 |
113 | padAmount = blksize - (LZ.length s `mod` blksize) | 145 | padAmount = blksize - (LZ.length s `mod` blksize) |
@@ -124,3 +156,41 @@ string2key s2k s = cipher | |||
124 | Vincent.KeySizeEnum xs -> error $ "Unknown key size in string2key" | 156 | Vincent.KeySizeEnum xs -> error $ "Unknown key size in string2key" |
125 | Vincent.KeySizeRange min max -> error $ "Unknown key size range in string2key" | 157 | Vincent.KeySizeRange min max -> error $ "Unknown key size range in string2key" |
126 | 158 | ||
159 | catchIO_ :: IO a -> IO a -> IO a | ||
160 | catchIO_ a h = Exception.catch a (\(_ :: IOException) -> h) | ||
161 | |||
162 | encryptSecretKey :: BS.ByteString -> OpenPGP.S2K -> OpenPGP.SymmetricAlgorithm -> OpenPGP.Packet -> IO (Maybe OpenPGP.Packet) | ||
163 | encryptSecretKey passphrase s2k salgo plain = do | ||
164 | flip catchIO_ (return Nothing) $ do | ||
165 | g <- fmap Vincent.cprgCreate $ Vincent.createEntropyPool | ||
166 | let _ = g :: Vincent.SystemRNG | ||
167 | return $ Just | ||
168 | plain { OpenPGP.key = [ x | x <- OpenPGP.key plain | ||
169 | , fst x `elem` OpenPGP.public_key_fields (OpenPGP.key_algorithm plain) ] | ||
170 | , OpenPGP.symmetric_algorithm = salgo | ||
171 | , OpenPGP.s2k = s2k | ||
172 | , OpenPGP.s2k_useage = s2k_usage_octet | ||
173 | , OpenPGP.encrypted_data = encd g | ||
174 | } | ||
175 | where | ||
176 | material = runPut $ mapM_ put $ do | ||
177 | f <- OpenPGP.secret_key_fields (OpenPGP.key_algorithm plain) | ||
178 | maybeToList $ lookup f (OpenPGP.key plain) | ||
179 | chk = LZ.fromChunks [ chkF material ] | ||
180 | decd = LZ.append material chk | ||
181 | encd g = fst $ withS2K' salgo s2k (toLazyBS passphrase) (simpleCFB g) decd | ||
182 | |||
183 | -- If the string-to-key usage octet is zero or 255, then a two-octet | ||
184 | -- checksum of the plaintext of the algorithm-specific portion (sum | ||
185 | -- of all octets, mod 65536). If the string-to-key usage octet was | ||
186 | -- 254, then a 20-octet SHA-1 hash of the plaintext of the | ||
187 | -- algorithm-specific portion. This checksum or hash is encrypted | ||
188 | -- together with the algorithm-specific fields (if string-to-key | ||
189 | -- usage octet is not zero). Note that for all other values, a | ||
190 | -- two-octet checksum is required. | ||
191 | s2k_usage_octet = 255 | ||
192 | -- chkSize = 2 | ||
193 | chkF = toStrictBS . encode . checksum . toStrictBS | ||
194 | |||
195 | |||
196 | -- k = string2key s2k passphrase -- OpenPGP.string2key hashBySymbol s2k passphrase | ||