dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

torsign.py (2787B)


      1 import argparse
      2 import base64
      3 import hashlib
      4 
      5 # All the code come from onionbalance codebase
      6 # https://gitlab.torproject.org/tpo/core/onionbalance/-/blob/main/onionbalance/hs_v3/ext/slow_ed25519.py
      7 
      8 
      9 def load_tor_key_from_disk(key_bytes):
     10     if key_bytes[:29] != b'== ed25519v1-secret: type0 ==':
     11         raise "Tor key does not start with Tor header"
     12     expanded_sk = key_bytes[32:]
     13     if len(expanded_sk) != 64:
     14         raise "Tor private key has the wrong length"
     15     return expanded_sk
     16 
     17 
     18 def sign(priv_key, msg):
     19     return signatureWithESK(msg, priv_key, publickeyFromESK(priv_key))
     20 
     21 
     22 def publickeyFromESK(h):
     23     a = decodeint(h[:32])
     24     A = scalarmult(B,a)
     25     return encodepoint(A)
     26 
     27 
     28 def signatureWithESK(m,h,pk):
     29     a = decodeint(h[:32])
     30     tohint = b''.join([bytes([h[i]]) for i in range(b//8,b//4)]) + m
     31     r = Hint(tohint)
     32     R = scalarmult(B,r)
     33     S = (r + Hint(encodepoint(R) + pk + m) * a) % l
     34     return encodepoint(R) + encodeint(S)
     35 
     36 
     37 def scalarmult(P,e):
     38     if e == 0: return [0,1]
     39     Q = scalarmult(P,e//2)
     40     Q = edwards(Q,Q)
     41     if e & 1: Q = edwards(Q,P)
     42     return Q
     43 
     44 
     45 def inv(x):
     46     return expmod(x,q-2,q)
     47 
     48 
     49 def expmod(b,e,m):
     50     if e == 0: return 1
     51     t = expmod(b,e//2,m)**2 % m
     52     if e & 1: t = (t*b) % m
     53     return t
     54 
     55 
     56 q = 2**255 - 19
     57 d = -121665 * inv(121666)
     58 I = expmod(2,(q-1)//4,q)
     59 
     60 
     61 def xrecover(y):
     62     xx = (y*y-1) * inv(d*y*y+1)
     63     x = expmod(xx,(q+3)//8,q)
     64     if (x*x - xx) % q != 0: x = (x*I) % q
     65     if x % 2 != 0: x = q-x
     66     return x
     67 
     68 
     69 b = 256
     70 l = 2**252 + 27742317777372353535851937790883648493
     71 By = 4 * inv(5)
     72 Bx = xrecover(By)
     73 B = [Bx % q,By % q]
     74 
     75 def edwards(P,Q):
     76     x1 = P[0]
     77     y1 = P[1]
     78     x2 = Q[0]
     79     y2 = Q[1]
     80     x3 = (x1*y2+x2*y1) * inv(1+d*x1*x2*y1*y2)
     81     y3 = (y1*y2+x1*x2) * inv(1-d*x1*x2*y1*y2)
     82     return [x3 % q,y3 % q]
     83 
     84 
     85 def H(m):
     86     return hashlib.sha512(m).digest()
     87 
     88 
     89 def bit(h,i):
     90     return (h[i//8] >> (i%8)) & 1
     91 
     92 
     93 def Hint(m):
     94     h = H(m)
     95     return sum(2**i * bit(h,i) for i in range(2*b))
     96 
     97 
     98 def encodepoint(P):
     99     x = P[0]
    100     y = P[1]
    101     bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1]
    102     return b''.join([bytes([sum([bits[i * 8 + j] << j for j in range(8)])]) for i in range(b//8)])
    103 
    104 
    105 def encodeint(y):
    106     bits = [(y >> i) & 1 for i in range(b)]
    107     return b''.join([bytes([sum([bits[i * 8 + j] << j for j in range(8)])]) for i in range(b//8)])
    108 
    109 
    110 def decodeint(s):
    111     return sum(2**i * bit(s,i) for i in range(0,b))
    112 
    113 
    114 parser = argparse.ArgumentParser()
    115 parser.add_argument('cert')
    116 parser.add_argument('-s', '--secret', default="hs_ed25519_secret_key")
    117 args = parser.parse_args()
    118 
    119 msg = open(args.cert, 'rb').read()
    120 pem_key_bytes = open(args.secret, 'rb').read()
    121 privkey = load_tor_key_from_disk(pem_key_bytes)
    122 msg_sig = sign(privkey, msg)
    123 print(base64.b64encode(msg_sig).decode())