summaryrefslogtreecommitdiff
path: root/KeyRing.hs
diff options
context:
space:
mode:
Diffstat (limited to 'KeyRing.hs')
-rw-r--r--KeyRing.hs126
1 files changed, 123 insertions, 3 deletions
diff --git a/KeyRing.hs b/KeyRing.hs
index b738a25..e3d41d8 100644
--- a/KeyRing.hs
+++ b/KeyRing.hs
@@ -14,6 +14,7 @@ import Data.Ord
14import Data.List 14import Data.List
15import Data.OpenPGP 15import Data.OpenPGP
16import Data.Functor 16import Data.Functor
17import Data.Monoid
17import Data.Bits ( (.|.) ) 18import Data.Bits ( (.|.) )
18import Control.Applicative ( liftA2, (<$>) ) 19import Control.Applicative ( liftA2, (<$>) )
19import System.Directory ( getHomeDirectory, doesFileExist ) 20import System.Directory ( getHomeDirectory, doesFileExist )
@@ -27,15 +28,16 @@ import ControlMaybe ( handleIO_ )
27import Data.ASN1.Types ( toASN1, ASN1Object, fromASN1 28import Data.ASN1.Types ( toASN1, ASN1Object, fromASN1
28 , ASN1(Start,End,IntVal,OID,BitString), ASN1ConstructionType(Sequence) ) 29 , ASN1(Start,End,IntVal,OID,BitString), ASN1ConstructionType(Sequence) )
29import Data.ASN1.BitArray ( BitArray(..), toBitArray ) 30import Data.ASN1.BitArray ( BitArray(..), toBitArray )
30import Data.ASN1.Encoding ( encodeASN1, encodeASN1', decodeASN1' ) 31import Data.ASN1.Encoding ( encodeASN1, encodeASN1', decodeASN1, decodeASN1' )
31import Data.ASN1.BinaryEncoding ( DER(..) ) 32import Data.ASN1.BinaryEncoding ( DER(..) )
32import Data.Time.Clock.POSIX ( getPOSIXTime ) 33import Data.Time.Clock.POSIX ( getPOSIXTime )
33import qualified Data.Map as Map 34import qualified Data.Map as Map
34import qualified Data.ByteString.Lazy as L ( null, readFile, writeFile, ByteString, toChunks ) 35import qualified Data.ByteString.Lazy as L ( pack, null, readFile, writeFile, ByteString, toChunks )
35import qualified Data.ByteString as S ( unpack, splitAt, concat, cons ) 36import qualified Data.ByteString as S ( unpack, splitAt, concat, cons )
36import qualified Data.ByteString.Lazy.Char8 as Char8 ( span, unpack, break ) 37import qualified Data.ByteString.Lazy.Char8 as Char8 ( span, unpack, break, concat, lines )
37import qualified Crypto.Types.PubKey.ECC as ECC 38import qualified Crypto.Types.PubKey.ECC as ECC
38import qualified Codec.Binary.Base32 as Base32 39import qualified Codec.Binary.Base32 as Base32
40import qualified Codec.Binary.Base64 as Base64
39import qualified Crypto.Hash.SHA1 as SHA1 41import qualified Crypto.Hash.SHA1 as SHA1
40import qualified Data.Text as T ( Text, unpack, pack, 42import qualified Data.Text as T ( Text, unpack, pack,
41 strip, reverse, drop, break, dropAround ) 43 strip, reverse, drop, break, dropAround )
@@ -168,12 +170,59 @@ data RSAPrivateKey = RSAPrivateKey
168 } 170 }
169 deriving Show 171 deriving Show
170 172
173instance ASN1Object RSAPrivateKey where
174 toASN1 rsa@(RSAPrivateKey {})
175 = \xs -> Start Sequence
176 : IntVal 0
177 : mpiVal rsaN
178 : mpiVal rsaE
179 : mpiVal rsaD
180 : mpiVal rsaP
181 : mpiVal rsaQ
182 : mpiVal rsaDmodP1
183 : mpiVal rsaDmodQminus1
184 : mpiVal rsaCoefficient
185 : End Sequence
186 : xs
187 where mpiVal f = IntVal x where MPI x = f rsa
188
189 fromASN1 ( Start Sequence
190 : IntVal _ -- version
191 : IntVal n
192 : IntVal e
193 : IntVal d
194 : IntVal p
195 : IntVal q
196 : IntVal dmodp1
197 : IntVal dmodqminus1
198 : IntVal coefficient
199 : ys) =
200 Right ( privkey, tail $ dropWhile notend ys)
201 where
202 notend (End Sequence) = False
203 notend _ = True
204 privkey = RSAPrivateKey
205 { rsaN = MPI n
206 , rsaE = MPI e
207 , rsaD = MPI d
208 , rsaP = MPI p
209 , rsaQ = MPI q
210 , rsaDmodP1 = MPI dmodp1
211 , rsaDmodQminus1 = MPI dmodqminus1
212 , rsaCoefficient = MPI coefficient
213 }
214 fromASN1 _ =
215 Left "fromASN1: RSAPrivateKey: unexpected format"
216
217
171 218
172data KikiCondition a = KikiSuccess a 219data KikiCondition a = KikiSuccess a
173 | FailedToLock [FilePath] 220 | FailedToLock [FilePath]
174 | BadPassphrase 221 | BadPassphrase
175 | FailedToMakeSignature 222 | FailedToMakeSignature
176 | CantFindHome 223 | CantFindHome
224 | AmbiguousKeySpec
225 | CannotImportMasterKey
177 deriving ( Functor, Show ) 226 deriving ( Functor, Show )
178 227
179instance FunctorToMaybe KikiCondition where 228instance FunctorToMaybe KikiCondition where
@@ -479,6 +528,10 @@ buildKeyDB secring pubring grip0 keyring = do
479 528
480 db <- foldM importWalletKey (KikiSuccess (db_rings,[])) wallet_keys 529 db <- foldM importWalletKey (KikiSuccess (db_rings,[])) wallet_keys
481 try db $ \(db,report) -> do 530 try db $ \(db,report) -> do
531
532 -- todo: import PEMFiles
533 -- use_db <- foldM (doImport decrypt) use_db0 (map snd imports)
534
482 return $ KikiSuccess ( (db, grip, wk), report ) 535 return $ KikiSuccess ( (db, grip, wk), report )
483 536
484torhash key = maybe "" id $ derToBase32 <$> derRSA key 537torhash key = maybe "" id $ derToBase32 <$> derRSA key
@@ -495,6 +548,73 @@ try x body =
495 Left e -> return e 548 Left e -> return e
496 Right x -> body x 549 Right x -> body x
497 550
551readKeyFromFile False "PEM" fname = do
552 -- warn $ fname ++ ": reading ..."
553 -- Note: The key's timestamp is included in it's fingerprint.
554 -- Therefore, we should attempt to preserve it.
555 timestamp <- handleIO_ (error $ fname++": modificaiton time?") $
556 modificationTime <$> getFileStatus fname
557 input <- L.readFile fname
558 let dta = extractPEM "RSA PRIVATE KEY" input
559 -- Char8.putStrLn $ "dta = " <> dta
560 let rsa = do
561 e <- decodeASN1 DER . L.pack <$> Base64.decode (Char8.unpack dta)
562 asn1 <- either (const Nothing) Just e
563 k <- either (const Nothing) (Just . fst) (fromASN1 asn1)
564 let _ = k :: RSAPrivateKey
565 return k
566 -- putStrLn $ "rsa = "++ show rsa
567 return . Message $ do
568 rsa <- maybeToList rsa
569 return $ SecretKeyPacket
570 { version = 4
571 , timestamp = toEnum (fromEnum timestamp)
572 , key_algorithm = RSA
573 , key = [ -- public fields...
574 ('n',rsaN rsa)
575 ,('e',rsaE rsa)
576 -- secret fields
577 ,('d',rsaD rsa)
578 ,('p',rsaQ rsa) -- Note: p & q swapped
579 ,('q',rsaP rsa) -- Note: p & q swapped
580 ,('u',rsaCoefficient rsa)
581 ]
582 -- , ecc_curve = def
583 , s2k_useage = 0
584 , s2k = S2K 100 ""
585 , symmetric_algorithm = Unencrypted
586 , encrypted_data = ""
587 , is_subkey = True
588 }
589readKeyFromFile is_public fmt filename = error ("unimplemented key type: "++fmt)
590
591extractPEM typ pem = dta
592 where
593 dta = case ys of
594 _:dta_lines -> Char8.concat dta_lines
595 [] -> ""
596 xs = dropWhile (/="-----BEGIN " <> typ <> "-----") (Char8.lines pem)
597 ys = takeWhile (/="-----END " <> typ <> "-----") xs
598
599
600doImport
601 :: Ord k =>
602 (Packet -> IO (KikiCondition Packet))
603 -> Map.Map k KeyData
604 -> ([Char], Maybe [Char], [k], t)
605 -> IO (KikiCondition (Map.Map k KeyData, [(FilePath,KikiReportAction)]))
606doImport doDecrypt db (fname,subspec,ms,_) = do
607 let fetchkey = readKeyFromFile False "PEM" fname
608 flip (maybe $ return CannotImportMasterKey)
609 subspec $ \tag -> do
610 Message parsedkey <- fetchkey
611 flip (maybe $ return $ KikiSuccess (db,[]))
612 (listToMaybe parsedkey) $ \key -> do
613 let (m0,tailms) = splitAt 1 ms
614 if (not (null tailms) || null m0)
615 then return AmbiguousKeySpec
616 else doImportG doDecrypt db m0 tag fname key
617
498doImportG 618doImportG
499 :: Ord k => 619 :: Ord k =>
500 (Packet -> IO (KikiCondition Packet)) 620 (Packet -> IO (KikiCondition Packet))