Added Tor addresses, ed25519 light library

This commit is contained in:
DataHoarder 2022-06-12 02:10:14 +02:00
parent e5709f249b
commit 9c7046aabd
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
6 changed files with 233 additions and 0 deletions

107
ed25519/key.go Normal file
View file

@ -0,0 +1,107 @@
package ed25519
import (
"bytes"
"crypto"
goEd25519 "crypto/ed25519"
"crypto/sha512"
"filippo.io/edwards25519"
"io"
"strconv"
)
type PrivateKey []byte
type PublicKey goEd25519.PublicKey
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = PrivateKeyFormSize + PublicKeySize
PrivateKeyFormSize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64
)
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
// Outline the function body so that the returned signature can be
// stack-allocated.
signature := make([]byte, SignatureSize)
sign(signature, privateKey, message)
return signature
}
func sign(signature, privateKey, message []byte) {
if l := len(privateKey); l != PrivateKeySize {
panic("ed25519: bad private key length: " + strconv.Itoa(l))
}
privateKey, publicKey := privateKey[:PrivateKeyFormSize], privateKey[PrivateKeyFormSize:]
s, _ := edwards25519.NewScalar().SetBytesWithClamping(privateKey[:32])
prefix := privateKey[32:]
mh := sha512.New()
mh.Write(prefix)
mh.Write(message)
messageDigest := make([]byte, 0, sha512.Size)
messageDigest = mh.Sum(messageDigest)
r, _ := edwards25519.NewScalar().SetUniformBytes(messageDigest)
R := (&edwards25519.Point{}).ScalarBaseMult(r)
kh := sha512.New()
kh.Write(R.Bytes())
kh.Write(publicKey)
kh.Write(message)
hramDigest := make([]byte, 0, sha512.Size)
hramDigest = kh.Sum(hramDigest)
k, _ := edwards25519.NewScalar().SetUniformBytes(hramDigest)
S := edwards25519.NewScalar().MultiplyAdd(k, s, r)
copy(signature[:32], R.Bytes())
copy(signature[32:], S.Bytes())
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
return goEd25519.Verify(goEd25519.PublicKey(publicKey), message, sig)
}
// NewKeyFromSeed calculates a private key from a seed. It will panic if
// len(seed) is not SeedSize. This function is provided for interoperability
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
// package.
func NewKeyFromSeed(seed []byte) PrivateKey {
return NewKeyFromStandard(goEd25519.NewKeyFromSeed(seed))
}
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
publicKey, privateKey, err := goEd25519.GenerateKey(rand)
if err != nil {
return nil, nil, err
}
return PublicKey(publicKey), NewKeyFromStandard(privateKey), nil
}
// Public returns the PublicKey corresponding to priv.
func (priv PrivateKey) Public() crypto.PublicKey {
publicKey := make([]byte, PublicKeySize)
copy(publicKey, priv[PrivateKeyFormSize:])
return PublicKey(publicKey)
}
// Equal reports whether priv and x have the same value.
func (priv PrivateKey) Equal(x crypto.PrivateKey) bool {
xx, ok := x.(PrivateKey)
if !ok {
return false
}
return bytes.Equal(priv, xx)
}

39
ed25519/utils.go Normal file
View file

@ -0,0 +1,39 @@
package ed25519
import (
goEd25519 "crypto/ed25519"
"crypto/sha512"
"filippo.io/edwards25519"
"strconv"
)
func NewKeyFromStandard(key goEd25519.PrivateKey) PrivateKey {
if l := len(key); l != goEd25519.PrivateKeySize {
panic("ed25519: bad private key length: " + strconv.Itoa(l))
}
seed, publicKey := key[:goEd25519.SeedSize], key[goEd25519.SeedSize:]
h := sha512.Sum512(seed)
s, _ := edwards25519.NewScalar().SetBytesWithClamping(h[:32])
// (a || RH)
priv := s.Bytes()
priv = append(priv, publicKey...)
return append(priv, h[32:]...)
}
func NewKeyFromRaw(h []byte) PrivateKey {
if l := len(h); l != PrivateKeyFormSize {
panic("ed25519: bad private key form length: " + strconv.Itoa(l))
}
s, _ := edwards25519.NewScalar().SetBytesWithClamping(h[:32])
A := (&edwards25519.Point{}).ScalarBaseMult(s)
publicKey := A.Bytes()
privateKey := make(PrivateKey, PrivateKeySize)
copy(privateKey, h)
copy(privateKey[PrivateKeyFormSize:], publicKey)
return privateKey
}

7
go.mod
View file

@ -1,3 +1,10 @@
module git.gammaspectra.live/givna.me/dns-api
go 1.18
require (
filippo.io/edwards25519 v1.0.0
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
)
require golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect

6
go.sum Normal file
View file

@ -0,0 +1,6 @@
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

53
utils.go Normal file
View file

@ -0,0 +1,53 @@
package dns_api
import (
"encoding/base32"
"encoding/base64"
"git.gammaspectra.live/givna.me/dns-api/ed25519"
"golang.org/x/crypto/sha3"
"strings"
)
var base32Encoding = base32.NewEncoding(strings.ToLower("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")).WithPadding(base32.NoPadding)
const onionV3Version = byte(0x3)
const onionChecksumData = ".onion checksum"
func PublicKeyToOnionV3(publicKey ed25519.PublicKey) string {
checksumBuf := make([]byte, 0, ed25519.PublicKeySize+1+len(onionChecksumData))
buf := make([]byte, 0, ed25519.PublicKeySize+2+1)
checksumBuf = append(checksumBuf, onionChecksumData...)
checksumBuf = append(checksumBuf, publicKey...)
checksumBuf = append(checksumBuf, onionV3Version)
h := sha3.New256()
h.Write(checksumBuf)
checksum := h.Sum([]byte{})[:2]
buf = append(buf, publicKey...)
buf = append(buf, checksum...)
buf = append(buf, onionV3Version)
return base32Encoding.EncodeToString(buf)
}
const torPrivateKeyPrefix = "== ed25519v1-secret: type0 ==\x00\x00\x00"
const torPublicKeyPrefix = "== ed25519v1-public: type0 ==\x00\x00\x00"
func DecodeTorPrivateKey(key string) ed25519.PrivateKey {
priv, err := base64.RawStdEncoding.DecodeString(key)
if err != nil {
return nil
}
return ed25519.NewKeyFromRaw(priv[32:])
}
func DecodePrivateKey(buf string) ed25519.PrivateKey {
priv, err := base64.RawStdEncoding.DecodeString(buf)
if err != nil {
return nil
}
return ed25519.NewKeyFromRaw(priv)
}

21
utils_test.go Normal file
View file

@ -0,0 +1,21 @@
package dns_api
import (
"git.gammaspectra.live/givna.me/dns-api/ed25519"
"testing"
)
const testPrivateKey = "PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAAC4WKtXqyPlfMRwf37uSI/0kZRY8rzFBCpcDPeCp15uUalOhEmCQtBYH7oihSe8J4Znf9Gzw1E7W377Y6K3ux8V"
const testOnionAddress = "testdqzsnwe6dpmbhgcm5arrvhqzhomxs27nn2sertphnmfx7z44gzyd"
func TestPublicKeyToOnionV3(t *testing.T) {
privateKey := DecodeTorPrivateKey(testPrivateKey)
if privateKey == nil {
t.Fail()
}
generatedAddress := PublicKeyToOnionV3(privateKey.Public().(ed25519.PublicKey))
if generatedAddress != testOnionAddress {
t.Errorf("expected address %s, got %s", testOnionAddress, generatedAddress)
}
}