diff options
-rw-r--r-- | CryptoCoins.hs | 66 | ||||
-rw-r--r-- | OpenPGP.hs | 44 | ||||
-rw-r--r-- | kiki.cabal | 6 | ||||
-rw-r--r-- | kiki.hs | 578 |
4 files changed, 569 insertions, 125 deletions
diff --git a/CryptoCoins.hs b/CryptoCoins.hs new file mode 100644 index 0000000..8ae092d --- /dev/null +++ b/CryptoCoins.hs | |||
@@ -0,0 +1,66 @@ | |||
1 | {-# LANGUAGE ViewPatterns #-} | ||
2 | module CryptoCoins where | ||
3 | |||
4 | import Numeric | ||
5 | import Data.Word | ||
6 | import Data.Maybe | ||
7 | |||
8 | data CoinNetwork = CoinNetwork | ||
9 | { network_name :: String | ||
10 | , public_byte_id :: Word8 | ||
11 | , private_byte_id :: Word8 | ||
12 | , source_code_uri :: String | ||
13 | } | ||
14 | deriving (Show,Read) | ||
15 | |||
16 | -- For forks of bitcoin, grep sources for PUBKEY_ADDRESS | ||
17 | -- That value + 0x80 will be the private_byte_id. | ||
18 | -- information source: https://raw.github.com/zamgo/PHPCoinAddress/master/README.md | ||
19 | coin_networks = | ||
20 | [ CoinNetwork "bitcoin" 0x00 0x80 "https://github.com/bitcoin/bitcoin" | ||
21 | , CoinNetwork "litecoin" 0x30 0xB0 "https://github.com/litecoin-project/litecoin" | ||
22 | , CoinNetwork "peercoin" 0x37 0xB7 "https://github.com/ppcoin/ppcoin" -- AKA: ppcoin | ||
23 | , CoinNetwork "namecoin" 0x34 0xB4 "https://github.com/namecoin/namecoin" | ||
24 | , CoinNetwork "bbqcoin" 0x05 0xD5 "https://github.com/overware/BBQCoin" | ||
25 | , CoinNetwork "bitbar" 0x19 0x99 "https://github.com/aLQ/bitbar" | ||
26 | , CoinNetwork "bytecoin" 0x12 0x80 "https://github.com/bryan-mills/bytecoin" | ||
27 | , CoinNetwork "chncoin" 0x1C 0x9C "https://github.com/CHNCoin/CHNCoin" | ||
28 | , CoinNetwork "devcoin" 0x00 0x80 "http://sourceforge.net/projects/galacticmilieu/files/DeVCoin" | ||
29 | , CoinNetwork "feathercoin" 0x0E 0x8E "https://github.com/FeatherCoin/FeatherCoin" | ||
30 | , CoinNetwork "freicoin" 0x00 0x80 "https://github.com/freicoin/freicoin" | ||
31 | , CoinNetwork "junkcoin" 0x10 0x90 "https://github.com/js2082/JKC" | ||
32 | , CoinNetwork "mincoin" 0x32 0xB2 "https://github.com/SandyCohen/mincoin" | ||
33 | , CoinNetwork "novacoin" 0x08 0x88 "https://github.com/CryptoManiac/novacoin" | ||
34 | , CoinNetwork "onecoin" 0x73 0xF3 "https://github.com/cre8r/onecoin" | ||
35 | , CoinNetwork "smallchange" 0x3E 0xBE "https://github.com/bfroemel/smallchange" | ||
36 | , CoinNetwork "terracoin" 0x00 0x80 "https://github.com/terracoin/terracoin" | ||
37 | , CoinNetwork "yacoin" 0x4D 0xCD "https://github.com/pocopoco/yacoin" | ||
38 | , CoinNetwork "bitcoin-t" 0x6F 0xEF "" | ||
39 | , CoinNetwork "bbqcoin-t" 0x19 0x99 "" | ||
40 | , CoinNetwork "bitbar-t" 0x73 0xF3 "" | ||
41 | ] | ||
42 | -- fairbrix - - https://github.com/coblee/Fairbrix | ||
43 | -- ixcoin - - https://github.com/ixcoin/ixcoin | ||
44 | -- royalcoin - - http://sourceforge.net/projects/royalcoin/ | ||
45 | |||
46 | lookupNetwork f b = listToMaybe $ filter (\n->f n==b) coin_networks | ||
47 | |||
48 | nameFromSecretByte :: Word8 -> String | ||
49 | nameFromSecretByte b = maybe (defaultName b) network_name (lookupNetwork private_byte_id b) | ||
50 | where | ||
51 | defaultName b = "?coin?"++hexit b | ||
52 | where | ||
53 | hexit b = pad0 $ showHex b "" | ||
54 | where pad0 [c] = '0':c:[] | ||
55 | pad0 cs = take 2 cs | ||
56 | |||
57 | publicByteFromName n = maybe (secretByteFromName n - 0x80) | ||
58 | -- exceptions to the above: bbqcoin, bytecoin | ||
59 | public_byte_id | ||
60 | (lookupNetwork network_name n) | ||
61 | |||
62 | secretByteFromName n = maybe (defaultID n) private_byte_id (lookupNetwork network_name n) | ||
63 | where | ||
64 | defaultID ('?':'c':'o':'i':'n':'?':(readHex->((x,_):_))) | ||
65 | = x | ||
66 | defaultID _ = 0x00 | ||
diff --git a/OpenPGP.hs b/OpenPGP.hs deleted file mode 100644 index 75054b3..0000000 --- a/OpenPGP.hs +++ /dev/null | |||
@@ -1,44 +0,0 @@ | |||
1 | module OpenPGP | ||
2 | ( verify | ||
3 | , fingerprint | ||
4 | , pgpSign | ||
5 | , decryptSecretKey | ||
6 | ) where | ||
7 | |||
8 | import Data.OpenPGP as OpenPGP | ||
9 | import Data.OpenPGP.CryptoAPI (verify,fingerprint,sign,decryptSecretKey) | ||
10 | import Data.Time.Clock.POSIX | ||
11 | import Control.Applicative ( (<$>) ) | ||
12 | import Crypto.Random (newGenIO,SystemRandom) | ||
13 | import ControlMaybe | ||
14 | |||
15 | now = floor <$> Data.Time.Clock.POSIX.getPOSIXTime | ||
16 | |||
17 | stampit timestamp sig = sig { hashed_subpackets = hashed' } | ||
18 | where | ||
19 | hashed_stamps = filter isStamp (hashed_subpackets sig) | ||
20 | unhashed_stamps = filter isStamp (unhashed_subpackets sig) | ||
21 | hashed' = case hashed_stamps ++ unhashed_stamps of | ||
22 | [] -> SignatureCreationTimePacket (fromIntegral timestamp) | ||
23 | : hashed_subpackets sig | ||
24 | _ -> hashed_subpackets sig | ||
25 | isStamp (SignatureCreationTimePacket {}) = True | ||
26 | isStamp _ = False | ||
27 | |||
28 | -- | Make a signature | ||
29 | -- | ||
30 | -- In order to set more options on a signature, pass in a signature packet. | ||
31 | pgpSign :: | ||
32 | OpenPGP.Message -- ^ SecretKeys, one of which will be used | ||
33 | -> OpenPGP.SignatureOver -- ^ Data to sign, and optional signature packet | ||
34 | -> OpenPGP.HashAlgorithm -- ^ HashAlgorithm to use in signature | ||
35 | -> String -- ^ KeyID of key to choose | ||
36 | -> IO (Maybe OpenPGP.SignatureOver) | ||
37 | pgpSign seckeys dta hash_algo keyid = | ||
38 | handleIO_ (return Nothing) $ do | ||
39 | timestamp <- now | ||
40 | g <- newGenIO :: IO SystemRandom | ||
41 | let sigs = map (stampit timestamp) $ signatures_over dta | ||
42 | dta' = dta { signatures_over = sigs } | ||
43 | let (r,g') = sign seckeys dta' hash_algo keyid timestamp g | ||
44 | return (Just r) | ||
@@ -15,11 +15,13 @@ Executable kiki | |||
15 | Main-is: kiki.hs | 15 | Main-is: kiki.hs |
16 | Build-Depends: base -any, cmdargs -any, directory -any, | 16 | Build-Depends: base -any, cmdargs -any, directory -any, |
17 | openpgp-crypto-api -any, | 17 | openpgp-crypto-api -any, |
18 | crypto-pubkey -any, cryptohash -any, | 18 | crypto-pubkey (>=0.2.3), cryptohash -any, |
19 | crypto-pubkey-types -any, | ||
19 | asn1-types -any, asn1-encoding -any, | 20 | asn1-types -any, asn1-encoding -any, |
20 | dataenc -any, text -any, pretty -any, pretty-show -any, | 21 | dataenc -any, text -any, pretty -any, pretty-show -any, |
21 | bytestring -any, openpgp (==0.6.1), binary -any, | 22 | bytestring -any, openpgp (==0.6.1), binary -any, |
22 | unix, time, crypto-api, cryptocipher (>=0.3.7), | 23 | unix, time, crypto-api, cryptocipher (>=0.3.7), |
23 | containers -any, process -any, filepath -any | 24 | containers -any, process -any, filepath -any, |
25 | hecc -any | ||
24 | ghc-options: -O2 | 26 | ghc-options: -O2 |
25 | c-sources: dotlock.c | 27 | c-sources: dotlock.c |
@@ -14,7 +14,7 @@ import GHC.IO.Exception ( ioException, IOErrorType(..) ) | |||
14 | import Data.IORef | 14 | import Data.IORef |
15 | import Data.Tuple | 15 | import Data.Tuple |
16 | import Data.Binary | 16 | import Data.Binary |
17 | import Data.OpenPGP | 17 | import Data.OpenPGP as OpenPGP |
18 | import qualified Data.ByteString.Lazy as L | 18 | import qualified Data.ByteString.Lazy as L |
19 | import qualified Data.ByteString.Lazy.Char8 as Char8 | 19 | import qualified Data.ByteString.Lazy.Char8 as Char8 |
20 | import qualified Data.ByteString as S | 20 | import qualified Data.ByteString as S |
@@ -23,8 +23,7 @@ import Control.Monad | |||
23 | import qualified Text.Show.Pretty as PP | 23 | import qualified Text.Show.Pretty as PP |
24 | import Text.PrettyPrint as PP hiding ((<>)) | 24 | import Text.PrettyPrint as PP hiding ((<>)) |
25 | import Data.List | 25 | import Data.List |
26 | -- import Data.OpenPGP.CryptoAPI (verify,fingerprint,sign,decryptSecretKey) | 26 | import Data.OpenPGP.Util (verify,fingerprint,decryptSecretKey,pgpSign) |
27 | import OpenPGP | ||
28 | import Data.Ord | 27 | import Data.Ord |
29 | import Data.Maybe | 28 | import Data.Maybe |
30 | import Data.Bits | 29 | import Data.Bits |
@@ -33,6 +32,12 @@ import Data.Text.Encoding | |||
33 | import qualified Codec.Binary.Base32 as Base32 | 32 | import qualified Codec.Binary.Base32 as Base32 |
34 | import qualified Codec.Binary.Base64 as Base64 | 33 | import qualified Codec.Binary.Base64 as Base64 |
35 | import qualified Crypto.Hash.SHA1 as SHA1 | 34 | import qualified Crypto.Hash.SHA1 as SHA1 |
35 | import qualified Crypto.Hash.SHA256 as SHA256 | ||
36 | import qualified Crypto.Hash.RIPEMD160 as RIPEMD160 | ||
37 | import qualified Crypto.Types.PubKey.ECC as ECC | ||
38 | -- import qualified Crypto.Types.PubKey.ECDSA as ECDSA | ||
39 | -- import qualified Crypto.PubKey.ECC.ECDSA as ECDSA | ||
40 | |||
36 | import Data.Char (toLower) | 41 | import Data.Char (toLower) |
37 | import qualified Crypto.PubKey.RSA as RSA | 42 | import qualified Crypto.PubKey.RSA as RSA |
38 | -- import Crypto.Random (newGenIO,SystemRandom) | 43 | -- import Crypto.Random (newGenIO,SystemRandom) |
@@ -40,6 +45,8 @@ import Data.ASN1.Types | |||
40 | import Data.ASN1.Encoding | 45 | import Data.ASN1.Encoding |
41 | import Data.ASN1.BinaryEncoding | 46 | import Data.ASN1.BinaryEncoding |
42 | import Data.ASN1.BitArray | 47 | import Data.ASN1.BitArray |
48 | import qualified Data.Foldable as Foldable | ||
49 | import qualified Data.Sequence as Sequence | ||
43 | import Control.Applicative | 50 | import Control.Applicative |
44 | import System.Environment | 51 | import System.Environment |
45 | import System.Directory | 52 | import System.Directory |
@@ -49,14 +56,15 @@ import System.Process | |||
49 | import System.Posix.IO (fdToHandle,fdRead) | 56 | import System.Posix.IO (fdToHandle,fdRead) |
50 | import System.Posix.Files | 57 | import System.Posix.Files |
51 | import System.Posix.Signals | 58 | import System.Posix.Signals |
59 | import System.Posix.Types (EpochTime) | ||
52 | import System.Process.Internals (runGenProcess_,defaultSignal) | 60 | import System.Process.Internals (runGenProcess_,defaultSignal) |
53 | import System.IO (hPutStrLn,stderr) | 61 | import System.IO (hPutStrLn,stderr,withFile,IOMode(..)) |
54 | import System.IO.Error | 62 | import System.IO.Error |
55 | import ControlMaybe | 63 | import ControlMaybe |
56 | import Data.Char | 64 | import Data.Char |
57 | import Control.Arrow (first,second) | 65 | import Control.Arrow (first,second) |
58 | import Data.Traversable hiding (mapM,forM) | 66 | import Data.Traversable hiding (mapM,forM,sequence) |
59 | import qualified Data.Traversable as Traversable (mapM,forM) | 67 | import qualified Data.Traversable as Traversable (mapM,forM,sequence) |
60 | import System.Console.CmdArgs | 68 | import System.Console.CmdArgs |
61 | -- import System.Posix.Time | 69 | -- import System.Posix.Time |
62 | import Data.Time.Clock.POSIX | 70 | import Data.Time.Clock.POSIX |
@@ -64,7 +72,30 @@ import Data.Monoid ((<>)) | |||
64 | -- import Data.X509 | 72 | -- import Data.X509 |
65 | import qualified Data.Map as Map | 73 | import qualified Data.Map as Map |
66 | import DotLock | 74 | import DotLock |
75 | import Codec.Crypto.ECC.Base -- hecc package | ||
76 | import Text.Printf | ||
77 | import Math.NumberTheory.Moduli | ||
78 | import qualified CryptoCoins as CryptoCoins | ||
79 | |||
80 | |||
81 | -- instance Default S.ByteString where def = S.empty | ||
82 | |||
83 | -- DER-encoded elliptic curve ids | ||
84 | nistp256_id = 0x2a8648ce3d030107 | ||
85 | secp256k1_id = 0x2b8104000a | ||
86 | |||
87 | isCryptoCoinKey p = | ||
88 | and [ isKey p | ||
89 | , key_algorithm p == ECDSA | ||
90 | , lookup 'c' (key p) == Just (MPI secp256k1_id) | ||
91 | ] | ||
67 | 92 | ||
93 | getCryptoCoinTag p | isSignaturePacket p = do | ||
94 | -- CryptoCoins.secret | ||
95 | let sps = hashed_subpackets p ++ unhashed_subpackets p | ||
96 | u <- listToMaybe $ mapMaybe usage sps | ||
97 | CryptoCoins.lookupNetwork CryptoCoins.network_name u | ||
98 | getCryptoCoinTag _ = Nothing | ||
68 | 99 | ||
69 | warn str = hPutStrLn stderr str | 100 | warn str = hPutStrLn stderr str |
70 | 101 | ||
@@ -201,6 +232,7 @@ derRSA rsa = do | |||
201 | k <- rsaKeyFromPacket rsa | 232 | k <- rsaKeyFromPacket rsa |
202 | return $ encodeASN1 DER (toASN1 k []) | 233 | return $ encodeASN1 DER (toASN1 k []) |
203 | 234 | ||
235 | rsaPrivateKeyFromPacket :: Packet -> Maybe RSAPrivateKey | ||
204 | rsaPrivateKeyFromPacket pkt@(SecretKeyPacket {}) = do | 236 | rsaPrivateKeyFromPacket pkt@(SecretKeyPacket {}) = do |
205 | -- public fields... | 237 | -- public fields... |
206 | n <- lookup 'n' $ key pkt | 238 | n <- lookup 'n' $ key pkt |
@@ -238,13 +270,11 @@ getPackets = do | |||
238 | Left (_,_,_) -> return [] | 270 | Left (_,_,_) -> return [] |
239 | 271 | ||
240 | 272 | ||
241 | instance Default S.ByteString where def = S.empty | ||
242 | |||
243 | secretToPublic pkt@(SecretKeyPacket {}) = | 273 | secretToPublic pkt@(SecretKeyPacket {}) = |
244 | PublicKeyPacket { version = version pkt | 274 | PublicKeyPacket { version = version pkt |
245 | , timestamp = timestamp pkt | 275 | , timestamp = timestamp pkt |
246 | , key_algorithm = key_algorithm pkt | 276 | , key_algorithm = key_algorithm pkt |
247 | , ecc_curve = ecc_curve pkt | 277 | -- , ecc_curve = ecc_curve pkt |
248 | , key = let seckey = key pkt | 278 | , key = let seckey = key pkt |
249 | pubs = public_key_fields (key_algorithm pkt) | 279 | pubs = public_key_fields (key_algorithm pkt) |
250 | in filter (\(k,v) -> k `elem` pubs) seckey | 280 | in filter (\(k,v) -> k `elem` pubs) seckey |
@@ -452,6 +482,10 @@ fpmatch grip key = | |||
452 | 482 | ||
453 | listKeys pkts = listKeysFiltered [] pkts | 483 | listKeys pkts = listKeysFiltered [] pkts |
454 | 484 | ||
485 | ecc_curve k = printf "%x" num :: String | ||
486 | where unmpi (MPI num) = num | ||
487 | num = maybe 0 unmpi $ lookup 'c' (key k) | ||
488 | |||
455 | listKeysFiltered grips pkts = do | 489 | listKeysFiltered grips pkts = do |
456 | let (certs,bs) = getBindings pkts | 490 | let (certs,bs) = getBindings pkts |
457 | as = accBindings bs | 491 | as = accBindings bs |
@@ -483,6 +517,9 @@ listKeysFiltered grips pkts = do | |||
483 | 3 -> " <-> " | 517 | 3 -> " <-> " |
484 | formkind = take kindcol $ defaultkind kind hashed ++ repeat ' ' | 518 | formkind = take kindcol $ defaultkind kind hashed ++ repeat ' ' |
485 | torhash = maybe "" id $ derToBase32 <$> derRSA sub | 519 | torhash = maybe "" id $ derToBase32 <$> derRSA sub |
520 | (netid,kind') = maybe (0x0,"bitcoin") | ||
521 | (\n->(CryptoCoins.publicByteFromName n,n)) | ||
522 | $ listToMaybe kind | ||
486 | unlines $ | 523 | unlines $ |
487 | concat [ " " | 524 | concat [ " " |
488 | -- , grip top | 525 | -- , grip top |
@@ -491,8 +528,13 @@ listKeysFiltered grips pkts = do | |||
491 | , " " | 528 | , " " |
492 | , fingerprint sub | 529 | , fingerprint sub |
493 | -- , " " ++ torhash | 530 | -- , " " ++ torhash |
531 | -- , " " ++ (concatMap (printf "%02X") $ S.unpack (ecc_curve sub)) | ||
494 | ] -- ++ ppShow hashed | 532 | ] -- ++ ppShow hashed |
495 | : showsigs claimants | 533 | : if isCryptoCoinKey sub |
534 | -- then (" " ++ "B⃦ " ++ bitcoinAddress sub) : showsigs claimants | ||
535 | -- then (" " ++ "BTC " ++ bitcoinAddress sub) : showsigs claimants | ||
536 | then (" " ++ "¢ "++kind'++":" ++ bitcoinAddress netid sub) : showsigs claimants | ||
537 | else showsigs claimants | ||
496 | torkeys = do | 538 | torkeys = do |
497 | (code,(top,sub), kind, hashed,claimants) <- subs | 539 | (code,(top,sub), kind, hashed,claimants) <- subs |
498 | guard ("tor" `elem` kind) | 540 | guard ("tor" `elem` kind) |
@@ -607,6 +649,42 @@ expandPath path (c:cs) | c/='/' = path ++ "/" ++ (c:cs) | |||
607 | expandPath path [] = [] | 649 | expandPath path [] = [] |
608 | 650 | ||
609 | 651 | ||
652 | -- type TimeStamp = Word32 | ||
653 | |||
654 | slurpWIPKeys :: System.Posix.Types.EpochTime -> L.ByteString -> ( [(Word8,Packet)], [L.ByteString]) | ||
655 | slurpWIPKeys stamp "" = ([],[]) | ||
656 | slurpWIPKeys stamp cs = | ||
657 | let (b58,xs) = Char8.span (\x -> elem x base58chars) cs | ||
658 | mb = decode_btc_key stamp (Char8.unpack b58) | ||
659 | in if L.null b58 | ||
660 | then let (ys,xs') = Char8.break (\x -> elem x base58chars) cs | ||
661 | (ks,js) = slurpWIPKeys stamp xs' | ||
662 | in (ks,ys:js) | ||
663 | else let (ks,js) = slurpWIPKeys stamp xs | ||
664 | in maybe (ks,b58:js) (\(net,Message [k])->((net,k):ks,js)) mb | ||
665 | |||
666 | readPacketsFromWallet :: | ||
667 | Maybe Packet | ||
668 | -> FilePath | ||
669 | -> IO [(Packet,Packet,(Packet,Map.Map FilePath Packet))] | ||
670 | readPacketsFromWallet wk fname = do | ||
671 | timestamp <- handleIO_ (error $ fname++": modificaiton time?") $ | ||
672 | modificationTime <$> getFileStatus fname | ||
673 | input <- L.readFile fname | ||
674 | let (ks,junk) = slurpWIPKeys timestamp input | ||
675 | when (not (null ks)) $ do | ||
676 | -- decrypt wk | ||
677 | -- create sigs | ||
678 | -- return key/sig pairs | ||
679 | return () | ||
680 | return $ do | ||
681 | wk <- maybeToList wk | ||
682 | guard (not $ null ks) | ||
683 | let prep (tagbyte,k) = (wk,k,(k,Map.singleton tag wk)) | ||
684 | where tag = CryptoCoins.nameFromSecretByte tagbyte | ||
685 | (wk,MarkerPacket,(MarkerPacket,Map.empty)) | ||
686 | :map prep ks | ||
687 | |||
610 | readPacketsFromFile :: FilePath -> IO Message | 688 | readPacketsFromFile :: FilePath -> IO Message |
611 | readPacketsFromFile fname = do | 689 | readPacketsFromFile fname = do |
612 | -- warn $ fname ++ ": reading..." | 690 | -- warn $ fname ++ ": reading..." |
@@ -614,7 +692,7 @@ readPacketsFromFile fname = do | |||
614 | return $ | 692 | return $ |
615 | case decodeOrFail input of | 693 | case decodeOrFail input of |
616 | Right (_,_,msg ) -> msg | 694 | Right (_,_,msg ) -> msg |
617 | Left (_,_,_) -> Message [] | 695 | Left (_,_,_) -> trace (fname++": read fail") $ Message [] |
618 | 696 | ||
619 | lockFiles fs = do | 697 | lockFiles fs = do |
620 | let dolock f = do | 698 | let dolock f = do |
@@ -806,26 +884,29 @@ guessKeyFormat 'S' "ssh-client" = "PEM" | |||
806 | guessKeyFormat 'S' "ssh-host" = "PEM" | 884 | guessKeyFormat 'S' "ssh-host" = "PEM" |
807 | guessKeyFormat _ _ = "PEM" -- "PGP" | 885 | guessKeyFormat _ _ = "PEM" -- "PGP" |
808 | 886 | ||
809 | writeKeyToFile False "PEM" fname packet = do | 887 | writeKeyToFile False "PEM" fname packet = |
810 | flip (maybe (return ())) | 888 | case key_algorithm packet of |
811 | (rsaPrivateKeyFromPacket packet) -- RSAPrivateKey | 889 | RSA -> do |
812 | $ \rsa -> do | 890 | flip (maybe (return ())) |
813 | let asn1 = toASN1 rsa [] | 891 | (rsaPrivateKeyFromPacket packet) -- RSAPrivateKey |
814 | bs = encodeASN1 DER asn1 | 892 | $ \rsa -> do |
815 | dta = Base64.encode (L.unpack bs) | 893 | let asn1 = toASN1 rsa [] |
816 | output = writePEM "RSA PRIVATE KEY" dta | 894 | bs = encodeASN1 DER asn1 |
817 | stamp = toEnum . fromEnum $ timestamp packet | 895 | dta = Base64.encode (L.unpack bs) |
818 | createDirectoryIfMissing True (takeDirectory fname) | 896 | output = writePEM "RSA PRIVATE KEY" dta |
819 | handleIO_ (warn $ fname ++ ": write failure") $ do | 897 | stamp = toEnum . fromEnum $ timestamp packet |
820 | saved_mask <- setFileCreationMask 0o077 | 898 | createDirectoryIfMissing True (takeDirectory fname) |
821 | writeFile fname output | 899 | handleIO_ (warn $ fname ++ ": write failure") $ do |
822 | -- Note: The key's timestamp is included in it's fingerprint. | 900 | saved_mask <- setFileCreationMask 0o077 |
823 | -- Therefore, we should attempt to preserve it. | 901 | writeFile fname output |
824 | setFileTimes fname stamp stamp | 902 | -- Note: The key's timestamp is included in it's fingerprint. |
825 | setFileCreationMask saved_mask | 903 | -- Therefore, we should attempt to preserve it. |
904 | setFileTimes fname stamp stamp | ||
905 | setFileCreationMask saved_mask | ||
906 | return () | ||
907 | warn $ fname ++ ": exported" | ||
826 | return () | 908 | return () |
827 | -- warn $ fname++ ": wrote" | 909 | algo -> warn $ fname ++ ": unable to export "++show algo++" key "++fingerprint packet |
828 | return () | ||
829 | 910 | ||
830 | readKeyFromFile False "PEM" fname = do | 911 | readKeyFromFile False "PEM" fname = do |
831 | -- warn $ fname ++ ": reading ..." | 912 | -- warn $ fname ++ ": reading ..." |
@@ -849,7 +930,6 @@ readKeyFromFile False "PEM" fname = do | |||
849 | { version = 4 | 930 | { version = 4 |
850 | , timestamp = toEnum (fromEnum timestamp) | 931 | , timestamp = toEnum (fromEnum timestamp) |
851 | , key_algorithm = RSA | 932 | , key_algorithm = RSA |
852 | , ecc_curve = def | ||
853 | , key = [ -- public fields... | 933 | , key = [ -- public fields... |
854 | ('n',rsaN rsa) | 934 | ('n',rsaN rsa) |
855 | ,('e',rsaE rsa) | 935 | ,('e',rsaE rsa) |
@@ -859,6 +939,7 @@ readKeyFromFile False "PEM" fname = do | |||
859 | ,('q',rsaP rsa) -- Note: p & q swapped | 939 | ,('q',rsaP rsa) -- Note: p & q swapped |
860 | ,('u',rsaCoefficient rsa) | 940 | ,('u',rsaCoefficient rsa) |
861 | ] | 941 | ] |
942 | -- , ecc_curve = def | ||
862 | , s2k_useage = 0 | 943 | , s2k_useage = 0 |
863 | , s2k = S2K 100 "" | 944 | , s2k = S2K 100 "" |
864 | , symmetric_algorithm = Unencrypted | 945 | , symmetric_algorithm = Unencrypted |
@@ -923,9 +1004,16 @@ origin p n = OriginFlags ispub n | |||
923 | type OriginMap = Map.Map FilePath OriginFlags | 1004 | type OriginMap = Map.Map FilePath OriginFlags |
924 | data MappedPacket = MappedPacket | 1005 | data MappedPacket = MappedPacket |
925 | { packet :: Packet | 1006 | { packet :: Packet |
1007 | , usage_tag :: Maybe String | ||
926 | , locations :: OriginMap | 1008 | , locations :: OriginMap |
927 | } | 1009 | } |
928 | 1010 | ||
1011 | mappedPacket filename p = MappedPacket | ||
1012 | { packet = p | ||
1013 | , usage_tag = Nothing | ||
1014 | , locations = Map.singleton filename (origin p (-1)) | ||
1015 | } | ||
1016 | |||
929 | type TrustMap = Map.Map FilePath Packet | 1017 | type TrustMap = Map.Map FilePath Packet |
930 | type SigAndTrust = ( MappedPacket | 1018 | type SigAndTrust = ( MappedPacket |
931 | , TrustMap ) -- trust packets | 1019 | , TrustMap ) -- trust packets |
@@ -945,7 +1033,7 @@ keykey key = | |||
945 | -- Note: The key's timestamp is included in it's fingerprint. | 1033 | -- Note: The key's timestamp is included in it's fingerprint. |
946 | -- Therefore, the same key with a different timestamp is | 1034 | -- Therefore, the same key with a different timestamp is |
947 | -- considered distinct using this keykey implementation. | 1035 | -- considered distinct using this keykey implementation. |
948 | fingerprint_material key -- TODO: smaller key? | 1036 | fingerprint_material (key {timestamp=0}) -- TODO: smaller key? |
949 | 1037 | ||
950 | 1038 | ||
951 | 1039 | ||
@@ -954,7 +1042,7 @@ uidkey (UserIDPacket str) = str | |||
954 | -- Compare master keys, LT is prefered for merging | 1042 | -- Compare master keys, LT is prefered for merging |
955 | keycomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT | 1043 | keycomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT |
956 | keycomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT | 1044 | keycomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT |
957 | keycomp a b | a==b = EQ | 1045 | keycomp a b | keykey a==keykey b = EQ |
958 | keycomp a b = error $ unlines ["Unable to merge keys:" | 1046 | keycomp a b = error $ unlines ["Unable to merge keys:" |
959 | , fingerprint a | 1047 | , fingerprint a |
960 | , PP.ppShow a | 1048 | , PP.ppShow a |
@@ -965,7 +1053,7 @@ keycomp a b = error $ unlines ["Unable to merge keys:" | |||
965 | -- Compare subkeys, LT is prefered for merging | 1053 | -- Compare subkeys, LT is prefered for merging |
966 | subcomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT | 1054 | subcomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT |
967 | subcomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT | 1055 | subcomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT |
968 | subcomp a b | a==b = EQ | 1056 | subcomp a b | keykey a==keykey b = EQ |
969 | subcomp a b = error $ unlines ["Unable to merge subs:" | 1057 | subcomp a b = error $ unlines ["Unable to merge subs:" |
970 | , fingerprint a | 1058 | , fingerprint a |
971 | , PP.ppShow a | 1059 | , PP.ppShow a |
@@ -975,10 +1063,16 @@ subcomp a b = error $ unlines ["Unable to merge subs:" | |||
975 | subcomp_m a b = subcomp (packet a) (packet b) | 1063 | subcomp_m a b = subcomp (packet a) (packet b) |
976 | 1064 | ||
977 | merge :: KeyDB -> FilePath -> Message -> KeyDB | 1065 | merge :: KeyDB -> FilePath -> Message -> KeyDB |
978 | merge db filename (Message ps) = foldl mergeit db (zip [0..] qs) | 1066 | merge db filename (Message ps) = merge_ db filename qs |
979 | where | 1067 | where |
980 | qs = scanPackets filename ps | 1068 | qs = scanPackets filename ps |
981 | asMapped n p = MappedPacket p (Map.singleton filename (origin p n)) | 1069 | |
1070 | merge_ :: KeyDB -> FilePath -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))] | ||
1071 | -> KeyDB | ||
1072 | merge_ db filename qs = foldl mergeit db (zip [0..] qs) | ||
1073 | where | ||
1074 | asMapped n p = let m = mappedPacket filename p | ||
1075 | in m { locations = fmap (\x->x {originalNum=n}) (locations m) } | ||
982 | asSigAndTrust n (p,tm) = (asMapped n p,tm) | 1076 | asSigAndTrust n (p,tm) = (asMapped n p,tm) |
983 | emptyUids = Map.empty | 1077 | emptyUids = Map.empty |
984 | -- mergeit db (_,_,TrustPacket {}) = db -- Filter TrustPackets | 1078 | -- mergeit db (_,_,TrustPacket {}) = db -- Filter TrustPackets |
@@ -997,8 +1091,8 @@ merge db filename (Message ps) = foldl mergeit db (zip [0..] qs) | |||
997 | = case v of | 1091 | = case v of |
998 | Nothing -> Just $ KeyData (asMapped n p) [] emptyUids Map.empty | 1092 | Nothing -> Just $ KeyData (asMapped n p) [] emptyUids Map.empty |
999 | Just (KeyData key sigs uids subkeys) | keykey (packet key) == keykey p | 1093 | Just (KeyData key sigs uids subkeys) | keykey (packet key) == keykey p |
1000 | -> Just $ KeyData ( MappedPacket (minimumBy keycomp [packet key,p]) | 1094 | -> Just $ KeyData ( (asMapped n (minimumBy keycomp [packet key,p])) |
1001 | (Map.insert filename (origin p n) (locations key)) ) | 1095 | { locations = Map.insert filename (origin p n) (locations key) } ) |
1002 | sigs | 1096 | sigs |
1003 | uids | 1097 | uids |
1004 | subkeys | 1098 | subkeys |
@@ -1028,8 +1122,8 @@ merge db filename (Message ps) = foldl mergeit db (zip [0..] qs) | |||
1028 | mergeSubkey :: Int -> Packet -> Maybe SubKey -> Maybe SubKey | 1122 | mergeSubkey :: Int -> Packet -> Maybe SubKey -> Maybe SubKey |
1029 | mergeSubkey n p Nothing = Just $ SubKey (asMapped n p) [] | 1123 | mergeSubkey n p Nothing = Just $ SubKey (asMapped n p) [] |
1030 | mergeSubkey n p (Just (SubKey key sigs)) = Just $ | 1124 | mergeSubkey n p (Just (SubKey key sigs)) = Just $ |
1031 | SubKey (MappedPacket (minimumBy subcomp [packet key,p]) | 1125 | SubKey ((asMapped n (minimumBy subcomp [packet key,p])) |
1032 | (Map.insert filename (origin p n) (locations key))) | 1126 | { locations = Map.insert filename (origin p n) (locations key) }) |
1033 | sigs | 1127 | sigs |
1034 | 1128 | ||
1035 | mergeUid :: Int ->(Packet,a) -> Maybe ([SigAndTrust],OriginMap) -> Maybe ([SigAndTrust],OriginMap) | 1129 | mergeUid :: Int ->(Packet,a) -> Maybe ([SigAndTrust],OriginMap) -> Maybe ([SigAndTrust],OriginMap) |
@@ -1049,15 +1143,15 @@ merge db filename (Message ps) = foldl mergeit db (zip [0..] qs) | |||
1049 | in xs ++ (mergeSameSig n sig y : ys') | 1143 | in xs ++ (mergeSameSig n sig y : ys') |
1050 | 1144 | ||
1051 | 1145 | ||
1052 | isSameSig (a,_) (MappedPacket b _,_) | isSignaturePacket a && isSignaturePacket b = | 1146 | isSameSig (a,_) (MappedPacket {packet=b},_) | isSignaturePacket a && isSignaturePacket b = |
1053 | a { unhashed_subpackets=[] } == b { unhashed_subpackets = [] } | 1147 | a { unhashed_subpackets=[] } == b { unhashed_subpackets = [] } |
1054 | isSameSig (a,_) (MappedPacket b _,_) = a==b | 1148 | isSameSig (a,_) (MappedPacket {packet=b},_) = a==b |
1055 | 1149 | ||
1056 | mergeSameSig :: Int -> (Packet,TrustMap) -> (MappedPacket,TrustMap) -> (MappedPacket, TrustMap) | 1150 | mergeSameSig :: Int -> (Packet,TrustMap) -> (MappedPacket,TrustMap) -> (MappedPacket, TrustMap) |
1057 | mergeSameSig n (a,ta) (MappedPacket b locs,tb) | isSignaturePacket a && isSignaturePacket b = | 1151 | mergeSameSig n (a,ta) (m@(MappedPacket{packet=b,locations=locs}),tb) | isSignaturePacket a && isSignaturePacket b = |
1058 | ( MappedPacket (b { unhashed_subpackets = | 1152 | ( m { packet = (b { unhashed_subpackets = |
1059 | foldl mergeItem (unhashed_subpackets b) (unhashed_subpackets a) }) | 1153 | foldl mergeItem (unhashed_subpackets b) (unhashed_subpackets a) }) |
1060 | (Map.insert filename (origin a n) locs) | 1154 | , locations = Map.insert filename (origin a n) locs } |
1061 | , tb `Map.union` ta ) | 1155 | , tb `Map.union` ta ) |
1062 | 1156 | ||
1063 | where | 1157 | where |
@@ -1100,21 +1194,22 @@ flattenAllUids fname ispub uids = | |||
1100 | 1194 | ||
1101 | flattenUid :: FilePath -> Bool -> (String,([SigAndTrust],OriginMap)) -> [MappedPacket] | 1195 | flattenUid :: FilePath -> Bool -> (String,([SigAndTrust],OriginMap)) -> [MappedPacket] |
1102 | flattenUid fname ispub (str,(sigs,om)) = | 1196 | flattenUid fname ispub (str,(sigs,om)) = |
1103 | MappedPacket (UserIDPacket str) om : concatSort fname head (unsig fname ispub) sigs | 1197 | (mappedPacket "" $ UserIDPacket str) {locations=om} : concatSort fname head (unsig fname ispub) sigs |
1104 | 1198 | ||
1105 | flattenSub :: FilePath -> Bool -> SubKey -> [MappedPacket] | 1199 | flattenSub :: FilePath -> Bool -> SubKey -> [MappedPacket] |
1106 | flattenSub fname ispub (SubKey key sigs) = unk ispub key: concatSort fname head (unsig fname ispub) sigs | 1200 | flattenSub fname ispub (SubKey key sigs) = unk ispub key: concatSort fname head (unsig fname ispub) sigs |
1107 | 1201 | ||
1108 | unk :: Bool -> MappedPacket -> MappedPacket | 1202 | unk :: Bool -> MappedPacket -> MappedPacket |
1109 | unk isPublic = if isPublic then toPacket secretToPublic else id | 1203 | unk isPublic = if isPublic then toPacket secretToPublic else id |
1110 | where toPacket f (MappedPacket p m) = MappedPacket (f p) m | 1204 | where toPacket f mp@(MappedPacket {packet=p}) = mp {packet=(f p)} |
1111 | 1205 | ||
1112 | unsig :: FilePath -> Bool -> SigAndTrust -> [MappedPacket] | 1206 | unsig :: FilePath -> Bool -> SigAndTrust -> [MappedPacket] |
1113 | unsig fname isPublic (sig,trustmap) = | 1207 | unsig fname isPublic (sig,trustmap) = |
1114 | [sig]++ map (asMapped (-1)) ( take 1 . Map.elems $ Map.filterWithKey f trustmap) | 1208 | [sig]++ map (asMapped (-1)) ( take 1 . Map.elems $ Map.filterWithKey f trustmap) |
1115 | where | 1209 | where |
1116 | f n _ = n==fname -- && trace ("fname=n="++show n) True | 1210 | f n _ = n==fname -- && trace ("fname=n="++show n) True |
1117 | asMapped n p = MappedPacket p (Map.singleton fname (origin p n)) | 1211 | asMapped n p = let m = mappedPacket fname p |
1212 | in m { locations = fmap (\x->x {originalNum=n}) (locations m) } | ||
1118 | 1213 | ||
1119 | ifSecret (SecretKeyPacket {}) t f = t | 1214 | ifSecret (SecretKeyPacket {}) t f = t |
1120 | ifSecret _ t f = f | 1215 | ifSecret _ t f = f |
@@ -1123,7 +1218,7 @@ showPacket :: Packet -> String | |||
1123 | showPacket p | isKey p = (if is_subkey p | 1218 | showPacket p | isKey p = (if is_subkey p |
1124 | then showPacket0 p | 1219 | then showPacket0 p |
1125 | else ifSecret p "----Secret-----" "----Public-----") | 1220 | else ifSecret p "----Secret-----" "----Public-----") |
1126 | ++ " "++ fingerprint p | 1221 | ++ " "++show (key_algorithm p)++" "++fingerprint p |
1127 | | isUserID p = showPacket0 p ++ " " ++ show (uidkey p) | 1222 | | isUserID p = showPacket0 p ++ " " ++ show (uidkey p) |
1128 | | otherwise = showPacket0 p | 1223 | | otherwise = showPacket0 p |
1129 | showPacket0 p = concat . take 1 $ words (show p) | 1224 | showPacket0 p = concat . take 1 $ words (show p) |
@@ -1172,26 +1267,78 @@ writeOutKeyrings lkmap db = do | |||
1172 | -- warn $ "writing "++f | 1267 | -- warn $ "writing "++f |
1173 | L.writeFile f (encode m) | 1268 | L.writeFile f (encode m) |
1174 | 1269 | ||
1175 | cross_merge keyrings f = do | 1270 | cross_merge doDecrypt grip0 keyrings wallets f = do |
1176 | let relock = do | 1271 | let relock = do |
1177 | (fsns,failed_locks) <- lockFiles keyrings | 1272 | (fsns,failed_locks) <- lockFiles keyrings |
1178 | forM_ failed_locks $ \f -> warn $ "Failed to lock: " ++ f | 1273 | (wsns,failed_wlocks) <- lockFiles wallets |
1179 | return (fsns,failed_locks) | 1274 | forM_ (failed_locks++failed_wlocks) $ \f -> warn $ "Failed to lock: " ++ f |
1275 | return (fsns,wsns,failed_locks,failed_wlocks) | ||
1180 | sec_n:_ = keyrings | 1276 | sec_n:_ = keyrings |
1181 | (fsns,failed_locks) <- relock | 1277 | (fsns,wsns,failed_locks,failed_wlocks) <- relock |
1182 | -- let (lks,fs) = unzip fsns | 1278 | -- let (lks,fs) = unzip fsns |
1183 | -- forM_ fs $ \f -> warn $ "locked: " ++ f | 1279 | -- forM_ fs $ \f -> warn $ "locked: " ++ f |
1184 | let readp n = fmap (n,) (readPacketsFromFile n) | 1280 | let readp n = fmap (n,) (readPacketsFromFile n) |
1281 | readw wk n = fmap (n,) (readPacketsFromWallet wk n) | ||
1185 | let pass n (fsns,failed_locks) = do | 1282 | let pass n (fsns,failed_locks) = do |
1186 | ms <- mapM readp (map snd fsns++failed_locks) | 1283 | ms <- mapM readp (map snd fsns++failed_locks) |
1187 | let db = foldl' (uncurry . merge) Map.empty ms | 1284 | let db0 = foldl' (uncurry . merge) Map.empty ms |
1188 | fstkey = listToMaybe $ mapMaybe isSecringKey ms | 1285 | fstkey = listToMaybe $ mapMaybe isSecringKey ms |
1189 | where isSecringKey (fn,Message ps) | 1286 | where isSecringKey (fn,Message ps) |
1190 | | fn==sec_n = listToMaybe ps | 1287 | | fn==sec_n = listToMaybe ps |
1191 | isSecringKey _ = Nothing | 1288 | isSecringKey _ = Nothing |
1289 | grip = grip0 `mplus` (fingerprint <$> fstkey) | ||
1290 | wk = listToMaybe $ do | ||
1291 | fp <- maybeToList grip | ||
1292 | elm <- Map.toList db0 | ||
1293 | guard $ matchSpec (KeyGrip fp) elm | ||
1294 | let undata (KeyData p _ _ _) = packet p | ||
1295 | return $ undata (snd elm) | ||
1296 | wms <- mapM (readw wk) (map snd wsns++failed_wlocks) | ||
1297 | let -- db1= foldl' (uncurry . merge_) db0 wms | ||
1298 | ts = do | ||
1299 | maybeToList wk | ||
1300 | (fname,xs) <- wms | ||
1301 | (_,sub,(_,m)) <- xs | ||
1302 | (tag,top) <- Map.toList m | ||
1303 | return (top,fname,sub,tag) | ||
1304 | |||
1305 | -- sig' <- makeSig doDecrypt top fname subkey_p tag mbsig | ||
1306 | importWalletKey db' (top,fname,sub,tag) = do | ||
1307 | doImportG doDecrypt | ||
1308 | db' | ||
1309 | (fmap keykey $ maybeToList wk) | ||
1310 | tag | ||
1311 | fname | ||
1312 | sub | ||
1313 | db <- foldM importWalletKey db0 ts | ||
1314 | let cs = do | ||
1315 | wk <- maybeToList wk | ||
1316 | let kk = keykey wk | ||
1317 | KeyData top topsigs uids subs <- maybeToList $ Map.lookup kk db | ||
1318 | (subkk,SubKey mp sigs) <- Map.toList subs | ||
1319 | let sub = packet mp | ||
1320 | guard $ isCryptoCoinKey sub | ||
1321 | tag <- take 1 $ mapMaybe getCryptoCoinTag (map (packet . fst) sigs) | ||
1322 | return (tag,mp) | ||
1323 | |||
1324 | -- export wallet keys | ||
1325 | forM_ wsns $ \(_,n) -> do | ||
1326 | let cs' = do | ||
1327 | (nw,mp) <- cs | ||
1328 | let fns = Map.keys (locations mp) | ||
1329 | -- trace ("COIN KEY: "++show fns) $ return () | ||
1330 | guard . not $ Map.member n (locations mp) | ||
1331 | let wip = walletImportFormat (CryptoCoins.private_byte_id nw) (packet mp) | ||
1332 | return (CryptoCoins.network_name nw,wip) | ||
1333 | handleIO_ (return ()) $ do | ||
1334 | withFile n AppendMode $ \fh -> do | ||
1335 | forM_ cs' $ \(net,wip) -> do | ||
1336 | warn $ n++": new WalletKey "++net | ||
1337 | hPutStrLn fh wip | ||
1338 | |||
1192 | -- unlockFiles fsns ----------- Originally, I did this to enable altering the gpg keyrings | 1339 | -- unlockFiles fsns ----------- Originally, I did this to enable altering the gpg keyrings |
1193 | ------------------------------- from external tools. | 1340 | ------------------------------- from external tools. |
1194 | (db',_) <- f (sec_n,fstkey) db | 1341 | (db',_) <- f (sec_n,grip) db |
1195 | -- lk <- relock --------------- The design is not quite safe, so it is disabled for now. | 1342 | -- lk <- relock --------------- The design is not quite safe, so it is disabled for now. |
1196 | let lk = (fsns,failed_locks) -- | 1343 | let lk = (fsns,failed_locks) -- |
1197 | ------------------------------- | 1344 | ------------------------------- |
@@ -1202,6 +1349,7 @@ cross_merge keyrings f = do | |||
1202 | let lkmap = Map.fromList $ map swap fsns | 1349 | let lkmap = Map.fromList $ map swap fsns |
1203 | writeOutKeyrings lkmap db | 1350 | writeOutKeyrings lkmap db |
1204 | unlockFiles fsns | 1351 | unlockFiles fsns |
1352 | unlockFiles wsns | ||
1205 | return () | 1353 | return () |
1206 | 1354 | ||
1207 | 1355 | ||
@@ -1234,12 +1382,16 @@ show_wk secring_file grip db = do | |||
1234 | 1382 | ||
1235 | show_all db = do | 1383 | show_all db = do |
1236 | let Message packets = flattenKeys True db | 1384 | let Message packets = flattenKeys True db |
1385 | -- let ks = filter isKey packets | ||
1386 | -- forM_ ks (warn . showPacket) | ||
1387 | -- warn $ "BEGIN LIST "++show (length packets)++" packets." | ||
1237 | putStrLn $ listKeys packets | 1388 | putStrLn $ listKeys packets |
1389 | -- warn $ "END LIST "++show (length packets)++" packets." | ||
1238 | 1390 | ||
1239 | show_pem keyspec wkgrip db = do | 1391 | show_pem keyspec wkgrip db = do |
1240 | let s = parseSpec wkgrip keyspec | 1392 | let s = parseSpec wkgrip keyspec |
1241 | flip (maybe $ warn (keyspec ++ ": not found") >> return ()) | 1393 | flip (maybe $ warn (keyspec ++ ": not found") >> return ()) |
1242 | (selectKey s db) | 1394 | (selectPublicKey s db) |
1243 | $ \k -> do | 1395 | $ \k -> do |
1244 | let rsa = pkcs8 . fromJust $ rsaKeyFromPacket k | 1396 | let rsa = pkcs8 . fromJust $ rsaKeyFromPacket k |
1245 | der = encodeASN1 DER (toASN1 rsa []) | 1397 | der = encodeASN1 DER (toASN1 rsa []) |
@@ -1247,6 +1399,14 @@ show_pem keyspec wkgrip db = do | |||
1247 | putStrLn $ | 1399 | putStrLn $ |
1248 | writePEM "PUBLIC KEY" qq -- ("TODO "++show keyspec) | 1400 | writePEM "PUBLIC KEY" qq -- ("TODO "++show keyspec) |
1249 | 1401 | ||
1402 | show_wip keyspec wkgrip db = do | ||
1403 | let s = parseSpec wkgrip keyspec | ||
1404 | flip (maybe $ warn (keyspec ++ ": not found") >> return ()) | ||
1405 | (selectSecretKey s db) | ||
1406 | $ \k -> do | ||
1407 | let nwb = maybe 0x80 CryptoCoins.secretByteFromName $ snd s | ||
1408 | putStrLn $ walletImportFormat nwb k | ||
1409 | |||
1250 | parseSpec :: String -> String -> (KeySpec,Maybe String) | 1410 | parseSpec :: String -> String -> (KeySpec,Maybe String) |
1251 | parseSpec grip spec = (topspec,subspec) | 1411 | parseSpec grip spec = (topspec,subspec) |
1252 | where | 1412 | where |
@@ -1343,7 +1503,6 @@ doExport doDecrypt (db,use_db) (fname,subspec,ms,cmd) = | |||
1343 | pun <- doDecrypt p | 1503 | pun <- doDecrypt p |
1344 | flip (maybe $ error "Bad passphrase?") pun $ \pun -> do | 1504 | flip (maybe $ error "Bad passphrase?") pun $ \pun -> do |
1345 | writeKeyToFile False "PEM" fname pun | 1505 | writeKeyToFile False "PEM" fname pun |
1346 | warn $ fname ++ ": exported" | ||
1347 | return (db,use_db) | 1506 | return (db,use_db) |
1348 | 1507 | ||
1349 | findTag tag wk subkey subsigs = (xs',minsig,ys') | 1508 | findTag tag wk subkey subsigs = (xs',minsig,ys') |
@@ -1374,25 +1533,242 @@ findTag tag wk subkey subsigs = (xs',minsig,ys') | |||
1374 | isNotation _ = False | 1533 | isNotation _ = False |
1375 | return (tag `elem` ks, sig) | 1534 | return (tag `elem` ks, sig) |
1376 | 1535 | ||
1536 | applyCurve curve x = x*x*x + x*a + b where (a,b)=(geta curve,getb curve) | ||
1537 | |||
1538 | secp256k1_oid = [1,3,132,0,10] | ||
1539 | secp256k1_curve = ECi l a b p r | ||
1540 | where | ||
1541 | -- y² = x³ + 7 (mod p) | ||
1542 | p = 0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F | ||
1543 | a = 0 | ||
1544 | b = 7 | ||
1545 | -- group order (also order of base point G) | ||
1546 | r = n | ||
1547 | n = 0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 | ||
1548 | -- cofactor | ||
1549 | h = 1 | ||
1550 | -- bit length | ||
1551 | l = 256 | ||
1552 | |||
1553 | secp256k1_G = ECPa secp256k1_curve | ||
1554 | 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 | ||
1555 | 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 | ||
1556 | {- | ||
1557 | The base point G in compressed form is: | ||
1558 | |||
1559 | G = 02 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798 | ||
1560 | |||
1561 | and in uncompressed form is: | ||
1562 | |||
1563 | G = 04 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798 | ||
1564 | 483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8 | ||
1565 | -} | ||
1566 | |||
1567 | base58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" | ||
1568 | |||
1569 | base58digits :: [Char] -> Maybe [Int] | ||
1570 | base58digits str = sequence mbs | ||
1571 | where | ||
1572 | mbs = map (flip elemIndex base58chars) str | ||
1573 | |||
1574 | -- 5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ | ||
1575 | base58_decode :: [Char] -> Maybe (Word8,[Word8]) | ||
1576 | base58_decode str = do | ||
1577 | ds <- base58digits str | ||
1578 | let n = foldl' (\a b-> a*58 + b) 0 $ ( map fromIntegral ds :: [Integer] ) | ||
1579 | rbytes = unfoldr getbyte n | ||
1580 | getbyte d = do | ||
1581 | guard (d/=0) | ||
1582 | let (q,b) = d `divMod` 256 | ||
1583 | return (fromIntegral b,q) | ||
1584 | |||
1585 | let (rcksum,rpayload) = splitAt 4 $ rbytes | ||
1586 | a_payload = reverse rpayload | ||
1587 | hash_result = S.take 4 . SHA256.hash . SHA256.hash . S.pack $ a_payload | ||
1588 | expected_hash = S.pack $ reverse rcksum | ||
1589 | (network_id,payload) = splitAt 1 a_payload | ||
1590 | |||
1591 | network_id <- listToMaybe network_id | ||
1592 | guard (hash_result==expected_hash) | ||
1593 | return (network_id,payload) | ||
1594 | |||
1595 | walletImportFormat idbyte k = secret_base58_foo | ||
1596 | where | ||
1597 | isSecret (SecretKeyPacket {}) = True | ||
1598 | isSecret _ = False | ||
1599 | secret_base58_foo = base58_encode seckey | ||
1600 | Just d = lookup 'd' (key k) | ||
1601 | (len16,bigendian) = S.splitAt 2 (S.concat $ L.toChunks $ encode d) | ||
1602 | seckey = S.cons idbyte bigendian | ||
1603 | |||
1604 | |||
1605 | base58_encode :: S.ByteString -> String | ||
1606 | base58_encode hash = replicate zcount '1' ++ map (base58chars !!) (reverse rdigits) | ||
1607 | where | ||
1608 | zcount = S.length . S.takeWhile (==0) $ hash | ||
1609 | cksum = S.take 4 . SHA256.hash . SHA256.hash $ hash | ||
1610 | n = foldl' (\a b->a*256+b) 0 . map asInteger $ concatMap S.unpack [hash, cksum] | ||
1611 | asInteger x = fromIntegral x :: Integer | ||
1612 | rdigits = unfoldr getdigit n | ||
1613 | where | ||
1614 | getdigit d = do | ||
1615 | guard (d/=0) | ||
1616 | let (q,b) = d `divMod` 58 | ||
1617 | return (fromIntegral b,q) | ||
1618 | |||
1619 | cannonical_eckey x y = 0x4:pad32(numToBytes x) ++ pad32(numToBytes y) :: [Word8] | ||
1620 | where | ||
1621 | numToBytes n = reverse $ unfoldr getbyte n | ||
1622 | where | ||
1623 | getbyte d = do | ||
1624 | guard (d/=0) | ||
1625 | let (q,b) = d `divMod` 256 | ||
1626 | return (fromIntegral b,q) | ||
1627 | pad32 xs = replicate zlen 0 ++ xs | ||
1628 | where | ||
1629 | zlen = 32 - length xs | ||
1630 | |||
1631 | oidToDER ns = S.pack $ b1 : concatMap encode ys | ||
1632 | where | ||
1633 | (xs,ys) = splitAt 2 ns | ||
1634 | b1 = fromIntegral $ foldl' (\a b->a*40+b) 0 xs | ||
1635 | encode x | x <= 127 = [fromIntegral x] | ||
1636 | | otherwise = (\(x:xs)-> reverse (x:map (0x80 .|.) xs)) | ||
1637 | (base128r x) | ||
1638 | base128r n = unfoldr getbyte n | ||
1639 | where | ||
1640 | getbyte d = do | ||
1641 | guard (d/=0) | ||
1642 | let (q,b) = d `divMod` 128 | ||
1643 | return (fromIntegral b,q) | ||
1644 | |||
1645 | nistp256=[1,2,840,10045,3,1,7] | ||
1646 | nistp256_der=[0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07] | ||
1647 | -- "\x2a\x86\x48\xce\x3d\x03\x01\x07" | ||
1648 | {- OID Curve description Curve name | ||
1649 | ---------------------------------------------------------------- | ||
1650 | 1.2.840.10045.3.1.7 NIST Curve P-256 [FIPS 186-2] "NIST P-256" | ||
1651 | 1.3.132.0.34 NIST Curve P-384 [FIPS 186-2] "NIST P-384" | ||
1652 | 1.3.132.0.35 NIST Curve P-521 [FIPS 186-2] "NIST P-521" | ||
1653 | |||
1654 | Implementations MUST implement "NIST P-256", "NIST P-384" and "NIST | ||
1655 | P-521". The hexadecimal representation used in the public and | ||
1656 | private key encodings are: | ||
1657 | |||
1658 | Curve Name Len Hexadecimal representation of the OID | ||
1659 | ---------------------------------------------------------------- | ||
1660 | "NIST P-256" 8 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 | ||
1661 | "NIST P-384" 6 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22 | ||
1662 | "NIST P-521" 6 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23 | ||
1663 | -} | ||
1664 | |||
1665 | bitcoinAddress network_id k = address | ||
1666 | where | ||
1667 | Just (MPI x) = lookup 'x' (key k) | ||
1668 | Just (MPI y) = lookup 'y' (key k) | ||
1669 | pub = cannonical_eckey x y | ||
1670 | hash = S.cons network_id . RIPEMD160.hash . SHA256.hash . S.pack $ pub | ||
1671 | address = base58_encode hash | ||
1672 | |||
1673 | -- gpg supported ECDSA curve: 2A8648CE3D030107 | ||
1674 | -- 2A 86 48 CE 3D 03 01 07 | ||
1675 | -- 1,2,134,72,206,61,3,1,7 | ||
1676 | -- 6*128+0x48 840 | ||
1677 | -- 0x4e*128+0x3d 10045 | ||
1678 | -- 1.2.840.10045.3.1.7 --> NIST P-256 | ||
1679 | -- | ||
1680 | decode_btc_key timestamp str = do | ||
1681 | (network_id,us) <- base58_decode str | ||
1682 | return . (network_id,) $ Message $ do | ||
1683 | let d = foldl' (\a b->a*256+b) 0 (map fromIntegral us :: [Integer]) | ||
1684 | xy = secp256k1_G `pmul` d | ||
1685 | x = getx xy | ||
1686 | y = gety xy | ||
1687 | -- y² = x³ + 7 (mod p) | ||
1688 | y' = sqrtModP' (applyCurve secp256k1_curve x) (getp secp256k1_curve) | ||
1689 | y'' = sqrtModPList (applyCurve secp256k1_curve x) (getp secp256k1_curve) | ||
1690 | pub = cannonical_eckey x y | ||
1691 | hash = S.cons network_id . RIPEMD160.hash . SHA256.hash . S.pack $ pub | ||
1692 | address = base58_encode hash | ||
1693 | pubstr = concatMap (printf "%02x") $ pub | ||
1694 | _ = pubstr :: String | ||
1695 | return $ {- trace (unlines ["pub="++show pubstr | ||
1696 | ,"add="++show address | ||
1697 | ,"y ="++show y | ||
1698 | ,"y' ="++show y' | ||
1699 | ,"y''="++show y'']) -} | ||
1700 | SecretKeyPacket | ||
1701 | { version = 4 | ||
1702 | , timestamp = toEnum (fromEnum timestamp) | ||
1703 | , key_algorithm = ECDSA | ||
1704 | , key = [ -- public fields... | ||
1705 | ('c',MPI secp256k1_id) -- secp256k1 (bitcoin curve) | ||
1706 | ,('l',MPI 256) | ||
1707 | ,('x',MPI x) | ||
1708 | ,('y',MPI y) | ||
1709 | -- secret fields | ||
1710 | ,('d',MPI d) | ||
1711 | ] | ||
1712 | , s2k_useage = 0 | ||
1713 | , s2k = S2K 100 "" | ||
1714 | , symmetric_algorithm = Unencrypted | ||
1715 | , encrypted_data = "" | ||
1716 | , is_subkey = True | ||
1717 | } | ||
1718 | |||
1719 | doBTCImport doDecrypt db (ms,subspec,content) = do | ||
1720 | let fetchkey = do | ||
1721 | timestamp <- now | ||
1722 | let mbk = fmap discardNetworkID $ decode_btc_key timestamp content | ||
1723 | discardNetworkID = snd | ||
1724 | return $ maybe (Message []) id mbk | ||
1725 | let error s = do | ||
1726 | warn s | ||
1727 | exitFailure | ||
1728 | flip (maybe $ error "Cannot import master key.") | ||
1729 | subspec $ \tag -> do | ||
1730 | Message parsedkey <- fetchkey | ||
1731 | flip (maybe $ return db) | ||
1732 | (listToMaybe parsedkey) $ \key -> do | ||
1733 | let (m0,tailms) = splitAt 1 ms | ||
1734 | when (not (null tailms) || null m0) | ||
1735 | $ error "Key specification is ambiguous." | ||
1736 | doImportG doDecrypt db m0 tag "" key | ||
1737 | |||
1377 | doImport doDecrypt db (fname,subspec,ms,_) = do | 1738 | doImport doDecrypt db (fname,subspec,ms,_) = do |
1739 | let fetchkey = readKeyFromFile False "PEM" fname | ||
1378 | let error s = do | 1740 | let error s = do |
1379 | warn s | 1741 | warn s |
1380 | exitFailure | 1742 | exitFailure |
1381 | flip (maybe $ error "Cannot import master key.") | 1743 | flip (maybe $ error "Cannot import master key.") |
1382 | subspec $ \tag -> do | 1744 | subspec $ \tag -> do |
1383 | Message parsedkey <- readKeyFromFile False "PEM" fname | 1745 | Message parsedkey <- fetchkey |
1384 | flip (maybe $ return db) | 1746 | flip (maybe $ return db) |
1385 | (listToMaybe parsedkey) $ \key -> do | 1747 | (listToMaybe parsedkey) $ \key -> do |
1386 | let (m0,tailms) = splitAt 1 ms | 1748 | let (m0,tailms) = splitAt 1 ms |
1387 | when (not (null tailms) || null m0) | 1749 | when (not (null tailms) || null m0) |
1388 | $ error "Key specification is ambiguous." | 1750 | $ error "Key specification is ambiguous." |
1751 | doImportG doDecrypt db m0 tag fname key | ||
1752 | |||
1753 | doImportG doDecrypt db m0 tag fname key = do | ||
1754 | let error s = do | ||
1755 | warn s | ||
1756 | exitFailure | ||
1389 | let kk = head m0 | 1757 | let kk = head m0 |
1390 | Just (KeyData top topsigs uids subs) = Map.lookup kk db | 1758 | Just (KeyData top topsigs uids subs) = Map.lookup kk db |
1391 | subkk = keykey key | 1759 | subkk = keykey key |
1392 | (is_new, subkey) = maybe (True, SubKey (MappedPacket key (Map.singleton fname (origin key (-1)))) | 1760 | (is_new, subkey) = maybe (True, SubKey (mappedPacket fname key) |
1393 | []) | 1761 | []) |
1394 | (False,) | 1762 | ( (False,) . addOrigin ) |
1395 | (Map.lookup subkk subs) | 1763 | (Map.lookup subkk subs) |
1764 | where | ||
1765 | addOrigin (SubKey mp sigs) = | ||
1766 | let mp' = mp | ||
1767 | { locations = Map.insert fname | ||
1768 | (origin (packet mp) (-1)) | ||
1769 | (locations mp) } | ||
1770 | in SubKey mp' sigs | ||
1771 | subs' = Map.insert subkk subkey subs | ||
1396 | 1772 | ||
1397 | istor = do | 1773 | istor = do |
1398 | guard (tag == "tor") | 1774 | guard (tag == "tor") |
@@ -1421,7 +1797,8 @@ doImport doDecrypt db (fname,subspec,ms,_) = do | |||
1421 | $ \sig -> do | 1797 | $ \sig -> do |
1422 | let om = Map.singleton fname (origin sig (-1)) | 1798 | let om = Map.singleton fname (origin sig (-1)) |
1423 | trust = Map.empty | 1799 | trust = Map.empty |
1424 | return $ Map.insert idstr ([(MappedPacket sig om,trust)],om) uids | 1800 | return $ Map.insert idstr ([( (mappedPacket fname sig) {locations=om} |
1801 | ,trust)],om) uids | ||
1425 | 1802 | ||
1426 | let SubKey subkey_p subsigs = subkey | 1803 | let SubKey subkey_p subsigs = subkey |
1427 | wk = packet top | 1804 | wk = packet top |
@@ -1438,7 +1815,7 @@ doImport doDecrypt db (fname,subspec,ms,_) = do | |||
1438 | Nothing -> doInsert Nothing db -- we need to create a new sig | 1815 | Nothing -> doInsert Nothing db -- we need to create a new sig |
1439 | Just (True,sig) -> -- we can deduce is_new == False | 1816 | Just (True,sig) -> -- we can deduce is_new == False |
1440 | -- we may need to add a tor id | 1817 | -- we may need to add a tor id |
1441 | return $ Map.insert kk (KeyData top topsigs uids' subs) db | 1818 | return $ Map.insert kk (KeyData top topsigs uids' subs') db |
1442 | Just (False,sig) -> doInsert (Just sig) db -- We have a sig, but is missing usage@ tag | 1819 | Just (False,sig) -> doInsert (Just sig) db -- We have a sig, but is missing usage@ tag |
1443 | 1820 | ||
1444 | 1821 | ||
@@ -1451,7 +1828,7 @@ makeSig doDecrypt top fname subkey_p tag mbsig = do | |||
1451 | flip (maybe $ error "Failed to make signature.") | 1828 | flip (maybe $ error "Failed to make signature.") |
1452 | (new_sig >>= listToMaybe . signatures_over) | 1829 | (new_sig >>= listToMaybe . signatures_over) |
1453 | $ \new_sig -> do | 1830 | $ \new_sig -> do |
1454 | let mp' = MappedPacket new_sig (Map.singleton fname (origin new_sig (-1))) | 1831 | let mp' = mappedPacket fname new_sig |
1455 | return (mp', Map.empty) | 1832 | return (mp', Map.empty) |
1456 | parsedkey = [packet $ subkey_p] | 1833 | parsedkey = [packet $ subkey_p] |
1457 | hashed0 = | 1834 | hashed0 = |
@@ -1479,7 +1856,9 @@ makeSig doDecrypt top fname subkey_p tag mbsig = do | |||
1479 | (sigpackets 0x19 | 1856 | (sigpackets 0x19 |
1480 | hashed0 | 1857 | hashed0 |
1481 | [IssuerPacket subgrip])) | 1858 | [IssuerPacket subgrip])) |
1482 | SHA1 | 1859 | (if key_algorithm (head parsedkey)==ECDSA |
1860 | then SHA256 | ||
1861 | else SHA1) | ||
1483 | subgrip | 1862 | subgrip |
1484 | let iss = IssuerPacket (fingerprint wk) | 1863 | let iss = IssuerPacket (fingerprint wk) |
1485 | cons_iss back_sig = iss : map EmbeddedSignaturePacket (signatures_over back_sig) | 1864 | cons_iss back_sig = iss : map EmbeddedSignaturePacket (signatures_over back_sig) |
@@ -1582,7 +1961,12 @@ kiki_usage = do | |||
1582 | ," --keyrings FILE FILE..." | 1961 | ," --keyrings FILE FILE..." |
1583 | ," Provide keyring files other than the implicit secring.gpg and" | 1962 | ," Provide keyring files other than the implicit secring.gpg and" |
1584 | ," pubring.gpg in the --homedir. This option is implicit unless" | 1963 | ," pubring.gpg in the --homedir. This option is implicit unless" |
1585 | ," --keypairs is used." | 1964 | ," --keypairs or --wallets is used." |
1965 | ,"" | ||
1966 | ," --wallets FILE FILE..." | ||
1967 | ," Provide wallet files with secret crypto-coin keys in Wallet" | ||
1968 | ," Import Format. The keys will be treated as subkeys of your" | ||
1969 | ," current working key (the one shown by --show-wk)." | ||
1586 | ,"" | 1970 | ,"" |
1587 | ," --keypairs KEYSPEC KEYSPEC..." | 1971 | ," --keypairs KEYSPEC KEYSPEC..." |
1588 | ," Each KEYSPEC specifies that a key should match the content and" | 1972 | ," Each KEYSPEC specifies that a key should match the content and" |
@@ -1602,6 +1986,9 @@ kiki_usage = do | |||
1602 | ," --show-pem SPEC" | 1986 | ," --show-pem SPEC" |
1603 | ," Outputs the PKCS #8 public key corresponding to SPEC." | 1987 | ," Outputs the PKCS #8 public key corresponding to SPEC." |
1604 | ,"" | 1988 | ,"" |
1989 | ," --show-wip SPEC" | ||
1990 | ," Outputs the secret crypto-coin key in Wallet Input Format." | ||
1991 | ,"" | ||
1605 | ," --help Shows this help screen." | 1992 | ," --help Shows this help screen." |
1606 | ] | 1993 | ] |
1607 | 1994 | ||
@@ -1639,7 +2026,7 @@ main = do | |||
1639 | , ("--show-wip",1) | 2026 | , ("--show-wip",1) |
1640 | , ("--help",0) | 2027 | , ("--help",0) |
1641 | ] | 2028 | ] |
1642 | argspec = map fst sargspec ++ ["--keyrings","--keypairs"] | 2029 | argspec = map fst sargspec ++ ["--keyrings","--keypairs","--wallets"] |
1643 | -- "--bitcoin-keypairs" | 2030 | -- "--bitcoin-keypairs" |
1644 | -- Disabled. We shouldn't accept private key | 2031 | -- Disabled. We shouldn't accept private key |
1645 | -- data on the command line. | 2032 | -- data on the command line. |
@@ -1670,6 +2057,17 @@ main = do | |||
1670 | guard $ take 1 bdmcb == "}" | 2057 | guard $ take 1 bdmcb == "}" |
1671 | let cmd = (drop 1 . reverse . drop 1) bdmcb | 2058 | let cmd = (drop 1 . reverse . drop 1) bdmcb |
1672 | Just (spec,file,cmd) | 2059 | Just (spec,file,cmd) |
2060 | btcpairs0 = | ||
2061 | flip map (maybe [] id $ Map.lookup "--bitcoin-keypairs" margs) $ \specfile -> do | ||
2062 | let (spec,efilecmd) = break (=='=') specfile | ||
2063 | (spec,protocnt) <- do | ||
2064 | return $ if take 1 efilecmd=="=" then (spec,drop 1 efilecmd) | ||
2065 | else ("",spec) | ||
2066 | let (proto,content) = break (==':') protocnt | ||
2067 | spec <- return $ if null spec then "bitcoin" else spec | ||
2068 | return $ | ||
2069 | if take 1 content =="=" then (spec,proto,drop 1 content) | ||
2070 | else (spec,"base58",proto) | ||
1673 | publics = | 2071 | publics = |
1674 | flip map (maybe [] id $ Map.lookup "--public" margs) $ \specfile -> do | 2072 | flip map (maybe [] id $ Map.lookup "--public" margs) $ \specfile -> do |
1675 | let (spec,efile) = break (=='=') specfile | 2073 | let (spec,efile) = break (=='=') specfile |
@@ -1677,6 +2075,7 @@ main = do | |||
1677 | let file= drop 1 efile | 2075 | let file= drop 1 efile |
1678 | Just (spec,file) | 2076 | Just (spec,file) |
1679 | keyrings_ = maybe [] id $ Map.lookup "--keyrings" margs | 2077 | keyrings_ = maybe [] id $ Map.lookup "--keyrings" margs |
2078 | wallets = maybe [] id $ Map.lookup "--wallets" margs | ||
1680 | passphrase_fd = concat <$> Map.lookup "--passphrase-fd" margs | 2079 | passphrase_fd = concat <$> Map.lookup "--passphrase-fd" margs |
1681 | decrypt wk = do | 2080 | decrypt wk = do |
1682 | -- warn $ "decryptKey "++fingerprint wk | 2081 | -- warn $ "decryptKey "++fingerprint wk |
@@ -1706,6 +2105,7 @@ main = do | |||
1706 | exitFailure | 2105 | exitFailure |
1707 | 2106 | ||
1708 | let keypairs = catMaybes keypairs0 | 2107 | let keypairs = catMaybes keypairs0 |
2108 | btcpairs = catMaybes btcpairs0 | ||
1709 | 2109 | ||
1710 | (homedir,secring,pubring,grip0) <- getHomeDir ( concat <$> Map.lookup "--homedir" margs) | 2110 | (homedir,secring,pubring,grip0) <- getHomeDir ( concat <$> Map.lookup "--homedir" margs) |
1711 | 2111 | ||
@@ -1717,9 +2117,7 @@ main = do | |||
1717 | putStrLn $ "keyrings = "++show keyrings | 2117 | putStrLn $ "keyrings = "++show keyrings |
1718 | -} | 2118 | -} |
1719 | 2119 | ||
1720 | cross_merge keyrings $ \(secfile,fstkey) db -> do | 2120 | cross_merge decrypt grip0 keyrings wallets $ \(secfile,grip) db -> do |
1721 | let grip = grip0 `mplus` (fingerprint <$> fstkey) | ||
1722 | |||
1723 | let get_use_db = maybe (return db) import_db | 2121 | let get_use_db = maybe (return db) import_db |
1724 | $ Map.lookup "--import" margs | 2122 | $ Map.lookup "--import" margs |
1725 | import_db _ = do | 2123 | import_db _ = do |
@@ -1733,7 +2131,7 @@ main = do | |||
1733 | altered = map (second append_loc) to_alters | 2131 | altered = map (second append_loc) to_alters |
1734 | append_loc (KeyData p a b c) = KeyData p' a b c | 2132 | append_loc (KeyData p a b c) = KeyData p' a b c |
1735 | where p' = p { locations = Map.insert pubring | 2133 | where p' = p { locations = Map.insert pubring |
1736 | (origin (packet p) (-1)) | 2134 | (origin (secretToPublic (packet p)) (-1)) |
1737 | (locations p) | 2135 | (locations p) |
1738 | } | 2136 | } |
1739 | dont_have (KeyData p _ _ _) = not . Map.member pubring | 2137 | dont_have (KeyData p _ _ _) = not . Map.member pubring |
@@ -1741,8 +2139,8 @@ main = do | |||
1741 | use_db0 <- get_use_db | 2139 | use_db0 <- get_use_db |
1742 | 2140 | ||
1743 | let pkeypairs = maybe [] id $ do | 2141 | let pkeypairs = maybe [] id $ do |
1744 | g <- grip | 2142 | keygrip <- grip |
1745 | return $ map (\(spec,f,cmd)-> (parseSpec g spec,f,cmd)) keypairs | 2143 | return $ map (\(spec,f,cmd)-> (parseSpec keygrip spec,f,cmd)) keypairs |
1746 | fs <- forM pkeypairs $ \((topspec,subspec),f,cmd) -> do | 2144 | fs <- forM pkeypairs $ \((topspec,subspec),f,cmd) -> do |
1747 | -- Note that it's important to discard the KeyData objects | 2145 | -- Note that it's important to discard the KeyData objects |
1748 | -- returned by filterMatches and retain only the keys. | 2146 | -- returned by filterMatches and retain only the keys. |
@@ -1752,8 +2150,24 @@ main = do | |||
1752 | f_found <- doesFileExist f | 2150 | f_found <- doesFileExist f |
1753 | return (f_found,(f,subspec,ms,cmd)) | 2151 | return (f_found,(f,subspec,ms,cmd)) |
1754 | 2152 | ||
2153 | |||
1755 | let (imports,exports) = partition fst fs | 2154 | let (imports,exports) = partition fst fs |
1756 | use_db <- foldM (doImport decrypt) use_db0 (map snd imports) | 2155 | use_db <- foldM (doImport decrypt) use_db0 (map snd imports) |
2156 | |||
2157 | let (btcs,bad_btcs) = partition isSupportedBTC btcpairs | ||
2158 | isSupportedBTC (spec,"base58",cnt) = True | ||
2159 | isSupportedBTC _ = False | ||
2160 | dblist = Map.toList use_db | ||
2161 | pbtcs = maybe [] id $ do | ||
2162 | keygrip <- grip | ||
2163 | let conv (spec,proto,cnt) = | ||
2164 | let (topspec,subspec) = parseSpec keygrip spec | ||
2165 | ms = map fst $ filterMatches topspec dblist | ||
2166 | in (ms,subspec,cnt) | ||
2167 | return $ map conv btcs | ||
2168 | |||
2169 | use_db <- foldM (doBTCImport decrypt) use_db pbtcs | ||
2170 | |||
1757 | (ret_db,_) <- foldM (doExport decrypt) (Just use_db,use_db) (map snd exports) | 2171 | (ret_db,_) <- foldM (doExport decrypt) (Just use_db,use_db) (map snd exports) |
1758 | 2172 | ||
1759 | use_db <- | 2173 | use_db <- |
@@ -1782,6 +2196,7 @@ main = do | |||
1782 | let shspec = Map.fromList [("--show-wk", const $ show_wk secfile grip) | 2196 | let shspec = Map.fromList [("--show-wk", const $ show_wk secfile grip) |
1783 | ,("--show-all",const $ show_all) | 2197 | ,("--show-all",const $ show_all) |
1784 | ,("--show-pem",\[x] -> show_pem x $ maybe "" id grip) | 2198 | ,("--show-pem",\[x] -> show_pem x $ maybe "" id grip) |
2199 | ,("--show-wip",\[x] -> show_wip x $ maybe "" id grip) | ||
1785 | ,("--help", \_ _ ->kiki_usage)] | 2200 | ,("--help", \_ _ ->kiki_usage)] |
1786 | shargs = mapMaybe (\(x:xs) -> (,xs) <$> Map.lookup x shspec) sargs | 2201 | shargs = mapMaybe (\(x:xs) -> (,xs) <$> Map.lookup x shspec) sargs |
1787 | 2202 | ||
@@ -1869,9 +2284,10 @@ main = do | |||
1869 | let uidxs0 = map packet $ flattenUid "" True (str,ps) | 2284 | let uidxs0 = map packet $ flattenUid "" True (str,ps) |
1870 | -- addition<- signSelfAuthTorKeys' selfkey g keys grip timestamp mkey uidxs0 | 2285 | -- addition<- signSelfAuthTorKeys' selfkey g keys grip timestamp mkey uidxs0 |
1871 | additional <- signSelfAuthTorKeys' selfkey keys grip mkey uidxs0 | 2286 | additional <- signSelfAuthTorKeys' selfkey keys grip mkey uidxs0 |
1872 | let ps' = ( map ( (,tmap) . flip MappedPacket om) additional | 2287 | let ps' = ( map ( (,tmap) . toMappedPacket om) additional |
1873 | ++ fst ps | 2288 | ++ fst ps |
1874 | , Map.union om (snd ps) ) | 2289 | , Map.union om (snd ps) ) |
2290 | toMappedPacket om p = (mappedPacket "" p) {locations=om} | ||
1875 | om = Map.singleton "--autosign" (origin p (-1)) where p = UserIDPacket str | 2291 | om = Map.singleton "--autosign" (origin p (-1)) where p = UserIDPacket str |
1876 | tmap = Map.empty | 2292 | tmap = Map.empty |
1877 | return ps' | 2293 | return ps' |
@@ -2080,12 +2496,12 @@ main = do | |||
2080 | 2496 | ||
2081 | -} | 2497 | -} |
2082 | 2498 | ||
2499 | {- | ||
2083 | doCmd cmd@(Cross_Merge {}) = do | 2500 | doCmd cmd@(Cross_Merge {}) = do |
2084 | (homedir,secring,pubring,grip0) <- getHomeDir (homedir cmd) | 2501 | (homedir,secring,pubring,grip0) <- getHomeDir (homedir cmd) |
2085 | -- grip0 may be empty, in which case we should use the first key | 2502 | -- grip0 may be empty, in which case we should use the first key |
2086 | cross_merge (secring:pubring:files cmd) $ \_ db -> return $ (Just db,db) | 2503 | cross_merge (secring:pubring:files cmd) $ \_ db -> return $ (Just db,db) |
2087 | 2504 | ||
2088 | {- | ||
2089 | doCmd cmd@(CatPub {}) = do | 2505 | doCmd cmd@(CatPub {}) = do |
2090 | let spec:files = catpub_args cmd | 2506 | let spec:files = catpub_args cmd |
2091 | let (topspec,subspec) = unprefix '/' spec | 2507 | let (topspec,subspec) = unprefix '/' spec |
@@ -2394,11 +2810,15 @@ isTopKey _ = False | |||
2394 | filterMatches :: KeySpec -> [(KeyKey,KeyData)] -> [(KeyKey,KeyData)] | 2810 | filterMatches :: KeySpec -> [(KeyKey,KeyData)] -> [(KeyKey,KeyData)] |
2395 | filterMatches spec ks = filter (matchSpec spec) ks | 2811 | filterMatches spec ks = filter (matchSpec spec) ks |
2396 | 2812 | ||
2397 | selectKey :: (KeySpec,Maybe String) -> KeyDB -> Maybe Packet | 2813 | selectSecretKey :: (KeySpec,Maybe String) -> KeyDB -> Maybe Packet |
2398 | selectKey (spec,mtag) db = do | 2814 | selectSecretKey (spec,mtag) db = selectKey0 False (spec,mtag) db |
2399 | -- Note: Because of the behavior of flattenKeys, | 2815 | |
2400 | -- selectKey cannot return a SecretKeyPacket | 2816 | selectPublicKey :: (KeySpec,Maybe String) -> KeyDB -> Maybe Packet |
2401 | let Message ps = flattenKeys True db | 2817 | selectPublicKey (spec,mtag) db = selectKey0 True (spec,mtag) db |
2818 | |||
2819 | selectKey0 :: Bool -> (KeySpec,Maybe String) -> KeyDB -> Maybe Packet | ||
2820 | selectKey0 wantPublic (spec,mtag) db = do | ||
2821 | let Message ps = flattenKeys wantPublic db | ||
2402 | ys = snd $ seek_key spec ps | 2822 | ys = snd $ seek_key spec ps |
2403 | flip (maybe (listToMaybe ys)) mtag $ \tag -> do | 2823 | flip (maybe (listToMaybe ys)) mtag $ \tag -> do |
2404 | let (subspec,ys1) = (KeyTag y tag,ys1) where y:ys1 = ys | 2824 | let (subspec,ys1) = (KeyTag y tag,ys1) where y:ys1 = ys |