summaryrefslogtreecommitdiff
path: root/Data/OpenPGP/Util/DecryptSecretKey.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Data/OpenPGP/Util/DecryptSecretKey.hs')
-rw-r--r--Data/OpenPGP/Util/DecryptSecretKey.hs126
1 files changed, 126 insertions, 0 deletions
diff --git a/Data/OpenPGP/Util/DecryptSecretKey.hs b/Data/OpenPGP/Util/DecryptSecretKey.hs
new file mode 100644
index 0000000..6bfc2af
--- /dev/null
+++ b/Data/OpenPGP/Util/DecryptSecretKey.hs
@@ -0,0 +1,126 @@
1{-# LANGUAGE CPP #-}
2module Data.OpenPGP.Util.DecryptSecretKey where
3
4import qualified Data.OpenPGP as OpenPGP
5import qualified Data.ByteString as BS
6import qualified Data.ByteString.Lazy as LZ
7import Data.Word (Word16)
8import Control.Monad (foldM)
9import Data.Binary (get,Binary,Get,encode)
10#if MIN_VERSION_binary(0,6,4)
11import Data.Binary.Get (runGetOrFail)
12#else
13import Control.Exception as Exception (handle,ErrorCall(..))
14import System.IO.Unsafe
15import Data.Binary.Get (runGet)
16#endif
17import Control.Applicative ( (<$>) )
18
19import Crypto.Hash.SHA1 as SHA1
20
21import qualified Crypto.Cipher.AES as Vincent
22import qualified Crypto.Cipher.Blowfish as Vincent
23
24import qualified Crypto.Cipher.Types as Vincent
25import qualified Data.Byteable as Vincent
26
27import Crypto.Cipher.Cast5 (CAST5_128)
28import Crypto.Cipher.ThomasToVincent
29import Data.OpenPGP.Util.Base (toStrictBS,toLazyBS,hashBySymbol)
30
31
32
33
34data Enciphered =
35 EncipheredWithIV !LZ.ByteString -- initial vector is appended to front of ByteString
36 | EncipheredZeroIV !LZ.ByteString -- initial vector is zero, ByteString contains only the block
37
38withIV :: (Vincent.BlockCipher k) => (Vincent.IV k -> LZ.ByteString -> LZ.ByteString) -> Enciphered -> LZ.ByteString
39withIV f (EncipheredWithIV s) = f iv bs
40 where
41 Just iv = Vincent.makeIV (toStrictBS ivbs)
42 (ivbs,bs) = LZ.splitAt (fromIntegral ivlen) s
43 ivlen = Vincent.byteableLength z
44 z = Vincent.nullIV
45 _ = Vincent.constEqBytes z iv
46withIV f (EncipheredZeroIV s) = f Vincent.nullIV s
47
48decryptSecretKey ::
49 BS.ByteString -- ^ Passphrase
50 -> OpenPGP.Packet -- ^ Encrypted SecretKeyPacket
51 -> Maybe OpenPGP.Packet -- ^ Decrypted SecretKeyPacket
52decryptSecretKey pass k@(OpenPGP.SecretKeyPacket {
53 OpenPGP.version = 4, OpenPGP.key_algorithm = kalgo,
54 OpenPGP.s2k = s2k, OpenPGP.symmetric_algorithm = salgo,
55 OpenPGP.key = existing, OpenPGP.encrypted_data = encd
56 }) | chkF material == toStrictBS chk =
57 fmap (\m -> k {
58 OpenPGP.s2k_useage = 0,
59 OpenPGP.symmetric_algorithm = OpenPGP.Unencrypted,
60 OpenPGP.encrypted_data = LZ.empty,
61 OpenPGP.key = m
62 }) parseMaterial
63 | otherwise = Nothing
64 where
65 parseMaterial = maybeGet
66 (foldM (\m f -> do {mpi <- get; return $ (f,mpi):m}) existing
67 (OpenPGP.secret_key_fields kalgo)) material
68 (material, chk) = LZ.splitAt (LZ.length decd - chkSize) decd
69 (chkSize, chkF)
70 | OpenPGP.s2k_useage k == 254 = (20, SHA1.hash . toStrictBS)
71 | otherwise = (2, toStrictBS . encode . checksum . toStrictBS)
72 -- Words16s are written as 2 bytes in big-endian (network) order
73 decd = string2sdecrypt 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
79decryptSecretKey _ _ = Nothing
80
81
82#if MIN_VERSION_binary(0,6,4)
83maybeGet :: (Binary a) => Get a -> LZ.ByteString -> Maybe a
84maybeGet g bs = (\(_,_,x) -> x) <$> hush (runGetOrFail g bs)
85 where
86 hush :: Either a b -> Maybe b
87 hush (Left _) = Nothing
88 hush (Right x) = Just x
89#else
90maybeGet :: (Binary a) => Get a -> LZ.ByteString -> Maybe a
91maybeGet g bs = unsafePerformIO $
92 handle (\(ErrorCall _)-> return Nothing) $ return . Just $ runGet g bs
93#endif
94
95
96
97string2sdecrypt :: OpenPGP.SymmetricAlgorithm -> OpenPGP.S2K -> LZ.ByteString -> Enciphered -> LZ.ByteString
98string2sdecrypt OpenPGP.AES128 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES128)
99string2sdecrypt OpenPGP.AES192 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES192)
100string2sdecrypt OpenPGP.AES256 s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.AES256)
101string2sdecrypt OpenPGP.Blowfish s2k s = withIV $ simpleUnCFB (string2key s2k s :: Vincent.Blowfish128)
102string2sdecrypt OpenPGP.CAST5 s2k s = withIV $ simpleUnCFB (string2key s2k s :: ThomasToVincent CAST5_128)
103string2sdecrypt algo _ _ = error $ "Unsupported symmetric algorithm : " ++ show algo ++ " in Data.OpenPGP.CryptoAPI.string2sdecrypt"
104
105simpleUnCFB :: (Vincent.BlockCipher k) => k -> Vincent.IV k -> LZ.ByteString -> LZ.ByteString
106simpleUnCFB k iv = padThenUnpad k (toLazyBS . Vincent.cfbDecrypt k iv . toStrictBS)
107 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
112 padded = s `LZ.append` LZ.replicate padAmount 0
113 padAmount = blksize - (LZ.length s `mod` blksize)
114 blksize = fromIntegral $ Vincent.blockSize k
115
116string2key :: (Vincent.BlockCipher k) => OpenPGP.S2K -> LZ.ByteString -> k
117string2key s2k s = cipher
118 where
119 cipher = Vincent.cipherInit k
120 Right k = Vincent.makeKey $ toStrictBS $
121 LZ.take ksize $ OpenPGP.string2key hashBySymbol s2k s
122 ksize = case Vincent.cipherKeySize cipher of
123 Vincent.KeySizeFixed n -> fromIntegral n
124 Vincent.KeySizeEnum xs -> error $ "Unknown key size in string2key"
125 Vincent.KeySizeRange min max -> error $ "Unknown key size range in string2key"
126