summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CryptoCoins.hs66
-rw-r--r--OpenPGP.hs44
-rw-r--r--kiki.cabal6
-rw-r--r--kiki.hs578
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 #-}
2module CryptoCoins where
3
4import Numeric
5import Data.Word
6import Data.Maybe
7
8data 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
19coin_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
46lookupNetwork f b = listToMaybe $ filter (\n->f n==b) coin_networks
47
48nameFromSecretByte :: Word8 -> String
49nameFromSecretByte 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
57publicByteFromName n = maybe (secretByteFromName n - 0x80)
58 -- exceptions to the above: bbqcoin, bytecoin
59 public_byte_id
60 (lookupNetwork network_name n)
61
62secretByteFromName 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 @@
1module OpenPGP
2 ( verify
3 , fingerprint
4 , pgpSign
5 , decryptSecretKey
6 ) where
7
8import Data.OpenPGP as OpenPGP
9import Data.OpenPGP.CryptoAPI (verify,fingerprint,sign,decryptSecretKey)
10import Data.Time.Clock.POSIX
11import Control.Applicative ( (<$>) )
12import Crypto.Random (newGenIO,SystemRandom)
13import ControlMaybe
14
15now = floor <$> Data.Time.Clock.POSIX.getPOSIXTime
16
17stampit 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.
31pgpSign ::
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)
37pgpSign 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)
diff --git a/kiki.cabal b/kiki.cabal
index 0a517f0..8ce59a6 100644
--- a/kiki.cabal
+++ b/kiki.cabal
@@ -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
diff --git a/kiki.hs b/kiki.hs
index 196d080..edc36d6 100644
--- a/kiki.hs
+++ b/kiki.hs
@@ -14,7 +14,7 @@ import GHC.IO.Exception ( ioException, IOErrorType(..) )
14import Data.IORef 14import Data.IORef
15import Data.Tuple 15import Data.Tuple
16import Data.Binary 16import Data.Binary
17import Data.OpenPGP 17import Data.OpenPGP as OpenPGP
18import qualified Data.ByteString.Lazy as L 18import qualified Data.ByteString.Lazy as L
19import qualified Data.ByteString.Lazy.Char8 as Char8 19import qualified Data.ByteString.Lazy.Char8 as Char8
20import qualified Data.ByteString as S 20import qualified Data.ByteString as S
@@ -23,8 +23,7 @@ import Control.Monad
23import qualified Text.Show.Pretty as PP 23import qualified Text.Show.Pretty as PP
24import Text.PrettyPrint as PP hiding ((<>)) 24import Text.PrettyPrint as PP hiding ((<>))
25import Data.List 25import Data.List
26-- import Data.OpenPGP.CryptoAPI (verify,fingerprint,sign,decryptSecretKey) 26import Data.OpenPGP.Util (verify,fingerprint,decryptSecretKey,pgpSign)
27import OpenPGP
28import Data.Ord 27import Data.Ord
29import Data.Maybe 28import Data.Maybe
30import Data.Bits 29import Data.Bits
@@ -33,6 +32,12 @@ import Data.Text.Encoding
33import qualified Codec.Binary.Base32 as Base32 32import qualified Codec.Binary.Base32 as Base32
34import qualified Codec.Binary.Base64 as Base64 33import qualified Codec.Binary.Base64 as Base64
35import qualified Crypto.Hash.SHA1 as SHA1 34import qualified Crypto.Hash.SHA1 as SHA1
35import qualified Crypto.Hash.SHA256 as SHA256
36import qualified Crypto.Hash.RIPEMD160 as RIPEMD160
37import 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
36import Data.Char (toLower) 41import Data.Char (toLower)
37import qualified Crypto.PubKey.RSA as RSA 42import 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
40import Data.ASN1.Encoding 45import Data.ASN1.Encoding
41import Data.ASN1.BinaryEncoding 46import Data.ASN1.BinaryEncoding
42import Data.ASN1.BitArray 47import Data.ASN1.BitArray
48import qualified Data.Foldable as Foldable
49import qualified Data.Sequence as Sequence
43import Control.Applicative 50import Control.Applicative
44import System.Environment 51import System.Environment
45import System.Directory 52import System.Directory
@@ -49,14 +56,15 @@ import System.Process
49import System.Posix.IO (fdToHandle,fdRead) 56import System.Posix.IO (fdToHandle,fdRead)
50import System.Posix.Files 57import System.Posix.Files
51import System.Posix.Signals 58import System.Posix.Signals
59import System.Posix.Types (EpochTime)
52import System.Process.Internals (runGenProcess_,defaultSignal) 60import System.Process.Internals (runGenProcess_,defaultSignal)
53import System.IO (hPutStrLn,stderr) 61import System.IO (hPutStrLn,stderr,withFile,IOMode(..))
54import System.IO.Error 62import System.IO.Error
55import ControlMaybe 63import ControlMaybe
56import Data.Char 64import Data.Char
57import Control.Arrow (first,second) 65import Control.Arrow (first,second)
58import Data.Traversable hiding (mapM,forM) 66import Data.Traversable hiding (mapM,forM,sequence)
59import qualified Data.Traversable as Traversable (mapM,forM) 67import qualified Data.Traversable as Traversable (mapM,forM,sequence)
60import System.Console.CmdArgs 68import System.Console.CmdArgs
61-- import System.Posix.Time 69-- import System.Posix.Time
62import Data.Time.Clock.POSIX 70import Data.Time.Clock.POSIX
@@ -64,7 +72,30 @@ import Data.Monoid ((<>))
64-- import Data.X509 72-- import Data.X509
65import qualified Data.Map as Map 73import qualified Data.Map as Map
66import DotLock 74import DotLock
75import Codec.Crypto.ECC.Base -- hecc package
76import Text.Printf
77import Math.NumberTheory.Moduli
78import qualified CryptoCoins as CryptoCoins
79
80
81-- instance Default S.ByteString where def = S.empty
82
83-- DER-encoded elliptic curve ids
84nistp256_id = 0x2a8648ce3d030107
85secp256k1_id = 0x2b8104000a
86
87isCryptoCoinKey p =
88 and [ isKey p
89 , key_algorithm p == ECDSA
90 , lookup 'c' (key p) == Just (MPI secp256k1_id)
91 ]
67 92
93getCryptoCoinTag 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
98getCryptoCoinTag _ = Nothing
68 99
69warn str = hPutStrLn stderr str 100warn 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
235rsaPrivateKeyFromPacket :: Packet -> Maybe RSAPrivateKey
204rsaPrivateKeyFromPacket pkt@(SecretKeyPacket {}) = do 236rsaPrivateKeyFromPacket 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
241instance Default S.ByteString where def = S.empty
242
243secretToPublic pkt@(SecretKeyPacket {}) = 273secretToPublic 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
453listKeys pkts = listKeysFiltered [] pkts 483listKeys pkts = listKeysFiltered [] pkts
454 484
485ecc_curve k = printf "%x" num :: String
486 where unmpi (MPI num) = num
487 num = maybe 0 unmpi $ lookup 'c' (key k)
488
455listKeysFiltered grips pkts = do 489listKeysFiltered 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)
607expandPath path [] = [] 649expandPath path [] = []
608 650
609 651
652-- type TimeStamp = Word32
653
654slurpWIPKeys :: System.Posix.Types.EpochTime -> L.ByteString -> ( [(Word8,Packet)], [L.ByteString])
655slurpWIPKeys stamp "" = ([],[])
656slurpWIPKeys 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
666readPacketsFromWallet ::
667 Maybe Packet
668 -> FilePath
669 -> IO [(Packet,Packet,(Packet,Map.Map FilePath Packet))]
670readPacketsFromWallet 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
610readPacketsFromFile :: FilePath -> IO Message 688readPacketsFromFile :: FilePath -> IO Message
611readPacketsFromFile fname = do 689readPacketsFromFile 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
619lockFiles fs = do 697lockFiles fs = do
620 let dolock f = do 698 let dolock f = do
@@ -806,26 +884,29 @@ guessKeyFormat 'S' "ssh-client" = "PEM"
806guessKeyFormat 'S' "ssh-host" = "PEM" 884guessKeyFormat 'S' "ssh-host" = "PEM"
807guessKeyFormat _ _ = "PEM" -- "PGP" 885guessKeyFormat _ _ = "PEM" -- "PGP"
808 886
809writeKeyToFile False "PEM" fname packet = do 887writeKeyToFile 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
830readKeyFromFile False "PEM" fname = do 911readKeyFromFile 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
923type OriginMap = Map.Map FilePath OriginFlags 1004type OriginMap = Map.Map FilePath OriginFlags
924data MappedPacket = MappedPacket 1005data MappedPacket = MappedPacket
925 { packet :: Packet 1006 { packet :: Packet
1007 , usage_tag :: Maybe String
926 , locations :: OriginMap 1008 , locations :: OriginMap
927 } 1009 }
928 1010
1011mappedPacket filename p = MappedPacket
1012 { packet = p
1013 , usage_tag = Nothing
1014 , locations = Map.singleton filename (origin p (-1))
1015 }
1016
929type TrustMap = Map.Map FilePath Packet 1017type TrustMap = Map.Map FilePath Packet
930type SigAndTrust = ( MappedPacket 1018type 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
955keycomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT 1043keycomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT
956keycomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT 1044keycomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT
957keycomp a b | a==b = EQ 1045keycomp a b | keykey a==keykey b = EQ
958keycomp a b = error $ unlines ["Unable to merge keys:" 1046keycomp 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
966subcomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT 1054subcomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT
967subcomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT 1055subcomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT
968subcomp a b | a==b = EQ 1056subcomp a b | keykey a==keykey b = EQ
969subcomp a b = error $ unlines ["Unable to merge subs:" 1057subcomp 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:"
975subcomp_m a b = subcomp (packet a) (packet b) 1063subcomp_m a b = subcomp (packet a) (packet b)
976 1064
977merge :: KeyDB -> FilePath -> Message -> KeyDB 1065merge :: KeyDB -> FilePath -> Message -> KeyDB
978merge db filename (Message ps) = foldl mergeit db (zip [0..] qs) 1066merge 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
1070merge_ :: KeyDB -> FilePath -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))]
1071 -> KeyDB
1072merge_ 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
1101flattenUid :: FilePath -> Bool -> (String,([SigAndTrust],OriginMap)) -> [MappedPacket] 1195flattenUid :: FilePath -> Bool -> (String,([SigAndTrust],OriginMap)) -> [MappedPacket]
1102flattenUid fname ispub (str,(sigs,om)) = 1196flattenUid 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
1105flattenSub :: FilePath -> Bool -> SubKey -> [MappedPacket] 1199flattenSub :: FilePath -> Bool -> SubKey -> [MappedPacket]
1106flattenSub fname ispub (SubKey key sigs) = unk ispub key: concatSort fname head (unsig fname ispub) sigs 1200flattenSub fname ispub (SubKey key sigs) = unk ispub key: concatSort fname head (unsig fname ispub) sigs
1107 1201
1108unk :: Bool -> MappedPacket -> MappedPacket 1202unk :: Bool -> MappedPacket -> MappedPacket
1109unk isPublic = if isPublic then toPacket secretToPublic else id 1203unk 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
1112unsig :: FilePath -> Bool -> SigAndTrust -> [MappedPacket] 1206unsig :: FilePath -> Bool -> SigAndTrust -> [MappedPacket]
1113unsig fname isPublic (sig,trustmap) = 1207unsig 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
1119ifSecret (SecretKeyPacket {}) t f = t 1214ifSecret (SecretKeyPacket {}) t f = t
1120ifSecret _ t f = f 1215ifSecret _ t f = f
@@ -1123,7 +1218,7 @@ showPacket :: Packet -> String
1123showPacket p | isKey p = (if is_subkey p 1218showPacket 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
1129showPacket0 p = concat . take 1 $ words (show p) 1224showPacket0 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
1175cross_merge keyrings f = do 1270cross_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
1235show_all db = do 1383show_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
1239show_pem keyspec wkgrip db = do 1391show_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
1402show_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
1250parseSpec :: String -> String -> (KeySpec,Maybe String) 1410parseSpec :: String -> String -> (KeySpec,Maybe String)
1251parseSpec grip spec = (topspec,subspec) 1411parseSpec 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
1349findTag tag wk subkey subsigs = (xs',minsig,ys') 1508findTag 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
1536applyCurve curve x = x*x*x + x*a + b where (a,b)=(geta curve,getb curve)
1537
1538secp256k1_oid = [1,3,132,0,10]
1539secp256k1_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
1553secp256k1_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
1567base58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
1568
1569base58digits :: [Char] -> Maybe [Int]
1570base58digits str = sequence mbs
1571 where
1572 mbs = map (flip elemIndex base58chars) str
1573
1574-- 5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ
1575base58_decode :: [Char] -> Maybe (Word8,[Word8])
1576base58_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
1595walletImportFormat 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
1605base58_encode :: S.ByteString -> String
1606base58_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
1619cannonical_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
1631oidToDER 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
1645nistp256=[1,2,840,10045,3,1,7]
1646nistp256_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
1665bitcoinAddress 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--
1680decode_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
1719doBTCImport 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
1377doImport doDecrypt db (fname,subspec,ms,_) = do 1738doImport 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
1753doImportG 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
2394filterMatches :: KeySpec -> [(KeyKey,KeyData)] -> [(KeyKey,KeyData)] 2810filterMatches :: KeySpec -> [(KeyKey,KeyData)] -> [(KeyKey,KeyData)]
2395filterMatches spec ks = filter (matchSpec spec) ks 2811filterMatches spec ks = filter (matchSpec spec) ks
2396 2812
2397selectKey :: (KeySpec,Maybe String) -> KeyDB -> Maybe Packet 2813selectSecretKey :: (KeySpec,Maybe String) -> KeyDB -> Maybe Packet
2398selectKey (spec,mtag) db = do 2814selectSecretKey (spec,mtag) db = selectKey0 False (spec,mtag) db
2399 -- Note: Because of the behavior of flattenKeys, 2815
2400 -- selectKey cannot return a SecretKeyPacket 2816selectPublicKey :: (KeySpec,Maybe String) -> KeyDB -> Maybe Packet
2401 let Message ps = flattenKeys True db 2817selectPublicKey (spec,mtag) db = selectKey0 True (spec,mtag) db
2818
2819selectKey0 :: Bool -> (KeySpec,Maybe String) -> KeyDB -> Maybe Packet
2820selectKey0 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