Skip to content

Commit

Permalink
primitives/sr25519: Enable generating secret keys from ed25519 keys
Browse files Browse the repository at this point in the history
  • Loading branch information
jberci committed Aug 23, 2023
1 parent db37f07 commit 6b45481
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 0 deletions.
28 changes: 28 additions & 0 deletions primitives/sr25519/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,34 @@ func NewSecretKeyFromBytes(b []byte) (*SecretKey, error) {
return &sk, nil
}

// NewSecretKeyFromEd25519Bytes constructs a SecretKey from the byte representation
// of an expanded Ed25519 key.
func NewSecretKeyFromEd25519Bytes(b []byte) (*SecretKey, error) {
if len(b) != SecretKeySize {
return nil, fmt.Errorf("sr25519: invalid expanded ed25519 key length")
}

// Check the scalar is clamped.
s := b[:SecretKeyScalarSize]
if (s[0]&0b0000_0111) != 0 || (s[31]&0b1100_0000) != 0b0100_0000 {
return nil, fmt.Errorf("sr25519: ed25519 scalar invalid")
}

scalar, err := scalarDivideByCofactor(s)
if err != nil {
return nil, err
}

var nonce [SecretKeyNonceSize]byte
copy(nonce[:], b[SecretKeyScalarSize:])

sk := &SecretKey{
key: scalar,
nonce: nonce,
}
return sk, nil
}

// GenerateSecretKey generates a SecretKey using entropy from rng.
// If rng is nil, crypto/rand.Reader will be used.
func GenerateSecretKey(rng io.Reader) (*SecretKey, error) {
Expand Down
55 changes: 55 additions & 0 deletions primitives/sr25519/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ package sr25519
import (
"bytes"
"crypto/rand"
"encoding/hex"
"testing"

"github.com/oasisprotocol/curve25519-voi/curve/scalar"
Expand Down Expand Up @@ -197,6 +198,60 @@ func TestSecretKey(t *testing.T) {
t.Fatalf("sk == sk2")
}
})
t.Run("Ed25519Bytes", func(t *testing.T) {
var err error

// Test some incorrect key byte slices.
for _, d := range []struct {
bytes string
message string
}{
{
bytes: "28b0ae221c6bb06856b287f60d7ea0d98552ea5a16db16956849aa371db3eb51fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca34aa",
message: "NewSecretKeyFromEd25519Bytes: using too long key must fail",
},
{
bytes: "28b0ae221c6bb06856b287f60d7ea0d98552ea5a16db16956849aa371db3eb51fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca",
message: "NewSecretKeyFromEd25519Bytes: using too short key must fail",
},
{
bytes: "28b0ae221c6bb06856b287f60d7ea0d98552ea5a16db16956849aa371db3ebd1fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca34",
message: "NewSecretKeyFromEd25519Bytes: scalar with fixed top bit must fail",
},
{
bytes: "2ab0ae221c6bb06856b287f60d7ea0d98552ea5a16db16956849aa371db3eb51fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca34",
message: "NewSecretKeyFromEd25519Bytes: scalar not divisible by 8 must fail",
},
} {
decoded := testhelpers.MustUnhex(t, d.bytes)
_, err = NewSecretKeyFromEd25519Bytes(decoded)
if err == nil {
t.Errorf(d.message)
}
}

// Valid. For the test bytes, see the schnorrkel Rust crate and
// its SecretKey::from_ed25519_bytes method documentation.
ed25519Bytes := testhelpers.MustUnhex(t, "28b0ae221c6bb06856b287f60d7ea0d98552ea5a16db16956849aa371db3eb51fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca34")
sk, err := NewSecretKeyFromEd25519Bytes(ed25519Bytes)
if err != nil {
t.Fatalf("NewSecretKeyFromEd25519Bytes: %v", err)
}

skBytes := make([]byte, scalar.ScalarSize)
if sk.key.ToBytes(skBytes) != nil {
t.Fatalf("sk.ToBytes: %v", err)
}
expectedSkBytes := testhelpers.MustUnhex(t, "05d65584630d16cd4af6d0bec10f34bb504a5dcb62dba2122d49f5a663763d0a")
if !bytes.Equal(skBytes, expectedSkBytes) {
t.Fatalf("secret key bytes differ (%v != %v)", hex.EncodeToString(skBytes), hex.EncodeToString(expectedSkBytes))
}

expectedNonce := testhelpers.MustUnhex(t, "fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca34")
if !bytes.Equal(sk.nonce[:], expectedNonce) {
t.Fatalf("nonce bytes differ: %v != %v", hex.EncodeToString(sk.nonce[:]), hex.EncodeToString(expectedNonce))
}
})
}

func TestPublicKey(t *testing.T) {
Expand Down

0 comments on commit 6b45481

Please sign in to comment.