diff --git a/methods/did-key/key.go b/methods/did-key/key.go index 37db891..3082c3b 100644 --- a/methods/did-key/key.go +++ b/methods/did-key/key.go @@ -49,45 +49,40 @@ func Decode(identifier string) (did.DID, error) { return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) } - d := DidKey{msi: msi} - switch code { case ed25519.MultibaseCode: - d.signature, err = ed25519.NewVerificationKey2020(fmt.Sprintf("did:key:%s#%s", msi, msi), bytes[read:], d) - if err != nil { - return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) - } - xpub, err := x25519.PublicKeyFromEd25519(bytes[read:]) - if err != nil { - return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) - } - xmsi := x25519.PublicKeyToMultibase(xpub) - d.keyAgreement, err = x25519.NewKeyAgreementKey2020(fmt.Sprintf("did:key:%s#%s", msi, xmsi), xpub, d) + pub, err := ed25519.PublicKeyFromBytes(bytes[read:]) if err != nil { return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) } + return FromPublicKey(pub) - // case P256: // TODO - // case Secp256k1: // TODO - // case RSA: // TODO - default: - return nil, fmt.Errorf("%w: unsupported did:key multicodec: 0x%x", did.ErrInvalidDid, code) + // case P256: // TODO + // case Secp256k1: // TODO + // case RSA: // TODO } - return d, nil + return nil, fmt.Errorf("%w: unsupported did:key multicodec: 0x%x", did.ErrInvalidDid, code) } func FromPublicKey(pub PublicKey) (did.DID, error) { var err error switch pub := pub.(type) { case ed25519.PublicKey: - d := DidKey{ - msi: ed25519.PublicKeyToMultibase(pub), - } + d := DidKey{msi: ed25519.PublicKeyToMultibase(pub)} d.signature, err = ed25519.NewVerificationKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, d.msi), pub, d) if err != nil { return nil, err } + xpub, err := x25519.PublicKeyFromEd25519(pub) + if err != nil { + return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) + } + xmsi := x25519.PublicKeyToMultibase(xpub) + d.keyAgreement, err = x25519.NewKeyAgreementKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, xmsi), xpub, d) + if err != nil { + return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) + } return d, nil default: diff --git a/verifications/ed25519/VerificationKey2020.go b/verifications/ed25519/VerificationKey2020.go index 7e5c935..dab39ae 100644 --- a/verifications/ed25519/VerificationKey2020.go +++ b/verifications/ed25519/VerificationKey2020.go @@ -14,6 +14,7 @@ import ( const ( MultibaseCode = uint64(0xed) JsonLdContext = "https://w3id.org/security/suites/ed25519-2020/v1" + Type = "Ed25519VerificationKey2020" ) var _ did.VerificationMethodSignature = &VerificationKey2020{} @@ -84,7 +85,7 @@ func (v VerificationKey2020) ID() string { } func (v VerificationKey2020) Type() string { - return "Ed25519VerificationKey2020" + return Type } func (v VerificationKey2020) Controller() string { diff --git a/verifications/ed25519/key.go b/verifications/ed25519/key.go index 189a9c0..072c4c6 100644 --- a/verifications/ed25519/key.go +++ b/verifications/ed25519/key.go @@ -22,7 +22,7 @@ func PublicKeyFromBytes(b []byte) (PublicKey, error) { if len(b) != PublicKeySize { return nil, fmt.Errorf("invalid ed25519 public key size") } - return ed25519.PublicKey(b), nil + return PublicKey(b), nil } // PublicKeyFromMultibase decodes the public key from its Multibase form diff --git a/verifications/x25519/KeyAgreementKey2020.go b/verifications/x25519/KeyAgreementKey2020.go index 5dc9231..256a60c 100644 --- a/verifications/x25519/KeyAgreementKey2020.go +++ b/verifications/x25519/KeyAgreementKey2020.go @@ -1,13 +1,11 @@ package x25519 import ( + "crypto/ecdh" "encoding/json" "errors" "fmt" - mbase "github.com/multiformats/go-multibase" - "github.com/multiformats/go-varint" - "github.com/INFURA/go-did" ) @@ -16,6 +14,7 @@ import ( const ( MultibaseCode = uint64(0xec) JsonLdContext = "https://w3id.org/security/suites/x25519-2020/v1" + Type = "X25519KeyAgreementKey2020" ) var _ did.VerificationMethodKeyAgreement = &KeyAgreementKey2020{} @@ -27,8 +26,8 @@ type KeyAgreementKey2020 struct { } func NewKeyAgreementKey2020(id string, pubkey PublicKey, controller did.DID) (*KeyAgreementKey2020, error) { - if len(pubkey) != PublicKeySize { - return nil, errors.New("invalid x25519 public key size") + if pubkey.Curve() != ecdh.X25519() { + return nil, errors.New("x25519 key curve must be X25519") } return &KeyAgreementKey2020{ @@ -70,7 +69,7 @@ func (k *KeyAgreementKey2020) UnmarshalJSON(bytes []byte) error { if len(k.id) == 0 { return errors.New("invalid id") } - k.pubkey, err = MultibaseToPublicKey(aux.PublicKeyMultibase) + k.pubkey, err = PublicKeyFromMultibase(aux.PublicKeyMultibase) if err != nil { return fmt.Errorf("invalid publicKeyMultibase: %w", err) } @@ -86,7 +85,7 @@ func (k KeyAgreementKey2020) ID() string { } func (k KeyAgreementKey2020) Type() string { - return "X25519KeyAgreementKey2020" + return Type } func (k KeyAgreementKey2020) Controller() string { @@ -97,35 +96,7 @@ func (k KeyAgreementKey2020) JsonLdContext() string { return JsonLdContext } -// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase -func PublicKeyToMultibase(pub PublicKey) string { - // can only fail with an invalid encoding, but it's hardcoded - bytes, _ := mbase.Encode(mbase.Base58BTC, append(varint.ToUvarint(MultibaseCode), pub...)) - return bytes -} - -// MultibaseToPublicKey decodes the public key from its publicKeyMultibase form -func MultibaseToPublicKey(multibase string) (PublicKey, error) { - baseCodec, bytes, err := mbase.Decode(multibase) - if err != nil { - return nil, err - } - // the specification enforces that encoding - if baseCodec != mbase.Base58BTC { - return nil, fmt.Errorf("not Base58BTC encoded") - } - code, read, err := varint.FromUvarint(bytes) - if err != nil { - return nil, err - } - if code != MultibaseCode { - return nil, fmt.Errorf("invalid code") - } - if read != 2 { - return nil, fmt.Errorf("unexpected multibase") - } - if len(bytes)-read != PublicKeySize { - return nil, fmt.Errorf("invalid ed25519 public key size") - } - return bytes[read:], nil +// TODO: make it part of did.VerificationMethodKeyAgreement in some way +func (k KeyAgreementKey2020) KeyAgreement(priv PrivateKey) ([]byte, error) { + return priv.ECDH(k.pubkey) } diff --git a/verifications/x25519/key.go b/verifications/x25519/key.go index c9bf602..6743fd3 100644 --- a/verifications/x25519/key.go +++ b/verifications/x25519/key.go @@ -1,171 +1,32 @@ package x25519 -// TODO: use ecdh.PublicKey instead of defining a custom type below? - -// type PublicKey ecdh.PublicKey -// -// func (p PublicKey) Equal(x crypto.PublicKey) bool { -// // TODO implement me -// panic("implement me") -// } -// -// type PrivateKey ecdh.PrivateKey -// -// func (p *PrivateKey) Public() crypto.PublicKey { -// key := p.(ecdh.PrivateKey) -// return key.Public() -// } -// -// func (p *PrivateKey) Equal(x crypto.PrivateKey) bool { -// // TODO implement me -// panic("implement me") -// } -// -// func GenerateKeyPair() (PublicKey, PrivateKey, error) { -// priv, err := ecdh.X25519().GenerateKey(rand.Reader) -// if err != nil { -// return nil, nil, err -// } -// return priv.Public().(PublicKey), priv, nil -// } -// -// // PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase -// func PublicKeyToMultibase(pub PublicKey) string { -// // can only fail with an invalid encoding, but it's hardcoded -// bytes, _ := mbase.Encode(mbase.Base58BTC, append(varint.ToUvarint(MultibaseCode), pub.Bytes()...)) -// return bytes -// } -// -// // MultibaseToPublicKey decodes the public key from its publicKeyMultibase form -// func MultibaseToPublicKey(multibase string) (PublicKey, error) { -// baseCodec, bytes, err := mbase.Decode(multibase) -// if err != nil { -// return nil, err -// } -// // the specification enforces that encoding -// if baseCodec != mbase.Base58BTC { -// return nil, fmt.Errorf("not Base58BTC encoded") -// } -// code, read, err := varint.FromUvarint(bytes) -// if err != nil { -// return nil, err -// } -// if code != MultibaseCode { -// return nil, fmt.Errorf("invalid code") -// } -// if read != 2 { -// return nil, fmt.Errorf("unexpected multibase") -// } -// if len(bytes)-read != ed25519.PublicKeySize { -// return nil, fmt.Errorf("invalid ed25519 public key size") -// } -// return bytes[read:], nil -// } - import ( - "bytes" - "crypto" + "crypto/ecdh" "crypto/rand" "fmt" - "io" "math/big" - "golang.org/x/crypto/curve25519" + mbase "github.com/multiformats/go-multibase" + "github.com/multiformats/go-varint" "github.com/INFURA/go-did/verifications/ed25519" ) -// This mirrors ed25519's structure for private/public "keys". We -// require dedicated types for these as they drive -// serialization/deserialization logic, as well as encryption types. -// -// Note that with the x25519 scheme, the private key is a sequence of -// 32 bytes, while the public key is the result of X25519(private, -// basepoint). -// -// Portions of this file are from Go's ed25519.go, which is -// Copyright 2016 The Go Authors. All rights reserved. +type PublicKey = *ecdh.PublicKey +type PrivateKey = *ecdh.PrivateKey -// Originally taken from github.com/lestrrat-go/jwx/v2/x25519. +const PublicKeySize = 32 -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 = 64 - // SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. - SeedSize = 32 -) - -// PublicKey is the type of X25519 public keys -type PublicKey []byte - -// NewKeyFromSeed calculates a private key from a seed. It will return -// an error if len(seed) is not SeedSize. This function is provided -// for interoperability with RFC 7748. RFC 7748's private keys -// correspond to seeds in this package. -func NewKeyFromSeed(seed []byte) (PrivateKey, error) { - privateKey := make([]byte, PrivateKeySize) - if len(seed) != SeedSize { - return nil, fmt.Errorf("unexpected seed size: %d", len(seed)) - } - copy(privateKey, seed) - public, err := curve25519.X25519(seed, curve25519.Basepoint) - if err != nil { - return nil, fmt.Errorf(`failed to compute public key: %w`, err) - } - copy(privateKey[SeedSize:], public) - - return privateKey, nil -} - -// GenerateKey generates a public/private key pair using entropy from rand. -// If rand is nil, crypto/rand.Reader will be used. -func GenerateKey() (PublicKey, PrivateKey, error) { - seed := make([]byte, SeedSize) - if _, err := io.ReadFull(rand.Reader, seed); err != nil { - return nil, nil, err - } - - privateKey, err := NewKeyFromSeed(seed) +func GenerateKeyPair() (PublicKey, PrivateKey, error) { + priv, err := ecdh.X25519().GenerateKey(rand.Reader) if err != nil { return nil, nil, err } - publicKey := make([]byte, PublicKeySize) - copy(publicKey, privateKey[SeedSize:]) - - return publicKey, privateKey, nil + return priv.Public().(PublicKey), priv, nil } -// Any methods implemented on PublicKey might need to also be implemented on -// PrivateKey, as the latter embeds the former and will expose its methods. - -// Equal reports whether pub and x have the same value. -func (pub PublicKey) Equal(x crypto.PublicKey) bool { - xx, ok := x.(PublicKey) - if !ok { - return false - } - return bytes.Equal(pub, xx) -} - -// PrivateKey is the type of X25519 private key -type PrivateKey []byte - -// Public returns the PublicKey corresponding to priv. -func (priv PrivateKey) Public() crypto.PublicKey { - publicKey := make([]byte, PublicKeySize) - copy(publicKey, priv[SeedSize:]) - 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) +func PublicKeyFromBytes(b []byte) (PublicKey, error) { + return ecdh.X25519().NewPublicKey(b) } func PublicKeyFromEd25519(pub ed25519.PublicKey) (PublicKey, error) { @@ -221,7 +82,37 @@ func PublicKeyFromEd25519(pub ed25519.PublicKey) (PublicKey, error) { // x25519 are little-endian, but big.Int give us big-endian. // See https://www.ietf.org/rfc/rfc7748.txt - return reverseBytes(res), nil + return ecdh.X25519().NewPublicKey(reverseBytes(res)) +} + +// PublicKeyFromMultibase decodes the public key from its Multibase form +func PublicKeyFromMultibase(multibase string) (PublicKey, error) { + baseCodec, bytes, err := mbase.Decode(multibase) + if err != nil { + return nil, err + } + // the specification enforces that encoding + if baseCodec != mbase.Base58BTC { + return nil, fmt.Errorf("not Base58BTC encoded") + } + code, read, err := varint.FromUvarint(bytes) + if err != nil { + return nil, err + } + if code != MultibaseCode { + return nil, fmt.Errorf("invalid code") + } + if read != 2 { + return nil, fmt.Errorf("unexpected multibase") + } + return ecdh.X25519().NewPublicKey(bytes[read:]) +} + +// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase +func PublicKeyToMultibase(pub PublicKey) string { + // can only fail with an invalid encoding, but it's hardcoded + bytes, _ := mbase.Encode(mbase.Base58BTC, append(varint.ToUvarint(MultibaseCode), pub.Bytes()...)) + return bytes } func reverseBytes(b []byte) []byte {