From 2933ba510c7ac41e39b54667e3cb5f11fdea929d Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Thu, 13 Oct 2022 17:47:14 +0200 Subject: Add hs_ed25519_secret_key_parsing --- pkg/oaddr/oaddr.go | 3 ++ pkg/oaddr/oaddr_test.go | 2 ++ pkg/okey/okey.go | 29 ++++++++++++++++++ pkg/okey/okey_test.go | 51 ++++++++++++++++++++++++++++++++ pkg/okey/testonly/hostname | 1 + pkg/okey/testonly/hs_ed25519_public_key | Bin 0 -> 64 bytes pkg/okey/testonly/hs_ed25519_secret_key | Bin 0 -> 96 bytes 7 files changed, 86 insertions(+) create mode 100644 pkg/okey/okey.go create mode 100644 pkg/okey/okey_test.go create mode 100644 pkg/okey/testonly/hostname create mode 100644 pkg/okey/testonly/hs_ed25519_public_key create mode 100644 pkg/okey/testonly/hs_ed25519_secret_key (limited to 'pkg') diff --git a/pkg/oaddr/oaddr.go b/pkg/oaddr/oaddr.go index f065ec9..fd8b6a4 100644 --- a/pkg/oaddr/oaddr.go +++ b/pkg/oaddr/oaddr.go @@ -8,6 +8,7 @@ import ( "fmt" "strings" + bed25519 "github.com/cretz/bine/torutil/ed25519" "golang.org/x/crypto/sha3" ) @@ -29,6 +30,8 @@ func NewFromSigner(s crypto.Signer) (addr OnionAddress, err error) { switch t := s.Public().(type) { case ed25519.PublicKey: addr, err = New(s.Public().(ed25519.PublicKey)) + case bed25519.PublicKey: + addr, err = New(s.Public().(bed25519.PublicKey)) default: err = fmt.Errorf("unknown key type: %v", t) } diff --git a/pkg/oaddr/oaddr_test.go b/pkg/oaddr/oaddr_test.go index a37d4de..549bd67 100644 --- a/pkg/oaddr/oaddr_test.go +++ b/pkg/oaddr/oaddr_test.go @@ -5,6 +5,7 @@ import ( "crypto/ed25519" "testing" + bed25519 "github.com/cretz/bine/torutil/ed25519" "sauteed-onions.org/onion-csr/internal/testonly" ) @@ -45,6 +46,7 @@ func TestNewFromSigner(t *testing.T) { }{ {"rsa key", testonly.RSAPriv(t), OnionAddress{}}, {"valid", testonly.Ed25519Priv(t, testPriv), newAddr(t, testPub)}, + {"valid", bed25519.FromCryptoPrivateKey(testonly.Ed25519Priv(t, testPriv)).PrivateKey(), newAddr(t, testPub)}, } { addr, err := NewFromSigner(table.priv) if got, want := err != nil, table.desc != "valid"; got != want { diff --git a/pkg/okey/okey.go b/pkg/okey/okey.go new file mode 100644 index 0000000..aba4f3e --- /dev/null +++ b/pkg/okey/okey.go @@ -0,0 +1,29 @@ +// Package okey provides access to onion service private keys +package okey + +import ( + "crypto" + "fmt" + "os" + + bed25519 "github.com/cretz/bine/torutil/ed25519" +) + +// New parses the content of Tor's hs_ed25519_secret_key file by interpretting +// bytes 32..96 as the 64-byte expanded seed. For reference, see: +// https://gitlab.torproject.org/tpo/core/tor/-/blob/main/src/feature/keymgt/loadkey.c#L379 +func New(b []byte) (crypto.Signer, error) { + if len(b) != 96 { + return nil, fmt.Errorf("invalid key file size: %d", len(b)) + } + return bed25519.PrivateKey(b[32:96]), nil +} + +// NewFromHSDir reads and parses the hs_ed25519_secret_key file in a given directory +func NewFromHSDir(dir string) (crypto.Signer, error) { + b, err := os.ReadFile(dir + "/hs_ed25519_secret_key") + if err != nil { + return nil, err + } + return New(b) +} diff --git a/pkg/okey/okey_test.go b/pkg/okey/okey_test.go new file mode 100644 index 0000000..2636bb4 --- /dev/null +++ b/pkg/okey/okey_test.go @@ -0,0 +1,51 @@ +package okey + +import ( + "bytes" + "os" + "testing" + + bed25519 "github.com/cretz/bine/torutil/ed25519" +) + +func TestNewFromHSDir(t *testing.T) { + if _, err := NewFromHSDir("no-such-directory"); err == nil { + t.Errorf("succeeded to read non-existing directory") + } + + want := bed25519.PublicKey(hsPubFromFile(t, "testonly")) + if s, err := NewFromHSDir("testonly"); err != nil { + t.Errorf("failed reading valid directory: %v", err) + } else if got, ok := s.Public().(bed25519.PublicKey); !ok { + t.Errorf("wrong key type: %T", s.Public()) + } else if !bytes.Equal(got, want) { + t.Errorf("got public key\n%x\nbut wanted\n%x", got, want) + } +} + +func TestNew(t *testing.T) { + for _, table := range []struct { + desc string + priv []byte + want bed25519.PublicKey + }{ + {"invalid: key size", make([]byte, 95), nil}, + } { + _, err := New(table.priv) + if got, want := err != nil, table.desc != "valid"; got != want { + t.Errorf("%s: got error %v but wanted %v: %v", table.desc, got, want, err) + } + } +} + +func hsPubFromFile(t *testing.T, dir string) []byte { + t.Helper() + b, err := os.ReadFile(dir + "/hs_ed25519_public_key") + if err != nil { + t.Fatal(err) + } + if len(b) != 64 { + t.Fatalf("invalid public key file size: %d", len(b)) + } + return b[32:64] +} diff --git a/pkg/okey/testonly/hostname b/pkg/okey/testonly/hostname new file mode 100644 index 0000000..ed58ba6 --- /dev/null +++ b/pkg/okey/testonly/hostname @@ -0,0 +1 @@ +a3xo2fquvmhntjo663f4255bpskuuop4qru66xuzcmwfdl4fgiwt3uid.onion diff --git a/pkg/okey/testonly/hs_ed25519_public_key b/pkg/okey/testonly/hs_ed25519_public_key new file mode 100644 index 0000000..0a7493d Binary files /dev/null and b/pkg/okey/testonly/hs_ed25519_public_key differ diff --git a/pkg/okey/testonly/hs_ed25519_secret_key b/pkg/okey/testonly/hs_ed25519_secret_key new file mode 100644 index 0000000..9ba4008 Binary files /dev/null and b/pkg/okey/testonly/hs_ed25519_secret_key differ -- cgit v1.2.3