x25519: use go's ecdh keys instead of a custom type
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user