summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2016-08-25 00:11:46 -0400
committerJoe Crayne <joe@jerkface.net>2019-07-01 09:19:55 -0400
commitdbab71d60e31e27e4331aa6b2c15d2fa9b78fbd8 (patch)
tree8b214350ea6785b2d23795694d38e66b34344bd1
parenta86fa24e7e78ae880f62f3910b02601fb8abf8be (diff)
encryptSecretKey
-rw-r--r--Data/OpenPGP/Util.hs1
-rw-r--r--Data/OpenPGP/Util/DecryptSecretKey.hs102
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 @@
1module Data.OpenPGP.Util 1module 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 #-}
2module Data.OpenPGP.Util.DecryptSecretKey where 4module Data.OpenPGP.Util.DecryptSecretKey where
3 5
4import qualified Data.OpenPGP as OpenPGP 6import qualified Data.OpenPGP as OpenPGP
5import qualified Data.ByteString as BS 7import qualified Data.ByteString as BS
6import qualified Data.ByteString.Lazy as LZ 8import qualified Data.ByteString.Lazy as LZ
7import Data.Word (Word16) 9import Data.Word (Word16)
10import Data.Maybe
8import Control.Monad (foldM) 11import Control.Monad (foldM)
9import Data.Binary (get,Binary,Get,encode) 12import Data.Binary (get,Binary,Get,encode,put)
10#if MIN_VERSION_binary(0,6,4) 13#if MIN_VERSION_binary(0,6,4)
11import Data.Binary.Get (runGetOrFail) 14import Data.Binary.Get (runGetOrFail)
12#else 15#else
@@ -14,6 +17,8 @@ import Control.Exception as Exception (handle,ErrorCall(..))
14import System.IO.Unsafe 17import System.IO.Unsafe
15import Data.Binary.Get (runGet) 18import Data.Binary.Get (runGet)
16#endif 19#endif
20import Control.Exception as Exception (IOException(..),catch)
21import Data.Binary.Put (runPut)
17import Control.Applicative ( (<$>) ) 22import Control.Applicative ( (<$>) )
18 23
19import Crypto.Hash.SHA1 as SHA1 24import Crypto.Hash.SHA1 as SHA1
@@ -24,6 +29,8 @@ import qualified Crypto.Cipher.Blowfish as Vincent
24import qualified Crypto.Cipher.Types as Vincent 29import qualified Crypto.Cipher.Types as Vincent
25import qualified Data.Byteable as Vincent 30import qualified Data.Byteable as Vincent
26 31
32import qualified Crypto.Random as Vincent
33
27import Crypto.Cipher.Cast5 (CAST5_128) 34import Crypto.Cipher.Cast5 (CAST5_128)
28import Crypto.Cipher.ThomasToVincent 35import Crypto.Cipher.ThomasToVincent
29import Data.OpenPGP.Util.Base (toStrictBS,toLazyBS,hashBySymbol) 36import 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
79decryptSecretKey _ _ = Nothing 82decryptSecretKey _ _ = Nothing
80 83
84checksum :: BS.ByteString -> Word16
85checksum 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)
83maybeGet :: (Binary a) => Get a -> LZ.ByteString -> Maybe a 90maybeGet :: (Binary a) => Get a -> LZ.ByteString -> Maybe a
@@ -94,20 +101,45 @@ maybeGet g bs = unsafePerformIO $
94 101
95 102
96 103
97string2sdecrypt :: OpenPGP.SymmetricAlgorithm -> OpenPGP.S2K -> LZ.ByteString -> Enciphered -> LZ.ByteString 104withS2K :: (forall k. (Vincent.BlockCipher k) => k -> Vincent.IV k -> LZ.ByteString -> LZ.ByteString)
98string2sdecrypt OpenPGP.AES128 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES128) 105 -> OpenPGP.SymmetricAlgorithm
99string2sdecrypt OpenPGP.AES192 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES192) 106 -> OpenPGP.S2K
100string2sdecrypt OpenPGP.AES256 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES256) 107 -> LZ.ByteString -> Enciphered -> LZ.ByteString
101string2sdecrypt OpenPGP.Blowfish s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.Blowfish128) 108withS2K codec OpenPGP.AES128 s2k s = withIV $ codec (string2key s2k s :: Vincent.AES128)
102string2sdecrypt OpenPGP.CAST5 s2k s = withIV $ simpleUnCFB (string2key s2k s :: ThomasToVincent CAST5_128) 109withS2K codec OpenPGP.AES192 s2k s = withIV $ codec (string2key s2k s :: Vincent.AES192)
103string2sdecrypt algo _ _ = error $ "Unsupported symmetric algorithm : " ++ show algo ++ " in Data.OpenPGP.CryptoAPI.string2sdecrypt" 110withS2K codec OpenPGP.AES256 s2k s = withIV $ codec (string2key s2k s :: Vincent.AES256)
111withS2K codec OpenPGP.Blowfish s2k s = withIV $ codec (string2key s2k s :: Vincent.Blowfish128)
112withS2K codec OpenPGP.CAST5 s2k s = withIV $ codec (string2key s2k s :: ThomasToVincent CAST5_128)
113withS2K codec algo _ _ = error $ "Unsupported symmetric algorithm : " ++ show algo ++ " in Data.OpenPGP.CryptoAPI.withS2K"
104 114
115withS2K' :: OpenPGP.SymmetricAlgorithm -> OpenPGP.S2K -> LZ.ByteString
116 -> (forall b. Vincent.BlockCipher b => b -> x) -> x
117withS2K' OpenPGP.AES128 s2k s f = f (string2key s2k s :: Vincent.AES128)
118withS2K' OpenPGP.AES192 s2k s f = f (string2key s2k s :: Vincent.AES192)
119withS2K' OpenPGP.AES256 s2k s f = f (string2key s2k s :: Vincent.AES256)
120withS2K' OpenPGP.Blowfish s2k s f = f (string2key s2k s :: Vincent.Blowfish128)
121withS2K' OpenPGP.CAST5 s2k s f = f (string2key s2k s :: ThomasToVincent CAST5_128)
122
123-- decryption codec for withS2K
105simpleUnCFB :: (Vincent.BlockCipher k) => k -> Vincent.IV k -> LZ.ByteString -> LZ.ByteString 124simpleUnCFB :: (Vincent.BlockCipher k) => k -> Vincent.IV k -> LZ.ByteString -> LZ.ByteString
106simpleUnCFB k iv = padThenUnpad k (toLazyBS . Vincent.cfbDecrypt k iv . toStrictBS) 125simpleUnCFB k iv = padThenUnpad k (toLazyBS . Vincent.cfbDecrypt k iv . toStrictBS)
126
127simpleCFB :: (Vincent.BlockCipher k, Vincent.CPRG g) => g -> k -> LZ.ByteString -> (LZ.ByteString, g)
128simpleCFB 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.
140padThenUnpad :: (Vincent.BlockCipher k) => k -> (LZ.ByteString -> LZ.ByteString) -> LZ.ByteString -> LZ.ByteString
141padThenUnpad 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
159catchIO_ :: IO a -> IO a -> IO a
160catchIO_ a h = Exception.catch a (\(_ :: IOException) -> h)
161
162encryptSecretKey :: BS.ByteString -> OpenPGP.S2K -> OpenPGP.SymmetricAlgorithm -> OpenPGP.Packet -> IO (Maybe OpenPGP.Packet)
163encryptSecretKey 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