summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2014-04-22 19:17:52 -0400
committerjoe <joe@jerkface.net>2014-04-22 19:17:52 -0400
commitbdfc90edae040cefccbe1018f1506c445608e460 (patch)
tree319b7d2fa8c7b27b1209ee429dd9d5ff9b9b8dd4
parent1257ddd6813fc75df11631d653a97eb45035188e (diff)
more cleanup of module kiki.hs
-rw-r--r--ExperimentalCruft.hs34
-rw-r--r--KeyRing.hs21
-rw-r--r--kiki.hs300
3 files changed, 123 insertions, 232 deletions
diff --git a/ExperimentalCruft.hs b/ExperimentalCruft.hs
index e3c9941..68f82d2 100644
--- a/ExperimentalCruft.hs
+++ b/ExperimentalCruft.hs
@@ -256,4 +256,38 @@ fmapWithRT g (MultiPass p kk) = MultiPass p (fmapWithRT g' kk)
256 where g' rt h = g rt . h 256 where g' rt h = g rt . h
257-} 257-}
258 258
259{-
260 -- This code is written for the hecc package.
261applyCurve curve x = x*x*x + x*a + b where (a,b)=(geta curve,getb curve)
262
263secp256k1_oid = [1,3,132,0,10]
264secp256k1_curve = ECi l a b p r
265 where
266 -- y² = x³ + 7 (mod p)
267 p = 0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
268 a = 0
269 b = 7
270 -- group order (also order of base point G)
271 r = n
272 n = 0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
273 -- cofactor
274 h = 1
275 -- bit length
276 l = 256
277
278secp256k1_G = ECPa secp256k1_curve
279 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
280 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
281 {-
282 The base point G in compressed form is:
283
284 G = 02 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798
285
286 and in uncompressed form is:
287
288 G = 04 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798
289 483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8
290 -}
291-}
292
259 293
diff --git a/KeyRing.hs b/KeyRing.hs
index 532acb8..cbbbf33 100644
--- a/KeyRing.hs
+++ b/KeyRing.hs
@@ -298,6 +298,20 @@ instance ASN1Object PKCS8_RSAPublicKey where
298 fromASN1 _ = 298 fromASN1 _ =
299 Left "fromASN1: RSAPublicKey: unexpected format" 299 Left "fromASN1: RSAPublicKey: unexpected format"
300 300
301{-
302RSAPrivateKey ::= SEQUENCE {
303 version Version,
304 modulus INTEGER, -- n
305 publicExponent INTEGER, -- e
306 privateExponent INTEGER, -- d
307 prime1 INTEGER, -- p
308 prime2 INTEGER, -- q
309 exponent1 INTEGER, -- d mod (p1) -- ?? d mod (p-1)
310 exponent2 INTEGER, -- d mod (q-1)
311 coefficient INTEGER, -- (inverse of q) mod p
312 otherPrimeInfos OtherPrimeInfos OPTIONAL
313 }
314-}
301data RSAPrivateKey = RSAPrivateKey 315data RSAPrivateKey = RSAPrivateKey
302 { rsaN :: MPI 316 { rsaN :: MPI
303 , rsaE :: MPI 317 , rsaE :: MPI
@@ -2112,7 +2126,12 @@ merge db filename (Message ps) = merge_ db filename qs
2112 updateTrust _ _ (pre,t) p = (pre,Map.insert filename p t) -- secret 2126 updateTrust _ _ (pre,t) p = (pre,Map.insert filename p t) -- secret
2113 2127
2114 2128
2115 2129{-
2130onionName :: KeyData -> (SockAddr,L.ByteString)
2131onionName kd = (addr,name)
2132 where
2133 (addr,(name:_,_)) = getHostnames kd
2134-}
2116 2135
2117merge_ :: KeyDB -> FilePath -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))] 2136merge_ :: KeyDB -> FilePath -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))]
2118 -> KeyDB 2137 -> KeyDB
diff --git a/kiki.hs b/kiki.hs
index 9bfbd2d..620d01e 100644
--- a/kiki.hs
+++ b/kiki.hs
@@ -6,81 +6,49 @@
6{-# LANGUAGE FlexibleInstances #-} 6{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE DeriveDataTypeable #-} 7{-# LANGUAGE DeriveDataTypeable #-}
8{-# LANGUAGE CPP #-} 8{-# LANGUAGE CPP #-}
9module Main where 9module Main ( main ) where
10 10
11import Data.IORef 11import Control.Applicative
12import Data.Binary
13import Data.OpenPGP as OpenPGP
14import qualified Data.ByteString.Lazy as L
15import qualified Data.ByteString.Lazy.Char8 as Char8
16import qualified Data.ByteString as S
17import Control.Monad 12import Control.Monad
18-- import qualified Text.Show.Pretty as PP 13import Data.ASN1.BinaryEncoding
19-- import Text.PrettyPrint as PP hiding ((<>)) 14import Data.ASN1.Encoding
15import Data.ASN1.Types
16import Data.Binary
17import Data.Bits
18import Data.Char
19import Data.IORef
20import Data.List 20import Data.List
21import Data.OpenPGP.Util (verify,fingerprint)
22import Data.Ord
23import Data.Maybe 21import Data.Maybe
24import Data.Bits 22import Data.OpenPGP
25import qualified Data.Text as T 23import Data.Ord
26import Data.Text.Encoding 24import Data.Text.Encoding
27import qualified Codec.Binary.Base64 as Base64
28import qualified Crypto.Hash.SHA256 as SHA256
29import qualified Crypto.Hash.RIPEMD160 as RIPEMD160
30-- import qualified Crypto.Types.PubKey.ECDSA as ECDSA
31-- import qualified Crypto.PubKey.ECC.ECDSA as ECDSA
32
33-- import Crypto.Random (newGenIO,SystemRandom)
34import Data.ASN1.Types
35import Data.ASN1.Encoding
36import Data.ASN1.BinaryEncoding
37import Control.Applicative
38import System.Environment 25import System.Environment
39import System.Exit 26import System.Exit
40import System.IO (hPutStrLn,stderr) 27import System.IO (hPutStrLn,stderr)
41import Data.Char 28import qualified Codec.Binary.Base64 as Base64
42import Control.Arrow (first,second) 29import qualified Crypto.Hash.RIPEMD160 as RIPEMD160
43-- import Data.Traversable hiding (mapM,forM,sequence) 30import qualified Crypto.Hash.SHA256 as SHA256
44-- import qualified Data.Traversable as Traversable (mapM,forM,sequence) 31import qualified Data.ByteString as S
45-- import System.Console.CmdArgs 32import qualified Data.ByteString.Lazy as L
46-- import System.Posix.Time 33import qualified Data.ByteString.Lazy.Char8 as Char8
47-- import Data.X509
48import qualified Data.Map as Map 34import qualified Data.Map as Map
49import DotLock 35import qualified Data.Text as T
50-- import Codec.Crypto.ECC.Base -- hecc package 36import Control.Arrow (first,second)
51-- import Text.Printf
52import qualified CryptoCoins
53import LengthPrefixedBE
54import Data.Binary.Put (putWord32be,runPut,putByteString)
55import Data.Binary.Get (runGet) 37import Data.Binary.Get (runGet)
38import Data.Binary.Put (putWord32be,runPut,putByteString)
56 39
40import DotLock
41import LengthPrefixedBE
57import KeyRing 42import KeyRing
58import Base58 43import Base58
44import qualified CryptoCoins
45import Data.OpenPGP.Util (verify,fingerprint)
59 46
60-- {-# ANN module ("HLint: ignore Eta reduce"::String) #-} 47-- {-# ANN module ("HLint: ignore Eta reduce"::String) #-}
61-- {-# ANN module ("HLint: ignore Use camelCase"::String) #-} 48-- {-# ANN module ("HLint: ignore Use camelCase"::String) #-}
62 49
63-- instance Default S.ByteString where def = S.empty
64
65warn str = hPutStrLn stderr str 50warn str = hPutStrLn stderr str
66 51
67
68
69{-
70RSAPrivateKey ::= SEQUENCE {
71 version Version,
72 modulus INTEGER, -- n
73 publicExponent INTEGER, -- e
74 privateExponent INTEGER, -- d
75 prime1 INTEGER, -- p
76 prime2 INTEGER, -- q
77 exponent1 INTEGER, -- d mod (p1) -- ?? d mod (p-1)
78 exponent2 INTEGER, -- d mod (q-1)
79 coefficient INTEGER, -- (inverse of q) mod p
80 otherPrimeInfos OtherPrimeInfos OPTIONAL
81}
82-}
83
84sshrsa :: Integer -> Integer -> Char8.ByteString 52sshrsa :: Integer -> Integer -> Char8.ByteString
85sshrsa e n = runPut $ do 53sshrsa e n = runPut $ do
86 putWord32be 7 54 putWord32be 7
@@ -98,18 +66,6 @@ decode_sshrsa bs = do
98 return $ RSAKey (MPI n) (MPI e) 66 return $ RSAKey (MPI n) (MPI e)
99 return rsakey 67 return rsakey
100 68
101
102
103{-
104getPackets :: IO [Packet]
105getPackets = do
106 input <- L.getContents
107 case decodeOrFail input of
108 Right (_,_,Message pkts) -> return pkts
109 Left (_,_,_) -> return []
110-}
111
112
113isCertificationSig (CertificationSignature {}) = True 69isCertificationSig (CertificationSignature {}) = True
114isCertificationSig _ = True 70isCertificationSig _ = True
115 71
@@ -214,12 +170,6 @@ fpmatch grip key =
214 170
215listKeys pkts = listKeysFiltered [] pkts 171listKeys pkts = listKeysFiltered [] pkts
216 172
217{-
218ecc_curve k = printf "%x" num :: String
219 where unmpi (MPI num) = num
220 num = maybe 0 unmpi $ lookup 'c' (key k)
221-}
222
223listKeysFiltered grips pkts = do 173listKeysFiltered grips pkts = do
224 let (certs,bs) = getBindings pkts 174 let (certs,bs) = getBindings pkts
225 as = accBindings bs 175 as = accBindings bs
@@ -304,9 +254,9 @@ listKeysFiltered grips pkts = do
304 "master-key " ++ fingerprint top ++ "\n" ++ uid ++" ...\n" ++ subkeys ++ "\n" 254 "master-key " ++ fingerprint top ++ "\n" ++ uid ++" ...\n" ++ subkeys ++ "\n"
305 255
306 256
307
308
309{- 257{-
258 - modify a UID to test the verify function properly
259 - fails
310modifyUID (UserIDPacket str) = UserIDPacket str' 260modifyUID (UserIDPacket str) = UserIDPacket str'
311 where 261 where
312 (fstname,rst) = break (==' ') str 262 (fstname,rst) = break (==' ') str
@@ -316,11 +266,6 @@ modifyUID (UserIDPacket str) = UserIDPacket str'
316modifyUID other = other 266modifyUID other = other
317-} 267-}
318 268
319
320
321-- type TimeStamp = Word32
322
323
324readPublicKey :: Char8.ByteString -> RSAPublicKey 269readPublicKey :: Char8.ByteString -> RSAPublicKey
325readPublicKey bs = fromMaybe er $ do 270readPublicKey bs = fromMaybe er $ do
326 let (pre,bs1) = Char8.splitAt 7 bs 271 let (pre,bs1) = Char8.splitAt 7 bs
@@ -333,18 +278,8 @@ readPublicKey bs = fromMaybe er $ do
333 where 278 where
334 er = error "Unsupported key format" 279 er = error "Unsupported key format"
335 280
336{- 281-- | Returns the given list with its last element modified.
337getPassphrase cmd = 282toLast :: (x -> x) -> [x] -> [x]
338 case passphrase_fd cmd of
339 Just fd -> do pwh <- fdToHandle (toEnum fd)
340 fmap trimCR $ S.hGetContents pwh
341 Nothing -> return ""
342-}
343
344
345#define HOMEOPTION (def &= explicit &= name "homedir" &= typDir )
346
347
348toLast f [] = [] 283toLast f [] = []
349toLast f [x] = [f x] 284toLast f [x] = [f x]
350toLast f (x:xs) = x : toLast f xs 285toLast f (x:xs) = x : toLast f xs
@@ -417,41 +352,6 @@ show_wip keyspec wkgrip db = do
417 let nwb = maybe 0x80 CryptoCoins.secretByteFromName $ snd s 352 let nwb = maybe 0x80 CryptoCoins.secretByteFromName $ snd s
418 putStrLn $ walletImportFormat nwb k 353 putStrLn $ walletImportFormat nwb k
419 354
420
421{-
422applyCurve curve x = x*x*x + x*a + b where (a,b)=(geta curve,getb curve)
423
424secp256k1_oid = [1,3,132,0,10]
425secp256k1_curve = ECi l a b p r
426 where
427 -- y² = x³ + 7 (mod p)
428 p = 0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
429 a = 0
430 b = 7
431 -- group order (also order of base point G)
432 r = n
433 n = 0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
434 -- cofactor
435 h = 1
436 -- bit length
437 l = 256
438
439secp256k1_G = ECPa secp256k1_curve
440 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
441 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
442 {-
443 The base point G in compressed form is:
444
445 G = 02 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798
446
447 and in uncompressed form is:
448
449 G = 04 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798
450 483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8
451 -}
452-}
453
454
455cannonical_eckey x y = 0x4:pad32(numToBytes x) ++ pad32(numToBytes y) :: [Word8] 355cannonical_eckey x y = 0x4:pad32(numToBytes x) ++ pad32(numToBytes y) :: [Word8]
456 where 356 where
457 numToBytes n = reverse $ unfoldr getbyte n 357 numToBytes n = reverse $ unfoldr getbyte n
@@ -473,22 +373,6 @@ bitcoinAddress network_id k = address
473 hash = S.cons network_id . RIPEMD160.hash . SHA256.hash . S.pack $ pub 373 hash = S.cons network_id . RIPEMD160.hash . SHA256.hash . S.pack $ pub
474 address = base58_encode hash 374 address = base58_encode hash
475 375
476-- gpg supported ECDSA curve: 2A8648CE3D030107
477-- 2A 86 48 CE 3D 03 01 07
478-- 1,2,134,72,206,61,3,1,7
479-- 6*128+0x48 840
480-- 0x4e*128+0x3d 10045
481-- 1.2.840.10045.3.1.7 --> NIST P-256
482--
483
484
485{-
486onionName :: KeyData -> (SockAddr,L.ByteString)
487onionName kd = (addr,name)
488 where
489 (addr,(name:_,_)) = getHostnames kd
490-}
491
492whoseKey :: RSAPublicKey -> KeyDB -> [KeyData] 376whoseKey :: RSAPublicKey -> KeyDB -> [KeyData]
493whoseKey rsakey db = filter matchkey (Map.elems db) 377whoseKey rsakey db = filter matchkey (Map.elems db)
494 where 378 where
@@ -510,6 +394,12 @@ whoseKey rsakey db = filter matchkey (Map.elems db)
510 s2 <- signatures . Message $ [k,sub,subsig] 394 s2 <- signatures . Message $ [k,sub,subsig]
511 signatures_over $ verify (Message [sub]) s2 395 signatures_over $ verify (Message [sub]) s2
512 396
397 isSameKey a b = sort (key apub) == sort (key bpub)
398 where
399 apub = secretToPublic a
400 bpub = secretToPublic b
401
402
513 403
514kiki_usage = putStr . unlines $ 404kiki_usage = putStr . unlines $
515 ["kiki - a pgp key editing utility" 405 ["kiki - a pgp key editing utility"
@@ -599,22 +489,40 @@ kiki_usage = putStr . unlines $
599 ," --help Shows this help screen." 489 ," --help Shows this help screen."
600 ] 490 ]
601 491
492doAutosign rt kd@(KeyData k ksigs umap submap) = ops
493 where
494 ops = map (\u -> InducerSignature u []) us
495 us = filter torStyle $ Map.keys umap
496 torStyle str = and [ uid_topdomain parsed == "onion"
497 , uid_realname parsed `elem` ["","Anonymous"]
498 , uid_user parsed == "root"
499 , fmap (match . fst) (lookup (packet k) torbindings)
500 == Just True ]
501 where parsed = parseUID str
502 match = (==subdom) . take (fromIntegral len)
503 subdom0 = L.fromChunks [encodeUtf8 (uid_subdomain parsed)]
504 subdom = Char8.unpack subdom0
505 len = T.length (uid_subdomain parsed)
506 torbindings = getTorKeys (map packet $ flattenTop "" True kd)
507 getTorKeys pub = do
508 xs <- groupBindings pub
509 (_,(top,sub),us,_,_) <- xs
510 guard ("tor" `elem` us)
511 let torhash = fromMaybe "" $ derToBase32 <$> derRSA sub
512 return (top,(torhash,sub))
513
514 groupBindings pub = gs
515 where (_,bindings) = getBindings pub
516 bindings' = accBindings bindings
517 code (c,(m,s),_,_,_) = (fingerprint_material m,-c)
518 ownerkey (_,(a,_),_,_,_) = a
519 sameMaster (ownerkey->a) (ownerkey->b)
520 = fingerprint_material a==fingerprint_material b
521 gs = groupBy sameMaster (sortBy (comparing code) bindings')
522
523
602main = do 524main = do
603 dotlock_init 525 dotlock_init
604 {-
605 args <- cmdArgs $ modes
606 [ Cross_Merge HOMEOPTION
607 (def &= opt ("passphrase"::String)
608 &= typ "FD"
609 &= (help . concat) ["file descriptor from "
610 ,"which to read passphrase"])
611 (def &= args &= typFile)
612 &= help "Merge multiple secret keyrings to stdout."
613 ]
614 &= program "kiki"
615 &= summary "kiki - a pgp key editing utility"
616 doCmd args
617 -}
618 args_raw <- getArgs 526 args_raw <- getArgs
619 let (args,trail1) = break (=="--") args_raw 527 let (args,trail1) = break (=="--") args_raw
620 trail = drop 1 trail1 528 trail = drop 1 trail1
@@ -671,20 +579,12 @@ main = do
671 guard $ take 1 bdmcb == "}" 579 guard $ take 1 bdmcb == "}"
672 let cmd = (drop 1 . reverse . drop 1) bdmcb 580 let cmd = (drop 1 . reverse . drop 1) bdmcb
673 Just (spec,file,cmd) 581 Just (spec,file,cmd)
674 {-
675 publics =
676 flip map (fromMaybe [] $ Map.lookup "--public" margs) $ \specfile -> do
677 let (spec,efile) = break (=='=') specfile
678 guard $ take 1 efile=="="
679 let file= drop 1 efile
680 Just (spec,file)
681 -}
682 keyrings_ = fromMaybe [] $ Map.lookup "--keyrings" margs 582 keyrings_ = fromMaybe [] $ Map.lookup "--keyrings" margs
683 wallets = fromMaybe [] $ Map.lookup "--wallets" margs 583 wallets = fromMaybe [] $ Map.lookup "--wallets" margs
684 passphrase_fd = concat <$> Map.lookup "--passphrase-fd" margs 584 passphrase_fd = concat <$> Map.lookup "--passphrase-fd" margs
685 585
686 when (any isNothing keypairs0) $ do 586 when (any isNothing keypairs0) $ do
687 warn "syntax error" 587 warn "Syntax error in key pair specification"
688 exitFailure 588 exitFailure
689 589
690 input_key <- maybe (return Nothing) 590 input_key <- maybe (return Nothing)
@@ -692,15 +592,7 @@ main = do
692 $ Map.lookup "--show-whose-key" margs 592 $ Map.lookup "--show-whose-key" margs
693 593
694 let keypairs = catMaybes keypairs0 594 let keypairs = catMaybes keypairs0
695 595 homespec = join . take 1 <$> Map.lookup "--homedir" margs
696 {-
697 putStrLn $ "wallets = "++show wallets
698 putStrLn $ "keypairs = "++show keypairs
699 putStrLn $ "keyrings = "++show keyrings_
700 putStrLn $ "publics = "++show publics
701 -}
702
703 let homespec = join . take 1 <$> Map.lookup "--homedir" margs
704 passfd = fmap (FileDesc . read) passphrase_fd 596 passfd = fmap (FileDesc . read) passphrase_fd
705 pems = flip map keypairs 597 pems = flip map keypairs
706 $ \(usage,path,cmd) -> 598 $ \(usage,path,cmd) ->
@@ -750,61 +642,7 @@ main = do
750 shargs = mapMaybe (\(x:xs) -> (,xs) <$> Map.lookup x shspec) sargs 642 shargs = mapMaybe (\(x:xs) -> (,xs) <$> Map.lookup x shspec) sargs
751 643
752 forM_ shargs $ \(cmd,args) -> cmd args (rtKeyDB rt) 644 forM_ shargs $ \(cmd,args) -> cmd args (rtKeyDB rt)
753 e -> putStrLn $ errorString e 645 err -> putStrLn $ errorString err
754 646
755 forM_ report $ \(fname,act) -> do 647 forM_ report $ \(fname,act) -> do
756 putStrLn $ fname ++ ": " ++ reportString act 648 putStrLn $ fname ++ ": " ++ reportString act
757 where
758
759 doAutosign rt kd@(KeyData k ksigs umap submap) = ops
760 where
761 ops = map (\u -> InducerSignature u []) us
762 us = filter torStyle $ Map.keys umap
763 torbindings = getTorKeys (map packet $ flattenTop "" True kd)
764 torStyle str = and [ uid_topdomain parsed == "onion"
765 , uid_realname parsed `elem` ["","Anonymous"]
766 , uid_user parsed == "root"
767 , fmap (match . fst) (lookup (packet k) torbindings)
768 == Just True ]
769 where parsed = parseUID str
770 match = (==subdom) . take (fromIntegral len)
771 subdom0 = L.fromChunks [encodeUtf8 (uid_subdomain parsed)]
772 subdom = Char8.unpack subdom0
773 len = T.length (uid_subdomain parsed)
774
775 getTorKeys pub = do
776 xs <- groupBindings pub
777 (_,(top,sub),us,_,_) <- xs
778 guard ("tor" `elem` us)
779 let torhash = fromMaybe "" $ derToBase32 <$> derRSA sub
780 return (top,(torhash,sub))
781
782isSameKey a b = sort (key apub) == sort (key bpub)
783 where
784 apub = secretToPublic a
785 bpub = secretToPublic b
786
787
788
789groupBindings pub =
790 let (_,bindings) = getBindings pub
791 bindings' = accBindings bindings
792 code (c,(m,s),_,_,_) = (fingerprint_material m,-c)
793 ownerkey (_,(a,_),_,_,_) = a
794 sameMaster (ownerkey->a) (ownerkey->b) = fingerprint_material a==fingerprint_material b
795 -- matchgrip ((code,(top,sub), kind, hashed,claimants):_) | fpmatch grip top = True
796 -- matchgrip _ = False
797 gs = {- filter matchgrip $ -} groupBy sameMaster (sortBy (comparing code) bindings')
798 in gs
799
800
801
802{-
803makeTorUID g timestamp wkun keyflags topkey torkey = uid:signatures_over sig
804 where
805 torhash sub = fromMaybe "" $ derToBase32 <$> derRSA sub
806 s = "Anonymous <root@" ++ take 16 (torhash torkey) ++ ".onion>"
807 uid = UserIDPacket s
808 sig = fst $ torsig g topkey wkun uid timestamp keyflags
809-}
810