diff --git a/verifications/ed25519/VerificationKey2020.go b/verifications/ed25519/VerificationKey2020.go index f9bce75..f3f0fde 100644 --- a/verifications/ed25519/VerificationKey2020.go +++ b/verifications/ed25519/VerificationKey2020.go @@ -12,7 +12,6 @@ import ( // Specification: https://w3c.github.io/cg-reports/credentials/CG-FINAL-di-eddsa-2020-20220724/ const ( - MultibaseCode = uint64(0xed) JsonLdContext = "https://w3id.org/security/suites/ed25519-2020/v1" Type = "Ed25519VerificationKey2020" ) diff --git a/verifications/ed25519/key.go b/verifications/ed25519/key.go index c5502ac..d03a4a1 100644 --- a/verifications/ed25519/key.go +++ b/verifications/ed25519/key.go @@ -18,6 +18,8 @@ const ( PrivateKeySize = ed25519.PrivateKeySize // SignatureSize is the size, in bytes, of signatures generated and verified by this package. SignatureSize = ed25519.SignatureSize + + MultibaseCode = uint64(0xed) ) func GenerateKeyPair() (PublicKey, PrivateKey, error) { @@ -30,7 +32,16 @@ func PublicKeyFromBytes(b []byte) (PublicKey, error) { if len(b) != PublicKeySize { return nil, fmt.Errorf("invalid ed25519 public key size") } - return PublicKey(b), nil + // make a copy + return PublicKey(append([]byte{}, b...)), nil +} + +// PublicKeyToBytes converts a public key to a byte slice. +func PublicKeyToBytes(pub PublicKey) []byte { + // Copy the private key to a fixed size buffer that can get allocated on the + // caller's stack after inlining. + var buf [PublicKeySize]byte + return append(buf[:0], pub...) } // PublicKeyFromMultibase decodes the public key from its Multibase form @@ -45,7 +56,7 @@ func PublicKeyFromMultibase(multibase string) (PublicKey, error) { if len(bytes) != PublicKeySize { return nil, fmt.Errorf("invalid ed25519 public key size") } - return bytes, nil + return PublicKeyFromBytes(bytes) } // PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase @@ -53,13 +64,22 @@ func PublicKeyToMultibase(pub PublicKey) string { return helpers.MultibaseEncode(MultibaseCode, pub) } -// PrivateKeyFromBytes converts a serialized public key to a PrivateKey. +// PrivateKeyFromBytes converts a serialized private key to a PrivateKey. // It errors if the slice is not the right size. func PrivateKeyFromBytes(b []byte) (PrivateKey, error) { - if len(b) != ed25519.PrivateKeySize { + if len(b) != PrivateKeySize { return nil, fmt.Errorf("invalid ed25519 private key size") } - return b, nil + // make a copy + return append([]byte{}, b...), nil +} + +// PrivateKeyToBytes converts a private key to a byte slice. +func PrivateKeyToBytes(priv PrivateKey) []byte { + // Copy the private key to a fixed size buffer that can get allocated on the + // caller's stack after inlining. + var buf [PrivateKeySize]byte + return append(buf[:0], priv...) } // Sign signs the message with privateKey and returns a signature. diff --git a/verifications/ed25519/key_test.go b/verifications/ed25519/key_test.go index 6068694..ecdc45f 100644 --- a/verifications/ed25519/key_test.go +++ b/verifications/ed25519/key_test.go @@ -16,6 +16,21 @@ func TestGenerateKey(t *testing.T) { require.True(t, pub.Equal(priv.Public())) } +func TestBytesRoundTrip(t *testing.T) { + pub, priv, err := ed25519.GenerateKeyPair() + require.NoError(t, err) + + bytes := ed25519.PublicKeyToBytes(pub) + rtPub, err := ed25519.PublicKeyFromBytes(bytes) + require.NoError(t, err) + require.True(t, pub.Equal(rtPub)) + + bytes = ed25519.PrivateKeyToBytes(priv) + rtPriv, err := ed25519.PrivateKeyFromBytes(bytes) + require.NoError(t, err) + require.True(t, priv.Equal(rtPriv)) +} + func TestMultibaseRoundTrip(t *testing.T) { pub, _, err := ed25519.GenerateKeyPair() require.NoError(t, err) diff --git a/verifications/p256/key.go b/verifications/p256/key.go new file mode 100644 index 0000000..e19caf9 --- /dev/null +++ b/verifications/p256/key.go @@ -0,0 +1,84 @@ +package p256 + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "fmt" + + helpers "github.com/INFURA/go-did/verifications/internal" +) + +type PublicKey = *ecdsa.PublicKey +type PrivateKey = *ecdsa.PrivateKey + +const ( + // TODO + PublicKeySize = 123456 + PrivateKeySize = 123456 + SignatureSize = 123456 + + MultibaseCode = uint64(0x1200) +) + +func GenerateKeyPair() (PublicKey, PrivateKey, error) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, nil, err + } + return priv.Public().(PublicKey), priv, nil +} + +// PublicKeyFromBytes converts a serialized public key to a PublicKey. +// It errors if the slice is not the right size. +func PublicKeyFromBytes(b []byte) (PublicKey, error) { + if len(b) != PublicKeySize { + return nil, fmt.Errorf("invalid P-256 public key size") + } + x, y := elliptic.UnmarshalCompressed(elliptic.P256(), b) + if x == nil { + return nil, fmt.Errorf("invalid P-256 public key") + } + return &ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}, nil +} + +// PublicKeyToBytes converts a public key to a byte slice. +func PublicKeyToBytes(pub PublicKey) (res []byte, err error) { + defer func() { + if rerr := recover(); rerr != nil { + err = fmt.Errorf("recovered panic: %s", rerr) + res = nil + } + }() + return x509.MarshalPKIXPublicKey(pub) +} + +// PublicKeyFromMultibase decodes the public key from its Multibase form +func PublicKeyFromMultibase(multibase string) (PublicKey, error) { + code, bytes, err := helpers.MultibaseDecode(multibase) + if err != nil { + return nil, err + } + if code != MultibaseCode { + return nil, fmt.Errorf("invalid code") + } + return PublicKeyFromBytes(bytes) +} + +// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase +func PublicKeyToMultibase(pub PublicKey) string { + bytes := elliptic.MarshalCompressed(elliptic.P256(), pub.X, pub.Y) + return helpers.MultibaseEncode(MultibaseCode, bytes) +} + +// PrivateKeyFromBytes converts a serialized public key to a PrivateKey. +// It errors if the slice is not the right size. +func PrivateKeyFromBytes(b []byte) (PrivateKey, error) { + if len(b) != PrivateKeySize { + return nil, fmt.Errorf("invalid P-256 private key size") + } + // TODO + + return nil, nil +} diff --git a/verifications/p256/key_test.go b/verifications/p256/key_test.go new file mode 100644 index 0000000..46e8dc0 --- /dev/null +++ b/verifications/p256/key_test.go @@ -0,0 +1,43 @@ +package p256_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/INFURA/go-did/verifications/ed25519" + "github.com/INFURA/go-did/verifications/p256" +) + +func TestGenerateKey(t *testing.T) { + pub, priv, err := p256.GenerateKeyPair() + require.NoError(t, err) + require.NotNil(t, pub) + require.NotNil(t, priv) + require.True(t, pub.Equal(priv.Public())) +} + +func TestBytesRoundTrip(t *testing.T) { + pub, priv, err := p256.GenerateKeyPair() + require.NoError(t, err) + + bytes := p256.PublicKeyToBytes(pub) + rtPub, err := p256.PublicKeyFromBytes(bytes) + require.NoError(t, err) + require.True(t, pub.Equal(rtPub)) + + bytes = p256.PrivateKeyToBytes(priv) + rtPriv, err := p256.PrivateKeyFromBytes(bytes) + require.NoError(t, err) + require.True(t, priv.Equal(rtPriv)) +} + +func TestMultibaseRoundTrip(t *testing.T) { + pub, _, err := p256.GenerateKeyPair() + require.NoError(t, err) + + mb := p256.PublicKeyToMultibase(pub) + rt, err := p256.PublicKeyFromMultibase(mb) + require.NoError(t, err) + require.Equal(t, pub, rt) +} diff --git a/verifications/x25519/KeyAgreementKey2020.go b/verifications/x25519/KeyAgreementKey2020.go index ad7d594..e61a8de 100644 --- a/verifications/x25519/KeyAgreementKey2020.go +++ b/verifications/x25519/KeyAgreementKey2020.go @@ -12,7 +12,6 @@ import ( // Specification: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 const ( - MultibaseCode = uint64(0xec) JsonLdContext = "https://w3id.org/security/suites/x25519-2020/v1" Type = "X25519KeyAgreementKey2020" ) diff --git a/verifications/x25519/key.go b/verifications/x25519/key.go index 83047ad..05ebe31 100644 --- a/verifications/x25519/key.go +++ b/verifications/x25519/key.go @@ -21,6 +21,8 @@ const ( PrivateKeySize = 32 // SignatureSize is the size, in bytes, of signatures generated and verified by this package. SignatureSize = 32 + + MultibaseCode = uint64(0xec) ) func GenerateKeyPair() (PublicKey, PrivateKey, error) { @@ -37,6 +39,11 @@ func PublicKeyFromBytes(b []byte) (PublicKey, error) { return ecdh.X25519().NewPublicKey(b) } +// PublicKeyToBytes converts a public key to a byte slice. +func PublicKeyToBytes(pub PublicKey) []byte { + return pub.Bytes() +} + // PublicKeyFromEd25519 converts an ed25519 public key to a x25519 public key. // It errors if the slice is not the right size. // @@ -106,7 +113,7 @@ func PublicKeyFromMultibase(multibase string) (PublicKey, error) { if code != MultibaseCode { return nil, fmt.Errorf("invalid code") } - return ecdh.X25519().NewPublicKey(bytes) + return PublicKeyFromBytes(bytes) } // PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase @@ -114,12 +121,17 @@ func PublicKeyToMultibase(pub PublicKey) string { return helpers.MultibaseEncode(MultibaseCode, pub.Bytes()) } -// PrivateKeyFromBytes converts a serialized public key to a PrivateKey. +// PrivateKeyFromBytes converts a serialized private key to a PrivateKey. // It errors if len(privateKey) is not [PrivateKeySize]. func PrivateKeyFromBytes(b []byte) (PrivateKey, error) { return ecdh.X25519().NewPrivateKey(b) } +// PrivateKeyToBytes converts a private key to a byte slice. +func PrivateKeyToBytes(priv PrivateKey) []byte { + return priv.Bytes() +} + // PrivateKeyFromEd25519 converts an ed25519 private key to a x25519 private key. // It errors if the slice is not the right size. // diff --git a/verifications/x25519/key_test.go b/verifications/x25519/key_test.go index a213856..1df61a4 100644 --- a/verifications/x25519/key_test.go +++ b/verifications/x25519/key_test.go @@ -20,6 +20,21 @@ func TestGenerateKey(t *testing.T) { require.True(t, pub.Equal(priv.Public())) } +func TestBytesRoundTrip(t *testing.T) { + pub, priv, err := x25519.GenerateKeyPair() + require.NoError(t, err) + + bytes := x25519.PublicKeyToBytes(pub) + rtPub, err := x25519.PublicKeyFromBytes(bytes) + require.NoError(t, err) + require.True(t, pub.Equal(rtPub)) + + bytes = x25519.PrivateKeyToBytes(priv) + rtPriv, err := x25519.PrivateKeyFromBytes(bytes) + require.NoError(t, err) + require.True(t, priv.Equal(rtPriv)) +} + func TestMultibaseRoundTrip(t *testing.T) { pub, _, err := x25519.GenerateKeyPair() require.NoError(t, err)