summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--KeyRing.hs74
-rw-r--r--kiki.hs64
2 files changed, 71 insertions, 67 deletions
diff --git a/KeyRing.hs b/KeyRing.hs
index 3268070..ee60765 100644
--- a/KeyRing.hs
+++ b/KeyRing.hs
@@ -19,7 +19,7 @@ import Data.Monoid
19import Data.Tuple ( swap ) 19import Data.Tuple ( swap )
20import Data.Bits ( (.|.) ) 20import Data.Bits ( (.|.) )
21import Control.Applicative ( liftA2, (<$>) ) 21import Control.Applicative ( liftA2, (<$>) )
22import System.Directory ( getHomeDirectory, doesFileExist ) 22import System.Directory ( getHomeDirectory, doesFileExist, createDirectoryIfMissing )
23import Control.Arrow ( first, second ) 23import Control.Arrow ( first, second )
24import Data.OpenPGP.Util (verify,fingerprint,decryptSecretKey,pgpSign) 24import Data.OpenPGP.Util (verify,fingerprint,decryptSecretKey,pgpSign)
25import Data.ByteString.Lazy ( ByteString ) 25import Data.ByteString.Lazy ( ByteString )
@@ -34,7 +34,8 @@ import Data.ASN1.Encoding ( encodeASN1, encodeASN1', decodeASN1, decodeASN1' )
34import Data.ASN1.BinaryEncoding ( DER(..) ) 34import Data.ASN1.BinaryEncoding ( DER(..) )
35import Data.Time.Clock.POSIX ( getPOSIXTime ) 35import Data.Time.Clock.POSIX ( getPOSIXTime )
36import qualified Data.Map as Map 36import qualified Data.Map as Map
37import qualified Data.ByteString.Lazy as L ( pack, null, readFile, writeFile, ByteString, toChunks ) 37import qualified Data.ByteString.Lazy as L ( unpack, pack, null, readFile, writeFile
38 , ByteString, toChunks )
38import qualified Data.ByteString as S ( unpack, splitAt, concat, cons ) 39import qualified Data.ByteString as S ( unpack, splitAt, concat, cons )
39import qualified Data.ByteString.Lazy.Char8 as Char8 ( span, unpack, break, concat, lines ) 40import qualified Data.ByteString.Lazy.Char8 as Char8 ( span, unpack, break, concat, lines )
40import qualified Crypto.Types.PubKey.ECC as ECC 41import qualified Crypto.Types.PubKey.ECC as ECC
@@ -44,7 +45,9 @@ import qualified Crypto.Hash.SHA1 as SHA1
44import qualified Data.Text as T ( Text, unpack, pack, 45import qualified Data.Text as T ( Text, unpack, pack,
45 strip, reverse, drop, break, dropAround ) 46 strip, reverse, drop, break, dropAround )
46import System.Posix.Types (EpochTime) 47import System.Posix.Types (EpochTime)
47import System.Posix.Files ( modificationTime, getFileStatus ) 48import System.Posix.Files ( modificationTime, getFileStatus
49 , setFileCreationMask, setFileTimes )
50import System.FilePath ( takeDirectory )
48import System.IO (hPutStrLn,withFile,IOMode(..)) 51import System.IO (hPutStrLn,withFile,IOMode(..))
49import Data.Binary ( encode ) 52import Data.Binary ( encode )
50 53
@@ -260,6 +263,8 @@ data KikiReportAction =
260 | WarnFailedToMakeSignature 263 | WarnFailedToMakeSignature
261 | FailedExternal Int 264 | FailedExternal Int
262 | ExternallyGeneratedFile 265 | ExternallyGeneratedFile
266 | UnableToExport KeyAlgorithm String
267 | FailedFileWrite
263 268
264data KikiResult a = KikiResult 269data KikiResult a = KikiResult
265 { kikiCondition :: KikiCondition a 270 { kikiCondition :: KikiCondition a
@@ -999,6 +1004,69 @@ subkeysForExport subspec (KeyData key _ _ subkeys) = do
999 sigtrusts 1004 sigtrusts
1000 in fmap fst v==Just True 1005 in fmap fst v==Just True
1001 1006
1007writePEM typ dta = pem
1008 where
1009 pem = unlines . concat $
1010 [ ["-----BEGIN " <> typ <> "-----"]
1011 , split64s dta
1012 , ["-----END " <> typ <> "-----"] ]
1013 split64s "" = []
1014 split64s dta = line : split64s rest where (line,rest) = splitAt 64 dta
1015
1016 -- 64 byte lines
1017
1018rsaPrivateKeyFromPacket :: Packet -> Maybe RSAPrivateKey
1019rsaPrivateKeyFromPacket pkt@(SecretKeyPacket {}) = do
1020 -- public fields...
1021 n <- lookup 'n' $ key pkt
1022 e <- lookup 'e' $ key pkt
1023 -- secret fields
1024 MPI d <- lookup 'd' $ key pkt
1025 MPI q <- lookup 'p' $ key pkt -- Note: p & q swapped
1026 MPI p <- lookup 'q' $ key pkt -- Note: p & q swapped
1027
1028 -- Note: Here we fail if 'u' key is missing.
1029 -- Ideally, it would be better to compute (inverse q) mod p
1030 -- see Algebra.Structures.EuclideanDomain.extendedEuclidAlg
1031 -- (package constructive-algebra)
1032 coefficient <- lookup 'u' $ key pkt
1033
1034 let dmodp1 = MPI $ d `mod` (p - 1)
1035 dmodqminus1 = MPI $ d `mod` (q - 1)
1036 return $ RSAPrivateKey
1037 { rsaN = n
1038 , rsaE = e
1039 , rsaD = MPI d
1040 , rsaP = MPI p
1041 , rsaQ = MPI q
1042 , rsaDmodP1 = dmodp1
1043 , rsaDmodQminus1 = dmodqminus1
1044 , rsaCoefficient = coefficient }
1045rsaPrivateKeyFromPacket _ = Nothing
1046
1047
1048writeKeyToFile False "PEM" fname packet =
1049 case key_algorithm packet of
1050 RSA -> do
1051 flip (maybe (return []))
1052 (rsaPrivateKeyFromPacket packet) -- RSAPrivateKey
1053 $ \rsa -> do
1054 let asn1 = toASN1 rsa []
1055 bs = encodeASN1 DER asn1
1056 dta = Base64.encode (L.unpack bs)
1057 output = writePEM "RSA PRIVATE KEY" dta
1058 stamp = toEnum . fromEnum $ timestamp packet
1059 createDirectoryIfMissing True (takeDirectory fname)
1060 handleIO_ (return [(fname, FailedFileWrite)]) $ do
1061 saved_mask <- setFileCreationMask 0o077
1062 writeFile fname output
1063 -- Note: The key's timestamp is included in it's fingerprint.
1064 -- Therefore, we should attempt to preserve it.
1065 setFileTimes fname stamp stamp
1066 setFileCreationMask saved_mask
1067 return [(fname, ExportedSubkey)]
1068 algo -> return [(fname, UnableToExport algo $ fingerprint packet)]
1069
1002writePEMKeys :: KeyDB 1070writePEMKeys :: KeyDB
1003 -> [(FilePath,Maybe String,[Packet],Maybe Initializer)] 1071 -> [(FilePath,Maybe String,[Packet],Maybe Initializer)]
1004 -> IO (KikiCondition [(FilePath,KikiReportAction)]) 1072 -> IO (KikiCondition [(FilePath,KikiReportAction)])
diff --git a/kiki.hs b/kiki.hs
index 43d170c..1586517 100644
--- a/kiki.hs
+++ b/kiki.hs
@@ -125,35 +125,6 @@ decode_sshrsa bs = do
125 return rsakey 125 return rsakey
126 126
127 127
128rsaPrivateKeyFromPacket :: Packet -> Maybe RSAPrivateKey
129rsaPrivateKeyFromPacket pkt@(SecretKeyPacket {}) = do
130 -- public fields...
131 n <- lookup 'n' $ key pkt
132 e <- lookup 'e' $ key pkt
133 -- secret fields
134 MPI d <- lookup 'd' $ key pkt
135 MPI q <- lookup 'p' $ key pkt -- Note: p & q swapped
136 MPI p <- lookup 'q' $ key pkt -- Note: p & q swapped
137
138 -- Note: Here we fail if 'u' key is missing.
139 -- Ideally, it would be better to compute (inverse q) mod p
140 -- see Algebra.Structures.EuclideanDomain.extendedEuclidAlg
141 -- (package constructive-algebra)
142 coefficient <- lookup 'u' $ key pkt
143
144 let dmodp1 = MPI $ d `mod` (p - 1)
145 dmodqminus1 = MPI $ d `mod` (q - 1)
146 return $ RSAPrivateKey
147 { rsaN = n
148 , rsaE = e
149 , rsaD = MPI d
150 , rsaP = MPI p
151 , rsaQ = MPI q
152 , rsaDmodP1 = dmodp1
153 , rsaDmodQminus1 = dmodqminus1
154 , rsaCoefficient = coefficient }
155rsaPrivateKeyFromPacket _ = Nothing
156
157 128
158{- 129{-
159getPackets :: IO [Packet] 130getPackets :: IO [Packet]
@@ -165,17 +136,6 @@ getPackets = do
165-} 136-}
166 137
167 138
168writePEM typ dta = pem
169 where
170 pem = unlines . concat $
171 [ ["-----BEGIN " <> typ <> "-----"]
172 , split64s dta
173 , ["-----END " <> typ <> "-----"] ]
174 split64s "" = []
175 split64s dta = line : split64s rest where (line,rest) = splitAt 64 dta
176
177 -- 64 byte lines
178
179isEmbeddedSignature (EmbeddedSignaturePacket {}) = True 139isEmbeddedSignature (EmbeddedSignaturePacket {}) = True
180isEmbeddedSignature _ = False 140isEmbeddedSignature _ = False
181 141
@@ -603,30 +563,6 @@ guessKeyFormat 'S' "ssh-client" = "PEM"
603guessKeyFormat 'S' "ssh-host" = "PEM" 563guessKeyFormat 'S' "ssh-host" = "PEM"
604guessKeyFormat _ _ = "PEM" -- "PGP" 564guessKeyFormat _ _ = "PEM" -- "PGP"
605 565
606writeKeyToFile False "PEM" fname packet =
607 case key_algorithm packet of
608 RSA -> do
609 flip (maybe (return ()))
610 (rsaPrivateKeyFromPacket packet) -- RSAPrivateKey
611 $ \rsa -> do
612 let asn1 = toASN1 rsa []
613 bs = encodeASN1 DER asn1
614 dta = Base64.encode (L.unpack bs)
615 output = writePEM "RSA PRIVATE KEY" dta
616 stamp = toEnum . fromEnum $ timestamp packet
617 createDirectoryIfMissing True (takeDirectory fname)
618 handleIO_ (warn $ fname ++ ": write failure") $ do
619 saved_mask <- setFileCreationMask 0o077
620 writeFile fname output
621 -- Note: The key's timestamp is included in it's fingerprint.
622 -- Therefore, we should attempt to preserve it.
623 setFileTimes fname stamp stamp
624 setFileCreationMask saved_mask
625 return ()
626 warn $ fname ++ ": exported"
627 return ()
628 algo -> warn $ fname ++ ": unable to export "++show algo++" key "++fingerprint packet
629
630readPublicKey :: Char8.ByteString -> RSAPublicKey 566readPublicKey :: Char8.ByteString -> RSAPublicKey
631readPublicKey bs = maybe er id $ do 567readPublicKey bs = maybe er id $ do
632 let (pre,bs1) = Char8.splitAt 7 bs 568 let (pre,bs1) = Char8.splitAt 7 bs