dkforest

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

commit a879a6e6c101872e5185d9d99a8e8df932effaa6
parent 2f9ffa5077552ed03dfde8b966634cee96386dd7
Author: n0tr1v <n0tr1v@protonmail.com>
Date:   Sun, 29 Jan 2023 05:59:26 -0800

script to sign an verify tor ed25519

Diffstat:
Acmd/onion_signer/main.go | 353+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 353 insertions(+), 0 deletions(-)

diff --git a/cmd/onion_signer/main.go b/cmd/onion_signer/main.go @@ -0,0 +1,353 @@ +package main + +import ( + "bytes" + "crypto/ed25519" + "crypto/sha512" + "encoding/base32" + "encoding/pem" + "fmt" + "golang.org/x/crypto/sha3" + "math/big" + "os" + "strings" +) + +func main() { + onionAddress := "your_onion_addr.onion" + pkPath := "/path/to/hs_ed25519_secret_key" + msg := []byte("message by n0tr1v") + + // Generate signature + pemKeyBytes, _ := os.ReadFile(pkPath) + identityPrivKey := LoadTorKeyFromDisk(pemKeyBytes) + identityPubKey := PublickeyFromESK(identityPrivKey) + fmt.Println("Onion address: ", AddressFromIdentityKey(identityPubKey)) + signature := sign(identityPrivKey, msg) + pemSignature := string(pem.EncodeToMemory(&pem.Block{Type: "SIGNATURE", Bytes: signature})) + fmt.Println(pemSignature) + + // Verify signature + block, _ := pem.Decode([]byte(pemSignature)) + if block == nil { + panic("failed to extract signature") + } + sig := block.Bytes + pub := identityKeyFromAddress(onionAddress) + isValid := ed25519.Verify(pub, msg, sig) + if !isValid { + panic("invalid signature") + } + fmt.Println("signature is valid") +} + +var ( + bB1 = []*big.Int{biMod(bx, q), biMod(by, q), bi(1), biMod(biMul(bx, by), q)} + l = biAdd(biExp(bi(2), bi(252)), biFromStr("27742317777372353535851937790883648493")) + b = 256 + by = biMul(bi(4), inv(bi(5))) + bx = xrecover(by) + q = biSub(biExp(bi(2), bi(255)), bi(19)) + bB = []*big.Int{biMod(bx, q), biMod(by, q)} + I = expmod(bi(2), biDiv(biSub(q, bi(1)), bi(4)), q) + d = bi(0).Mul(bi(-121665), inv(bi(121666))) + d1 = biMod(biMul(bi(-121665), inv(bi(121666))), q) +) + +func identityKeyFromAddress(onionAddr string) ed25519.PublicKey { + trimmedAddr := strings.TrimSuffix(onionAddr, ".onion") + upperAddr := strings.ToUpper(trimmedAddr) + decodedAddr, _ := base32.StdEncoding.DecodeString(upperAddr) + return decodedAddr[:32] +} + +func sign(identityPrivKey, msg []byte) []byte { + return signatureWithESK(msg, identityPrivKey, PublickeyFromESK(identityPrivKey)) +} + +func biFromStr(v string) (out *big.Int) { + out = new(big.Int) + _, _ = fmt.Sscan(v, out) + return +} + +func signatureWithESK(msg, blindedEsk, blindedKey []byte) []byte { + a := decodeInt(blindedEsk[:32]) + lines := make([][]byte, 0) + for i := b / 8; i < b/4; i++ { + lines = append(lines, blindedEsk[i:i+1]) + } + toHint := append(bytes.Join(lines, []byte("")), msg...) + r := hint(toHint) + R := Scalarmult1(bB1, r) + S := biMod(biAdd(r, biMul(hint([]byte(string(Encodepoint(R))+string(blindedKey)+string(msg))), a)), l) + + return append(Encodepoint(R), encodeint(S)...) +} + +func edwardsDouble(P []*big.Int) []*big.Int { + // This is formula sequence 'dbl-2008-hwcd' from + // http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + x1 := P[0] + y1 := P[1] + z1 := P[2] + a := biMod(biMul(x1, x1), q) + b := biMod(biMul(y1, y1), q) + c := biMod(biMul(biMul(bi(2), z1), z1), q) + e := biMod(biSub(biSub(biMul(biAdd(x1, y1), biAdd(x1, y1)), a), b), q) + g := biAdd(biMul(a, bi(-1)), b) + f := biSub(g, c) + h := biSub(biMul(a, bi(-1)), b) + x3 := biMul(e, f) + y3 := biMul(g, h) + t3 := biMul(e, h) + z3 := biMul(f, g) + return []*big.Int{biMod(x3, q), biMod(y3, q), biMod(z3, q), biMod(t3, q)} +} + +func Scalarmult1(P []*big.Int, e *big.Int) []*big.Int { + if e.Cmp(bi(0)) == 0 { + return []*big.Int{bi(0), bi(1), bi(1), bi(0)} + } + Q := Scalarmult1(P, biDiv(e, bi(2))) + Q = edwardsDouble(Q) + if biAnd(e, bi(1)).Int64() == 1 { + //if e.And(e, bi(1)).Int64() == 1 { + Q = edwardsAdd(Q, P) + } + return Q +} + +func edwardsAdd(P, Q []*big.Int) []*big.Int { + // This is formula sequence 'addition-add-2008-hwcd-3' from + // http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + x1 := P[0] + y1 := P[1] + z1 := P[2] + t1 := P[3] + x2 := Q[0] + y2 := Q[1] + z2 := Q[2] + t2 := Q[3] + a := biMod(biMul(biSub(y1, x1), biSub(y2, x2)), q) + b := biMod(biMul(biAdd(y1, x1), biAdd(y2, x2)), q) + c := biMod(biMul(biMul(biMul(t1, bi(2)), d1), t2), q) + dd := biMod(biMul(biMul(z1, bi(2)), z2), q) + e := biSub(b, a) + f := biSub(dd, c) + g := biAdd(dd, c) + h := biAdd(b, a) + x3 := biMul(e, f) + y3 := biMul(g, h) + t3 := biMul(e, h) + z3 := biMul(f, g) + return []*big.Int{biMod(x3, q), biMod(y3, q), biMod(z3, q), biMod(t3, q)} +} + +func Encodepoint(P []*big.Int) []byte { + x := P[0] + y := P[1] + z := P[2] + //t := P[3] + zi := inv(z) + x = biMod(biMul(x, zi), q) + y = biMod(biMul(y, zi), q) + bits := make([]uint8, 0) + for i := 0; i < b-1; i++ { + bits = append(bits, uint8(biAnd(biRsh(y, uint(i)), bi(1)).Int64())) + } + bits = append(bits, uint8(biAnd(x, bi(1)).Int64())) + by := make([]uint8, 0) + for i := 0; i < b/8; i++ { + sum := uint8(0) + for j := 0; j < 8; j++ { + sum += bits[i*8+j] << j + } + by = append(by, sum) + } + return by +} + +func hint(m []byte) *big.Int { + tmp := sha512.Sum512(m) + h := tmp[:] + sum := bi(0) + for i := 0; i < 2*b; i++ { + sum = biAdd(sum, biMul(biExp(bi(2), bi(int64(i))), bi(int64(Bit(h, int64(i)))))) + } + return sum +} + +func AddressFromIdentityKey(pub ed25519.PublicKey) string { + var checksumBytes bytes.Buffer + checksumBytes.Write([]byte(".onion checksum")) + checksumBytes.Write(pub) + checksumBytes.Write([]byte{0x03}) + checksum := sha3.Sum256(checksumBytes.Bytes()) + var onionAddressBytes bytes.Buffer + onionAddressBytes.Write(pub) + onionAddressBytes.Write(checksum[:2]) + onionAddressBytes.Write([]byte{0x03}) + addr := strings.ToLower(base32.StdEncoding.EncodeToString(onionAddressBytes.Bytes())) + return addr + ".onion" +} + +func encodeint(y *big.Int) []byte { + bits := make([]*big.Int, 0) + for i := 0; i < b; i++ { + bits = append(bits, biAnd(biRsh(y, uint(i)), bi(1))) + } + final := make([]byte, 0) + for i := 0; i < b/8; i++ { + sum := bi(0) + for j := 0; j < 8; j++ { + sum = biAdd(sum, biLsh(bits[i*8+j], uint(j))) + } + final = append(final, byte(sum.Uint64())) + } + return final +} + +func PublickeyFromESK(h []byte) ed25519.PublicKey { + a := decodeInt(h[:32]) + A := scalarmult(bB, a) + return encodepoint(A) +} + +// LoadTorKeyFromDisk load a private identity key from little-t-tor. +func LoadTorKeyFromDisk(keyBytes []byte) ed25519.PrivateKey { + if !bytes.Equal(keyBytes[:29], []byte("== ed25519v1-secret: type0 ==")) { + panic("Tor key does not start with Tor header") + } + expandedSk := keyBytes[32:] + + // The rest should be 64 bytes (a,h): + // 32 bytes for secret scalar 'a' + // 32 bytes for PRF key 'h' + if len(expandedSk) != 64 { + panic("Tor private key has the wrong length") + } + return expandedSk +} + +func encodepoint(P []*big.Int) []byte { + x := P[0] + y := P[1] + bits := make([]uint8, 0) + for i := 0; i < b-1; i++ { + bits = append(bits, uint8(biAnd(biRsh(y, uint(i)), bi(1)).Int64())) + } + bits = append(bits, uint8(biAnd(x, bi(1)).Int64())) + by := make([]uint8, 0) + for i := 0; i < b/8; i++ { + sum := uint8(0) + for j := 0; j < 8; j++ { + sum += bits[i*8+j] << j + } + by = append(by, sum) + } + return by +} + +func decodeInt(s []uint8) *big.Int { + sum := bi(0) + for i := 0; i < 256; i++ { + e := biExp(bi(2), bi(int64(i))) + m := bi(int64(Bit(s, int64(i)))) + sum = sum.Add(sum, biMul(e, m)) + } + return sum +} + +func scalarmult(P []*big.Int, e *big.Int) []*big.Int { + if e.Cmp(bi(0)) == 0 { + return []*big.Int{bi(0), bi(1)} + } + Q := scalarmult(P, biDiv(e, bi(2))) + Q = edwards(Q, Q) + if e.And(e, bi(1)).Int64() == 1 { + Q = edwards(Q, P) + } + return Q +} + +func edwards(P, Q []*big.Int) []*big.Int { + x1 := P[0] + y1 := P[1] + x2 := Q[0] + y2 := Q[1] + x3 := biMul(biAdd(biMul(x1, y2), biMul(x2, y1)), inv(biAdd(bi(1), biMul(biMul(biMul(biMul(d, x1), x2), y1), y2)))) + y3 := biMul(biAdd(biMul(y1, y2), biMul(x1, x2)), inv(biSub(bi(1), biMul(biMul(biMul(biMul(d, x1), x2), y1), y2)))) + return []*big.Int{biMod(x3, q), biMod(y3, q)} +} + +func xrecover(y *big.Int) *big.Int { + xx := biMul(biSub(biMul(y, y), bi(1)), inv(biAdd(biMul(biMul(d, y), y), bi(1)))) + x := expmod(xx, biDiv(biAdd(q, bi(3)), bi(8)), q) + if biMod(biSub(biMul(x, x), xx), q).Int64() != 0 { + x = biMod(biMul(x, I), q) + } + if biMod(x, bi(2)).Int64() != 0 { + x = biSub(q, x) + } + return x +} + +func inv(x *big.Int) *big.Int { + return expmod(x, biSub(q, bi(2)), q) +} + +func expmod(b, e, m *big.Int) *big.Int { + if e.Cmp(bi(0)) == 0 { + return bi(1) + } + t := biMod(biExp(expmod(b, biDiv(e, bi(2)), m), bi(2)), m) + if biAnd(e, bi(1)).Int64() == 1 { + t = biMod(biMul(t, b), m) + } + return t +} + +func Bit(h []uint8, i int64) uint8 { + return (h[i/8] >> (i % 8)) & 1 +} + +func bi(v int64) *big.Int { + return big.NewInt(v) +} + +func biExp(a, b *big.Int) *big.Int { + return bi(0).Exp(a, b, nil) +} + +func biDiv(a, b *big.Int) *big.Int { + return bi(0).Div(a, b) +} + +func biSub(a, b *big.Int) *big.Int { + return bi(0).Sub(a, b) +} + +func biAdd(a, b *big.Int) *big.Int { + return bi(0).Add(a, b) +} + +func biAnd(a, b *big.Int) *big.Int { + return bi(0).And(a, b) +} + +func biLsh(a *big.Int, b uint) *big.Int { + return bi(0).Lsh(a, b) +} + +func biRsh(a *big.Int, b uint) *big.Int { + return bi(0).Rsh(a, b) +} + +func biMul(a, b *big.Int) *big.Int { + return bi(0).Mul(a, b) +} + +func biMod(a, b *big.Int) *big.Int { + return bi(0).Mod(a, b) +}