Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

primitives/sr25519: Enable generating secret keys from ed25519 keys #121

Merged
merged 1 commit into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
peternose marked this conversation as resolved.
Show resolved Hide resolved
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
Loading