crypto: reasonably complete the keypair absraction layer, and use it
This commit is contained in:
@@ -1,43 +1,74 @@
|
||||
package crypto
|
||||
|
||||
type PublicKey interface {
|
||||
// Equal returns true if other is the same PublicKey
|
||||
Equal(other PublicKey) bool
|
||||
|
||||
// ToBytes serializes the PublicKey into "raw bytes", without metadata or structure.
|
||||
// This format can make some assumptions and may not be what you expect.
|
||||
// Ideally, this format is defined by the same specification as the underlying crypto scheme.
|
||||
ToBytes() []byte
|
||||
|
||||
// ToPublicKeyMultibase format the PublicKey into a string compatible with a PublicKeyMultibase field
|
||||
// in a DID Document.
|
||||
ToPublicKeyMultibase() string
|
||||
|
||||
// ToX509DER serializes the PublicKey into the X.509 DER (binary) format.
|
||||
ToX509DER() []byte
|
||||
|
||||
// ToX509PEM serializes the PublicKey into the X.509 PEM (string) format.
|
||||
ToX509PEM() string
|
||||
}
|
||||
|
||||
type PrivateKey interface {
|
||||
// Equal returns true if other is the same PrivateKey
|
||||
Equal(other PrivateKey) bool
|
||||
|
||||
// Public returns the matching PublicKey.
|
||||
Public() PublicKey
|
||||
|
||||
// ToBytes serializes the PrivateKey into "raw bytes", without metadata or structure.
|
||||
// This format can make some assumptions and may not be what you expect.
|
||||
// Ideally, this format is defined by the same specification as the underlying crypto scheme.
|
||||
ToBytes() []byte
|
||||
|
||||
// ToPKCS8DER serializes the PrivateKey into the PKCS#8 DER (binary) format.
|
||||
ToPKCS8DER() []byte
|
||||
|
||||
// ToPKCS8PEM serializes the PrivateKey into the PKCS#8 PEM (string) format.
|
||||
ToPKCS8PEM() string
|
||||
}
|
||||
|
||||
type SigningPublicKey interface {
|
||||
PublicKey
|
||||
|
||||
// VerifyBytes checks a signature in the "raw bytes" format.
|
||||
// This format can make some assumptions and may not be what you expect.
|
||||
// Ideally, this format is defined by the same specification as the underlying crypto scheme.
|
||||
VerifyBytes(message, signature []byte) bool
|
||||
|
||||
// VerifyASN1 checks a signature in the ASN.1 format.
|
||||
VerifyASN1(message, signature []byte) bool
|
||||
}
|
||||
|
||||
type SigningPrivateKey interface {
|
||||
PrivateKey
|
||||
|
||||
// SignToBytes creates a signature in the "raw bytes" format.
|
||||
// This format can make some assumptions and may not be what you expect.
|
||||
// Ideally, this format is defined by the same specification as the underlying crypto scheme.
|
||||
SignToBytes(message []byte) ([]byte, error)
|
||||
|
||||
// SignToASN1 creates a signature in the ASN.1 format.
|
||||
SignToASN1(message []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type KeyExchangePublicKey interface {
|
||||
PublicKey
|
||||
type KeyExchangePrivateKey interface {
|
||||
PrivateKey
|
||||
|
||||
// PrivateKeyIsCompatible checks that the given PrivateKey is compatible to perform key exchange.
|
||||
PrivateKeyIsCompatible(local PrivateKey) bool
|
||||
// PublicKeyIsCompatible checks that the given PublicKey is compatible to perform key exchange.
|
||||
PublicKeyIsCompatible(remote PublicKey) bool
|
||||
|
||||
// ECDH computes the shared key using the given PrivateKey.
|
||||
ECDH(local PrivateKey) ([]byte, error)
|
||||
// KeyExchange computes the shared key using the given PublicKey.
|
||||
KeyExchange(remote PublicKey) ([]byte, error)
|
||||
}
|
||||
|
||||
@@ -229,25 +229,34 @@ func TestSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](t *testing.T, har
|
||||
require.NoError(t, err)
|
||||
pub2, priv2, err := harness.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
pub3, _, err := harness.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
|
||||
kePub1, ok := (crypto.PublicKey(pub1)).(crypto.KeyExchangePublicKey)
|
||||
kePriv1, ok := crypto.PrivateKey(priv1).(crypto.KeyExchangePrivateKey)
|
||||
if !ok {
|
||||
t.Skip("Key exchange is not implemented")
|
||||
}
|
||||
kePub2 := (crypto.PublicKey(pub2)).(crypto.KeyExchangePublicKey)
|
||||
kePriv2 := crypto.PrivateKey(priv2).(crypto.KeyExchangePrivateKey)
|
||||
|
||||
// TODO: test with incompatible private keys
|
||||
require.True(t, kePub1.PrivateKeyIsCompatible(priv2))
|
||||
require.True(t, kePub2.PrivateKeyIsCompatible(priv1))
|
||||
// TODO: test with incompatible public keys
|
||||
require.True(t, kePriv1.PublicKeyIsCompatible(pub2))
|
||||
require.True(t, kePriv2.PublicKeyIsCompatible(pub1))
|
||||
|
||||
k1, err := kePub1.ECDH(priv2)
|
||||
// 1 --> 2
|
||||
kA, err := kePriv1.KeyExchange(pub2)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, k1)
|
||||
k2, err := kePub2.ECDH(priv1)
|
||||
require.NotEmpty(t, kA)
|
||||
// 2 --> 1
|
||||
kB, err := kePriv2.KeyExchange(pub1)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, k2)
|
||||
require.NotEmpty(t, kB)
|
||||
// 2 --> 3
|
||||
kC, err := kePriv2.KeyExchange(pub3)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, kC)
|
||||
|
||||
require.Equal(t, k1, k2)
|
||||
require.Equal(t, kA, kB)
|
||||
require.NotEqual(t, kB, kC)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -459,5 +468,24 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: add key exchange benchmarks
|
||||
b.Run("Key exchange", func(b *testing.B) {
|
||||
if _, ok := (crypto.PrivateKey(*new(PrivT))).(crypto.KeyExchangePrivateKey); !ok {
|
||||
b.Skip("Key echange is not implemented")
|
||||
}
|
||||
|
||||
b.Run("KeyExchange", func(b *testing.B) {
|
||||
_, priv1, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
kePriv1 := (crypto.PrivateKey(priv1)).(crypto.KeyExchangePrivateKey)
|
||||
pub2, _, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = kePriv1.KeyExchange(pub2)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
)
|
||||
|
||||
var _ crypto.SigningPrivateKey = (*PrivateKey)(nil)
|
||||
var _ crypto.KeyExchangePrivateKey = (*PrivateKey)(nil)
|
||||
|
||||
type PrivateKey ecdsa.PrivateKey
|
||||
|
||||
@@ -116,7 +117,29 @@ func (p *PrivateKey) SignToASN1(message []byte) ([]byte, error) {
|
||||
// Hash the message with SHA-256
|
||||
hash := sha256.Sum256(message)
|
||||
|
||||
// Use ecdsa.SignASN1 for direct ASN.1 DER encoding
|
||||
return ecdsa.SignASN1(rand.Reader, (*ecdsa.PrivateKey)(p), hash[:])
|
||||
|
||||
}
|
||||
|
||||
func (p *PrivateKey) PublicKeyIsCompatible(remote crypto.PublicKey) bool {
|
||||
if _, ok := remote.(*PublicKey); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PrivateKey) KeyExchange(remote crypto.PublicKey) ([]byte, error) {
|
||||
if remote, ok := remote.(*PublicKey); ok {
|
||||
// First, we need to convert the ECDSA (signing only) to the equivalent ECDH keys
|
||||
ecdhPriv, err := (*ecdsa.PrivateKey)(p).ECDH()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdhPub, err := (*ecdsa.PublicKey)(remote).ECDH()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ecdhPriv.ECDH(ecdhPub)
|
||||
}
|
||||
return nil, fmt.Errorf("incompatible public key")
|
||||
}
|
||||
|
||||
@@ -113,7 +113,6 @@ func (p *PublicKey) VerifyBytes(message, signature []byte) bool {
|
||||
r := new(big.Int).SetBytes(signature[:32])
|
||||
s := new(big.Int).SetBytes(signature[32:])
|
||||
|
||||
// Use ecdsa.Verify
|
||||
return ecdsa.Verify((*ecdsa.PublicKey)(p), hash[:], r, s)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
)
|
||||
|
||||
var _ crypto.PrivateKey = (*PrivateKey)(nil)
|
||||
var _ crypto.KeyExchangePrivateKey = (*PrivateKey)(nil)
|
||||
|
||||
type PrivateKey ecdh.PrivateKey
|
||||
|
||||
@@ -95,3 +95,17 @@ func (p *PrivateKey) ToPKCS8PEM() string {
|
||||
Bytes: der,
|
||||
}))
|
||||
}
|
||||
|
||||
func (p *PrivateKey) PublicKeyIsCompatible(remote crypto.PublicKey) bool {
|
||||
if _, ok := remote.(*PublicKey); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PrivateKey) KeyExchange(remote crypto.PublicKey) ([]byte, error) {
|
||||
if local, ok := remote.(*PublicKey); ok {
|
||||
return (*ecdh.PrivateKey)(p).ECDH((*ecdh.PublicKey)(local))
|
||||
}
|
||||
return nil, fmt.Errorf("incompatible public key")
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
helpers "github.com/INFURA/go-did/crypto/internal"
|
||||
)
|
||||
|
||||
var _ crypto.KeyExchangePublicKey = (*PublicKey)(nil)
|
||||
var _ crypto.PublicKey = (*PublicKey)(nil)
|
||||
|
||||
type PublicKey ecdh.PublicKey
|
||||
|
||||
@@ -145,20 +145,6 @@ func (p *PublicKey) ToX509PEM() string {
|
||||
}))
|
||||
}
|
||||
|
||||
func (p *PublicKey) PrivateKeyIsCompatible(local crypto.PrivateKey) bool {
|
||||
if _, ok := local.(*PrivateKey); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PublicKey) ECDH(local crypto.PrivateKey) ([]byte, error) {
|
||||
if local, ok := local.(*PrivateKey); ok {
|
||||
return (*ecdh.PrivateKey)(local).ECDH((*ecdh.PublicKey)(p))
|
||||
}
|
||||
return nil, fmt.Errorf("incompatible private key")
|
||||
}
|
||||
|
||||
func reverseBytes(b []byte) []byte {
|
||||
r := make([]byte, len(b))
|
||||
for i := 0; i < len(b); i++ {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto/x25519"
|
||||
_ "github.com/INFURA/go-did/methods/did-key"
|
||||
"github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
|
||||
func ExampleSignature() {
|
||||
|
||||
@@ -51,11 +51,11 @@ func TestRoundTrip(t *testing.T) {
|
||||
|
||||
// basic testing
|
||||
require.Equal(t, "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", doc.ID())
|
||||
require.Equal(t, ed25519.Type, doc.Authentication()[0].Type())
|
||||
require.Equal(t, ed25519.Type, doc.Assertion()[0].Type())
|
||||
require.Equal(t, x25519.Type, doc.KeyAgreement()[0].Type())
|
||||
require.Equal(t, ed25519.Type, doc.CapabilityInvocation()[0].Type())
|
||||
require.Equal(t, ed25519.Type, doc.CapabilityDelegation()[0].Type())
|
||||
require.Equal(t, ed25519vm.Type, doc.Authentication()[0].Type())
|
||||
require.Equal(t, ed25519vm.Type, doc.Assertion()[0].Type())
|
||||
require.Equal(t, x25519vm.Type, doc.KeyAgreement()[0].Type())
|
||||
require.Equal(t, ed25519vm.Type, doc.CapabilityInvocation()[0].Type())
|
||||
require.Equal(t, ed25519vm.Type, doc.CapabilityDelegation()[0].Type())
|
||||
|
||||
roundtrip, err := json.Marshal(doc)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package did
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
)
|
||||
|
||||
// DID is a decoded (i.e. from a string) Decentralized Identifier.
|
||||
@@ -110,20 +111,8 @@ type VerificationMethodKeyAgreement interface {
|
||||
VerificationMethod
|
||||
|
||||
// PrivateKeyIsCompatible checks that the given PrivateKey is compatible with this method.
|
||||
PrivateKeyIsCompatible(local PrivateKey) bool
|
||||
PrivateKeyIsCompatible(local crypto.KeyExchangePrivateKey) bool
|
||||
|
||||
// ECDH computes the shared key using the given PrivateKey.
|
||||
ECDH(local PrivateKey) ([]byte, error)
|
||||
}
|
||||
|
||||
// Below are the interfaces for crypto.PublicKey and crypto.PrivateKey in the go standard library.
|
||||
// They are not defined there for compatibility reasons, so we need to define them here.
|
||||
|
||||
type PublicKey interface {
|
||||
Equal(x crypto.PublicKey) bool
|
||||
}
|
||||
|
||||
type PrivateKey interface {
|
||||
Public() crypto.PublicKey
|
||||
Equal(x crypto.PrivateKey) bool
|
||||
// KeyExchange computes the shared key using the given PrivateKey.
|
||||
KeyExchange(local crypto.KeyExchangePrivateKey) ([]byte, error)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@ import (
|
||||
"github.com/multiformats/go-varint"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
"github.com/INFURA/go-did/crypto/p256"
|
||||
"github.com/INFURA/go-did/crypto/x25519"
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
"github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
@@ -55,8 +59,13 @@ func Decode(identifier string) (did.DID, error) {
|
||||
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
|
||||
}
|
||||
return FromPublicKey(pub)
|
||||
case p256.MultibaseCode:
|
||||
pub, err := p256.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
|
||||
}
|
||||
@@ -64,12 +73,12 @@ func Decode(identifier string) (did.DID, error) {
|
||||
return nil, fmt.Errorf("%w: unsupported did:key multicodec: 0x%x", did.ErrInvalidDid, code)
|
||||
}
|
||||
|
||||
func FromPublicKey(pub did.PublicKey) (did.DID, error) {
|
||||
func FromPublicKey(pub crypto.PublicKey) (did.DID, error) {
|
||||
var err error
|
||||
switch pub := pub.(type) {
|
||||
case ed25519.PublicKey:
|
||||
d := DidKey{msi: ed25519.PublicKeyToMultibase(pub)}
|
||||
d.signature, err = ed25519.NewVerificationKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, d.msi), pub, d)
|
||||
d := DidKey{msi: pub.ToPublicKeyMultibase()}
|
||||
d.signature, err = ed25519vm.NewVerificationKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, d.msi), pub, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -77,20 +86,22 @@ func FromPublicKey(pub did.PublicKey) (did.DID, error) {
|
||||
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)
|
||||
xmsi := xpub.ToPublicKeyMultibase()
|
||||
d.keyAgreement, err = x25519vm.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
|
||||
// case *p256.PublicKey:
|
||||
// d := DidKey{msi: pub.ToPublicKeyMultibase()}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported public key: %T", pub)
|
||||
}
|
||||
}
|
||||
|
||||
func FromPrivateKey(priv did.PrivateKey) (did.DID, error) {
|
||||
return FromPublicKey(priv.Public().(did.PublicKey))
|
||||
func FromPrivateKey(priv crypto.PrivateKey) (did.DID, error) {
|
||||
return FromPublicKey(priv.Public().(crypto.PublicKey))
|
||||
}
|
||||
|
||||
func (d DidKey) Method() string {
|
||||
|
||||
@@ -8,36 +8,31 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
didkey "github.com/INFURA/go-did/methods/did-key"
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
)
|
||||
|
||||
func ExampleGenerateKeyPair() {
|
||||
// Generate a key pair
|
||||
pub, priv, err := ed25519.GenerateKeyPair()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Public key:", ed25519.PublicKeyToMultibase(pub))
|
||||
fmt.Println("Private key:", base64.StdEncoding.EncodeToString(priv))
|
||||
handleErr(err)
|
||||
fmt.Println("Public key:", pub.ToPublicKeyMultibase())
|
||||
fmt.Println("Private key:", base64.StdEncoding.EncodeToString(priv.ToBytes()))
|
||||
|
||||
// Make the associated did:key
|
||||
dk, err := didkey.FromPrivateKey(priv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
handleErr(err)
|
||||
fmt.Println("Did:", dk.String())
|
||||
|
||||
// Produce a signature
|
||||
msg := []byte("message")
|
||||
sig := ed25519.Sign(priv, msg)
|
||||
sig, err := priv.SignToBytes(msg)
|
||||
handleErr(err)
|
||||
fmt.Println("Signature:", base64.StdEncoding.EncodeToString(sig))
|
||||
|
||||
// Resolve the DID and verify a signature
|
||||
doc, err := dk.Document()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
handleErr(err)
|
||||
ok, _ := did.TryAllVerify(doc.Authentication(), msg, sig)
|
||||
fmt.Println("Signature verified:", ok)
|
||||
}
|
||||
@@ -72,3 +67,9 @@ func TestEquivalence(t *testing.T) {
|
||||
require.True(t, did0A.Equal(did0B))
|
||||
require.False(t, did0A.Equal(did1))
|
||||
}
|
||||
|
||||
func handleErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package did
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
)
|
||||
|
||||
// TryAllVerify tries to verify the signature with all the methods in the slice.
|
||||
@@ -19,10 +21,10 @@ func TryAllVerify(methods []VerificationMethodSignature, data []byte, sig []byte
|
||||
// FindMatchingKeyAgreement tries to find a matching key agreement method for the given private key type.
|
||||
// It returns the shared key as well as the selected method.
|
||||
// If no matching method is found, it returns an error.
|
||||
func FindMatchingKeyAgreement(methods []VerificationMethodKeyAgreement, priv PrivateKey) ([]byte, VerificationMethodKeyAgreement, error) {
|
||||
func FindMatchingKeyAgreement(methods []VerificationMethodKeyAgreement, priv crypto.KeyExchangePrivateKey) ([]byte, VerificationMethodKeyAgreement, error) {
|
||||
for _, method := range methods {
|
||||
if method.PrivateKeyIsCompatible(priv) {
|
||||
key, err := method.ECDH(priv)
|
||||
key, err := method.KeyExchange(priv)
|
||||
return key, method, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package ed25519
|
||||
package ed25519vm
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
)
|
||||
|
||||
// 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"
|
||||
)
|
||||
@@ -21,15 +20,11 @@ var _ did.VerificationMethodSignature = &VerificationKey2020{}
|
||||
|
||||
type VerificationKey2020 struct {
|
||||
id string
|
||||
pubkey PublicKey
|
||||
pubkey ed25519.PublicKey
|
||||
controller string
|
||||
}
|
||||
|
||||
func NewVerificationKey2020(id string, pubkey PublicKey, controller did.DID) (*VerificationKey2020, error) {
|
||||
if len(pubkey) != PublicKeySize {
|
||||
return nil, errors.New("invalid ed25519 public key size")
|
||||
}
|
||||
|
||||
func NewVerificationKey2020(id string, pubkey ed25519.PublicKey, controller did.DID) (*VerificationKey2020, error) {
|
||||
return &VerificationKey2020{
|
||||
id: id,
|
||||
pubkey: pubkey,
|
||||
@@ -47,7 +42,7 @@ func (v VerificationKey2020) MarshalJSON() ([]byte, error) {
|
||||
ID: v.ID(),
|
||||
Type: v.Type(),
|
||||
Controller: v.Controller(),
|
||||
PublicKeyMultibase: PublicKeyToMultibase(v.pubkey),
|
||||
PublicKeyMultibase: v.pubkey.ToPublicKeyMultibase(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -69,7 +64,7 @@ func (v *VerificationKey2020) UnmarshalJSON(bytes []byte) error {
|
||||
if len(v.id) == 0 {
|
||||
return errors.New("invalid id")
|
||||
}
|
||||
v.pubkey, err = PublicKeyFromMultibase(aux.PublicKeyMultibase)
|
||||
v.pubkey, err = ed25519.PublicKeyFromPublicKeyMultibase(aux.PublicKeyMultibase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyMultibase: %w", err)
|
||||
}
|
||||
@@ -97,5 +92,5 @@ func (v VerificationKey2020) JsonLdContext() string {
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) Verify(data []byte, sig []byte) bool {
|
||||
return ed25519.Verify(v.pubkey, data, sig)
|
||||
return v.pubkey.VerifyBytes(data, sig)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package ed25519_test
|
||||
package ed25519vm_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
_ "github.com/INFURA/go-did/methods/did-key"
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
)
|
||||
@@ -20,7 +21,7 @@ func TestJsonRoundTrip(t *testing.T) {
|
||||
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
|
||||
}`
|
||||
|
||||
var vk ed25519.VerificationKey2020
|
||||
var vk ed25519vm.VerificationKey2020
|
||||
err := json.Unmarshal([]byte(data), &vk)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -37,9 +38,9 @@ func TestSignature(t *testing.T) {
|
||||
pk, err := ed25519.PublicKeyFromBytes(pkBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
contDid := "did:key:" + ed25519.PublicKeyToMultibase(pk)
|
||||
contDid := "did:key:" + pk.ToPublicKeyMultibase()
|
||||
controller := did.MustParse(contDid)
|
||||
vk, err := ed25519.NewVerificationKey2020("foo", pk, controller)
|
||||
vk, err := ed25519vm.NewVerificationKey2020("foo", pk, controller)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, tc := range []struct {
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/INFURA/go-did/verifications/internal"
|
||||
)
|
||||
|
||||
type PublicKey = ed25519.PublicKey
|
||||
type PrivateKey = ed25519.PrivateKey
|
||||
|
||||
const (
|
||||
// PublicKeySize is the size, in bytes, of public keys as used in this package.
|
||||
PublicKeySize = ed25519.PublicKeySize
|
||||
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
|
||||
PrivateKeySize = ed25519.PrivateKeySize
|
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = ed25519.SignatureSize
|
||||
)
|
||||
|
||||
func GenerateKeyPair() (PublicKey, PrivateKey, error) {
|
||||
return ed25519.GenerateKey(rand.Reader)
|
||||
}
|
||||
|
||||
// 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 ed25519 public key size")
|
||||
}
|
||||
return PublicKey(b), nil
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
if len(bytes) != PublicKeySize {
|
||||
return nil, fmt.Errorf("invalid ed25519 public key size")
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase
|
||||
func PublicKeyToMultibase(pub PublicKey) string {
|
||||
return helpers.MultibaseEncode(MultibaseCode, pub)
|
||||
}
|
||||
|
||||
// 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) != ed25519.PrivateKeySize {
|
||||
return nil, fmt.Errorf("invalid ed25519 private key size")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Sign signs the message with privateKey and returns a signature.
|
||||
// It will panic if len(privateKey) is not [PrivateKeySize].
|
||||
func Sign(privateKey PrivateKey, message []byte) []byte {
|
||||
return ed25519.Sign(privateKey, message)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package ed25519_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
)
|
||||
|
||||
func TestGenerateKey(t *testing.T) {
|
||||
pub, priv, err := ed25519.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, pub)
|
||||
require.NotNil(t, priv)
|
||||
require.True(t, pub.Equal(priv.Public()))
|
||||
}
|
||||
|
||||
func TestMultibaseRoundTrip(t *testing.T) {
|
||||
pub, _, err := ed25519.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
|
||||
mb := ed25519.PublicKeyToMultibase(pub)
|
||||
rt, err := ed25519.PublicKeyFromMultibase(mb)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, pub, rt)
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
mbase "github.com/multiformats/go-multibase"
|
||||
"github.com/multiformats/go-varint"
|
||||
)
|
||||
|
||||
// MultibaseDecode is a helper for decoding multibase public keys.
|
||||
func MultibaseDecode(multibase string) (uint64, []byte, error) {
|
||||
baseCodec, bytes, err := mbase.Decode(multibase)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
// the specification enforces that encoding
|
||||
if baseCodec != mbase.Base58BTC {
|
||||
return 0, nil, fmt.Errorf("not Base58BTC encoded")
|
||||
}
|
||||
code, read, err := varint.FromUvarint(bytes)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if read != 2 {
|
||||
return 0, nil, fmt.Errorf("unexpected multibase")
|
||||
}
|
||||
return code, bytes[read:], nil
|
||||
}
|
||||
|
||||
// MultibaseEncode is a helper for encoding multibase public keys.
|
||||
func MultibaseEncode(code uint64, bytes []byte) string {
|
||||
// can only fail with an invalid encoding, but it's hardcoded
|
||||
res, _ := mbase.Encode(mbase.Base58BTC, append(varint.ToUvarint(code), bytes...))
|
||||
return res
|
||||
}
|
||||
@@ -19,10 +19,10 @@ func UnmarshalJSON(data []byte) (did.VerificationMethod, error) {
|
||||
|
||||
var res did.VerificationMethod
|
||||
switch aux.Type {
|
||||
case ed25519.Type:
|
||||
res = &ed25519.VerificationKey2020{}
|
||||
case x25519.Type:
|
||||
res = &x25519.KeyAgreementKey2020{}
|
||||
case ed25519vm.Type:
|
||||
res = &ed25519vm.VerificationKey2020{}
|
||||
case x25519vm.Type:
|
||||
res = &x25519vm.KeyAgreementKey2020{}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown verification type: %s", aux.Type)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package x25519
|
||||
package x25519vm
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/x25519"
|
||||
)
|
||||
|
||||
// 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"
|
||||
)
|
||||
@@ -21,15 +21,11 @@ var _ did.VerificationMethodKeyAgreement = &KeyAgreementKey2020{}
|
||||
|
||||
type KeyAgreementKey2020 struct {
|
||||
id string
|
||||
pubkey PublicKey
|
||||
pubkey *x25519.PublicKey
|
||||
controller string
|
||||
}
|
||||
|
||||
func NewKeyAgreementKey2020(id string, pubkey PublicKey, controller did.DID) (*KeyAgreementKey2020, error) {
|
||||
if pubkey.Curve() != ecdh.X25519() {
|
||||
return nil, errors.New("x25519 key curve must be X25519")
|
||||
}
|
||||
|
||||
func NewKeyAgreementKey2020(id string, pubkey *x25519.PublicKey, controller did.DID) (*KeyAgreementKey2020, error) {
|
||||
return &KeyAgreementKey2020{
|
||||
id: id,
|
||||
pubkey: pubkey,
|
||||
@@ -47,7 +43,7 @@ func (k KeyAgreementKey2020) MarshalJSON() ([]byte, error) {
|
||||
ID: k.ID(),
|
||||
Type: k.Type(),
|
||||
Controller: k.Controller(),
|
||||
PublicKeyMultibase: PublicKeyToMultibase(k.pubkey),
|
||||
PublicKeyMultibase: k.pubkey.ToPublicKeyMultibase(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -69,7 +65,7 @@ func (k *KeyAgreementKey2020) UnmarshalJSON(bytes []byte) error {
|
||||
if len(k.id) == 0 {
|
||||
return errors.New("invalid id")
|
||||
}
|
||||
k.pubkey, err = PublicKeyFromMultibase(aux.PublicKeyMultibase)
|
||||
k.pubkey, err = x25519.PublicKeyFromPublicKeyMultibase(aux.PublicKeyMultibase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyMultibase: %w", err)
|
||||
}
|
||||
@@ -96,21 +92,10 @@ func (k KeyAgreementKey2020) JsonLdContext() string {
|
||||
return JsonLdContext
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) PrivateKeyIsCompatible(local did.PrivateKey) bool {
|
||||
_, ok := local.(PrivateKey)
|
||||
return ok
|
||||
func (k KeyAgreementKey2020) PrivateKeyIsCompatible(local crypto.KeyExchangePrivateKey) bool {
|
||||
return local.PublicKeyIsCompatible(k.pubkey)
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) ECDH(local did.PrivateKey) ([]byte, error) {
|
||||
cast, ok := local.(PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("private key type doesn't match the public key type")
|
||||
}
|
||||
if cast == nil {
|
||||
return nil, errors.New("invalid private key")
|
||||
}
|
||||
if k.pubkey.Curve() != cast.Curve() {
|
||||
return nil, errors.New("key curves don't match")
|
||||
}
|
||||
return cast.ECDH(k.pubkey)
|
||||
func (k KeyAgreementKey2020) KeyExchange(local crypto.KeyExchangePrivateKey) ([]byte, error) {
|
||||
return local.KeyExchange(k.pubkey)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package x25519_test
|
||||
package x25519vm_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/INFURA/go-did/verifications/x25519"
|
||||
x25519vm "github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
|
||||
func TestJsonRoundTrip(t *testing.T) {
|
||||
@@ -17,7 +17,7 @@ func TestJsonRoundTrip(t *testing.T) {
|
||||
"publicKeyMultibase": "z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW"
|
||||
}`
|
||||
|
||||
var vm x25519.KeyAgreementKey2020
|
||||
var vm x25519vm.KeyAgreementKey2020
|
||||
err := json.Unmarshal([]byte(data), &vm)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
package x25519
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
helpers "github.com/INFURA/go-did/verifications/internal"
|
||||
)
|
||||
|
||||
type PublicKey = *ecdh.PublicKey
|
||||
type PrivateKey = *ecdh.PrivateKey
|
||||
|
||||
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 = 32
|
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = 32
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return ecdh.X25519().NewPublicKey(b)
|
||||
}
|
||||
|
||||
// PublicKeyFromEd25519 converts an ed25519 public key to a x25519 public key.
|
||||
// It errors if the slice is not the right size.
|
||||
//
|
||||
// This function is based on the algorithm described in https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm#name-curve25519
|
||||
func PublicKeyFromEd25519(pub ed25519.PublicKey) (PublicKey, error) {
|
||||
// Conversion formula is u = (1 + y) / (1 - y) (mod p)
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm#name-ecdh-with-montgomery-coordi
|
||||
|
||||
if len(pub) != ed25519.PublicKeySize {
|
||||
return nil, fmt.Errorf("invalid ed25519 public key size")
|
||||
}
|
||||
|
||||
// Make a copy and clear the sign bit (MSB of last byte)
|
||||
// This is because ed25519 serialize as bytes with 255 bit for Y, and one bit for the sign.
|
||||
// We only want Y, and the sign is irrelevant for the conversion.
|
||||
pubCopy := make([]byte, ed25519.PublicKeySize)
|
||||
copy(pubCopy, pub)
|
||||
pubCopy[ed25519.PublicKeySize-1] &= 0x7F
|
||||
|
||||
// ed25519 are little-endian, but big.Int expects big-endian
|
||||
// See https://www.rfc-editor.org/rfc/rfc8032
|
||||
y := new(big.Int).SetBytes(reverseBytes(pubCopy))
|
||||
one := big.NewInt(1)
|
||||
negOne := big.NewInt(-1)
|
||||
|
||||
if y.Cmp(one) == 0 || y.Cmp(negOne) == 0 {
|
||||
return nil, fmt.Errorf("x25519 undefined for this public key")
|
||||
}
|
||||
|
||||
// p = 2^255-19
|
||||
//
|
||||
// Equivalent to:
|
||||
// two := big.NewInt(2)
|
||||
// exp := big.NewInt(255)
|
||||
// p := new(big.Int).Exp(two, exp, nil)
|
||||
// p.Sub(p, big.NewInt(19))
|
||||
//
|
||||
p := new(big.Int).SetBytes([]byte{
|
||||
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed,
|
||||
})
|
||||
|
||||
onePlusY := new(big.Int).Add(one, y)
|
||||
oneMinusY := new(big.Int).Sub(one, y)
|
||||
oneMinusYInv := new(big.Int).ModInverse(oneMinusY, p)
|
||||
u := new(big.Int).Mul(onePlusY, oneMinusYInv)
|
||||
u.Mod(u, p)
|
||||
|
||||
// make sure we get 32 bytes, pad if necessary
|
||||
uBytes := u.Bytes()
|
||||
res := make([]byte, PublicKeySize)
|
||||
copy(res[PublicKeySize-len(uBytes):], uBytes)
|
||||
|
||||
// x25519 are little-endian, but big.Int gives us big-endian.
|
||||
// See https://www.ietf.org/rfc/rfc7748.txt
|
||||
return ecdh.X25519().NewPublicKey(reverseBytes(res))
|
||||
}
|
||||
|
||||
// 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 ecdh.X25519().NewPublicKey(bytes)
|
||||
}
|
||||
|
||||
// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase
|
||||
func PublicKeyToMultibase(pub PublicKey) string {
|
||||
return helpers.MultibaseEncode(MultibaseCode, pub.Bytes())
|
||||
}
|
||||
|
||||
// PrivateKeyFromBytes converts a serialized public key to a PrivateKey.
|
||||
// It errors if len(privateKey) is not [PrivateKeySize].
|
||||
func PrivateKeyFromBytes(b []byte) (PrivateKey, error) {
|
||||
return ecdh.X25519().NewPrivateKey(b)
|
||||
}
|
||||
|
||||
// PrivateKeyFromEd25519 converts an ed25519 private key to a x25519 private key.
|
||||
// It errors if the slice is not the right size.
|
||||
//
|
||||
// This function is based on the algorithm described in https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm#name-curve25519
|
||||
func PrivateKeyFromEd25519(priv ed25519.PrivateKey) (PrivateKey, error) {
|
||||
if len(priv) != ed25519.PrivateKeySize {
|
||||
return nil, fmt.Errorf("invalid ed25519 private key size")
|
||||
}
|
||||
|
||||
// get the 32-byte seed (first half of the private key)
|
||||
seed := priv.Seed()
|
||||
|
||||
h := sha512.Sum512(seed)
|
||||
|
||||
// clamp as per the X25519 spec
|
||||
h[0] &= 248
|
||||
h[31] &= 127
|
||||
h[31] |= 64
|
||||
|
||||
return ecdh.X25519().NewPrivateKey(h[:32])
|
||||
}
|
||||
|
||||
func reverseBytes(b []byte) []byte {
|
||||
r := make([]byte, len(b))
|
||||
for i := 0; i < len(b); i++ {
|
||||
r[i] = b[len(b)-1-i]
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package x25519_test
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
"github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
|
||||
func TestGenerateKey(t *testing.T) {
|
||||
pub, priv, err := x25519.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, pub)
|
||||
require.NotNil(t, priv)
|
||||
require.Equal(t, ecdh.X25519(), pub.Curve())
|
||||
require.Equal(t, ecdh.X25519(), priv.Curve())
|
||||
require.True(t, pub.Equal(priv.Public()))
|
||||
}
|
||||
|
||||
func TestMultibaseRoundTrip(t *testing.T) {
|
||||
pub, _, err := x25519.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
|
||||
mb := x25519.PublicKeyToMultibase(pub)
|
||||
rt, err := x25519.PublicKeyFromMultibase(mb)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, pub, rt)
|
||||
}
|
||||
|
||||
func TestEd25519ToX25519(t *testing.T) {
|
||||
// Known pubkey ed25519 --> x25519
|
||||
for _, tc := range []struct {
|
||||
pubEdMultibase string
|
||||
pubXMultibase string
|
||||
}{
|
||||
{
|
||||
// From https://w3c-ccg.github.io/did-key-spec/#ed25519-with-x25519
|
||||
pubEdMultibase: "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
|
||||
pubXMultibase: "z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p",
|
||||
},
|
||||
} {
|
||||
t.Run(tc.pubEdMultibase, func(t *testing.T) {
|
||||
pubEd, err := ed25519.PublicKeyFromMultibase(tc.pubEdMultibase)
|
||||
require.NoError(t, err)
|
||||
pubX, err := x25519.PublicKeyFromEd25519(pubEd)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.pubXMultibase, x25519.PublicKeyToMultibase(pubX))
|
||||
})
|
||||
}
|
||||
|
||||
// Check that ed25519 --> x25519 match for pubkeys and privkeys
|
||||
t.Run("ed25519 --> x25519 priv+pub are matching", func(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
pubEd, privEd, err := ed25519.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
|
||||
pubX, err := x25519.PublicKeyFromEd25519(pubEd)
|
||||
require.NoError(t, err)
|
||||
privX, err := x25519.PrivateKeyFromEd25519(privEd)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, pubX.Equal(privX.PublicKey()))
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user