diff options
author | joe <joe@jerkface.net> | 2013-10-30 18:59:19 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2013-10-30 18:59:19 -0400 |
commit | 0b65ae400ee5f2d04b188c618a5927aa7113d9be (patch) | |
tree | 9e174e7b996d9d8a5c727509f40ea75a074b217e | |
parent | 749306b18c26c868b1653e854a51ad7f6bf83cb9 (diff) |
Functional add command for adding subkeys to a gpg keyring.
-rw-r--r-- | keys.hs | 356 |
1 files changed, 346 insertions, 10 deletions
@@ -25,6 +25,7 @@ import Data.Bits | |||
25 | import qualified Data.Text as T | 25 | import qualified Data.Text as T |
26 | import Data.Text.Encoding | 26 | import Data.Text.Encoding |
27 | import qualified Codec.Binary.Base32 as Base32 | 27 | import qualified Codec.Binary.Base32 as Base32 |
28 | import qualified Codec.Binary.Base64 as Base64 | ||
28 | import qualified Crypto.Hash.SHA1 as SHA1 | 29 | import qualified Crypto.Hash.SHA1 as SHA1 |
29 | import Data.Char (toLower) | 30 | import Data.Char (toLower) |
30 | import qualified Crypto.PubKey.RSA as RSA | 31 | import qualified Crypto.PubKey.RSA as RSA |
@@ -44,9 +45,11 @@ import System.Console.CmdArgs | |||
44 | -- import System.Posix.Time | 45 | -- import System.Posix.Time |
45 | import Data.Time.Clock.POSIX | 46 | import Data.Time.Clock.POSIX |
46 | import System.Posix.IO (fdToHandle,fdRead) | 47 | import System.Posix.IO (fdToHandle,fdRead) |
48 | import System.Posix.Files | ||
47 | import Data.Monoid ((<>)) | 49 | import Data.Monoid ((<>)) |
50 | -- import Data.X509 | ||
48 | 51 | ||
49 | data RSAPublicKey = RSAKey MPI MPI | 52 | data RSAPublicKey = RSAKey MPI MPI deriving Show |
50 | 53 | ||
51 | instance ASN1Object RSAPublicKey where | 54 | instance ASN1Object RSAPublicKey where |
52 | toASN1 (RSAKey (MPI n) (MPI e)) | 55 | toASN1 (RSAKey (MPI n) (MPI e)) |
@@ -60,6 +63,77 @@ instance ASN1Object RSAPublicKey where | |||
60 | fromASN1 _ = | 63 | fromASN1 _ = |
61 | Left "fromASN1: RSAPublicKey: unexpected format" | 64 | Left "fromASN1: RSAPublicKey: unexpected format" |
62 | 65 | ||
66 | data RSAPrivateKey = RSAPrivateKey | ||
67 | { rsaN :: MPI | ||
68 | , rsaE :: MPI | ||
69 | , rsaD :: MPI | ||
70 | , rsaP :: MPI | ||
71 | , rsaQ :: MPI | ||
72 | , rsaDmodP1 :: MPI | ||
73 | , rsaDmodQminus1 :: MPI | ||
74 | , rsaCoefficient :: MPI | ||
75 | } | ||
76 | deriving Show | ||
77 | |||
78 | {- | ||
79 | RSAPrivateKey ::= SEQUENCE { | ||
80 | version Version, | ||
81 | modulus INTEGER, -- n | ||
82 | publicExponent INTEGER, -- e | ||
83 | privateExponent INTEGER, -- d | ||
84 | prime1 INTEGER, -- p | ||
85 | prime2 INTEGER, -- q | ||
86 | exponent1 INTEGER, -- d mod (p1) | ||
87 | exponent2 INTEGER, -- d mod (q-1) | ||
88 | coefficient INTEGER, -- (inverse of q) mod p | ||
89 | otherPrimeInfos OtherPrimeInfos OPTIONAL | ||
90 | } | ||
91 | -} | ||
92 | |||
93 | instance ASN1Object RSAPrivateKey where | ||
94 | toASN1 rsa@(RSAPrivateKey {}) | ||
95 | = \xs -> Start Sequence | ||
96 | : IntVal 0 | ||
97 | : mpiVal rsaN | ||
98 | : mpiVal rsaE | ||
99 | : mpiVal rsaD | ||
100 | : mpiVal rsaP | ||
101 | : mpiVal rsaQ | ||
102 | : mpiVal rsaDmodP1 | ||
103 | : mpiVal rsaDmodQminus1 | ||
104 | : mpiVal rsaCoefficient | ||
105 | : End Sequence | ||
106 | : xs | ||
107 | where mpiVal f = IntVal x where MPI x = f rsa | ||
108 | |||
109 | fromASN1 ( Start Sequence | ||
110 | : IntVal _ -- version | ||
111 | : IntVal n | ||
112 | : IntVal e | ||
113 | : IntVal d | ||
114 | : IntVal p | ||
115 | : IntVal q | ||
116 | : IntVal dmodp1 | ||
117 | : IntVal dmodqminus1 | ||
118 | : IntVal coefficient | ||
119 | : ys) = | ||
120 | Right ( privkey, tail $ dropWhile notend ys) | ||
121 | where | ||
122 | notend (End Sequence) = False | ||
123 | notend _ = True | ||
124 | privkey = RSAPrivateKey | ||
125 | { rsaN = MPI n | ||
126 | , rsaE = MPI e | ||
127 | , rsaD = MPI d | ||
128 | , rsaP = MPI p | ||
129 | , rsaQ = MPI q | ||
130 | , rsaDmodP1 = MPI dmodp1 | ||
131 | , rsaDmodQminus1 = MPI dmodqminus1 | ||
132 | , rsaCoefficient = MPI coefficient | ||
133 | } | ||
134 | fromASN1 _ = | ||
135 | Left "fromASN1: RSAPrivateKey: unexpected format" | ||
136 | |||
63 | rsaKeyFromPacket p@(PublicKeyPacket {}) = do | 137 | rsaKeyFromPacket p@(PublicKeyPacket {}) = do |
64 | n <- lookup 'n' $ key p | 138 | n <- lookup 'n' $ key p |
65 | e <- lookup 'e' $ key p | 139 | e <- lookup 'e' $ key p |
@@ -93,6 +167,15 @@ secretToPublic pkt@(SecretKeyPacket {}) = | |||
93 | } | 167 | } |
94 | secretToPublic pkt = pkt | 168 | secretToPublic pkt = pkt |
95 | 169 | ||
170 | |||
171 | extractPEM typ pem = dta | ||
172 | where | ||
173 | dta = case ys of | ||
174 | _:dta_lines -> Char8.concat dta_lines | ||
175 | [] -> "" | ||
176 | xs = dropWhile (/="-----BEGIN " <> typ <> "-----") (Char8.lines pem) | ||
177 | ys = takeWhile (/="-----END " <> typ <> "-----") xs | ||
178 | |||
96 | isKey (PublicKeyPacket {}) = True | 179 | isKey (PublicKeyPacket {}) = True |
97 | isKey (SecretKeyPacket {}) = True | 180 | isKey (SecretKeyPacket {}) = True |
98 | isKey _ = False | 181 | isKey _ = False |
@@ -117,6 +200,8 @@ isSubkeySignature _ = False | |||
117 | isMasterKey k@(PublicKeyPacket {}) = not $ is_subkey k | 200 | isMasterKey k@(PublicKeyPacket {}) = not $ is_subkey k |
118 | isMasterKey _ = False | 201 | isMasterKey _ = False |
119 | 202 | ||
203 | now = floor <$> Data.Time.Clock.POSIX.getPOSIXTime | ||
204 | |||
120 | usage (NotationDataPacket | 205 | usage (NotationDataPacket |
121 | { human_readable = True | 206 | { human_readable = True |
122 | , notation_name = "usage@" | 207 | , notation_name = "usage@" |
@@ -146,6 +231,8 @@ grip k = drop 32 $ fingerprint k | |||
146 | 231 | ||
147 | smallpr k = drop 24 $ fingerprint k | 232 | smallpr k = drop 24 $ fingerprint k |
148 | 233 | ||
234 | -- matchpr computes the fingerprint of the given key truncated to | ||
235 | -- be the same lenght as the given fingerprint for comparison. | ||
149 | matchpr fp k = reverse $ zipWith const (reverse (fingerprint k)) fp | 236 | matchpr fp k = reverse $ zipWith const (reverse (fingerprint k)) fp |
150 | 237 | ||
151 | 238 | ||
@@ -590,6 +677,46 @@ multiCommand ti choices = | |||
590 | 677 | ||
591 | trimCR bs = fst $ S.spanEnd (\x -> x==10 || x==13) bs | 678 | trimCR bs = fst $ S.spanEnd (\x -> x==10 || x==13) bs |
592 | 679 | ||
680 | guessKeyFormat 'P' "ssh-client" = "SSH" | ||
681 | guessKeyFormat 'S' "ssh-client" = "PEM" | ||
682 | guessKeyFormat 'S' "ssh-host" = "PEM" | ||
683 | guessKeyFormat _ _ = "PEM" -- "PGP" | ||
684 | |||
685 | readKeyFromFile False "PEM" fname = do | ||
686 | timestamp <- modificationTime <$> getFileStatus fname | ||
687 | input <- L.readFile fname | ||
688 | let dta = extractPEM "RSA PRIVATE KEY" input | ||
689 | -- Char8.putStrLn $ "dta = " <> dta | ||
690 | let rsa = do | ||
691 | e <- decodeASN1 DER . L.pack <$> Base64.decode (Char8.unpack dta) | ||
692 | asn1 <- either (const Nothing) Just e | ||
693 | k <- either (const Nothing) (Just . fst) (fromASN1 asn1) | ||
694 | let _ = k :: RSAPrivateKey | ||
695 | return k | ||
696 | -- putStrLn $ "rsa = "++ show rsa | ||
697 | return . Message $ do | ||
698 | rsa <- maybeToList rsa | ||
699 | return $ SecretKeyPacket | ||
700 | { version = 4 | ||
701 | , timestamp = toEnum (fromEnum timestamp) | ||
702 | , key_algorithm = RSA | ||
703 | , key = [ -- public fields... | ||
704 | ('n',rsaN rsa) | ||
705 | ,('e',rsaE rsa) | ||
706 | -- secret fields | ||
707 | ,('d',rsaD rsa) | ||
708 | ,('p',rsaQ rsa) -- Note: p & q swapped | ||
709 | ,('q',rsaP rsa) -- Note: p & q swapped | ||
710 | ,('u',rsaCoefficient rsa) | ||
711 | ] | ||
712 | , s2k_useage = 0 | ||
713 | , s2k = S2K 100 "" | ||
714 | , symmetric_algorithm = Unencrypted | ||
715 | , encrypted_data = "" | ||
716 | , is_subkey = True | ||
717 | } | ||
718 | readKeyFromFile is_public fmt filename = error ("unimplemented key type: "++fmt) | ||
719 | |||
593 | data Arguments = | 720 | data Arguments = |
594 | List { homedir :: Maybe FilePath } | 721 | List { homedir :: Maybe FilePath } |
595 | | WorkingKey { homedir :: Maybe FilePath } | 722 | | WorkingKey { homedir :: Maybe FilePath } |
@@ -599,8 +726,25 @@ data Arguments = | |||
599 | , output :: FilePath} | 726 | , output :: FilePath} |
600 | | Public { homedir :: Maybe FilePath | 727 | | Public { homedir :: Maybe FilePath |
601 | , output :: FilePath} | 728 | , output :: FilePath} |
729 | | Add { homedir :: Maybe FilePath | ||
730 | , passphrase_fd :: Maybe Int | ||
731 | , key_usage :: String | ||
732 | , seckey :: String | ||
733 | , output :: FilePath } | ||
734 | | PemFP { homedir :: Maybe FilePath | ||
735 | , seckey :: String } | ||
736 | | Decrypt { homedir :: Maybe FilePath | ||
737 | , passphrase_fd :: Maybe Int | ||
738 | , output :: FilePath } | ||
602 | deriving (Show, Data, Typeable) | 739 | deriving (Show, Data, Typeable) |
603 | 740 | ||
741 | getPassphrase cmd = | ||
742 | case passphrase_fd cmd of | ||
743 | Just fd -> do pwh <- fdToHandle (toEnum fd) | ||
744 | fmap trimCR $ S.hGetContents pwh | ||
745 | Nothing -> return "" | ||
746 | |||
747 | |||
604 | #define HOMEOPTION (def &= explicit &= name "homedir" &= typDir ) | 748 | #define HOMEOPTION (def &= explicit &= name "homedir" &= typDir ) |
605 | 749 | ||
606 | main = do | 750 | main = do |
@@ -610,9 +754,13 @@ main = do | |||
610 | &= auto | 754 | &= auto |
611 | , WorkingKey HOMEOPTION | 755 | , WorkingKey HOMEOPTION |
612 | &= help "Shows the current working key set that will be used to make signatures." | 756 | &= help "Shows the current working key set that will be used to make signatures." |
757 | , Public HOMEOPTION | ||
758 | (def &= argPos 1 &= typFile ) | ||
759 | &= help "Extract public keys into the given file." | ||
613 | , AutoSign HOMEOPTION | 760 | , AutoSign HOMEOPTION |
614 | (def &= opt ("passphrase"::String) | 761 | (def &= opt ("passphrase"::String) |
615 | &= (help . concat) ["file descriptor from" | 762 | &= typ "FD" |
763 | &= (help . concat) ["file descriptor from " | ||
616 | ,"which to read passphrase"]) | 764 | ,"which to read passphrase"]) |
617 | (def &= argPos 1 &= typFile ) | 765 | (def &= argPos 1 &= typFile ) |
618 | (def &=argPos 2 &= typFile) | 766 | (def &=argPos 2 &= typFile) |
@@ -620,9 +768,36 @@ main = do | |||
620 | [ "Copies the first file to the second while adding" | 768 | [ "Copies the first file to the second while adding" |
621 | , " signatures for tor-style uids that match" | 769 | , " signatures for tor-style uids that match" |
622 | , " cross-certified keys." ] | 770 | , " cross-certified keys." ] |
623 | , Public HOMEOPTION | 771 | , Decrypt HOMEOPTION |
624 | (def &= argPos 1 &= typFile ) | 772 | (def &= opt ("passphrase"::String) |
625 | &= help "Extract public keys into the given file." | 773 | &= typ "FD" |
774 | &= (help . concat) ["file descriptor from " | ||
775 | ,"which to read passphrase"]) | ||
776 | (def &= argPos 1 &= typFile ) | ||
777 | -- (def &= argPos 3 &= typ "PUBLIC-KEY") | ||
778 | &= (help . concat) | ||
779 | [ "Remove password protection from the working keyring" | ||
780 | , " and save the result into the given file."] | ||
781 | , Add HOMEOPTION | ||
782 | (def &= opt ("passphrase"::String) | ||
783 | &= typ "FD" | ||
784 | &= (help . concat) ["file descriptor from " | ||
785 | ,"which to read passphrase"]) | ||
786 | (def &= argPos 1 &= typ "USAGE") | ||
787 | (def &= argPos 2 &= typ "PRIVATE-KEY") | ||
788 | (def &= argPos 3 &= typFile) | ||
789 | -- (def &= argPos 3 &= typ "PUBLIC-KEY") | ||
790 | &= (help . concat) | ||
791 | [ "Add a subkey." | ||
792 | , " USAGE is the usage@ annotation of the subkey." | ||
793 | , " Keys are specified as FMT:FILE where" | ||
794 | , " FMT may be one of following: PEM." | ||
795 | , " Results are written to the given file." ] | ||
796 | |||
797 | , PemFP HOMEOPTION | ||
798 | (def &= argPos 1 &= typFile ) | ||
799 | &= (help . concat) | ||
800 | [ "Display the fingerprint of a PEM key pair."] | ||
626 | ] | 801 | ] |
627 | &= program "keys" | 802 | &= program "keys" |
628 | &= summary "keys - a pgp key editing utility" | 803 | &= summary "keys - a pgp key editing utility" |
@@ -762,6 +937,8 @@ main = do | |||
762 | doCmd cmd@(WorkingKey {}) = do | 937 | doCmd cmd@(WorkingKey {}) = do |
763 | (homedir,secring,grip) <- getHomeDir cmd | 938 | (homedir,secring,grip) <- getHomeDir cmd |
764 | (Message sec) <- readPacketsFromFile secring | 939 | (Message sec) <- readPacketsFromFile secring |
940 | -- let s2k' = map s2k (filter isKey sec) | ||
941 | -- putStrLn $ "s2k = " ++ show s2k' | ||
765 | putStrLn $ listKeysFiltered (maybeToList grip) sec | 942 | putStrLn $ listKeysFiltered (maybeToList grip) sec |
766 | return () | 943 | return () |
767 | 944 | ||
@@ -771,10 +948,7 @@ main = do | |||
771 | , grip -- Just "AD1CA892FCF4ED9829C762269BDEA5B4D5643321" | 948 | , grip -- Just "AD1CA892FCF4ED9829C762269BDEA5B4D5643321" |
772 | ) <- getPGPEnviron cmd | 949 | ) <- getPGPEnviron cmd |
773 | S8.putStrLn $ "pasphrase_fd = " <> S8.pack (show (passphrase_fd cmd)) | 950 | S8.putStrLn $ "pasphrase_fd = " <> S8.pack (show (passphrase_fd cmd)) |
774 | pw <- case passphrase_fd cmd of | 951 | pw <- getPassphrase cmd |
775 | Just fd -> do pwh <- fdToHandle (toEnum fd) | ||
776 | fmap trimCR $ S.hGetContents pwh | ||
777 | Nothing -> return "" | ||
778 | -- putStrLn $ "getPGPEnviron -> " ++ show (homedir,length sec,grip) | 952 | -- putStrLn $ "getPGPEnviron -> " ++ show (homedir,length sec,grip) |
779 | (Message pub) <- readPacketsFromFile (input cmd) | 953 | (Message pub) <- readPacketsFromFile (input cmd) |
780 | putStrLn $ listKeys pub | 954 | putStrLn $ listKeys pub |
@@ -800,8 +974,8 @@ main = do | |||
800 | isTorID _ = False | 974 | isTorID _ = False |
801 | 975 | ||
802 | g <- newGenIO | 976 | g <- newGenIO |
977 | timestamp <- now | ||
803 | -- timestamp <- epochTime | 978 | -- timestamp <- epochTime |
804 | timestamp <- floor <$> Data.Time.Clock.POSIX.getPOSIXTime | ||
805 | let xs:xss = groupBy (\_ (b,_)->not b) marked | 979 | let xs:xss = groupBy (\_ (b,_)->not b) marked |
806 | pub' = map (snd . cleanup) xs | 980 | pub' = map (snd . cleanup) xs |
807 | ++ concatMap (signSelfAuthTorKeys pw (g::SystemRandom) sec grip timestamp) | 981 | ++ concatMap (signSelfAuthTorKeys pw (g::SystemRandom) sec grip timestamp) |
@@ -823,6 +997,161 @@ main = do | |||
823 | bs = encode (Message pub) | 997 | bs = encode (Message pub) |
824 | L.writeFile (output cmd) bs | 998 | L.writeFile (output cmd) bs |
825 | 999 | ||
1000 | doCmd cmd@(Decrypt {}) = do | ||
1001 | ( homedir -- e3ozbhvej4jvlu43.onion/gpg/gnupghome | ||
1002 | , sec -- e3ozbhvej4jvlu43.onion/gpg/gnupghome/secring.gpg | ||
1003 | , grip -- Just "AD1CA892FCF4ED9829C762269BDEA5B4D5643321" | ||
1004 | ) <- getPGPEnviron cmd | ||
1005 | pw <- getPassphrase cmd | ||
1006 | |||
1007 | let sec' = map decrypt sec | ||
1008 | decrypt k@(SecretKeyPacket {}) = k -- TODO | ||
1009 | |||
1010 | L.writeFile (output cmd) (encode $ Message sec') | ||
1011 | |||
1012 | {- | ||
1013 | let wk = grip >>= find_key fingerprint (Message sec) | ||
1014 | case wk of | ||
1015 | Nothing -> error "No working key?" | ||
1016 | Just wk -> do | ||
1017 | putStrLn $ "wk = " ++ fingerprint wk | ||
1018 | -} | ||
1019 | |||
1020 | doCmd cmd@(Add {}) = do | ||
1021 | ( homedir -- e3ozbhvej4jvlu43.onion/gpg/gnupghome | ||
1022 | , sec -- e3ozbhvej4jvlu43.onion/gpg/gnupghome/secring.gpg | ||
1023 | , grip -- Just "AD1CA892FCF4ED9829C762269BDEA5B4D5643321" | ||
1024 | ) <- getPGPEnviron cmd | ||
1025 | pw <- getPassphrase cmd | ||
1026 | |||
1027 | flip (maybe (error "No working key?")) grip $ \grip -> do | ||
1028 | |||
1029 | let (pre, wk:subs) = seek_key grip sec | ||
1030 | wkun = do | ||
1031 | k <- decryptSecretKey pw wk | ||
1032 | guard (symmetric_algorithm k == Unencrypted) | ||
1033 | return k | ||
1034 | |||
1035 | flip (maybe (error "Bad passphrase?")) wkun$ \wkun -> do | ||
1036 | |||
1037 | let (uids,subkeys) = break isSubkey subs | ||
1038 | isSubkey p = isKey p && is_subkey p | ||
1039 | |||
1040 | let parseKeySpec hint spec = case break (==':') spec of | ||
1041 | (fmt,_:file) -> (fmt,file) | ||
1042 | (file,"") -> (guessKeyFormat hint (key_usage cmd), file) | ||
1043 | (secfmt,secfile) = parseKeySpec 'S' $ seckey cmd | ||
1044 | -- (pubfmt,pubfile) = parseKeySpec 'P' $ pubkey cmd | ||
1045 | Message parsedkey <- readKeyFromFile False secfmt secfile | ||
1046 | -- -- Message pubkey <- readKeyFromFile True pubfmt pubfile | ||
1047 | |||
1048 | -- putStrLn $ "parsedkey = " ++ show (head parsedkey) | ||
1049 | -- putStrLn $ "----------" | ||
1050 | |||
1051 | {- | ||
1052 | let seckeys = filter isSecretKey sec | ||
1053 | isSecretKey (SecretKeyPacket {}) = True | ||
1054 | isSecretKey _ = False | ||
1055 | algos = map symmetric_algorithm seckeys | ||
1056 | putStrLn $ show $ symmetric_algorithm wk | ||
1057 | putStrLn $ show $ s2k wk | ||
1058 | putStrLn $ show $ s2k_useage wk | ||
1059 | putStrLn $ PP.ppShow sec | ||
1060 | let -- e = encryptSecretKey wk pw (head seckey) | ||
1061 | e = head seckey | ||
1062 | d = if symmetric_algorithm e /= Unencrypted | ||
1063 | then maybeToList $ decryptSecretKey pw e | ||
1064 | else [e] | ||
1065 | putStrLn $ "e = " ++ show (e) | ||
1066 | -} | ||
1067 | -- putStrLn $ "wkun = " ++ show wkun | ||
1068 | -- putStrLn $ "head subkeys = " ++ show (head subkeys) | ||
1069 | |||
1070 | g <- newGenIO | ||
1071 | timestamp <- now | ||
1072 | |||
1073 | let | ||
1074 | new_sig = fst $ sign (Message [wkun]) | ||
1075 | (SubkeySignature wk | ||
1076 | (head parsedkey) | ||
1077 | (sigpackets 0x18 | ||
1078 | hashed0 | ||
1079 | ( IssuerPacket (fingerprint wk) | ||
1080 | : map EmbeddedSignaturePacket (signatures_over back_sig)))) | ||
1081 | SHA1 | ||
1082 | grip | ||
1083 | timestamp | ||
1084 | (g::SystemRandom) | ||
1085 | sigpackets typ hashed unhashed = return $ | ||
1086 | signaturePacket | ||
1087 | 4 -- version | ||
1088 | typ -- 0x18 subkey binding sig, or 0x19 back-signature | ||
1089 | RSA | ||
1090 | SHA1 | ||
1091 | hashed | ||
1092 | unhashed | ||
1093 | 0 -- Word16 -- Left 16 bits of the signed hash value | ||
1094 | [] -- [MPI] | ||
1095 | |||
1096 | hashed0 = | ||
1097 | [ KeyFlagsPacket | ||
1098 | { certify_keys = True | ||
1099 | , sign_data = True | ||
1100 | , encrypt_communication = True | ||
1101 | , encrypt_storage = True | ||
1102 | , split_key = False | ||
1103 | , authentication = True | ||
1104 | , group_key = False } | ||
1105 | , NotationDataPacket | ||
1106 | { human_readable = True | ||
1107 | , notation_name = "usage@" | ||
1108 | , notation_value = key_usage cmd | ||
1109 | } | ||
1110 | ] | ||
1111 | |||
1112 | subgrip = fingerprint (head parsedkey) | ||
1113 | |||
1114 | back_sig = fst $ sign (Message parsedkey) | ||
1115 | (SubkeySignature wk | ||
1116 | (head parsedkey) | ||
1117 | (sigpackets 0x19 | ||
1118 | hashed0 | ||
1119 | [IssuerPacket subgrip])) | ||
1120 | SHA1 | ||
1121 | subgrip | ||
1122 | timestamp | ||
1123 | (g::SystemRandom) | ||
1124 | |||
1125 | let sec' = pre ++ [wk] ++ uids ++ parsedkey ++ signatures_over new_sig ++ subkeys | ||
1126 | putStrLn $ listKeys sec' | ||
1127 | |||
1128 | L.writeFile (output cmd) (encode (Message sec')) | ||
1129 | |||
1130 | {- | ||
1131 | let backsigs = do | ||
1132 | sig <- signatures (Message sec') | ||
1133 | sigover <- signatures_over sig | ||
1134 | subp <- unhashed_subpackets sigover | ||
1135 | -- guard (isEmbeddedSignature subp) | ||
1136 | subp <- maybeToList (backsig subp) | ||
1137 | over <- signatures (Message (filter isKey sec ++ [subp])) | ||
1138 | return over | ||
1139 | |||
1140 | -- putStrLn $ PP.ppShow backsigs | ||
1141 | -} | ||
1142 | |||
1143 | return () | ||
1144 | |||
1145 | doCmd cmd@(PemFP {}) = do | ||
1146 | let parseKeySpec hint spec = case break (==':') spec of | ||
1147 | (fmt,_:file) -> (fmt,file) | ||
1148 | (file,"") -> (guessKeyFormat hint ("ssh-host"), file) | ||
1149 | (secfmt,secfile) = parseKeySpec 'S' $ seckey cmd | ||
1150 | Message seckey <- readKeyFromFile False secfmt secfile | ||
1151 | -- Message pubkey <- readKeyFromFile True pubfmt pubfile | ||
1152 | putStrLn $ fingerprint (head seckey) | ||
1153 | |||
1154 | |||
826 | 1155 | ||
827 | 1156 | ||
828 | groupBindings pub = | 1157 | groupBindings pub = |
@@ -836,3 +1165,10 @@ groupBindings pub = | |||
836 | gs = {- filter matchgrip $ -} groupBy sameMaster (sortBy (comparing code) bindings') | 1165 | gs = {- filter matchgrip $ -} groupBy sameMaster (sortBy (comparing code) bindings') |
837 | in gs | 1166 | in gs |
838 | 1167 | ||
1168 | |||
1169 | seek_key :: String -> [Packet] -> ([Packet],[Packet]) | ||
1170 | seek_key grip sec = (pre, subs) | ||
1171 | where | ||
1172 | (pre,subs) = break pred sec | ||
1173 | pred p@(SecretKeyPacket {}) = matchpr grip p == grip | ||
1174 | pred _ = False | ||