Merge pull request #11 from INFURA/jwk
complete support for did:key, extensive abstraction layer for crypto handling
This commit is contained in:
38
crypto/_allkeys/allkeys.go
Normal file
38
crypto/_allkeys/allkeys.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package allkeys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
helpers "github.com/INFURA/go-did/crypto/internal"
|
||||
"github.com/INFURA/go-did/crypto/p256"
|
||||
"github.com/INFURA/go-did/crypto/p384"
|
||||
"github.com/INFURA/go-did/crypto/p521"
|
||||
"github.com/INFURA/go-did/crypto/rsa"
|
||||
"github.com/INFURA/go-did/crypto/secp256k1"
|
||||
"github.com/INFURA/go-did/crypto/x25519"
|
||||
)
|
||||
|
||||
var decoders = map[uint64]func(b []byte) (crypto.PublicKey, error){
|
||||
ed25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return ed25519.PublicKeyFromBytes(b) },
|
||||
p256.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p256.PublicKeyFromBytes(b) },
|
||||
p384.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p384.PublicKeyFromBytes(b) },
|
||||
p521.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p521.PublicKeyFromBytes(b) },
|
||||
rsa.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return rsa.PublicKeyFromPKCS1DER(b) },
|
||||
secp256k1.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return secp256k1.PublicKeyFromBytes(b) },
|
||||
x25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return x25519.PublicKeyFromBytes(b) },
|
||||
}
|
||||
|
||||
// PublicKeyFromPublicKeyMultibase decodes the public key from its PublicKeyMultibase form
|
||||
func PublicKeyFromPublicKeyMultibase(multibase string) (crypto.PublicKey, error) {
|
||||
code, pubBytes, err := helpers.PublicKeyMultibaseDecode(multibase)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid publicKeyMultibase: %w", err)
|
||||
}
|
||||
decoder, ok := decoders[code]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported publicKeyMultibase code: %d", code)
|
||||
}
|
||||
return decoder(pubBytes)
|
||||
}
|
||||
@@ -29,6 +29,9 @@ type TestHarness[PubT crypto.PublicKey, PrivT crypto.PrivateKey] struct {
|
||||
|
||||
MultibaseCode uint64
|
||||
|
||||
DefaultHash crypto.Hash
|
||||
OtherHashes []crypto.Hash
|
||||
|
||||
PublicKeyBytesSize int
|
||||
PrivateKeyBytesSize int
|
||||
SignatureBytesSize int
|
||||
@@ -78,8 +81,14 @@ func TestSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](t *testing.T, har
|
||||
})
|
||||
|
||||
t.Run("Equality", func(t *testing.T) {
|
||||
if !pubImplements[PubT, crypto.PublicKeyToBytes]() {
|
||||
t.Skip("Public key does not implement crypto.PublicKeyToBytes")
|
||||
}
|
||||
|
||||
pub1, priv1, err := harness.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
pub1Tb := (crypto.PublicKey(pub1)).(crypto.PublicKeyToBytes)
|
||||
priv1Tb := (crypto.PrivateKey(priv1)).(crypto.PrivateKeyToBytes)
|
||||
pub2, priv2, err := harness.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -88,29 +97,35 @@ func TestSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](t *testing.T, har
|
||||
require.False(t, pub1.Equal(pub2))
|
||||
require.False(t, priv1.Equal(priv2))
|
||||
|
||||
pub1copy, err := harness.PublicKeyFromBytes(pub1.ToBytes())
|
||||
pub1copy, err := harness.PublicKeyFromBytes(pub1Tb.ToBytes())
|
||||
require.NoError(t, err)
|
||||
require.True(t, pub1.Equal(pub1copy))
|
||||
require.True(t, pub1copy.Equal(pub1))
|
||||
|
||||
priv1copy, err := harness.PrivateKeyFromBytes(priv1.ToBytes())
|
||||
priv1copy, err := harness.PrivateKeyFromBytes(priv1Tb.ToBytes())
|
||||
require.NoError(t, err)
|
||||
require.True(t, priv1.Equal(priv1copy))
|
||||
require.True(t, priv1copy.Equal(priv1))
|
||||
})
|
||||
|
||||
t.Run("BytesRoundTrip", func(t *testing.T) {
|
||||
if !pubImplements[PubT, crypto.PublicKeyToBytes]() {
|
||||
t.Skip("Public key does not implement crypto.PublicKeyToBytes")
|
||||
}
|
||||
|
||||
pub, priv, err := harness.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
pubTb := (crypto.PublicKey(pub)).(crypto.PublicKeyToBytes)
|
||||
privTb := (crypto.PrivateKey(priv)).(crypto.PrivateKeyToBytes)
|
||||
|
||||
bytes := pub.ToBytes()
|
||||
bytes := pubTb.ToBytes()
|
||||
stats.bytesPubSize = len(bytes)
|
||||
rtPub, err := harness.PublicKeyFromBytes(bytes)
|
||||
require.NoError(t, err)
|
||||
require.True(t, pub.Equal(rtPub))
|
||||
require.Equal(t, harness.PublicKeyBytesSize, len(bytes))
|
||||
|
||||
bytes = priv.ToBytes()
|
||||
bytes = privTb.ToBytes()
|
||||
stats.bytesPrivSize = len(bytes)
|
||||
rtPriv, err := harness.PrivateKeyFromBytes(bytes)
|
||||
require.NoError(t, err)
|
||||
@@ -175,55 +190,93 @@ func TestSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](t *testing.T, har
|
||||
pub, priv, err := harness.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
|
||||
spub, ok := (crypto.PublicKey(pub)).(crypto.SigningPublicKey)
|
||||
if !ok {
|
||||
t.Skip("Signature is not implemented")
|
||||
}
|
||||
spriv, ok := (crypto.PrivateKey(priv)).(crypto.SigningPrivateKey)
|
||||
if !ok {
|
||||
t.Skip("Signature is not implemented")
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
type testcase struct {
|
||||
name string
|
||||
signer func(msg []byte) ([]byte, error)
|
||||
verifier func(msg []byte, sig []byte) bool
|
||||
signer func(msg []byte, opts ...crypto.SigningOption) ([]byte, error)
|
||||
verifier func(msg []byte, sig []byte, opts ...crypto.SigningOption) bool
|
||||
expectedSize int
|
||||
stats *int
|
||||
}{
|
||||
{
|
||||
defaultHash crypto.Hash
|
||||
otherHashes []crypto.Hash
|
||||
}
|
||||
var tcs []testcase
|
||||
|
||||
if pubImplements[PubT, crypto.PublicKeySigningBytes]() {
|
||||
t.Run("Bytes signature", func(t *testing.T) {
|
||||
spub := (crypto.PublicKey(pub)).(crypto.PublicKeySigningBytes)
|
||||
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningBytes)
|
||||
|
||||
tcs = append(tcs, testcase{
|
||||
name: "Bytes signature",
|
||||
signer: spriv.SignToBytes,
|
||||
verifier: spub.VerifyBytes,
|
||||
expectedSize: harness.SignatureBytesSize,
|
||||
stats: &stats.sigRawSize,
|
||||
},
|
||||
{
|
||||
defaultHash: harness.DefaultHash,
|
||||
otherHashes: harness.OtherHashes,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if pubImplements[PubT, crypto.PublicKeySigningASN1]() {
|
||||
t.Run("ASN.1 signature", func(t *testing.T) {
|
||||
spub := (crypto.PublicKey(pub)).(crypto.PublicKeySigningASN1)
|
||||
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningASN1)
|
||||
|
||||
tcs = append(tcs, testcase{
|
||||
name: "ASN.1 signature",
|
||||
signer: spriv.SignToASN1,
|
||||
verifier: spub.VerifyASN1,
|
||||
stats: &stats.sigAsn1Size,
|
||||
},
|
||||
} {
|
||||
defaultHash: harness.DefaultHash,
|
||||
otherHashes: harness.OtherHashes,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
msg := []byte("message")
|
||||
|
||||
sig, err := tc.signer(msg)
|
||||
sigNoParams, err := tc.signer(msg)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, sigNoParams)
|
||||
|
||||
sigDefault, err := tc.signer(msg, crypto.WithSigningHash(tc.defaultHash))
|
||||
require.NoError(t, err)
|
||||
|
||||
if tc.expectedSize > 0 {
|
||||
require.Equal(t, tc.expectedSize, len(sigNoParams))
|
||||
}
|
||||
*tc.stats = len(sigNoParams)
|
||||
|
||||
// signatures might be different (i.e. non-deterministic), but they should verify the same way
|
||||
valid := tc.verifier(msg, sigNoParams)
|
||||
require.True(t, valid)
|
||||
valid = tc.verifier(msg, sigDefault)
|
||||
require.True(t, valid)
|
||||
|
||||
valid = tc.verifier([]byte("wrong message"), sigNoParams)
|
||||
require.False(t, valid)
|
||||
valid = tc.verifier([]byte("wrong message"), sigDefault)
|
||||
require.False(t, valid)
|
||||
})
|
||||
for _, hash := range tc.otherHashes {
|
||||
t.Run(fmt.Sprintf("%s-%s", tc.name, hash.String()), func(t *testing.T) {
|
||||
msg := []byte("message")
|
||||
|
||||
sig, err := tc.signer(msg, crypto.WithSigningHash(hash))
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, sig)
|
||||
|
||||
if tc.expectedSize > 0 {
|
||||
require.Equal(t, tc.expectedSize, len(sig))
|
||||
}
|
||||
*tc.stats = len(sig)
|
||||
|
||||
valid := tc.verifier(msg, sig)
|
||||
valid := tc.verifier(msg, sig, crypto.WithSigningHash(hash))
|
||||
require.True(t, valid)
|
||||
|
||||
valid = tc.verifier([]byte("wrong message"), sig)
|
||||
require.False(t, valid)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("KeyExchange", func(t *testing.T) {
|
||||
@@ -234,11 +287,11 @@ func TestSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](t *testing.T, har
|
||||
pub3, _, err := harness.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
|
||||
kePriv1, ok := crypto.PrivateKey(priv1).(crypto.KeyExchangePrivateKey)
|
||||
kePriv1, ok := crypto.PrivateKey(priv1).(crypto.PrivateKeyKeyExchange)
|
||||
if !ok {
|
||||
t.Skip("Key exchange is not implemented")
|
||||
}
|
||||
kePriv2 := crypto.PrivateKey(priv2).(crypto.KeyExchangePrivateKey)
|
||||
kePriv2 := crypto.PrivateKey(priv2).(crypto.PrivateKeyKeyExchange)
|
||||
|
||||
// TODO: test with incompatible public keys
|
||||
require.True(t, kePriv1.PublicKeyIsCompatible(pub2))
|
||||
@@ -271,20 +324,26 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
|
||||
})
|
||||
|
||||
b.Run("Bytes", func(b *testing.B) {
|
||||
if !pubImplements[PubT, crypto.PublicKeyToBytes]() {
|
||||
b.Skip("Public key does not implement crypto.PublicKeyToBytes")
|
||||
}
|
||||
|
||||
b.Run("PubToBytes", func(b *testing.B) {
|
||||
pub, _, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
pubTb := (crypto.PublicKey(pub)).(crypto.PublicKeyToBytes)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = pub.ToBytes()
|
||||
_ = pubTb.ToBytes()
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("PubFromBytes", func(b *testing.B) {
|
||||
pub, _, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
buf := pub.ToBytes()
|
||||
pubTb := (crypto.PublicKey(pub)).(crypto.PublicKeyToBytes)
|
||||
buf := pubTb.ToBytes()
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -295,17 +354,19 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
|
||||
b.Run("PrivToBytes", func(b *testing.B) {
|
||||
_, priv, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
privTb := (crypto.PrivateKey(priv)).(crypto.PrivateKeyToBytes)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = priv.ToBytes()
|
||||
_ = privTb.ToBytes()
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("PrivFromBytes", func(b *testing.B) {
|
||||
_, priv, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
buf := priv.ToBytes()
|
||||
privTb := (crypto.PrivateKey(priv)).(crypto.PrivateKeyToBytes)
|
||||
buf := privTb.ToBytes()
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -403,15 +464,15 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
|
||||
})
|
||||
|
||||
b.Run("Signatures", func(b *testing.B) {
|
||||
if _, ok := (crypto.PublicKey(*new(PubT))).(crypto.SigningPublicKey); !ok {
|
||||
b.Skip("Signature is not implemented")
|
||||
b.Run("Sign to Bytes signature", func(b *testing.B) {
|
||||
if !pubImplements[PubT, crypto.PublicKeySigningBytes]() {
|
||||
b.Skip("Signature to bytes is not implemented")
|
||||
}
|
||||
|
||||
b.Run("Sign to Bytes signature", func(b *testing.B) {
|
||||
_, priv, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
|
||||
spriv := (crypto.PrivateKey(priv)).(crypto.SigningPrivateKey)
|
||||
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningBytes)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -422,11 +483,15 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
|
||||
})
|
||||
|
||||
b.Run("Verify from Bytes signature", func(b *testing.B) {
|
||||
if !pubImplements[PubT, crypto.PublicKeySigningBytes]() {
|
||||
b.Skip("Signature to bytes is not implemented")
|
||||
}
|
||||
|
||||
pub, priv, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
|
||||
spub := (crypto.PublicKey(pub)).(crypto.SigningPublicKey)
|
||||
spriv := (crypto.PrivateKey(priv)).(crypto.SigningPrivateKey)
|
||||
spub := (crypto.PublicKey(pub)).(crypto.PublicKeySigningBytes)
|
||||
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningBytes)
|
||||
sig, err := spriv.SignToBytes([]byte("message"))
|
||||
require.NoError(b, err)
|
||||
|
||||
@@ -439,10 +504,14 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
|
||||
})
|
||||
|
||||
b.Run("Sign to ASN.1 signature", func(b *testing.B) {
|
||||
if !pubImplements[PubT, crypto.PublicKeySigningASN1]() {
|
||||
b.Skip("Signature to ASN.1 is not implemented")
|
||||
}
|
||||
|
||||
_, priv, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
|
||||
spriv := (crypto.PrivateKey(priv)).(crypto.SigningPrivateKey)
|
||||
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningASN1)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -453,11 +522,15 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
|
||||
})
|
||||
|
||||
b.Run("Verify from ASN.1 signature", func(b *testing.B) {
|
||||
if !pubImplements[PubT, crypto.PublicKeySigningASN1]() {
|
||||
b.Skip("Signature to ASN.1 is not implemented")
|
||||
}
|
||||
|
||||
pub, priv, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
|
||||
spub := (crypto.PublicKey(pub)).(crypto.SigningPublicKey)
|
||||
spriv := (crypto.PrivateKey(priv)).(crypto.SigningPrivateKey)
|
||||
spub := (crypto.PublicKey(pub)).(crypto.PublicKeySigningASN1)
|
||||
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningASN1)
|
||||
sig, err := spriv.SignToASN1([]byte("message"))
|
||||
require.NoError(b, err)
|
||||
|
||||
@@ -471,14 +544,14 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
|
||||
})
|
||||
|
||||
b.Run("Key exchange", func(b *testing.B) {
|
||||
if _, ok := (crypto.PrivateKey(*new(PrivT))).(crypto.KeyExchangePrivateKey); !ok {
|
||||
b.Skip("Key echange is not implemented")
|
||||
if !privImplements[PrivT, crypto.PrivateKeyKeyExchange]() {
|
||||
b.Skip("Key exchange is not implemented")
|
||||
}
|
||||
|
||||
b.Run("KeyExchange", func(b *testing.B) {
|
||||
_, priv1, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
kePriv1 := (crypto.PrivateKey(priv1)).(crypto.KeyExchangePrivateKey)
|
||||
kePriv1 := (crypto.PrivateKey(priv1)).(crypto.PrivateKeyKeyExchange)
|
||||
pub2, _, err := harness.GenerateKeyPair()
|
||||
require.NoError(b, err)
|
||||
|
||||
@@ -491,3 +564,13 @@ func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, ha
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func privImplements[PrivT crypto.PrivateKey, wanted crypto.PrivateKey]() bool {
|
||||
_, ok := crypto.PrivateKey(*new(PrivT)).(wanted)
|
||||
return ok
|
||||
}
|
||||
|
||||
func pubImplements[PubT crypto.PublicKey, wanted crypto.PublicKey]() bool {
|
||||
_, ok := crypto.PublicKey(*new(PubT)).(wanted)
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ed25519
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/_testsuite"
|
||||
)
|
||||
|
||||
@@ -17,6 +18,8 @@ var harness = testsuite.TestHarness[PublicKey, PrivateKey]{
|
||||
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
|
||||
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
|
||||
MultibaseCode: MultibaseCode,
|
||||
DefaultHash: crypto.SHA512,
|
||||
OtherHashes: nil,
|
||||
PublicKeyBytesSize: PublicKeyBytesSize,
|
||||
PrivateKeyBytesSize: PrivateKeyBytesSize,
|
||||
SignatureBytesSize: SignatureBytesSize,
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
)
|
||||
|
||||
var _ crypto.SigningPrivateKey = &PrivateKey{}
|
||||
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
|
||||
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
|
||||
var _ crypto.PrivateKeyToBytes = &PrivateKey{}
|
||||
|
||||
type PrivateKey struct {
|
||||
k ed25519.PrivateKey
|
||||
@@ -28,13 +30,24 @@ func PrivateKeyFromBytes(b []byte) (PrivateKey, error) {
|
||||
return PrivateKey{k: append([]byte{}, b...)}, nil
|
||||
}
|
||||
|
||||
func PrivateKeyFromSeed(seed []byte) (PrivateKey, error) {
|
||||
if len(seed) != ed25519.SeedSize {
|
||||
return PrivateKey{}, fmt.Errorf("invalid ed25519 seed size")
|
||||
}
|
||||
return PrivateKey{k: ed25519.NewKeyFromSeed(seed)}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8DER decodes a PKCS#8 DER (binary) encoded private key.
|
||||
func PrivateKeyFromPKCS8DER(bytes []byte) (PrivateKey, error) {
|
||||
priv, err := x509.ParsePKCS8PrivateKey(bytes)
|
||||
if err != nil {
|
||||
return PrivateKey{}, err
|
||||
}
|
||||
return PrivateKey{k: priv.(ed25519.PrivateKey)}, nil
|
||||
edPriv, ok := priv.(ed25519.PrivateKey)
|
||||
if !ok {
|
||||
return PrivateKey{}, fmt.Errorf("invalid private key type")
|
||||
}
|
||||
return PrivateKey{k: edPriv}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
|
||||
@@ -60,13 +73,21 @@ func (p PrivateKey) Public() crypto.PublicKey {
|
||||
return PublicKey{k: p.k.Public().(ed25519.PublicKey)}
|
||||
}
|
||||
|
||||
func (p PrivateKey) SignToBytes(message []byte) ([]byte, error) {
|
||||
func (p PrivateKey) SignToBytes(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
if params.Hash != crypto.Hash(0) && params.Hash != crypto.SHA512 {
|
||||
return nil, fmt.Errorf("ed25519 does not support custom hash functions")
|
||||
}
|
||||
return ed25519.Sign(p.k, message), nil
|
||||
}
|
||||
|
||||
// SignToASN1 creates a signature with ASN.1 encoding.
|
||||
// This ASN.1 encoding uses a BIT STRING, which would be correct for an X.509 certificate.
|
||||
func (p PrivateKey) SignToASN1(message []byte) ([]byte, error) {
|
||||
func (p PrivateKey) SignToASN1(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
if params.Hash != crypto.Hash(0) && params.Hash != crypto.SHA512 {
|
||||
return nil, fmt.Errorf("ed25519 does not support custom hash functions")
|
||||
}
|
||||
sig := ed25519.Sign(p.k, message)
|
||||
var b cryptobyte.Builder
|
||||
b.AddASN1BitString(sig)
|
||||
|
||||
@@ -10,10 +10,12 @@ import (
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/_helpers"
|
||||
"github.com/INFURA/go-did/crypto/internal"
|
||||
)
|
||||
|
||||
var _ crypto.SigningPublicKey = &PublicKey{}
|
||||
var _ crypto.PublicKeySigningBytes = PublicKey{}
|
||||
var _ crypto.PublicKeySigningASN1 = PublicKey{}
|
||||
var _ crypto.PublicKeyToBytes = PublicKey{}
|
||||
|
||||
type PublicKey struct {
|
||||
k ed25519.PublicKey
|
||||
@@ -97,13 +99,23 @@ func (p PublicKey) Equal(other crypto.PublicKey) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p PublicKey) VerifyBytes(message, signature []byte) bool {
|
||||
func (p PublicKey) VerifyBytes(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
if params.Hash != crypto.Hash(0) && params.Hash != crypto.SHA512 {
|
||||
// ed25519 does not support custom hash functions
|
||||
return false
|
||||
}
|
||||
return ed25519.Verify(p.k, message, signature)
|
||||
}
|
||||
|
||||
// VerifyASN1 verifies a signature with ASN.1 encoding.
|
||||
// This ASN.1 encoding uses a BIT STRING, which would be correct for an X.509 certificate.
|
||||
func (p PublicKey) VerifyASN1(message, signature []byte) bool {
|
||||
func (p PublicKey) VerifyASN1(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
if params.Hash != crypto.Hash(0) && params.Hash != crypto.SHA512 {
|
||||
// ed25519 does not support custom hash functions
|
||||
return false
|
||||
}
|
||||
var s cryptobyte.String = signature
|
||||
var bitString asn1.BitString
|
||||
|
||||
|
||||
102
crypto/hash.go
Normal file
102
crypto/hash.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
stdcrypto "crypto"
|
||||
"hash"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// As the standard crypto library prohibits from registering additional hash algorithm (like keccak),
|
||||
// below is essentially an extension of that mechanism to allow it.
|
||||
|
||||
func init() {
|
||||
RegisterHash(KECCAK_256, sha3.NewLegacyKeccak256)
|
||||
RegisterHash(KECCAK_512, sha3.NewLegacyKeccak512)
|
||||
}
|
||||
|
||||
type Hash uint
|
||||
|
||||
// HashFunc simply returns the value of h so that [Hash] implements [SignerOpts].
|
||||
func (h Hash) HashFunc() Hash {
|
||||
return h
|
||||
}
|
||||
|
||||
func (h Hash) String() string {
|
||||
if h < maxStdHash {
|
||||
return stdcrypto.Hash(h).String()
|
||||
}
|
||||
|
||||
// Extensions
|
||||
switch h {
|
||||
case KECCAK_256:
|
||||
return "Keccak-256"
|
||||
case KECCAK_512:
|
||||
return "Keccak-512"
|
||||
|
||||
default:
|
||||
return "unknown hash value " + strconv.Itoa(int(h))
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// From "crypto"
|
||||
MD4 Hash = 1 + iota // import golang.org/x/crypto/md4
|
||||
MD5 // import crypto/md5
|
||||
SHA1 // import crypto/sha1
|
||||
SHA224 // import crypto/sha256
|
||||
SHA256 // import crypto/sha256
|
||||
SHA384 // import crypto/sha512
|
||||
SHA512 // import crypto/sha512
|
||||
MD5SHA1 // no implementation; MD5+SHA1 used for TLS RSA
|
||||
RIPEMD160 // import golang.org/x/crypto/ripemd160
|
||||
SHA3_224 // import golang.org/x/crypto/sha3
|
||||
SHA3_256 // import golang.org/x/crypto/sha3
|
||||
SHA3_384 // import golang.org/x/crypto/sha3
|
||||
SHA3_512 // import golang.org/x/crypto/sha3
|
||||
SHA512_224 // import crypto/sha512
|
||||
SHA512_256 // import crypto/sha512
|
||||
BLAKE2s_256 // import golang.org/x/crypto/blake2s
|
||||
BLAKE2b_256 // import golang.org/x/crypto/blake2b
|
||||
BLAKE2b_384 // import golang.org/x/crypto/blake2b
|
||||
BLAKE2b_512 // import golang.org/x/crypto/blake2b
|
||||
|
||||
maxStdHash
|
||||
|
||||
// Extensions
|
||||
KECCAK_256
|
||||
KECCAK_512
|
||||
|
||||
maxHash
|
||||
)
|
||||
|
||||
var hashes = make([]func() hash.Hash, maxHash-maxStdHash-1)
|
||||
|
||||
// New returns a new hash.Hash calculating the given hash function. New panics
|
||||
// if the hash function is not linked into the binary.
|
||||
func (h Hash) New() hash.Hash {
|
||||
if h > 0 && h < maxStdHash {
|
||||
return stdcrypto.Hash(h).New()
|
||||
}
|
||||
if h > maxStdHash && h < maxHash {
|
||||
f := hashes[h-maxStdHash-1]
|
||||
if f != nil {
|
||||
return f()
|
||||
}
|
||||
}
|
||||
panic("crypto: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
|
||||
}
|
||||
|
||||
// RegisterHash registers a function that returns a new instance of the given
|
||||
// hash function. This is intended to be called from the init function in
|
||||
// packages that implement hash functions.
|
||||
func RegisterHash(h Hash, f func() hash.Hash) {
|
||||
if h >= maxHash {
|
||||
panic("crypto: RegisterHash of unknown hash function")
|
||||
}
|
||||
if h <= maxStdHash {
|
||||
panic("crypto: RegisterHash of standard hash function")
|
||||
}
|
||||
hashes[h-maxStdHash-1] = f
|
||||
}
|
||||
@@ -1,14 +1,11 @@
|
||||
package crypto
|
||||
|
||||
// Public Key
|
||||
|
||||
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
|
||||
@@ -20,6 +17,33 @@ type PublicKey interface {
|
||||
ToX509PEM() string
|
||||
}
|
||||
|
||||
type PublicKeyToBytes interface {
|
||||
PublicKey
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
type PublicKeySigningBytes 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, opts ...SigningOption) bool
|
||||
}
|
||||
|
||||
type PublicKeySigningASN1 interface {
|
||||
PublicKey
|
||||
|
||||
// VerifyASN1 checks a signature in the ASN.1 format.
|
||||
VerifyASN1(message, signature []byte, opts ...SigningOption) bool
|
||||
}
|
||||
|
||||
// Private Key
|
||||
|
||||
type PrivateKey interface {
|
||||
// Equal returns true if other is the same PrivateKey
|
||||
Equal(other PrivateKey) bool
|
||||
@@ -27,11 +51,6 @@ type PrivateKey interface {
|
||||
// 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
|
||||
|
||||
@@ -39,31 +58,32 @@ type PrivateKey interface {
|
||||
ToPKCS8PEM() string
|
||||
}
|
||||
|
||||
type SigningPublicKey interface {
|
||||
PublicKey
|
||||
type PrivateKeyToBytes interface {
|
||||
PrivateKey
|
||||
|
||||
// VerifyBytes checks a signature in the "raw bytes" format.
|
||||
// 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.
|
||||
VerifyBytes(message, signature []byte) bool
|
||||
|
||||
// VerifyASN1 checks a signature in the ASN.1 format.
|
||||
VerifyASN1(message, signature []byte) bool
|
||||
ToBytes() []byte
|
||||
}
|
||||
|
||||
type SigningPrivateKey interface {
|
||||
type PrivateKeySigningBytes 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)
|
||||
SignToBytes(message []byte, opts ...SigningOption) ([]byte, error)
|
||||
}
|
||||
|
||||
type KeyExchangePrivateKey interface {
|
||||
type PrivateKeySigningASN1 interface {
|
||||
PrivateKey
|
||||
|
||||
// SignToASN1 creates a signature in the ASN.1 format.
|
||||
SignToASN1(message []byte, opts ...SigningOption) ([]byte, error)
|
||||
}
|
||||
|
||||
type PrivateKeyKeyExchange interface {
|
||||
PrivateKey
|
||||
|
||||
// PublicKeyIsCompatible checks that the given PublicKey is compatible to perform key exchange.
|
||||
|
||||
37
crypto/internal/asn1.go
Normal file
37
crypto/internal/asn1.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
"golang.org/x/crypto/cryptobyte/asn1"
|
||||
)
|
||||
|
||||
// Taken from crypto/ecdsa
|
||||
|
||||
func EncodeSignatureToASN1(r, s []byte) ([]byte, error) {
|
||||
var b cryptobyte.Builder
|
||||
b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
|
||||
addASN1IntBytes(b, r)
|
||||
addASN1IntBytes(b, s)
|
||||
})
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
// addASN1IntBytes encodes in ASN.1 a positive integer represented as
|
||||
// a big-endian byte slice with zero or more leading zeroes.
|
||||
func addASN1IntBytes(b *cryptobyte.Builder, bytes []byte) {
|
||||
for len(bytes) > 0 && bytes[0] == 0 {
|
||||
bytes = bytes[1:]
|
||||
}
|
||||
if len(bytes) == 0 {
|
||||
b.SetError(errors.New("invalid integer"))
|
||||
return
|
||||
}
|
||||
b.AddASN1(asn1.INTEGER, func(c *cryptobyte.Builder) {
|
||||
if bytes[0]&0x80 != 0 {
|
||||
c.AddUint8(0)
|
||||
}
|
||||
c.AddBytes(bytes)
|
||||
})
|
||||
}
|
||||
216
crypto/jwk/private.go
Normal file
216
crypto/jwk/private.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"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/p384"
|
||||
"github.com/INFURA/go-did/crypto/p521"
|
||||
"github.com/INFURA/go-did/crypto/rsa"
|
||||
"github.com/INFURA/go-did/crypto/secp256k1"
|
||||
"github.com/INFURA/go-did/crypto/x25519"
|
||||
)
|
||||
|
||||
type PrivateJwk struct {
|
||||
Privkey crypto.PrivateKey
|
||||
}
|
||||
|
||||
func (pj PrivateJwk) MarshalJSON() ([]byte, error) {
|
||||
switch privkey := pj.Privkey.(type) {
|
||||
case ed25519.PrivateKey:
|
||||
pubkey := privkey.Public().(ed25519.PublicKey)
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
D string `json:"d"`
|
||||
}{
|
||||
Kty: "OKP",
|
||||
Crv: "Ed25519",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.ToBytes()),
|
||||
D: base64.RawURLEncoding.EncodeToString(privkey.Seed()),
|
||||
})
|
||||
case *p256.PrivateKey:
|
||||
pubkey := privkey.Public().(*p256.PublicKey)
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
D string `json:"d"`
|
||||
}{
|
||||
Kty: "EC",
|
||||
Crv: "P-256",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.XBytes()),
|
||||
Y: base64.RawURLEncoding.EncodeToString(pubkey.YBytes()),
|
||||
D: base64.RawURLEncoding.EncodeToString(privkey.ToBytes()),
|
||||
})
|
||||
case *p384.PrivateKey:
|
||||
pubkey := privkey.Public().(*p384.PublicKey)
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
D string `json:"d"`
|
||||
}{
|
||||
Kty: "EC",
|
||||
Crv: "P-384",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.XBytes()),
|
||||
Y: base64.RawURLEncoding.EncodeToString(pubkey.YBytes()),
|
||||
D: base64.RawURLEncoding.EncodeToString(privkey.ToBytes()),
|
||||
})
|
||||
case *p521.PrivateKey:
|
||||
pubkey := privkey.Public().(*p521.PublicKey)
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
D string `json:"d"`
|
||||
}{
|
||||
Kty: "EC",
|
||||
Crv: "P-521",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.XBytes()),
|
||||
Y: base64.RawURLEncoding.EncodeToString(pubkey.YBytes()),
|
||||
D: base64.RawURLEncoding.EncodeToString(privkey.ToBytes()),
|
||||
})
|
||||
case *rsa.PrivateKey:
|
||||
pubkey := privkey.Public().(*rsa.PublicKey)
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
N string `json:"n"`
|
||||
E string `json:"e"`
|
||||
D string `json:"d"`
|
||||
P string `json:"p"`
|
||||
Q string `json:"q"`
|
||||
Dp string `json:"dp"`
|
||||
Dq string `json:"dq"`
|
||||
Qi string `json:"qi"`
|
||||
}{
|
||||
Kty: "RSA",
|
||||
N: base64.RawURLEncoding.EncodeToString(pubkey.NBytes()),
|
||||
E: base64.RawURLEncoding.EncodeToString(pubkey.EBytes()),
|
||||
D: base64.RawURLEncoding.EncodeToString(privkey.DBytes()),
|
||||
P: base64.RawURLEncoding.EncodeToString(privkey.PBytes()),
|
||||
Q: base64.RawURLEncoding.EncodeToString(privkey.QBytes()),
|
||||
Dp: base64.RawURLEncoding.EncodeToString(privkey.DpBytes()),
|
||||
Dq: base64.RawURLEncoding.EncodeToString(privkey.DqBytes()),
|
||||
Qi: base64.RawURLEncoding.EncodeToString(privkey.QiBytes()),
|
||||
})
|
||||
case *secp256k1.PrivateKey:
|
||||
pubkey := privkey.Public().(*secp256k1.PublicKey)
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
D string `json:"d"`
|
||||
}{
|
||||
Kty: "EC",
|
||||
Crv: "secp256k1",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.XBytes()),
|
||||
Y: base64.RawURLEncoding.EncodeToString(pubkey.YBytes()),
|
||||
D: base64.RawURLEncoding.EncodeToString(privkey.ToBytes()),
|
||||
})
|
||||
case *x25519.PrivateKey:
|
||||
pubkey := privkey.Public().(*x25519.PublicKey)
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
D string `json:"d"`
|
||||
}{
|
||||
Kty: "OKP",
|
||||
Crv: "X25519",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.ToBytes()),
|
||||
D: base64.RawURLEncoding.EncodeToString(privkey.ToBytes()),
|
||||
})
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported key type %T", privkey)
|
||||
}
|
||||
}
|
||||
|
||||
func (pj *PrivateJwk) UnmarshalJSON(bytes []byte) error {
|
||||
aux := make(map[string]string)
|
||||
err := json.Unmarshal(bytes, &aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch aux["kty"] {
|
||||
case "EC": // Elliptic curve
|
||||
// we only use D, ignore X/Y which will be recomputed from D
|
||||
d, err := base64.RawURLEncoding.DecodeString(aux["d"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid d parameter with kty=EC: %w", err)
|
||||
}
|
||||
switch aux["crv"] {
|
||||
case "P-256":
|
||||
pj.Privkey, err = p256.PrivateKeyFromBytes(d)
|
||||
return err
|
||||
case "P-384":
|
||||
pj.Privkey, err = p384.PrivateKeyFromBytes(d)
|
||||
return err
|
||||
case "P-521":
|
||||
pj.Privkey, err = p521.PrivateKeyFromBytes(d)
|
||||
return err
|
||||
case "secp256k1":
|
||||
pj.Privkey, err = secp256k1.PrivateKeyFromBytes(d)
|
||||
return err
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported Curve %s", aux["crv"])
|
||||
}
|
||||
|
||||
case "RSA":
|
||||
// we only use N,E,D,P,Q ignore Dp/Dq/Qi which will be recomputed from other parameters
|
||||
n, err := base64.RawURLEncoding.DecodeString(aux["n"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid n parameter with kty=RSA: %w", err)
|
||||
}
|
||||
e, err := base64.RawURLEncoding.DecodeString(aux["e"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid e parameter with kty=RSA: %w", err)
|
||||
}
|
||||
d, err := base64.RawURLEncoding.DecodeString(aux["d"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid d parameter with kty=RSA: %w", err)
|
||||
}
|
||||
p, err := base64.RawURLEncoding.DecodeString(aux["p"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid p parameter with kty=RSA: %w", err)
|
||||
}
|
||||
q, err := base64.RawURLEncoding.DecodeString(aux["q"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid q parameter with kty=RSA: %w", err)
|
||||
}
|
||||
pj.Privkey, err = rsa.PrivateKeyFromNEDPQ(n, e, d, p, q)
|
||||
return err
|
||||
|
||||
case "OKP": // Octet key pair
|
||||
d, err := base64.RawURLEncoding.DecodeString(aux["d"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid x parameter with kty=OKP: %w", err)
|
||||
}
|
||||
switch aux["crv"] {
|
||||
case "Ed25519":
|
||||
pj.Privkey, err = ed25519.PrivateKeyFromSeed(d)
|
||||
return err
|
||||
case "X25519":
|
||||
pj.Privkey, err = x25519.PrivateKeyFromBytes(d)
|
||||
return err
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported Curve %s", aux["crv"])
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported key type %s", aux["kty"])
|
||||
}
|
||||
}
|
||||
114
crypto/jwk/private_test.go
Normal file
114
crypto/jwk/private_test.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Origin: https://github.com/w3c-ccg/did-key-spec/tree/main/test-vectors
|
||||
|
||||
func TestPrivateJwkRoundtrip(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
in string
|
||||
}{
|
||||
{
|
||||
name: "RSA-2048",
|
||||
in: `{
|
||||
"kty": "RSA",
|
||||
"n": "sbX82NTV6IylxCh7MfV4hlyvaniCajuP97GyOqSvTmoEdBOflFvZ06kR_9D6ctt45Fk6hskfnag2GG69NALVH2o4RCR6tQiLRpKcMRtDYE_thEmfBvDzm_VVkOIYfxu-Ipuo9J_S5XDNDjczx2v-3oDh5-CIHkU46hvFeCvpUS-L8TJSbgX0kjVk_m4eIb9wh63rtmD6Uz_KBtCo5mmR4TEtcLZKYdqMp3wCjN-TlgHiz_4oVXWbHUefCEe8rFnX1iQnpDHU49_SaXQoud1jCaexFn25n-Aa8f8bc5Vm-5SeRwidHa6ErvEhTvf1dz6GoNPp2iRvm-wJ1gxwWJEYPQ",
|
||||
"e": "AQAB",
|
||||
"d": "Eym3sT4KLwBzo5pl5nY83-hAti92iLQRizkrKe22RbNi9Y1kKOBatdtGaJqFVztZZu5ERGKNuTd5VdsjJeekSbXviVGRtdHNCvgmRZlWA5261AgIUPxMmKW062GmGJbKQvscFfziBgHK6tyDBd8cZavqMFHi-7ilMYF7IsFBcJKM85x_30pnfd4YwhGQIc9hzv238aOwYKg8c-MzYhEVUnL273jaiLVlfZWQ5ca-GXJHmdOb_Y4fE5gpXfPFBseqleXsMp0VuXxCEsN30LIJHYscdPtbzLD3LFbuMJglFbQqYqssqymILGqJ7Tc2mB2LmXevfqRWz5D7A_K1WzvuoQ",
|
||||
"p": "3CWT55Vc9CmUKavtV11fwwCU3lha99eRsl7Yo6HJLudpKV8zJ5bojTPqrowAjiHxyz3ITPCY3WgSX_q1n_4093U51rYermMfHQmrY_l7EgwxvNNMdsH4uMwHhz5vUNks6svtmkJL4JwQe8HPimHMdruCrPZKs0gajO59uNL-0rk",
|
||||
"q": "zqcpEWpGAeJS0ZzTElrClcl6iQaElAV-KcOVqSOSm25FA2_QE7Px9FTGTrPDBivH5P9iT7SgAWwPypYiCJeDxZ_Rt1FbQvR0gfhzp9_eZJERd4BPaHdcNoXQXVgqezTxbJha064iJhYKHI72zB4rsBS5o4n7qWvqUSXNMYdV_6U",
|
||||
"dp": "gcUE8rZxHNyFoiretWktUd2943Nh7Eb-c47FVW_BEA0JSIH9vZCPdOztogaVLTOFPLEmqXQKKDl422sGNVG8F0La3V5tp452gL96cGxXx8O4bf6ATGD7JLPgnDCJnbbna2Daptv9rmFQtiMBHCmaRUMzPJHSZuxR-lF7er-lxsE",
|
||||
"dq": "Id2bCVOVLXHdiKReor9k7A8cmaAL0gYkasu2lwVRXU9w1-NXAiOXHydVaEhlSXmbRJflkJJVNmZzIAwCf830tko-oAAhKJPPFA2XRoeVdn2fkynf2YrV_cloICP2skI23kkJeW8sAXnTJmL3ZvP6zNxYn8hZCaa5u5qqSdeX7FE",
|
||||
"qi": "WKIToXXnjl7GDbz7jCNbX9nWYOE5BDNzVmwiVOnyGoTZfwJ_qtgizj7pOapxi6dT9S9mMavmeAi6LAsEe1WUWtaKSNhbNh0PUGGXlXHGlhkS8jI1ot0e-scrHAuACE567YQ4VurpNorPKtZ5UENXIn74DEmt4l5m6902VF3X5Wo"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "RSA-4096",
|
||||
in: `{
|
||||
"kty": "RSA",
|
||||
"n": "qMCkFFRFWtzUyZeK8mgJdyM6SEQcXC5E6JwCRVDld-jlJs8sXNOE_vliexq34wZRQ4hk53-JPFlvZ_QjRgIxdUxSMiZ3S5hlNVvvRaue6SMakA9ugQhnfXaWORro0UbPuHLms-bg5StDP8-8tIezu9c1H1FjwPcdbV6rAvKhyhnsM10qP3v2CPbdE0q3FOsihoKuTelImtO110E7N6fLn4U3EYbC4OyViqlrP1o_1M-R-tiM1cb4pD7XKJnIs6ryZdfOQSPBJwjNqSdN6Py_tdrFgPDTyacSSdpTVADOM2IMAoYbhV1N5APhnjOHBRFyKkF1HffQKpmXQLBqvUNNjuhmpVKWBtrTdcCKrglFXiw0cKGHKxIirjmiOlB_HYHg5UdosyE3_1Txct2U7-WBB6QXak1UgxCzgKYBDI8UPA0RlkUuHHP_Zg0fVXrXIInHO04MYxUeSps5qqyP6dJBu_v_BDn3zUq6LYFwJ_-xsU7zbrKYB4jaRlHPoCj_eDC-rSA2uQ4KXHBB8_aAqNFC9ukWxc26Ifz9dF968DLuL30bi-ZAa2oUh492Pw1bg89J7i4qTsOOfpQvGyDV7TGhKuUG3Hbumfr2w16S-_3EI2RIyd1nYsflE6ZmCkZQMG_lwDAFXaqfyGKEDouJuja4XH8r4fGWeGTrozIoniXT1HU",
|
||||
"e": "AQAB",
|
||||
"d": "TMq1H-clVG7PihkjCqJbRFLMj9wmx6_qfauYwPBKK-HYfWujdW5vxBO6Q-jpqy7RxhiISmxYCBVuw_BuKMqQtR8Q_G9StBzaWYjHfn3Vp6Poz4umLqOjbI2NWNks_ybpGbd30oAK8V5ZkO04ozJpkN4i92hzK3mIc5-z1HiTNUPMn6cStab0VCn6em_ylltV774CEcRJ3OLgid7OUspRt_rID3qyreYbOulTu5WXHIGEnZDzrciIlz1dbcVldpUhD0VAP5ZErD2uUP5oztBNcTTn0YBF8CrOALuQVdaz_t_sNS3P0kWeT1eQ0QwDskO5Hw-Aey2tFeWk1bQyLoQ1A0jsw8mDbkO2zrGfJoxmVBkueTK-q64_n1kV7W1aeJFRj4NwEWmwcrs8GSOGOn38fGB_Y3Kci04qvD6L0QZbFkAVzcJracnxbTdHCEX0jsAAPbYC8M_8PyrPJvPC4IAAWTRrSRbysb7r7viRf4A1vTK9VT7uYyxj7Kzx2cU12d9QBXYfdQ2744bUE7HqN-Vh2rHvv2l5v6vzBRoZ5_OhHHVeUYwC9LouE9lSVAObbFM-Qe1SvzbbwN91LziI7UzUc_xMAEiNwt6PpnIAWAhdvSRawEllTwUcn89udHd5UhiAcm-RQOqXIdA9Aly6d8TT8R1p-ZnQ_gbZyBZeS39AuvU",
|
||||
"p": "1p4cypsJeTyVXXc5bQpvzVenPy78OHXtGcFQnbTjW8x1GsvJ-rlHAcjUImd44pgNQNe-iYpeUg3KqfONeedNgQCFd8kP7GoVAd45mEvsGBXvjoCXOBMQlsf8UU_hm_LKhVvTvTmMGoudnNv5qYNDMCGJGzwoG-aSvROlIoXzHmDnusZ-hKsDxM9j0PPz21t99Y_Fr30Oq3FIWXPVmLYmfyZYQkxm9a9WNMkqRbwJuMwGI6V9ABsQ1dW_KJzp_aEBbJLcDr9DsWhm9ErLeAlzyaDYEai6wCtKm9em4LDwCbKhJq3hWEp1sIG-hwx1sk7N4i-b8lBijjEQE-dbSQxUlw",
|
||||
"q": "yUqMejfrttGujadj7Uf7q91KM7nbQGny4TjD-CqibcFE-s2_DExCgP1wfhUPfJr2uPQDIe4g12uaNoa5GbCSDaQwEmQpurC_5mazt-z-_tbI24hoPQm5Hq67fZz-jDE_3OccLPLIWtajJqmxHbbB5VqskMuXo8KDxPRfBQBhykmb9_5M8pY2ggZOV4shCUn5E9nOnvibvw5Wx4CBtWUtca4rhpd3mVen1d8xCe4xTG_ni_w1lwdxzU1GmRFqgTuZWzL0r2FKzJg7hju1SOEe4tKMxQ-xs2HyNaMM__SLsNmS3lsYZ8r2hqcjEMQQZI0T_O-3BjIpyg986P8j055E0w",
|
||||
"dp": "DujzJRw6P0L3OYQT6EBmXgSt6NTRzvZaX4SvnhU4CmOc6xynTpTamwQhwLYhjtRzb0LNyO5k-RxeLQpvlL1-A-1OWHEOeyUvim6u36a-ozm659KFLu8cIu2H2PpMuTHX4gXsIuRBmIKEk6YwpRcqbsiVpt-6BZ4yKZKY0Vou9rhSwQYTOhJLc7vYumaIVX_4szumxzdP8pcvKI_EkhRtfj3iudBnAsCIo6gqGKgkoMMD1iwkEALRW5m66w5jrywlVi6pvRiKkmOna2da1V8KvUJAYJGxT7JyP3tu64M_Wd0gFvjTg_fAT1_kJau27YlOAl2-Xso43poH_OoAzIVfxw",
|
||||
"dq": "XI6Z76z9BxB9mgcpTLc3wzw63XQNnB3bn7JRcjBwhdVD2at3uLjsL5HaAy-98kbzQfJ56kUr9sI0o_Po8yYc0ob3z80c3wpdAx2gb-dbDWVH8KJVhBOPestPzR--cEpJGlNuwkBU3mgplyKaHZamq8a46M-lB5jurEbN1mfpj3GvdSYKzdVCdSFfLqP76eCI1pblinW4b-6w-oVdn0JJ1icHPpkxVmJW-2Hok69iHcqrBtRO9AZpTsTEvKekeI4mIyhYGLi9AzzQyhV0c3GImTXFoutng5t7GyzBUoRpI0W4YeQzYa6TEzGRTylIfGPemATF_OReENp0TlLbb3gsHw",
|
||||
"qi": "m7uZk4AsOfJ1V2RY8lmEF518toCV7juKuS_b_OUx8B0dRG0_kbF1cH-Tmrgsya3bwkYx5HeZG81rX7SRjh-0nVPOMW3tGqU5U9f59DXqvOItJIJ6wvWvWXnuna2-NstYCotFQWadIKjk4wjEKj-a4NJt4D_F4csyeyqWOH2DiUFzBGGxxdEoD5t_HEeNXuWQ6-SiV0x5ZVMln3TSh7IOMl70Smm8HcQF5mOsWg3N0wIg-yffxPrs6r15TRuW1MfT-bZk2GLrtHF1TkIoT1e00jWK4eBl2oRxiJGONUBMTEHV85Fr0yztnA99AgHnrMbE_4ehvev4h5DEWvFyFuJN_g"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "ed25519",
|
||||
in: `{
|
||||
"kty": "OKP",
|
||||
"crv": "Ed25519",
|
||||
"x": "_eT7oDCtAC98L31MMx9J0T-w7HR-zuvsY08f9MvKne8",
|
||||
"d": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "p-256",
|
||||
in: `{
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns",
|
||||
"y": "efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM",
|
||||
"d": "gPh-VvVS8MbvKQ9LSVVmfnxnKjHn4Tqj0bmbpehRlpc"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "p-384",
|
||||
in: `{
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc",
|
||||
"y": "y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv",
|
||||
"d": "hAyGZNj9031guBCdpAOaZkO-E5m-LKLYnMIq0-msrp8JLctseaOeNTHmP3uKVWwX"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "p-521",
|
||||
in: `{
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS",
|
||||
"y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC",
|
||||
"d": "AHwRaNaGs0jkj_pT6PK2aHep7dJK-yxyoL2bIfVRAceq1baxoiFDo3W14c8E2YZn1k5S53r4a11flhQdaB5guJ_X"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "secp256k1",
|
||||
in: `{
|
||||
"kty": "EC",
|
||||
"crv": "secp256k1",
|
||||
"x": "TEIJN9vnTq1EXMkqzo7yN_867-foKc2pREv45Fw_QA8",
|
||||
"y": "9yiymlzdxKCiRbYq7p-ArRB-C1ytjHE-eb7RDTi6rVc",
|
||||
"d": "J5yKm7OXFsXDEutteGYeT0CAfQJwIlHLSYkQxKtgiyo"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "x25519",
|
||||
in: `{
|
||||
"kty": "OKP",
|
||||
"crv": "X25519",
|
||||
"x": "jRIz3oriXDNZmnb35XQb7K1UIlz3ae1ao1YSqLeBXHs",
|
||||
"d": "aEAAB3VBFPCQtgF3N__wRiXhMOgeiRGstpPC3gnJ1Eo"
|
||||
}`,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var priv PrivateJwk
|
||||
err := json.Unmarshal([]byte(tc.in), &priv)
|
||||
require.NoError(t, err)
|
||||
|
||||
bytes, err := json.Marshal(priv)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, tc.in, string(bytes))
|
||||
})
|
||||
}
|
||||
}
|
||||
179
crypto/jwk/public.go
Normal file
179
crypto/jwk/public.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"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/p384"
|
||||
"github.com/INFURA/go-did/crypto/p521"
|
||||
"github.com/INFURA/go-did/crypto/rsa"
|
||||
"github.com/INFURA/go-did/crypto/secp256k1"
|
||||
"github.com/INFURA/go-did/crypto/x25519"
|
||||
)
|
||||
|
||||
// Specification:
|
||||
// - https://www.rfc-editor.org/rfc/rfc7517#section-4 (JWK)
|
||||
// - https://www.iana.org/assignments/jose/jose.xhtml#web-key-types (key parameters)
|
||||
|
||||
type PublicJwk struct {
|
||||
Pubkey crypto.PublicKey
|
||||
}
|
||||
|
||||
func (pj PublicJwk) MarshalJSON() ([]byte, error) {
|
||||
switch pubkey := pj.Pubkey.(type) {
|
||||
case ed25519.PublicKey:
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
}{
|
||||
Kty: "OKP",
|
||||
Crv: "Ed25519",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.ToBytes()),
|
||||
})
|
||||
case *p256.PublicKey:
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
}{
|
||||
Kty: "EC",
|
||||
Crv: "P-256",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.XBytes()),
|
||||
Y: base64.RawURLEncoding.EncodeToString(pubkey.YBytes()),
|
||||
})
|
||||
case *p384.PublicKey:
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
}{
|
||||
Kty: "EC",
|
||||
Crv: "P-384",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.XBytes()),
|
||||
Y: base64.RawURLEncoding.EncodeToString(pubkey.YBytes()),
|
||||
})
|
||||
case *p521.PublicKey:
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
}{
|
||||
Kty: "EC",
|
||||
Crv: "P-521",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.XBytes()),
|
||||
Y: base64.RawURLEncoding.EncodeToString(pubkey.YBytes()),
|
||||
})
|
||||
case *rsa.PublicKey:
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
N string `json:"n"`
|
||||
E string `json:"e"`
|
||||
}{
|
||||
Kty: "RSA",
|
||||
N: base64.RawURLEncoding.EncodeToString(pubkey.NBytes()),
|
||||
E: base64.RawURLEncoding.EncodeToString(pubkey.EBytes()),
|
||||
})
|
||||
case *secp256k1.PublicKey:
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
}{
|
||||
Kty: "EC",
|
||||
Crv: "secp256k1",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.XBytes()),
|
||||
Y: base64.RawURLEncoding.EncodeToString(pubkey.YBytes()),
|
||||
})
|
||||
case *x25519.PublicKey:
|
||||
return json.Marshal(struct {
|
||||
Kty string `json:"kty"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
}{
|
||||
Kty: "OKP",
|
||||
Crv: "X25519",
|
||||
X: base64.RawURLEncoding.EncodeToString(pubkey.ToBytes()),
|
||||
})
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported key type %T", pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
func (pj *PublicJwk) UnmarshalJSON(bytes []byte) error {
|
||||
aux := make(map[string]string)
|
||||
err := json.Unmarshal(bytes, &aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch aux["kty"] {
|
||||
case "EC": // Elliptic curve
|
||||
x, err := base64.RawURLEncoding.DecodeString(aux["x"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid x parameter with kty=EC: %w", err)
|
||||
}
|
||||
y, err := base64.RawURLEncoding.DecodeString(aux["y"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid y parameter with kty=EC: %w", err)
|
||||
}
|
||||
switch aux["crv"] {
|
||||
case "P-256":
|
||||
pj.Pubkey, err = p256.PublicKeyFromXY(x, y)
|
||||
return err
|
||||
case "P-384":
|
||||
pj.Pubkey, err = p384.PublicKeyFromXY(x, y)
|
||||
return err
|
||||
case "P-521":
|
||||
pj.Pubkey, err = p521.PublicKeyFromXY(x, y)
|
||||
return err
|
||||
case "secp256k1":
|
||||
pj.Pubkey, err = secp256k1.PublicKeyFromXY(x, y)
|
||||
return err
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported Curve %s", aux["crv"])
|
||||
}
|
||||
|
||||
case "RSA":
|
||||
n, err := base64.RawURLEncoding.DecodeString(aux["n"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid n parameter with kty=RSA: %w", err)
|
||||
}
|
||||
e, err := base64.RawURLEncoding.DecodeString(aux["e"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid e parameter with kty=RSA: %w", err)
|
||||
}
|
||||
pj.Pubkey, err = rsa.PublicKeyFromNE(n, e)
|
||||
return err
|
||||
|
||||
case "OKP": // Octet key pair
|
||||
x, err := base64.RawURLEncoding.DecodeString(aux["x"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid x parameter with kty=OKP: %w", err)
|
||||
}
|
||||
switch aux["crv"] {
|
||||
case "Ed25519":
|
||||
pj.Pubkey, err = ed25519.PublicKeyFromBytes(x)
|
||||
return err
|
||||
case "X25519":
|
||||
pj.Pubkey, err = x25519.PublicKeyFromBytes(x)
|
||||
return err
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported Curve %s", aux["crv"])
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported key type %s", aux["kty"])
|
||||
}
|
||||
}
|
||||
88
crypto/jwk/public_test.go
Normal file
88
crypto/jwk/public_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Origin: https://github.com/w3c-ccg/did-key-spec/tree/main/test-vectors
|
||||
|
||||
func TestPublicJwkRoundtrip(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
in string
|
||||
}{
|
||||
{
|
||||
name: "RSA",
|
||||
in: `{
|
||||
"kty": "RSA",
|
||||
"n": "sbX82NTV6IylxCh7MfV4hlyvaniCajuP97GyOqSvTmoEdBOflFvZ06kR_9D6ctt45Fk6hskfnag2GG69NALVH2o4RCR6tQiLRpKcMRtDYE_thEmfBvDzm_VVkOIYfxu-Ipuo9J_S5XDNDjczx2v-3oDh5-CIHkU46hvFeCvpUS-L8TJSbgX0kjVk_m4eIb9wh63rtmD6Uz_KBtCo5mmR4TEtcLZKYdqMp3wCjN-TlgHiz_4oVXWbHUefCEe8rFnX1iQnpDHU49_SaXQoud1jCaexFn25n-Aa8f8bc5Vm-5SeRwidHa6ErvEhTvf1dz6GoNPp2iRvm-wJ1gxwWJEYPQ",
|
||||
"e": "AQAB"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "ed25519",
|
||||
in: `{
|
||||
"kty": "OKP",
|
||||
"crv": "Ed25519",
|
||||
"x": "_eT7oDCtAC98L31MMx9J0T-w7HR-zuvsY08f9MvKne8"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "p-256",
|
||||
in: `{
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns",
|
||||
"y": "efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "p-384",
|
||||
in: `{
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc",
|
||||
"y": "y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "p-521",
|
||||
in: `{
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS",
|
||||
"y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "secp256k1",
|
||||
in: `{
|
||||
"kty": "EC",
|
||||
"crv": "secp256k1",
|
||||
"x": "TEIJN9vnTq1EXMkqzo7yN_867-foKc2pREv45Fw_QA8",
|
||||
"y": "9yiymlzdxKCiRbYq7p-ArRB-C1ytjHE-eb7RDTi6rVc"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "x25519",
|
||||
in: `{
|
||||
"kty": "OKP",
|
||||
"crv": "X25519",
|
||||
"x": "467ap28wHJGEXJAb4mLrokqq8A-txA_KmoQTcj31XzU"
|
||||
}`,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var pub PublicJwk
|
||||
err := json.Unmarshal([]byte(tc.in), &pub)
|
||||
require.NoError(t, err)
|
||||
|
||||
bytes, err := json.Marshal(pub)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, tc.in, string(bytes))
|
||||
})
|
||||
}
|
||||
}
|
||||
29
crypto/options.go
Normal file
29
crypto/options.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package crypto
|
||||
|
||||
type SigningOpts struct {
|
||||
Hash Hash
|
||||
}
|
||||
|
||||
func CollectSigningOptions(opts []SigningOption) SigningOpts {
|
||||
res := SigningOpts{}
|
||||
for _, opt := range opts {
|
||||
opt(&res)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (opts SigningOpts) HashOrDefault(_default Hash) Hash {
|
||||
if opts.Hash == 0 {
|
||||
return _default
|
||||
}
|
||||
return opts.Hash
|
||||
}
|
||||
|
||||
type SigningOption func(opts *SigningOpts)
|
||||
|
||||
// WithSigningHash specify the hash algorithm to be used for signatures
|
||||
func WithSigningHash(hash Hash) SigningOption {
|
||||
return func(opts *SigningOpts) {
|
||||
opts.Hash = hash
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,16 @@ import (
|
||||
|
||||
const (
|
||||
// PublicKeyBytesSize is the size, in bytes, of public keys in raw bytes.
|
||||
PublicKeyBytesSize = 33
|
||||
PublicKeyBytesSize = 1 + coordinateSize
|
||||
// PrivateKeyBytesSize is the size, in bytes, of private keys in raw bytes.
|
||||
PrivateKeyBytesSize = 32
|
||||
PrivateKeyBytesSize = coordinateSize
|
||||
// SignatureBytesSize is the size, in bytes, of signatures in raw bytes.
|
||||
SignatureBytesSize = 64
|
||||
SignatureBytesSize = 2 * coordinateSize
|
||||
|
||||
MultibaseCode = uint64(0x1200)
|
||||
|
||||
// coordinateSize is the size, in bytes, of one coordinate in the elliptic curve.
|
||||
coordinateSize = 32
|
||||
)
|
||||
|
||||
func GenerateKeyPair() (*PublicKey, *PrivateKey, error) {
|
||||
@@ -23,7 +26,7 @@ func GenerateKeyPair() (*PublicKey, *PrivateKey, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
pub := priv.Public().(*ecdsa.PublicKey)
|
||||
return (*PublicKey)(pub), (*PrivateKey)(priv), nil
|
||||
return &PublicKey{k: pub}, &PrivateKey{k: priv}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package p256
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/_testsuite"
|
||||
)
|
||||
|
||||
@@ -17,6 +21,8 @@ var harness = testsuite.TestHarness[*PublicKey, *PrivateKey]{
|
||||
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
|
||||
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
|
||||
MultibaseCode: MultibaseCode,
|
||||
DefaultHash: crypto.SHA256,
|
||||
OtherHashes: []crypto.Hash{crypto.SHA224, crypto.SHA384, crypto.SHA512},
|
||||
PublicKeyBytesSize: PublicKeyBytesSize,
|
||||
PrivateKeyBytesSize: PrivateKeyBytesSize,
|
||||
SignatureBytesSize: SignatureBytesSize,
|
||||
@@ -29,3 +35,25 @@ func TestSuite(t *testing.T) {
|
||||
func BenchmarkSuite(b *testing.B) {
|
||||
testsuite.BenchSuite(b, harness)
|
||||
}
|
||||
|
||||
func TestSignatureASN1(t *testing.T) {
|
||||
// openssl ecparam -genkey -name prime256v1 -noout -out private.pem
|
||||
// openssl ec -in private.pem -pubout -out public.pem
|
||||
// echo -n "message" | openssl dgst -sha256 -sign private.pem -out signature.der
|
||||
// echo -n "message" | openssl dgst -sha256 -verify public.pem -signature signature.der
|
||||
|
||||
pubPem := `-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+UhEHZqcaKn+qhNtMmW843ZTRkX/
|
||||
6GzxOWoRD2nv3EewARM90akj2UAKwQjJR9ibm78XtdlryvWG1v8TWb8INA==
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
pub, err := PublicKeyFromX509PEM(pubPem)
|
||||
require.NoError(t, err)
|
||||
|
||||
b64sig := `MEQCIHPslthrLAYgwfqYaUmtGJqwmH7sRf5FEnnKgzcHIF8fAiB9+qovdvN6yJKkBwoQCw798uWr
|
||||
0nOUE55ftB8EgX/Jbg==`
|
||||
sig, err := base64.StdEncoding.DecodeString(b64sig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, pub.VerifyASN1([]byte("message"), sig))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
@@ -13,10 +12,14 @@ import (
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
)
|
||||
|
||||
var _ crypto.SigningPrivateKey = (*PrivateKey)(nil)
|
||||
var _ crypto.KeyExchangePrivateKey = (*PrivateKey)(nil)
|
||||
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
|
||||
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
|
||||
var _ crypto.PrivateKeyToBytes = &PrivateKey{}
|
||||
var _ crypto.PrivateKeyKeyExchange = &PrivateKey{}
|
||||
|
||||
type PrivateKey ecdsa.PrivateKey
|
||||
type PrivateKey struct {
|
||||
k *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
// PrivateKeyFromBytes converts a serialized public key to a PrivateKey.
|
||||
// This compact serialization format is the raw key material, without metadata or structure.
|
||||
@@ -26,15 +29,17 @@ func PrivateKeyFromBytes(b []byte) (*PrivateKey, error) {
|
||||
return nil, fmt.Errorf("invalid P-256 private key size")
|
||||
}
|
||||
|
||||
res := &ecdsa.PrivateKey{
|
||||
res := &PrivateKey{
|
||||
k: &ecdsa.PrivateKey{
|
||||
D: new(big.Int).SetBytes(b),
|
||||
PublicKey: ecdsa.PublicKey{Curve: elliptic.P256()},
|
||||
},
|
||||
}
|
||||
|
||||
// recompute the public key
|
||||
res.PublicKey.X, res.PublicKey.Y = res.PublicKey.Curve.ScalarBaseMult(b)
|
||||
res.k.PublicKey.X, res.k.PublicKey.Y = res.k.PublicKey.Curve.ScalarBaseMult(b)
|
||||
|
||||
return (*PrivateKey)(res), nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8DER decodes a PKCS#8 DER (binary) encoded private key.
|
||||
@@ -43,8 +48,11 @@ func PrivateKeyFromPKCS8DER(bytes []byte) (*PrivateKey, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdsaPriv := priv.(*ecdsa.PrivateKey)
|
||||
return (*PrivateKey)(ecdsaPriv), nil
|
||||
ecdsaPriv, ok := priv.(*ecdsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid private key type")
|
||||
}
|
||||
return &PrivateKey{k: ecdsaPriv}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
|
||||
@@ -61,25 +69,25 @@ func PrivateKeyFromPKCS8PEM(str string) (*PrivateKey, error) {
|
||||
|
||||
func (p *PrivateKey) Equal(other crypto.PrivateKey) bool {
|
||||
if other, ok := other.(*PrivateKey); ok {
|
||||
return (*ecdsa.PrivateKey)(p).Equal((*ecdsa.PrivateKey)(other))
|
||||
return p.k.Equal(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PrivateKey) Public() crypto.PublicKey {
|
||||
ecdhPub := (*ecdsa.PrivateKey)(p).Public().(*ecdsa.PublicKey)
|
||||
return (*PublicKey)(ecdhPub)
|
||||
ecdhPub := p.k.Public().(*ecdsa.PublicKey)
|
||||
return &PublicKey{k: ecdhPub}
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [PrivateKeyBytesSize]byte
|
||||
((*ecdsa.PrivateKey)(p)).D.FillBytes(buf[:])
|
||||
(p.k).D.FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToPKCS8DER() []byte {
|
||||
res, _ := x509.MarshalPKCS8PrivateKey((*ecdsa.PrivateKey)(p))
|
||||
res, _ := x509.MarshalPKCS8PrivateKey(p.k)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -91,33 +99,35 @@ func (p *PrivateKey) ToPKCS8PEM() string {
|
||||
}))
|
||||
}
|
||||
|
||||
/*
|
||||
Note: signatures for the crypto.SigningPrivateKey interface assumes SHA256,
|
||||
which should be correct almost always. If there is a need to use a different
|
||||
hash function, we can add separate functions that have that flexibility.
|
||||
*/
|
||||
// The default signing hash is SHA-256.
|
||||
func (p *PrivateKey) SignToBytes(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
func (p *PrivateKey) SignToBytes(message []byte) ([]byte, error) {
|
||||
// Hash the message with SHA-256
|
||||
hash := sha256.Sum256(message)
|
||||
hasher := params.HashOrDefault(crypto.SHA256).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
r, s, err := ecdsa.Sign(rand.Reader, (*ecdsa.PrivateKey)(p), hash[:])
|
||||
r, s, err := ecdsa.Sign(rand.Reader, p.k, hash[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig := make([]byte, 64)
|
||||
r.FillBytes(sig[:32])
|
||||
s.FillBytes(sig[32:])
|
||||
sig := make([]byte, SignatureBytesSize)
|
||||
r.FillBytes(sig[:SignatureBytesSize/2])
|
||||
s.FillBytes(sig[SignatureBytesSize/2:])
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
func (p *PrivateKey) SignToASN1(message []byte) ([]byte, error) {
|
||||
// Hash the message with SHA-256
|
||||
hash := sha256.Sum256(message)
|
||||
// The default signing hash is SHA-256.
|
||||
func (p *PrivateKey) SignToASN1(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
return ecdsa.SignASN1(rand.Reader, (*ecdsa.PrivateKey)(p), hash[:])
|
||||
hasher := params.HashOrDefault(crypto.SHA256).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
return ecdsa.SignASN1(rand.Reader, p.k, hash[:])
|
||||
}
|
||||
|
||||
func (p *PrivateKey) PublicKeyIsCompatible(remote crypto.PublicKey) bool {
|
||||
@@ -130,11 +140,11 @@ func (p *PrivateKey) PublicKeyIsCompatible(remote crypto.PublicKey) bool {
|
||||
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()
|
||||
ecdhPriv, err := p.k.ECDH()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdhPub, err := (*ecdsa.PublicKey)(remote).ECDH()
|
||||
ecdhPub, err := remote.k.ECDH()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,19 +3,22 @@ package p256
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
helpers "github.com/INFURA/go-did/crypto/_helpers"
|
||||
helpers "github.com/INFURA/go-did/crypto/internal"
|
||||
)
|
||||
|
||||
var _ crypto.SigningPublicKey = (*PublicKey)(nil)
|
||||
var _ crypto.PublicKeySigningBytes = &PublicKey{}
|
||||
var _ crypto.PublicKeySigningASN1 = &PublicKey{}
|
||||
var _ crypto.PublicKeyToBytes = &PublicKey{}
|
||||
|
||||
type PublicKey ecdsa.PublicKey
|
||||
type PublicKey struct {
|
||||
k *ecdsa.PublicKey
|
||||
}
|
||||
|
||||
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
|
||||
// This compact serialization format is the raw key material, without metadata or structure.
|
||||
@@ -28,7 +31,21 @@ func PublicKeyFromBytes(b []byte) (*PublicKey, error) {
|
||||
if x == nil {
|
||||
return nil, fmt.Errorf("invalid P-256 public key")
|
||||
}
|
||||
return (*PublicKey)(&ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}), nil
|
||||
return &PublicKey{k: &ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromXY converts x and y coordinates into a PublicKey.
|
||||
func PublicKeyFromXY(x, y []byte) (*PublicKey, error) {
|
||||
pub := &PublicKey{k: &ecdsa.PublicKey{
|
||||
Curve: elliptic.P256(),
|
||||
X: new(big.Int).SetBytes(x),
|
||||
Y: new(big.Int).SetBytes(y),
|
||||
}}
|
||||
|
||||
if !elliptic.P256().IsOnCurve(pub.k.X, pub.k.Y) {
|
||||
return nil, fmt.Errorf("invalid P-256 public key")
|
||||
}
|
||||
return pub, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromPublicKeyMultibase decodes the public key from its Multibase form
|
||||
@@ -49,8 +66,11 @@ func PublicKeyFromX509DER(bytes []byte) (*PublicKey, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdsaPub := pub.(*ecdsa.PublicKey)
|
||||
return (*PublicKey)(ecdsaPub), nil
|
||||
ecdsaPub, ok := pub.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid public key")
|
||||
}
|
||||
return &PublicKey{k: ecdsaPub}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromX509PEM decodes an X.509 PEM (string) encoded public key.
|
||||
@@ -65,26 +85,38 @@ func PublicKeyFromX509PEM(str string) (*PublicKey, error) {
|
||||
return PublicKeyFromX509DER(block.Bytes)
|
||||
}
|
||||
|
||||
func (p *PublicKey) XBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [coordinateSize]byte
|
||||
(p.k).X.FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PublicKey) YBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [coordinateSize]byte
|
||||
(p.k).Y.FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PublicKey) Equal(other crypto.PublicKey) bool {
|
||||
if other, ok := other.(*PublicKey); ok {
|
||||
return (*ecdsa.PublicKey)(p).Equal((*ecdsa.PublicKey)(other))
|
||||
return p.k.Equal(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToBytes() []byte {
|
||||
ecdsaPub := (*ecdsa.PublicKey)(p)
|
||||
return elliptic.MarshalCompressed(elliptic.P256(), ecdsaPub.X, ecdsaPub.Y)
|
||||
return elliptic.MarshalCompressed(elliptic.P256(), p.k.X, p.k.Y)
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToPublicKeyMultibase() string {
|
||||
ecdsaPub := (*ecdsa.PublicKey)(p)
|
||||
bytes := elliptic.MarshalCompressed(elliptic.P256(), ecdsaPub.X, ecdsaPub.Y)
|
||||
bytes := elliptic.MarshalCompressed(elliptic.P256(), p.k.X, p.k.Y)
|
||||
return helpers.PublicKeyMultibaseEncode(MultibaseCode, bytes)
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToX509DER() []byte {
|
||||
res, _ := x509.MarshalPKIXPublicKey((*ecdsa.PublicKey)(p))
|
||||
res, _ := x509.MarshalPKIXPublicKey(p.k)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -96,29 +128,29 @@ func (p *PublicKey) ToX509PEM() string {
|
||||
}))
|
||||
}
|
||||
|
||||
/*
|
||||
Note: signatures for the crypto.SigningPrivateKey interface assumes SHA256,
|
||||
which should be correct almost always. If there is a need to use a different
|
||||
hash function, we can add separate functions that have that flexibility.
|
||||
*/
|
||||
|
||||
func (p *PublicKey) VerifyBytes(message, signature []byte) bool {
|
||||
// The default signing hash is SHA-256.
|
||||
func (p *PublicKey) VerifyBytes(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
if len(signature) != SignatureBytesSize {
|
||||
return false
|
||||
}
|
||||
|
||||
// Hash the message with SHA-256
|
||||
hash := sha256.Sum256(message)
|
||||
// For some reason, the go crypto library in ecdsa.Verify() encodes the signature as ASN.1 to then decode it.
|
||||
// This means it's actually more efficient to encode the signature as ASN.1 here.
|
||||
sigAsn1, err := helpers.EncodeSignatureToASN1(signature[:SignatureBytesSize/2], signature[SignatureBytesSize/2:])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
r := new(big.Int).SetBytes(signature[:32])
|
||||
s := new(big.Int).SetBytes(signature[32:])
|
||||
|
||||
return ecdsa.Verify((*ecdsa.PublicKey)(p), hash[:], r, s)
|
||||
return p.VerifyASN1(message, sigAsn1, opts...)
|
||||
}
|
||||
|
||||
func (p *PublicKey) VerifyASN1(message, signature []byte) bool {
|
||||
// Hash the message with SHA-256
|
||||
hash := sha256.Sum256(message)
|
||||
// The default signing hash is SHA-256.
|
||||
func (p *PublicKey) VerifyASN1(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
return ecdsa.VerifyASN1((*ecdsa.PublicKey)(p), hash[:], signature)
|
||||
hasher := params.HashOrDefault(crypto.SHA256).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
return ecdsa.VerifyASN1(p.k, hash[:], signature)
|
||||
}
|
||||
|
||||
35
crypto/p384/key.go
Normal file
35
crypto/p384/key.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package p384
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeyBytesSize is the size, in bytes, of public keys in raw bytes.
|
||||
PublicKeyBytesSize = 1 + coordinateSize
|
||||
// PrivateKeyBytesSize is the size, in bytes, of private keys in raw bytes.
|
||||
PrivateKeyBytesSize = coordinateSize
|
||||
// SignatureBytesSize is the size, in bytes, of signatures in raw bytes.
|
||||
SignatureBytesSize = 2 * coordinateSize
|
||||
|
||||
MultibaseCode = uint64(0x1201)
|
||||
|
||||
// coordinateSize is the size, in bytes, of one coordinate in the elliptic curve.
|
||||
coordinateSize = 48
|
||||
)
|
||||
|
||||
func GenerateKeyPair() (*PublicKey, *PrivateKey, error) {
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pub := priv.Public().(*ecdsa.PublicKey)
|
||||
return &PublicKey{k: pub}, &PrivateKey{k: priv}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
pemPubBlockType = "PUBLIC KEY"
|
||||
pemPrivBlockType = "PRIVATE KEY"
|
||||
)
|
||||
34
crypto/p384/key_test.go
Normal file
34
crypto/p384/key_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package p384
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/_testsuite"
|
||||
)
|
||||
|
||||
var harness = testsuite.TestHarness[*PublicKey, *PrivateKey]{
|
||||
Name: "p384",
|
||||
GenerateKeyPair: GenerateKeyPair,
|
||||
PublicKeyFromBytes: PublicKeyFromBytes,
|
||||
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
|
||||
PublicKeyFromX509DER: PublicKeyFromX509DER,
|
||||
PublicKeyFromX509PEM: PublicKeyFromX509PEM,
|
||||
PrivateKeyFromBytes: PrivateKeyFromBytes,
|
||||
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
|
||||
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
|
||||
MultibaseCode: MultibaseCode,
|
||||
DefaultHash: crypto.SHA384,
|
||||
OtherHashes: []crypto.Hash{crypto.SHA256, crypto.SHA512},
|
||||
PublicKeyBytesSize: PublicKeyBytesSize,
|
||||
PrivateKeyBytesSize: PrivateKeyBytesSize,
|
||||
SignatureBytesSize: SignatureBytesSize,
|
||||
}
|
||||
|
||||
func TestSuite(t *testing.T) {
|
||||
testsuite.TestSuite(t, harness)
|
||||
}
|
||||
|
||||
func BenchmarkSuite(b *testing.B) {
|
||||
testsuite.BenchSuite(b, harness)
|
||||
}
|
||||
155
crypto/p384/private.go
Normal file
155
crypto/p384/private.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package p384
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
)
|
||||
|
||||
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
|
||||
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
|
||||
var _ crypto.PrivateKeyToBytes = &PrivateKey{}
|
||||
var _ crypto.PrivateKeyKeyExchange = &PrivateKey{}
|
||||
|
||||
type PrivateKey struct {
|
||||
k *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
// PrivateKeyFromBytes converts a serialized public key to a PrivateKey.
|
||||
// This compact serialization format is the raw key material, without metadata or structure.
|
||||
// It errors if the slice is not the right size.
|
||||
func PrivateKeyFromBytes(b []byte) (*PrivateKey, error) {
|
||||
if len(b) != PrivateKeyBytesSize {
|
||||
return nil, fmt.Errorf("invalid P-384 private key size")
|
||||
}
|
||||
|
||||
res := &PrivateKey{
|
||||
k: &ecdsa.PrivateKey{
|
||||
D: new(big.Int).SetBytes(b),
|
||||
PublicKey: ecdsa.PublicKey{Curve: elliptic.P384()},
|
||||
},
|
||||
}
|
||||
|
||||
// recompute the public key
|
||||
res.k.PublicKey.X, res.k.PublicKey.Y = res.k.PublicKey.Curve.ScalarBaseMult(b)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8DER decodes a PKCS#8 DER (binary) encoded private key.
|
||||
func PrivateKeyFromPKCS8DER(bytes []byte) (*PrivateKey, error) {
|
||||
priv, err := x509.ParsePKCS8PrivateKey(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdsaPriv, ok := priv.(*ecdsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid private key type")
|
||||
}
|
||||
return &PrivateKey{k: ecdsaPriv}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
|
||||
func PrivateKeyFromPKCS8PEM(str string) (*PrivateKey, error) {
|
||||
block, _ := pem.Decode([]byte(str))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block")
|
||||
}
|
||||
if block.Type != pemPrivBlockType {
|
||||
return nil, fmt.Errorf("incorrect PEM block type")
|
||||
}
|
||||
return PrivateKeyFromPKCS8DER(block.Bytes)
|
||||
}
|
||||
|
||||
func (p *PrivateKey) Equal(other crypto.PrivateKey) bool {
|
||||
if other, ok := other.(*PrivateKey); ok {
|
||||
return p.k.Equal(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PrivateKey) Public() crypto.PublicKey {
|
||||
ecdhPub := p.k.Public().(*ecdsa.PublicKey)
|
||||
return &PublicKey{k: ecdhPub}
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [PrivateKeyBytesSize]byte
|
||||
(p.k).D.FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToPKCS8DER() []byte {
|
||||
res, _ := x509.MarshalPKCS8PrivateKey(p.k)
|
||||
return res
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToPKCS8PEM() string {
|
||||
der := p.ToPKCS8DER()
|
||||
return string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: pemPrivBlockType,
|
||||
Bytes: der,
|
||||
}))
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-384.
|
||||
func (p *PrivateKey) SignToBytes(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hasher := params.HashOrDefault(crypto.SHA384).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
r, s, err := ecdsa.Sign(rand.Reader, p.k, hash[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig := make([]byte, SignatureBytesSize)
|
||||
r.FillBytes(sig[:SignatureBytesSize/2])
|
||||
s.FillBytes(sig[SignatureBytesSize/2:])
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-384.
|
||||
func (p *PrivateKey) SignToASN1(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hasher := params.HashOrDefault(crypto.SHA384).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
return ecdsa.SignASN1(rand.Reader, p.k, 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 := p.k.ECDH()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdhPub, err := remote.k.ECDH()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ecdhPriv.ECDH(ecdhPub)
|
||||
}
|
||||
return nil, fmt.Errorf("incompatible public key")
|
||||
}
|
||||
156
crypto/p384/public.go
Normal file
156
crypto/p384/public.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package p384
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
helpers "github.com/INFURA/go-did/crypto/internal"
|
||||
)
|
||||
|
||||
var _ crypto.PublicKeySigningBytes = &PublicKey{}
|
||||
var _ crypto.PublicKeySigningASN1 = &PublicKey{}
|
||||
var _ crypto.PublicKeyToBytes = &PublicKey{}
|
||||
|
||||
type PublicKey struct {
|
||||
k *ecdsa.PublicKey
|
||||
}
|
||||
|
||||
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
|
||||
// This compact serialization format is the raw key material, without metadata or structure.
|
||||
// It errors if the slice is not the right size.
|
||||
func PublicKeyFromBytes(b []byte) (*PublicKey, error) {
|
||||
if len(b) != PublicKeyBytesSize {
|
||||
return nil, fmt.Errorf("invalid P-384 public key size")
|
||||
}
|
||||
x, y := elliptic.UnmarshalCompressed(elliptic.P384(), b)
|
||||
if x == nil {
|
||||
return nil, fmt.Errorf("invalid P-384 public key")
|
||||
}
|
||||
return &PublicKey{k: &ecdsa.PublicKey{Curve: elliptic.P384(), X: x, Y: y}}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromXY converts x and y coordinates into a PublicKey.
|
||||
func PublicKeyFromXY(x, y []byte) (*PublicKey, error) {
|
||||
pub := &PublicKey{k: &ecdsa.PublicKey{
|
||||
Curve: elliptic.P384(),
|
||||
X: new(big.Int).SetBytes(x),
|
||||
Y: new(big.Int).SetBytes(y),
|
||||
}}
|
||||
|
||||
if !elliptic.P384().IsOnCurve(pub.k.X, pub.k.Y) {
|
||||
return nil, fmt.Errorf("invalid P-384 public key")
|
||||
}
|
||||
return pub, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromPublicKeyMultibase decodes the public key from its Multibase form
|
||||
func PublicKeyFromPublicKeyMultibase(multibase string) (*PublicKey, error) {
|
||||
code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if code != MultibaseCode {
|
||||
return nil, fmt.Errorf("invalid code")
|
||||
}
|
||||
return PublicKeyFromBytes(bytes)
|
||||
}
|
||||
|
||||
// PublicKeyFromX509DER decodes an X.509 DER (binary) encoded public key.
|
||||
func PublicKeyFromX509DER(bytes []byte) (*PublicKey, error) {
|
||||
pub, err := x509.ParsePKIXPublicKey(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdsaPub, ok := pub.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid public key")
|
||||
}
|
||||
return &PublicKey{k: ecdsaPub}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromX509PEM decodes an X.509 PEM (string) encoded public key.
|
||||
func PublicKeyFromX509PEM(str string) (*PublicKey, error) {
|
||||
block, _ := pem.Decode([]byte(str))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block")
|
||||
}
|
||||
if block.Type != pemPubBlockType {
|
||||
return nil, fmt.Errorf("incorrect PEM block type")
|
||||
}
|
||||
return PublicKeyFromX509DER(block.Bytes)
|
||||
}
|
||||
|
||||
func (p *PublicKey) XBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [coordinateSize]byte
|
||||
(p.k).X.FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PublicKey) YBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [coordinateSize]byte
|
||||
(p.k).Y.FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PublicKey) Equal(other crypto.PublicKey) bool {
|
||||
if other, ok := other.(*PublicKey); ok {
|
||||
return p.k.Equal(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToBytes() []byte {
|
||||
return elliptic.MarshalCompressed(elliptic.P384(), p.k.X, p.k.Y)
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToPublicKeyMultibase() string {
|
||||
bytes := elliptic.MarshalCompressed(elliptic.P384(), p.k.X, p.k.Y)
|
||||
return helpers.PublicKeyMultibaseEncode(MultibaseCode, bytes)
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToX509DER() []byte {
|
||||
res, _ := x509.MarshalPKIXPublicKey(p.k)
|
||||
return res
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToX509PEM() string {
|
||||
der := p.ToX509DER()
|
||||
return string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: pemPubBlockType,
|
||||
Bytes: der,
|
||||
}))
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-384.
|
||||
func (p *PublicKey) VerifyBytes(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
if len(signature) != SignatureBytesSize {
|
||||
return false
|
||||
}
|
||||
|
||||
// For some reason, the go crypto library in ecdsa.Verify() encodes the signature as ASN.1 to then decode it.
|
||||
// This means it's actually more efficient to encode the signature as ASN.1 here.
|
||||
sigAsn1, err := helpers.EncodeSignatureToASN1(signature[:SignatureBytesSize/2], signature[SignatureBytesSize/2:])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return p.VerifyASN1(message, sigAsn1, opts...)
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-384.
|
||||
func (p *PublicKey) VerifyASN1(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hasher := params.HashOrDefault(crypto.SHA384).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
return ecdsa.VerifyASN1(p.k, hash[:], signature)
|
||||
}
|
||||
35
crypto/p521/key.go
Normal file
35
crypto/p521/key.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package p521
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeyBytesSize is the size, in bytes, of public keys in raw bytes.
|
||||
PublicKeyBytesSize = 1 + coordinateSize
|
||||
// PrivateKeyBytesSize is the size, in bytes, of private keys in raw bytes.
|
||||
PrivateKeyBytesSize = coordinateSize
|
||||
// SignatureBytesSize is the size, in bytes, of signatures in raw bytes.
|
||||
SignatureBytesSize = 2 * coordinateSize
|
||||
|
||||
MultibaseCode = uint64(0x1202)
|
||||
|
||||
// coordinateSize is the size, in bytes, of one coordinate in the elliptic curve.
|
||||
coordinateSize = 66
|
||||
)
|
||||
|
||||
func GenerateKeyPair() (*PublicKey, *PrivateKey, error) {
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pub := priv.Public().(*ecdsa.PublicKey)
|
||||
return &PublicKey{k: pub}, &PrivateKey{k: priv}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
pemPubBlockType = "PUBLIC KEY"
|
||||
pemPrivBlockType = "PRIVATE KEY"
|
||||
)
|
||||
34
crypto/p521/key_test.go
Normal file
34
crypto/p521/key_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package p521
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/_testsuite"
|
||||
)
|
||||
|
||||
var harness = testsuite.TestHarness[*PublicKey, *PrivateKey]{
|
||||
Name: "p521",
|
||||
GenerateKeyPair: GenerateKeyPair,
|
||||
PublicKeyFromBytes: PublicKeyFromBytes,
|
||||
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
|
||||
PublicKeyFromX509DER: PublicKeyFromX509DER,
|
||||
PublicKeyFromX509PEM: PublicKeyFromX509PEM,
|
||||
PrivateKeyFromBytes: PrivateKeyFromBytes,
|
||||
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
|
||||
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
|
||||
MultibaseCode: MultibaseCode,
|
||||
DefaultHash: crypto.SHA512,
|
||||
OtherHashes: []crypto.Hash{crypto.SHA384},
|
||||
PublicKeyBytesSize: PublicKeyBytesSize,
|
||||
PrivateKeyBytesSize: PrivateKeyBytesSize,
|
||||
SignatureBytesSize: SignatureBytesSize,
|
||||
}
|
||||
|
||||
func TestSuite(t *testing.T) {
|
||||
testsuite.TestSuite(t, harness)
|
||||
}
|
||||
|
||||
func BenchmarkSuite(b *testing.B) {
|
||||
testsuite.BenchSuite(b, harness)
|
||||
}
|
||||
155
crypto/p521/private.go
Normal file
155
crypto/p521/private.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package p521
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
)
|
||||
|
||||
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
|
||||
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
|
||||
var _ crypto.PrivateKeyToBytes = &PrivateKey{}
|
||||
var _ crypto.PrivateKeyKeyExchange = &PrivateKey{}
|
||||
|
||||
type PrivateKey struct {
|
||||
k *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
// PrivateKeyFromBytes converts a serialized public key to a PrivateKey.
|
||||
// This compact serialization format is the raw key material, without metadata or structure.
|
||||
// It errors if the slice is not the right size.
|
||||
func PrivateKeyFromBytes(b []byte) (*PrivateKey, error) {
|
||||
if len(b) != PrivateKeyBytesSize {
|
||||
return nil, fmt.Errorf("invalid P-521 private key size")
|
||||
}
|
||||
|
||||
res := &PrivateKey{
|
||||
k: &ecdsa.PrivateKey{
|
||||
D: new(big.Int).SetBytes(b),
|
||||
PublicKey: ecdsa.PublicKey{Curve: elliptic.P521()},
|
||||
},
|
||||
}
|
||||
|
||||
// recompute the public key
|
||||
res.k.PublicKey.X, res.k.PublicKey.Y = res.k.PublicKey.Curve.ScalarBaseMult(b)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8DER decodes a PKCS#8 DER (binary) encoded private key.
|
||||
func PrivateKeyFromPKCS8DER(bytes []byte) (*PrivateKey, error) {
|
||||
priv, err := x509.ParsePKCS8PrivateKey(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdsaPriv, ok := priv.(*ecdsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid private key type")
|
||||
}
|
||||
return &PrivateKey{k: ecdsaPriv}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
|
||||
func PrivateKeyFromPKCS8PEM(str string) (*PrivateKey, error) {
|
||||
block, _ := pem.Decode([]byte(str))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block")
|
||||
}
|
||||
if block.Type != pemPrivBlockType {
|
||||
return nil, fmt.Errorf("incorrect PEM block type")
|
||||
}
|
||||
return PrivateKeyFromPKCS8DER(block.Bytes)
|
||||
}
|
||||
|
||||
func (p *PrivateKey) Equal(other crypto.PrivateKey) bool {
|
||||
if other, ok := other.(*PrivateKey); ok {
|
||||
return p.k.Equal(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PrivateKey) Public() crypto.PublicKey {
|
||||
ecdhPub := p.k.Public().(*ecdsa.PublicKey)
|
||||
return &PublicKey{k: ecdhPub}
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [PrivateKeyBytesSize]byte
|
||||
(p.k).D.FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToPKCS8DER() []byte {
|
||||
res, _ := x509.MarshalPKCS8PrivateKey(p.k)
|
||||
return res
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToPKCS8PEM() string {
|
||||
der := p.ToPKCS8DER()
|
||||
return string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: pemPrivBlockType,
|
||||
Bytes: der,
|
||||
}))
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-512.
|
||||
func (p *PrivateKey) SignToBytes(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hasher := params.HashOrDefault(crypto.SHA512).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
r, s, err := ecdsa.Sign(rand.Reader, p.k, hash[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig := make([]byte, SignatureBytesSize)
|
||||
r.FillBytes(sig[:SignatureBytesSize/2])
|
||||
s.FillBytes(sig[SignatureBytesSize/2:])
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-512.
|
||||
func (p *PrivateKey) SignToASN1(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hasher := params.HashOrDefault(crypto.SHA512).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
return ecdsa.SignASN1(rand.Reader, p.k, 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 := p.k.ECDH()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdhPub, err := remote.k.ECDH()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ecdhPriv.ECDH(ecdhPub)
|
||||
}
|
||||
return nil, fmt.Errorf("incompatible public key")
|
||||
}
|
||||
156
crypto/p521/public.go
Normal file
156
crypto/p521/public.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package p521
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
helpers "github.com/INFURA/go-did/crypto/internal"
|
||||
)
|
||||
|
||||
var _ crypto.PublicKeySigningBytes = &PublicKey{}
|
||||
var _ crypto.PublicKeySigningASN1 = &PublicKey{}
|
||||
var _ crypto.PublicKeyToBytes = &PublicKey{}
|
||||
|
||||
type PublicKey struct {
|
||||
k *ecdsa.PublicKey
|
||||
}
|
||||
|
||||
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
|
||||
// This compact serialization format is the raw key material, without metadata or structure.
|
||||
// It errors if the slice is not the right size.
|
||||
func PublicKeyFromBytes(b []byte) (*PublicKey, error) {
|
||||
if len(b) != PublicKeyBytesSize {
|
||||
return nil, fmt.Errorf("invalid P-521 public key size")
|
||||
}
|
||||
x, y := elliptic.UnmarshalCompressed(elliptic.P521(), b)
|
||||
if x == nil {
|
||||
return nil, fmt.Errorf("invalid P-521 public key")
|
||||
}
|
||||
return &PublicKey{k: &ecdsa.PublicKey{Curve: elliptic.P521(), X: x, Y: y}}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromXY converts x and y coordinates into a PublicKey.
|
||||
func PublicKeyFromXY(x, y []byte) (*PublicKey, error) {
|
||||
pub := &PublicKey{k: &ecdsa.PublicKey{
|
||||
Curve: elliptic.P521(),
|
||||
X: new(big.Int).SetBytes(x),
|
||||
Y: new(big.Int).SetBytes(y),
|
||||
}}
|
||||
|
||||
if !elliptic.P521().IsOnCurve(pub.k.X, pub.k.Y) {
|
||||
return nil, fmt.Errorf("invalid P-521 public key")
|
||||
}
|
||||
return pub, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromPublicKeyMultibase decodes the public key from its Multibase form
|
||||
func PublicKeyFromPublicKeyMultibase(multibase string) (*PublicKey, error) {
|
||||
code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if code != MultibaseCode {
|
||||
return nil, fmt.Errorf("invalid code")
|
||||
}
|
||||
return PublicKeyFromBytes(bytes)
|
||||
}
|
||||
|
||||
// PublicKeyFromX509DER decodes an X.509 DER (binary) encoded public key.
|
||||
func PublicKeyFromX509DER(bytes []byte) (*PublicKey, error) {
|
||||
pub, err := x509.ParsePKIXPublicKey(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdsaPub, ok := pub.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid public key")
|
||||
}
|
||||
return &PublicKey{k: ecdsaPub}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromX509PEM decodes an X.509 PEM (string) encoded public key.
|
||||
func PublicKeyFromX509PEM(str string) (*PublicKey, error) {
|
||||
block, _ := pem.Decode([]byte(str))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block")
|
||||
}
|
||||
if block.Type != pemPubBlockType {
|
||||
return nil, fmt.Errorf("incorrect PEM block type")
|
||||
}
|
||||
return PublicKeyFromX509DER(block.Bytes)
|
||||
}
|
||||
|
||||
func (p *PublicKey) XBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [coordinateSize]byte
|
||||
(p.k).X.FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PublicKey) YBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [coordinateSize]byte
|
||||
(p.k).Y.FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PublicKey) Equal(other crypto.PublicKey) bool {
|
||||
if other, ok := other.(*PublicKey); ok {
|
||||
return p.k.Equal(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToBytes() []byte {
|
||||
return elliptic.MarshalCompressed(elliptic.P521(), p.k.X, p.k.Y)
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToPublicKeyMultibase() string {
|
||||
bytes := elliptic.MarshalCompressed(elliptic.P521(), p.k.X, p.k.Y)
|
||||
return helpers.PublicKeyMultibaseEncode(MultibaseCode, bytes)
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToX509DER() []byte {
|
||||
res, _ := x509.MarshalPKIXPublicKey(p.k)
|
||||
return res
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToX509PEM() string {
|
||||
der := p.ToX509DER()
|
||||
return string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: pemPubBlockType,
|
||||
Bytes: der,
|
||||
}))
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-512.
|
||||
func (p *PublicKey) VerifyBytes(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
if len(signature) != SignatureBytesSize {
|
||||
return false
|
||||
}
|
||||
|
||||
// For some reason, the go crypto library in ecdsa.Verify() encodes the signature as ASN.1 to then decode it.
|
||||
// This means it's actually more efficient to encode the signature as ASN.1 here.
|
||||
sigAsn1, err := helpers.EncodeSignatureToASN1(signature[:SignatureBytesSize/2], signature[SignatureBytesSize/2:])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return p.VerifyASN1(message, sigAsn1, opts...)
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-512.
|
||||
func (p *PublicKey) VerifyASN1(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hasher := params.HashOrDefault(crypto.SHA512).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
return ecdsa.VerifyASN1(p.k, hash[:], signature)
|
||||
}
|
||||
43
crypto/rsa/key.go
Normal file
43
crypto/rsa/key.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package rsa
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
)
|
||||
|
||||
const (
|
||||
MultibaseCode = uint64(0x1205)
|
||||
|
||||
MinRsaKeyBits = 2048
|
||||
MaxRsaKeyBits = 8192
|
||||
)
|
||||
|
||||
func GenerateKeyPair(bits int) (*PublicKey, *PrivateKey, error) {
|
||||
if bits < MinRsaKeyBits || bits > MaxRsaKeyBits {
|
||||
return nil, nil, fmt.Errorf("invalid key size: %d", bits)
|
||||
}
|
||||
priv, err := rsa.GenerateKey(rand.Reader, bits)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &PublicKey{k: &priv.PublicKey}, &PrivateKey{k: priv}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
pemPubBlockType = "PUBLIC KEY"
|
||||
pemPrivBlockType = "PRIVATE KEY"
|
||||
)
|
||||
|
||||
func defaultSigHash(keyLen int) crypto.Hash {
|
||||
switch {
|
||||
case keyLen <= 2048:
|
||||
return crypto.SHA256
|
||||
case keyLen <= 3072:
|
||||
return crypto.SHA384
|
||||
default:
|
||||
return crypto.SHA512
|
||||
}
|
||||
}
|
||||
164
crypto/rsa/key_test.go
Normal file
164
crypto/rsa/key_test.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package rsa
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/_testsuite"
|
||||
)
|
||||
|
||||
var harness2048 = testsuite.TestHarness[*PublicKey, *PrivateKey]{
|
||||
Name: "rsa-2048",
|
||||
GenerateKeyPair: func() (*PublicKey, *PrivateKey, error) { return GenerateKeyPair(2048) },
|
||||
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
|
||||
PublicKeyFromX509DER: PublicKeyFromX509DER,
|
||||
PublicKeyFromX509PEM: PublicKeyFromX509PEM,
|
||||
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
|
||||
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
|
||||
MultibaseCode: MultibaseCode,
|
||||
DefaultHash: crypto.SHA256,
|
||||
OtherHashes: []crypto.Hash{crypto.SHA384, crypto.SHA512},
|
||||
}
|
||||
|
||||
var harness3072 = testsuite.TestHarness[*PublicKey, *PrivateKey]{
|
||||
Name: "rsa-3072",
|
||||
GenerateKeyPair: func() (*PublicKey, *PrivateKey, error) { return GenerateKeyPair(3072) },
|
||||
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
|
||||
PublicKeyFromX509DER: PublicKeyFromX509DER,
|
||||
PublicKeyFromX509PEM: PublicKeyFromX509PEM,
|
||||
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
|
||||
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
|
||||
MultibaseCode: MultibaseCode,
|
||||
DefaultHash: crypto.SHA384,
|
||||
OtherHashes: []crypto.Hash{crypto.SHA512},
|
||||
}
|
||||
|
||||
var harness4096 = testsuite.TestHarness[*PublicKey, *PrivateKey]{
|
||||
Name: "rsa-4096",
|
||||
GenerateKeyPair: func() (*PublicKey, *PrivateKey, error) { return GenerateKeyPair(4096) },
|
||||
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
|
||||
PublicKeyFromX509DER: PublicKeyFromX509DER,
|
||||
PublicKeyFromX509PEM: PublicKeyFromX509PEM,
|
||||
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
|
||||
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
|
||||
MultibaseCode: MultibaseCode,
|
||||
DefaultHash: crypto.SHA512,
|
||||
OtherHashes: []crypto.Hash{},
|
||||
}
|
||||
|
||||
func TestSuite2048(t *testing.T) {
|
||||
testsuite.TestSuite(t, harness2048)
|
||||
}
|
||||
|
||||
func TestSuite3072(t *testing.T) {
|
||||
testsuite.TestSuite(t, harness3072)
|
||||
}
|
||||
|
||||
func TestSuite4096(t *testing.T) {
|
||||
testsuite.TestSuite(t, harness4096)
|
||||
}
|
||||
|
||||
func BenchmarkSuite2048(b *testing.B) {
|
||||
testsuite.BenchSuite(b, harness2048)
|
||||
}
|
||||
|
||||
func BenchmarkSuite3072(b *testing.B) {
|
||||
testsuite.BenchSuite(b, harness3072)
|
||||
}
|
||||
|
||||
func BenchmarkSuite4096(b *testing.B) {
|
||||
testsuite.BenchSuite(b, harness4096)
|
||||
}
|
||||
|
||||
func TestPublicKeyX509(t *testing.T) {
|
||||
// openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
|
||||
// openssl pkey -in private_key.pem -pubout -out public_key.pem
|
||||
pem := `-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyLFQUbVVo/rctJaCzR5z
|
||||
g622eUNBwZmA1vnDEXnHWBl3y5RJF5zyTdlouujjmEuu6qsXk1NCNQ3dLH2iquI8
|
||||
iFFAhS4kTX6JS+wR3vHLhga1oFkPceGFEUG/3vxn52ozFs8hikhq/P09HmLub7Vc
|
||||
VklwrGvTbEa5Fn/2Kz6olw5ExYI14Unsl+A3iw8AXPL9/acD+ehoyx3/zKFrVTKx
|
||||
e9jdoWX8L7IpqM2HOSu23/3E2IwH2GdY0C8575AiD/O555hie7JHkzF3I4E85gPd
|
||||
ZgXYFShIfgOzDV0q4oP0pzqYkErhdjOpigCMjDuIC4OueZYqYJrP2rdpzuqoqk07
|
||||
NwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
|
||||
pub, err := PublicKeyFromX509PEM(pem)
|
||||
require.NoError(t, err)
|
||||
|
||||
rt := pub.ToX509PEM()
|
||||
require.Equal(t, pem, rt)
|
||||
}
|
||||
|
||||
func TestPrivateKeyPKCS8(t *testing.T) {
|
||||
// openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
|
||||
pem := `-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDIsVBRtVWj+ty0
|
||||
loLNHnODrbZ5Q0HBmYDW+cMRecdYGXfLlEkXnPJN2Wi66OOYS67qqxeTU0I1Dd0s
|
||||
faKq4jyIUUCFLiRNfolL7BHe8cuGBrWgWQ9x4YURQb/e/GfnajMWzyGKSGr8/T0e
|
||||
Yu5vtVxWSXCsa9NsRrkWf/YrPqiXDkTFgjXhSeyX4DeLDwBc8v39pwP56GjLHf/M
|
||||
oWtVMrF72N2hZfwvsimozYc5K7bf/cTYjAfYZ1jQLznvkCIP87nnmGJ7skeTMXcj
|
||||
gTzmA91mBdgVKEh+A7MNXSrig/SnOpiQSuF2M6mKAIyMO4gLg655lipgms/at2nO
|
||||
6qiqTTs3AgMBAAECggEAVFVqZoN4QumSYBKVUYOX0AAp2ygflC6gnPWkeo39bjB5
|
||||
jiM4WcNacMtIvq5JoYBANx2BUSfd/PRf+ierOPrLrA7UuYJLwALJyA0h71kVCLN+
|
||||
FC0Il/bIF5nU+mt/cBfI8y9ELVtEFh6GVeQFxQxlil7fCZ1f4TKQ6XsJI1/3sU2P
|
||||
hbOuyfKKiWym8n5BV6NP3gotjnT01I+seplx3oMOKIaGl0KMgkuU2r8o8WMjA7Gx
|
||||
1WWPJDpUdyYDYSUH8PubXowHkE+2RXddZ+tGvS8mF/A4Q0hdj2T9XvzyZ813O9Tv
|
||||
n522A9QQE8YlqwAYh4z3VoNhz+Fi1mQfYsIblNygSQKBgQDrk+kB/dz92RPhP/rh
|
||||
zAOvwRuI2TOaw98kdgpVlb6gMVmN2EWkzkdnwQDJhV+MFZob4wi+TpsDPv4fjubq
|
||||
gqbM/MYc0kNtIEA4GkIJLCK5Hh7c6kCQfya+/eq4Ju6C3+I4R46/+9E7ixA83Zjf
|
||||
ftqTlYOrlMby84Lvsf81LtiMiQKBgQDaFzXpDBPOIaup68k9NeZyXHKI8wNQXkui
|
||||
JyjM9A3U2D8O9Yty8G+Oq0B4oUGlyenMGJiQmf3bAffJBkLCMXCGXYD8CCKsiSJ6
|
||||
R6XBfbpPkzCwl67FFN/8Z0nxZ0lbxd2ZMTC4qxH4peD5TNZM89kTpSNXPrr55zzm
|
||||
qREmxisZvwKBgQCNK3jBScjpkfFY1UdZkjFPXDBM5KQJBYGtztLIkNDIHGqnFsg9
|
||||
R6QAp+b53GPyhWtxdK7jpCU+X7xXWwJD3AFq67sowFPJjD8Pn6Sc7IbuWf9ysSn5
|
||||
rUihwXWr3yCk6tcclL0VjSjIPsB/SOf4XoNLV5is9J34Lzbyvr7JtwXryQKBgQCM
|
||||
m3xRdUzrkD/J/M+w3ChoQPxDGVJgpXrj35Vplku4l3cIYPz4LNXvyK93VpgpmGVZ
|
||||
Bd6PFAlcAwfLHnM6Gn/u0SgQ1fns/TkyVzEh77qIBWDV6eVvAQdsBvfgYPQl7Arz
|
||||
8ofz969NfTzv3j8oO+sPxF9lp3cLGa/lEsmREyDEpwKBgQCvW+NK93oajo358gKh
|
||||
/xfSv7yMiSL26NcIgHmQouZVXJ3Dg0KSISx8tgY0/7TwC2mPa0Ryhpb/3HtAIXoY
|
||||
eqkQGHqnC4voxSoati667mMGdHL1+12WvQmhfTLCWmZ5ccNlR+aFD20TGbMxnejS
|
||||
XnARctVkIcUYORcYwvuu9meDkw==
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
|
||||
priv, err := PrivateKeyFromPKCS8PEM(pem)
|
||||
require.NoError(t, err)
|
||||
|
||||
rt := priv.ToPKCS8PEM()
|
||||
require.Equal(t, pem, rt)
|
||||
}
|
||||
|
||||
func TestSignatureASN1(t *testing.T) {
|
||||
// openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
|
||||
// openssl pkey -in private.pem -pubout -out public.pem
|
||||
// echo -n "message" | openssl dgst -sha256 -sign private.pem -out signature.der
|
||||
// echo -n "message" | openssl dgst -sha256 -verify public.pem -signature signature.der
|
||||
|
||||
pubPem := `-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmtKXCTkUDcKbZGsEEUTo
|
||||
16xblCyh6zmA8pXVGgmC66QiTNdKzxdwTykXTDs9sEre9ea34h2M7dwrA1weAmBu
|
||||
grAXe0QmIXIqjFKRKdfty09yWVtKF7FGwEMlhKftWC225R+tRuLwbKG4cCSzHxcf
|
||||
JfqCYqGDM7BrF39ilQzFYw5sUiWn3ppRPWa2oEV3cw19zFnHMbEHIQIdFyCcIv5x
|
||||
GUSJ6sJVp0YvsODsZbA+Zyb2UMRfXD8fDHm9bJQCY0x/wGJLfvJmWtZLciwc145U
|
||||
BN3SezY30NviZtZBKWjXgb6gL69L94U10/8ghmA30DY7bKs4+/7R2nOw91CO4rCo
|
||||
1QIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
pub, err := PublicKeyFromX509PEM(pubPem)
|
||||
require.NoError(t, err)
|
||||
|
||||
b64sig := `BdvBkZWxIVE2mfM48H1WlOs3k9NzyS4oUxAMOZWNNTYDU6+DLbhZ7Hnt3rRKX3m6f1cX5DCsHcPC
|
||||
6sNtsR8Xp9u09GWCN/K28fF7Pcl0E87MdhAUL7jKNK5bb1XWx/GCUmoKXRZiR/gA10iB2Lmjd1MC
|
||||
HItTCig91gmFm5PO67u9yM+cqE2nGyOh13/kT5Np9MUyaE9dkjoQGum23Ta6m7v0atWsPhO5aVVI
|
||||
76vLwGhYAhQe22RxBlPRXyRInr0EnVgHQOe211o//erPZYQAm+N1kK+yjV8NbPxJX+r5sYUE19NL
|
||||
MCB+kOgWk51uJwuiuHlffGMBPxku/t+skxI7Bw==`
|
||||
sig, err := base64.StdEncoding.DecodeString(b64sig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, pub.VerifyASN1([]byte("message"), sig))
|
||||
}
|
||||
164
crypto/rsa/private.go
Normal file
164
crypto/rsa/private.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package rsa
|
||||
|
||||
import (
|
||||
stdcrypto "crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
)
|
||||
|
||||
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
|
||||
|
||||
type PrivateKey struct {
|
||||
k *rsa.PrivateKey
|
||||
}
|
||||
|
||||
func PrivateKeyFromNEDPQ(n, e, d, p, q []byte) (*PrivateKey, error) {
|
||||
pub, err := PublicKeyFromNE(n, e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dBInt := new(big.Int).SetBytes(d)
|
||||
pBInt := new(big.Int).SetBytes(p)
|
||||
qBInt := new(big.Int).SetBytes(q)
|
||||
|
||||
priv := &rsa.PrivateKey{
|
||||
PublicKey: *pub.k,
|
||||
D: dBInt,
|
||||
Primes: []*big.Int{pBInt, qBInt},
|
||||
}
|
||||
|
||||
err = priv.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
priv.Precompute()
|
||||
|
||||
return &PrivateKey{k: priv}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8DER decodes a PKCS#8 DER (binary) encoded private key.
|
||||
func PrivateKeyFromPKCS8DER(bytes []byte) (*PrivateKey, error) {
|
||||
priv, err := x509.ParsePKCS8PrivateKey(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsaPriv, ok := priv.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid private key type")
|
||||
}
|
||||
return &PrivateKey{k: rsaPriv}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
|
||||
func PrivateKeyFromPKCS8PEM(str string) (*PrivateKey, error) {
|
||||
block, _ := pem.Decode([]byte(str))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block")
|
||||
}
|
||||
if block.Type != pemPrivBlockType {
|
||||
return nil, fmt.Errorf("incorrect PEM block type")
|
||||
}
|
||||
return PrivateKeyFromPKCS8DER(block.Bytes)
|
||||
}
|
||||
|
||||
func (p *PrivateKey) BitLen() int {
|
||||
return p.k.N.BitLen()
|
||||
}
|
||||
|
||||
func (p *PrivateKey) DBytes() []byte {
|
||||
byteLength := (p.k.D.BitLen() + 7) / 8 // Round up to the nearest byte
|
||||
buf := make([]byte, byteLength)
|
||||
p.k.D.FillBytes(buf)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p *PrivateKey) PBytes() []byte {
|
||||
byteLength := (p.k.Primes[0].BitLen() + 7) / 8 // Round up to the nearest byte
|
||||
buf := make([]byte, byteLength)
|
||||
p.k.Primes[0].FillBytes(buf)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p *PrivateKey) QBytes() []byte {
|
||||
byteLength := (p.k.Primes[1].BitLen() + 7) / 8 // Round up to the nearest byte
|
||||
buf := make([]byte, byteLength)
|
||||
p.k.Primes[1].FillBytes(buf)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p *PrivateKey) DpBytes() []byte {
|
||||
if p.k.Precomputed.Dp == nil {
|
||||
p.k.Precompute()
|
||||
}
|
||||
byteLength := (p.k.Precomputed.Dp.BitLen() + 7) / 8 // Round up to the nearest byte
|
||||
buf := make([]byte, byteLength)
|
||||
p.k.Precomputed.Dp.FillBytes(buf)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p *PrivateKey) DqBytes() []byte {
|
||||
if p.k.Precomputed.Dq == nil {
|
||||
p.k.Precompute()
|
||||
}
|
||||
byteLength := (p.k.Precomputed.Dq.BitLen() + 7) / 8 // Round up to the nearest byte
|
||||
buf := make([]byte, byteLength)
|
||||
p.k.Precomputed.Dq.FillBytes(buf)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p *PrivateKey) QiBytes() []byte {
|
||||
if p.k.Precomputed.Qinv == nil {
|
||||
p.k.Precompute()
|
||||
}
|
||||
byteLength := (p.k.Precomputed.Qinv.BitLen() + 7) / 8 // Round up to the nearest byte
|
||||
buf := make([]byte, byteLength)
|
||||
p.k.Precomputed.Qinv.FillBytes(buf)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p *PrivateKey) Equal(other crypto.PrivateKey) bool {
|
||||
if other, ok := other.(*PrivateKey); ok {
|
||||
return p.k.Equal(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PrivateKey) Public() crypto.PublicKey {
|
||||
rsaPub := p.k.Public().(*rsa.PublicKey)
|
||||
return &PublicKey{k: rsaPub}
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToPKCS8DER() []byte {
|
||||
res, _ := x509.MarshalPKCS8PrivateKey(p.k)
|
||||
return res
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToPKCS8PEM() string {
|
||||
der := p.ToPKCS8DER()
|
||||
return string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: pemPrivBlockType,
|
||||
Bytes: der,
|
||||
}))
|
||||
}
|
||||
|
||||
// SignToASN1 produce a PKCS#1 v1.5 signature.
|
||||
// The default signing hash is:
|
||||
// - SHA-256 for keys of length 2048 bits and under
|
||||
// - SHA-384 for keys of length 3072 bits and under
|
||||
// - SHA-512 for higher key length
|
||||
func (p *PrivateKey) SignToASN1(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hashCode := params.HashOrDefault(defaultSigHash(p.k.N.BitLen()))
|
||||
hasher := hashCode.New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
return rsa.SignPKCS1v15(rand.Reader, p.k, stdcrypto.Hash(hashCode), hash)
|
||||
}
|
||||
148
crypto/rsa/public.go
Normal file
148
crypto/rsa/public.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package rsa
|
||||
|
||||
import (
|
||||
stdcrypto "crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
helpers "github.com/INFURA/go-did/crypto/internal"
|
||||
)
|
||||
|
||||
var _ crypto.PublicKeySigningASN1 = &PublicKey{}
|
||||
|
||||
type PublicKey struct {
|
||||
k *rsa.PublicKey
|
||||
}
|
||||
|
||||
func PublicKeyFromPKCS1DER(bytes []byte) (*PublicKey, error) {
|
||||
pub, err := x509.ParsePKCS1PublicKey(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PublicKey{k: pub}, nil
|
||||
}
|
||||
|
||||
func PublicKeyFromNE(n, e []byte) (*PublicKey, error) {
|
||||
nBInt := new(big.Int).SetBytes(n)
|
||||
// some basic checks
|
||||
if nBInt.Sign() <= 0 {
|
||||
return nil, fmt.Errorf("invalid modulus")
|
||||
}
|
||||
if nBInt.BitLen() < MinRsaKeyBits {
|
||||
return nil, fmt.Errorf("key length too small")
|
||||
}
|
||||
if nBInt.BitLen() > MaxRsaKeyBits {
|
||||
return nil, fmt.Errorf("key length too large")
|
||||
}
|
||||
if nBInt.Bit(0) == 0 {
|
||||
return nil, fmt.Errorf("modulus must be odd")
|
||||
}
|
||||
|
||||
eBInt := new(big.Int).SetBytes(e)
|
||||
// some basic checks
|
||||
if !eBInt.IsInt64() {
|
||||
return nil, fmt.Errorf("invalid exponent")
|
||||
}
|
||||
if eBInt.Sign() <= 0 {
|
||||
return nil, fmt.Errorf("exponent must be positive")
|
||||
}
|
||||
if eBInt.Bit(0) == 0 {
|
||||
return nil, fmt.Errorf("exponent must be odd")
|
||||
}
|
||||
return &PublicKey{k: &rsa.PublicKey{N: nBInt, E: int(eBInt.Int64())}}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromPublicKeyMultibase decodes the public key from its Multibase form
|
||||
func PublicKeyFromPublicKeyMultibase(multibase string) (*PublicKey, error) {
|
||||
code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if code != MultibaseCode {
|
||||
return nil, fmt.Errorf("invalid code")
|
||||
}
|
||||
return PublicKeyFromX509DER(bytes)
|
||||
}
|
||||
|
||||
// PublicKeyFromX509DER decodes an X.509 DER (binary) encoded public key.
|
||||
func PublicKeyFromX509DER(bytes []byte) (*PublicKey, error) {
|
||||
pub, err := x509.ParsePKIXPublicKey(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsaPub, ok := pub.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid public key")
|
||||
}
|
||||
return &PublicKey{k: rsaPub}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromX509PEM decodes an X.509 PEM (string) encoded public key.
|
||||
func PublicKeyFromX509PEM(str string) (*PublicKey, error) {
|
||||
block, _ := pem.Decode([]byte(str))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block")
|
||||
}
|
||||
if block.Type != pemPubBlockType {
|
||||
return nil, fmt.Errorf("incorrect PEM block type")
|
||||
}
|
||||
return PublicKeyFromX509DER(block.Bytes)
|
||||
}
|
||||
|
||||
func (p *PublicKey) BitLen() int {
|
||||
return p.k.N.BitLen()
|
||||
}
|
||||
|
||||
func (p *PublicKey) NBytes() []byte {
|
||||
return p.k.N.Bytes()
|
||||
}
|
||||
|
||||
func (p *PublicKey) EBytes() []byte {
|
||||
return new(big.Int).SetInt64(int64(p.k.E)).Bytes()
|
||||
}
|
||||
|
||||
func (p *PublicKey) Equal(other crypto.PublicKey) bool {
|
||||
if other, ok := other.(*PublicKey); ok {
|
||||
return p.k.Equal(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToPublicKeyMultibase() string {
|
||||
bytes := p.ToX509DER()
|
||||
return helpers.PublicKeyMultibaseEncode(MultibaseCode, bytes)
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToX509DER() []byte {
|
||||
res, _ := x509.MarshalPKIXPublicKey(p.k)
|
||||
return res
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToX509PEM() string {
|
||||
der := p.ToX509DER()
|
||||
return string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: pemPubBlockType,
|
||||
Bytes: der,
|
||||
}))
|
||||
}
|
||||
|
||||
// VerifyASN1 verifies a PKCS#1 v1.5 signature.
|
||||
// The default signing hash is:
|
||||
// - SHA-256 for keys of length 2048 bits and under
|
||||
// - SHA-384 for keys of length 3072 bits and under
|
||||
// - SHA-512 for higher key length
|
||||
func (p *PublicKey) VerifyASN1(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hashCode := params.HashOrDefault(defaultSigHash(p.k.N.BitLen()))
|
||||
hasher := hashCode.New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
err := rsa.VerifyPKCS1v15(p.k, stdcrypto.Hash(hashCode), hash, signature)
|
||||
return err == nil
|
||||
}
|
||||
43
crypto/secp256k1/key.go
Normal file
43
crypto/secp256k1/key.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package secp256k1
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeyBytesSize is the size, in bytes, of public keys in raw bytes.
|
||||
PublicKeyBytesSize = secp256k1.PubKeyBytesLenCompressed
|
||||
// PrivateKeyBytesSize is the size, in bytes, of private keys in raw bytes.
|
||||
PrivateKeyBytesSize = secp256k1.PrivKeyBytesLen
|
||||
// SignatureBytesSize is the size, in bytes, of signatures in raw bytes.
|
||||
SignatureBytesSize = 64
|
||||
|
||||
MultibaseCode = uint64(0xe7)
|
||||
|
||||
// coordinateSize is the size, in bytes, of one coordinate in the elliptic curve.
|
||||
coordinateSize = 32
|
||||
)
|
||||
|
||||
func GenerateKeyPair() (*PublicKey, *PrivateKey, error) {
|
||||
priv, err := secp256k1.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pub := priv.PubKey()
|
||||
return &PublicKey{k: pub}, &PrivateKey{k: priv}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
pemPubBlockType = "PUBLIC KEY"
|
||||
pemPrivBlockType = "PRIVATE KEY"
|
||||
)
|
||||
|
||||
var (
|
||||
// Elliptic curve public key (OID: 1.2.840.10045.2.1)
|
||||
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
|
||||
|
||||
// Curve is secp256k1 (OID: 1.3.132.0.10)
|
||||
oidSecp256k1 = asn1.ObjectIdentifier{1, 3, 132, 0, 10}
|
||||
)
|
||||
105
crypto/secp256k1/key_test.go
Normal file
105
crypto/secp256k1/key_test.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package secp256k1
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/_testsuite"
|
||||
)
|
||||
|
||||
var harness = testsuite.TestHarness[*PublicKey, *PrivateKey]{
|
||||
Name: "secp256k1",
|
||||
GenerateKeyPair: GenerateKeyPair,
|
||||
PublicKeyFromBytes: PublicKeyFromBytes,
|
||||
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
|
||||
PublicKeyFromX509DER: PublicKeyFromX509DER,
|
||||
PublicKeyFromX509PEM: PublicKeyFromX509PEM,
|
||||
PrivateKeyFromBytes: PrivateKeyFromBytes,
|
||||
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
|
||||
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
|
||||
MultibaseCode: MultibaseCode,
|
||||
DefaultHash: crypto.SHA256,
|
||||
OtherHashes: []crypto.Hash{crypto.KECCAK_256},
|
||||
PublicKeyBytesSize: PublicKeyBytesSize,
|
||||
PrivateKeyBytesSize: PrivateKeyBytesSize,
|
||||
SignatureBytesSize: SignatureBytesSize,
|
||||
}
|
||||
|
||||
func TestSuite(t *testing.T) {
|
||||
testsuite.TestSuite(t, harness)
|
||||
}
|
||||
|
||||
func BenchmarkSuite(b *testing.B) {
|
||||
testsuite.BenchSuite(b, harness)
|
||||
}
|
||||
|
||||
func TestPublicKeyX509(t *testing.T) {
|
||||
// openssl ecparam -genkey -name secp256k1 | openssl pkcs8 -topk8 -nocrypt -out secp256k1-key.pem
|
||||
// openssl pkey -in secp256k1-key.pem -pubout -out secp256k1-pubkey.pem
|
||||
pem := `-----BEGIN PUBLIC KEY-----
|
||||
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEFVP6HKjIReiiUgrC+t+FjG5u0PXIoBmN
|
||||
V1MMmoOFfKlrD/HuWUjjlw0mDKZcG7AM7JKPTWMOCcvUR2B8BUO3VQ==
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
|
||||
pub, err := PublicKeyFromX509PEM(pem)
|
||||
require.NoError(t, err)
|
||||
|
||||
rt := pub.ToX509PEM()
|
||||
require.Equal(t, pem, rt)
|
||||
}
|
||||
|
||||
func TestPrivateKeyPKCS8(t *testing.T) {
|
||||
// openssl ecparam -genkey -name secp256k1 | openssl pkcs8 -topk8 -nocrypt -out secp256k1-key.pem
|
||||
pem := `-----BEGIN PRIVATE KEY-----
|
||||
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgZW9JcJ1kN+DW2IFgqKJu
|
||||
KS+39/xVa0n2J+lCr7hYGTihRANCAAQVU/ocqMhF6KJSCsL634WMbm7Q9cigGY1X
|
||||
Uwyag4V8qWsP8e5ZSOOXDSYMplwbsAzsko9NYw4Jy9RHYHwFQ7dV
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
|
||||
priv, err := PrivateKeyFromPKCS8PEM(pem)
|
||||
require.NoError(t, err)
|
||||
|
||||
rt := priv.ToPKCS8PEM()
|
||||
require.Equal(t, pem, rt)
|
||||
}
|
||||
|
||||
func FuzzPrivateKeyFromPKCS8PEM(f *testing.F) {
|
||||
f.Add(`-----BEGIN PRIVATE KEY-----
|
||||
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgZW9JcJ1kN+DW2IFgqKJu
|
||||
KS+39/xVa0n2J+lCr7hYGTihRANCAAQVU/ocqMhF6KJSCsL634WMbm7Q9cigGY1X
|
||||
Uwyag4V8qWsP8e5ZSOOXDSYMplwbsAzsko9NYw4Jy9RHYHwFQ7dV
|
||||
-----END PRIVATE KEY-----
|
||||
`)
|
||||
|
||||
f.Fuzz(func(t *testing.T, data string) {
|
||||
// looking for panics
|
||||
_, _ = PrivateKeyFromPKCS8PEM(data)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSignatureASN1(t *testing.T) {
|
||||
// openssl ecparam -genkey -name secp256k1 -noout -out private.pem
|
||||
// openssl ec -in private.pem -pubout -out public.pem
|
||||
// echo -n "message" | openssl dgst -sha256 -sign private.pem -out signature.der
|
||||
// echo -n "message" | openssl dgst -sha256 -verify public.pem -signature signature.der
|
||||
|
||||
pubPem := `-----BEGIN PUBLIC KEY-----
|
||||
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEszL1+ZFqUMAHjLAyzMW7xMBPZek/8cNj
|
||||
1qI7EgQooB3f8Sh7JwvXu8cosRnjjvYVvS7OliRsbvuceCQ7HBC4fA==
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
pub, err := PublicKeyFromX509PEM(pubPem)
|
||||
require.NoError(t, err)
|
||||
|
||||
b64sig := `MEYCIQDv5SLy768FbOafzDlrxIeeoEn7tKpYBSK6WcKaOZ6AJAIhAKXV6VAwiPq4uk9TpGyFN5JK
|
||||
8jZPrQ7hdRR5veKKDX2w`
|
||||
sig, err := base64.StdEncoding.DecodeString(b64sig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, pub.VerifyASN1([]byte("message"), sig))
|
||||
}
|
||||
219
crypto/secp256k1/private.go
Normal file
219
crypto/secp256k1/private.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package secp256k1
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
)
|
||||
|
||||
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
|
||||
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
|
||||
var _ crypto.PrivateKeyKeyExchange = &PrivateKey{}
|
||||
|
||||
type PrivateKey struct {
|
||||
k *secp256k1.PrivateKey
|
||||
}
|
||||
|
||||
// PrivateKeyFromBytes converts a serialized public key to a PrivateKey.
|
||||
// This compact serialization format is the raw key material, without metadata or structure.
|
||||
// It errors if the slice is not the right size.
|
||||
func PrivateKeyFromBytes(b []byte) (*PrivateKey, error) {
|
||||
if len(b) != PrivateKeyBytesSize {
|
||||
return nil, fmt.Errorf("invalid secp256k1 private key size")
|
||||
}
|
||||
return &PrivateKey{k: secp256k1.PrivKeyFromBytes(b)}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8DER decodes a PKCS#8 DER (binary) encoded private key.
|
||||
func PrivateKeyFromPKCS8DER(bytes []byte) (*PrivateKey, error) {
|
||||
// Parse the PKCS#8 structure
|
||||
var pkcs8 struct {
|
||||
Version int
|
||||
Algo pkix.AlgorithmIdentifier
|
||||
PrivateKey []byte
|
||||
}
|
||||
if _, err := asn1.Unmarshal(bytes, &pkcs8); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse PKCS#8 structure: %w", err)
|
||||
}
|
||||
|
||||
// Check if this is an Elliptic curve public key (OID: 1.2.840.10045.2.1)
|
||||
if !pkcs8.Algo.Algorithm.Equal(oidPublicKeyECDSA) {
|
||||
return nil, fmt.Errorf("not an EC private key, got OID: %v", pkcs8.Algo.Algorithm)
|
||||
}
|
||||
|
||||
// Extract the curve OID from parameters
|
||||
var namedCurveOID asn1.ObjectIdentifier
|
||||
if _, err := asn1.Unmarshal(pkcs8.Algo.Parameters.FullBytes, &namedCurveOID); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse curve parameters: %w", err)
|
||||
}
|
||||
|
||||
// Check if the curve is secp256k1 (OID: 1.3.132.0.10)
|
||||
if !namedCurveOID.Equal(oidSecp256k1) {
|
||||
return nil, fmt.Errorf("unsupported curve, expected secp256k1 (1.3.132.0.10), got: %v", namedCurveOID)
|
||||
}
|
||||
|
||||
// Parse the EC private key structure (RFC 5915)
|
||||
var ecPrivKey struct {
|
||||
Version int
|
||||
PrivateKey []byte
|
||||
PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
|
||||
}
|
||||
|
||||
if _, err := asn1.Unmarshal(pkcs8.PrivateKey, &ecPrivKey); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse alliptic curve private key: %w", err)
|
||||
}
|
||||
|
||||
// Validate the EC private key version
|
||||
if ecPrivKey.Version != 1 {
|
||||
return nil, fmt.Errorf("unsupported EC private key version: %d", ecPrivKey.Version)
|
||||
}
|
||||
|
||||
// Validate private key length
|
||||
if len(ecPrivKey.PrivateKey) != PrivateKeyBytesSize {
|
||||
return nil, fmt.Errorf("invalid secp256k1 private key length: %d, expected %d", len(ecPrivKey.PrivateKey), PrivateKeyBytesSize)
|
||||
}
|
||||
|
||||
// Create the secp256k1 private key
|
||||
privKeySecp256k1 := secp256k1.PrivKeyFromBytes(ecPrivKey.PrivateKey)
|
||||
|
||||
return &PrivateKey{k: privKeySecp256k1}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
|
||||
func PrivateKeyFromPKCS8PEM(str string) (*PrivateKey, error) {
|
||||
block, _ := pem.Decode([]byte(str))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block")
|
||||
}
|
||||
if block.Type != pemPrivBlockType {
|
||||
return nil, fmt.Errorf("incorrect PEM block type")
|
||||
}
|
||||
return PrivateKeyFromPKCS8DER(block.Bytes)
|
||||
}
|
||||
|
||||
func (p *PrivateKey) Equal(other crypto.PrivateKey) bool {
|
||||
if other, ok := other.(*PrivateKey); ok {
|
||||
return p.k.PubKey().IsEqual(other.k.PubKey())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PrivateKey) Public() crypto.PublicKey {
|
||||
return &PublicKey{k: p.k.PubKey()}
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToBytes() []byte {
|
||||
return p.k.Serialize()
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToPKCS8DER() []byte {
|
||||
pubkeyBytes := p.k.PubKey().SerializeUncompressed()
|
||||
|
||||
// Create the EC private key structure
|
||||
// This follows RFC 5915 format for EC private keys
|
||||
ecPrivateKey := struct {
|
||||
Version int
|
||||
PrivateKey []byte
|
||||
Parameters asn1.RawValue `asn1:"optional,explicit,tag:0"`
|
||||
PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
|
||||
}{
|
||||
Version: 1,
|
||||
PrivateKey: p.k.Serialize(),
|
||||
// Parameters are omitted since they're specified in the algorithm identifier
|
||||
|
||||
// Pubkey could be omitted, but we include it to match openssl behavior
|
||||
PublicKey: asn1.BitString{
|
||||
Bytes: pubkeyBytes,
|
||||
BitLength: 8 * len(pubkeyBytes),
|
||||
},
|
||||
}
|
||||
|
||||
ecPrivKeyDER, err := asn1.Marshal(ecPrivateKey)
|
||||
if err != nil {
|
||||
panic(err) // This should not happen with valid key data
|
||||
}
|
||||
|
||||
// Create the PKCS#8 structure
|
||||
pkcs8 := struct {
|
||||
Version int
|
||||
Algo pkix.AlgorithmIdentifier
|
||||
PrivateKey []byte
|
||||
}{
|
||||
Version: 0,
|
||||
Algo: pkix.AlgorithmIdentifier{
|
||||
// Elliptic curve public key (OID: 1.2.840.10045.2.1)
|
||||
Algorithm: oidPublicKeyECDSA,
|
||||
Parameters: asn1.RawValue{
|
||||
FullBytes: must(asn1.Marshal(oidSecp256k1)),
|
||||
},
|
||||
},
|
||||
PrivateKey: ecPrivKeyDER,
|
||||
}
|
||||
|
||||
der, err := asn1.Marshal(pkcs8)
|
||||
if err != nil {
|
||||
panic(err) // This should not happen with valid key data
|
||||
}
|
||||
|
||||
return der
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToPKCS8PEM() string {
|
||||
der := p.ToPKCS8DER()
|
||||
return string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: pemPrivBlockType,
|
||||
Bytes: der,
|
||||
}))
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-256.
|
||||
func (p *PrivateKey) SignToBytes(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hasher := params.HashOrDefault(crypto.SHA256).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
sig := ecdsa.Sign(p.k, hash)
|
||||
r := sig.R()
|
||||
s := sig.S()
|
||||
|
||||
res := make([]byte, SignatureBytesSize)
|
||||
r.PutBytesUnchecked(res[:SignatureBytesSize/2])
|
||||
s.PutBytesUnchecked(res[SignatureBytesSize/2:])
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-256.
|
||||
func (p *PrivateKey) SignToASN1(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hasher := params.HashOrDefault(crypto.SHA256).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
sig := ecdsa.Sign(p.k, hash)
|
||||
|
||||
return sig.Serialize(), nil
|
||||
}
|
||||
|
||||
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 {
|
||||
return secp256k1.GenerateSharedSecret(p.k, remote.k), nil
|
||||
}
|
||||
return nil, fmt.Errorf("incompatible public key")
|
||||
}
|
||||
211
crypto/secp256k1/public.go
Normal file
211
crypto/secp256k1/public.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package secp256k1
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
helpers "github.com/INFURA/go-did/crypto/internal"
|
||||
)
|
||||
|
||||
var _ crypto.PublicKeySigningBytes = &PublicKey{}
|
||||
var _ crypto.PublicKeySigningASN1 = &PublicKey{}
|
||||
|
||||
type PublicKey struct {
|
||||
k *secp256k1.PublicKey
|
||||
}
|
||||
|
||||
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
|
||||
// This compact serialization format is the raw key material, without metadata or structure.
|
||||
// It errors if the slice is not the right size.
|
||||
func PublicKeyFromBytes(b []byte) (*PublicKey, error) {
|
||||
pub, err := secp256k1.ParsePubKey(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PublicKey{k: pub}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromXY converts x and y coordinates into a PublicKey.
|
||||
func PublicKeyFromXY(x, y []byte) (*PublicKey, error) {
|
||||
var xf, yf secp256k1.FieldVal
|
||||
if xf.SetByteSlice(x) {
|
||||
return nil, fmt.Errorf("invalid secp255k1 public key")
|
||||
}
|
||||
if yf.SetByteSlice(y) {
|
||||
return nil, fmt.Errorf("invalid secp255k1 public key")
|
||||
}
|
||||
return &PublicKey{k: secp256k1.NewPublicKey(&xf, &yf)}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromPublicKeyMultibase decodes the public key from its Multibase form
|
||||
func PublicKeyFromPublicKeyMultibase(multibase string) (*PublicKey, error) {
|
||||
code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if code != MultibaseCode {
|
||||
return nil, fmt.Errorf("invalid code")
|
||||
}
|
||||
return PublicKeyFromBytes(bytes)
|
||||
}
|
||||
|
||||
// PublicKeyFromX509DER decodes an X.509 DER (binary) encoded public key.
|
||||
func PublicKeyFromX509DER(bytes []byte) (*PublicKey, error) {
|
||||
// Parse the X.509 SubjectPublicKeyInfo structure
|
||||
var spki struct {
|
||||
Algorithm pkix.AlgorithmIdentifier
|
||||
SubjectPublicKey asn1.BitString
|
||||
}
|
||||
|
||||
if _, err := asn1.Unmarshal(bytes, &spki); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse X.509 SubjectPublicKeyInfo: %w", err)
|
||||
}
|
||||
|
||||
// Check if this is an Elliptic curve public key (OID: 1.2.840.10045.2.1)
|
||||
if !spki.Algorithm.Algorithm.Equal(oidPublicKeyECDSA) {
|
||||
return nil, fmt.Errorf("not an Elliptic curve public key, got OID: %v", spki.Algorithm.Algorithm)
|
||||
}
|
||||
|
||||
// Extract the curve OID from parameters
|
||||
var namedCurveOID asn1.ObjectIdentifier
|
||||
if _, err := asn1.Unmarshal(spki.Algorithm.Parameters.FullBytes, &namedCurveOID); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse curve parameters: %w", err)
|
||||
}
|
||||
// Check if this is secp256k1 (OID: 1.3.132.0.10)
|
||||
if !namedCurveOID.Equal(oidSecp256k1) {
|
||||
return nil, fmt.Errorf("unsupported curve, expected secp256k1 (1.3.132.0.10), got: %v", namedCurveOID)
|
||||
}
|
||||
|
||||
pubKey, err := secp256k1.ParsePubKey(spki.SubjectPublicKey.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse secp256k1 public key: %w", err)
|
||||
}
|
||||
|
||||
return &PublicKey{k: pubKey}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromX509PEM decodes an X.509 PEM (string) encoded public key.
|
||||
func PublicKeyFromX509PEM(str string) (*PublicKey, error) {
|
||||
block, _ := pem.Decode([]byte(str))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block")
|
||||
}
|
||||
if block.Type != pemPubBlockType {
|
||||
return nil, fmt.Errorf("incorrect PEM block type")
|
||||
}
|
||||
return PublicKeyFromX509DER(block.Bytes)
|
||||
}
|
||||
|
||||
func (p *PublicKey) XBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [coordinateSize]byte
|
||||
p.k.X().FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PublicKey) YBytes() []byte {
|
||||
// fixed size buffer that can get allocated on the caller's stack after inlining.
|
||||
var buf [coordinateSize]byte
|
||||
p.k.Y().FillBytes(buf[:])
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (p *PublicKey) Equal(other crypto.PublicKey) bool {
|
||||
if other, ok := other.(*PublicKey); ok {
|
||||
return p.k.IsEqual(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToBytes() []byte {
|
||||
// 33-byte compressed format
|
||||
return p.k.SerializeCompressed()
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToPublicKeyMultibase() string {
|
||||
return helpers.PublicKeyMultibaseEncode(MultibaseCode, p.k.SerializeCompressed())
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToX509DER() []byte {
|
||||
pubKeyBytes := p.k.SerializeUncompressed()
|
||||
|
||||
// Create the X.509 SubjectPublicKeyInfo structure
|
||||
spki := struct {
|
||||
Algorithm pkix.AlgorithmIdentifier
|
||||
SubjectPublicKey asn1.BitString
|
||||
}{
|
||||
Algorithm: pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidPublicKeyECDSA,
|
||||
Parameters: asn1.RawValue{
|
||||
FullBytes: must(asn1.Marshal(oidSecp256k1)),
|
||||
},
|
||||
},
|
||||
SubjectPublicKey: asn1.BitString{
|
||||
Bytes: pubKeyBytes,
|
||||
BitLength: len(pubKeyBytes) * 8,
|
||||
},
|
||||
}
|
||||
|
||||
der, err := asn1.Marshal(spki)
|
||||
if err != nil {
|
||||
panic(err) // This should not happen with valid key data
|
||||
}
|
||||
|
||||
return der
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToX509PEM() string {
|
||||
der := p.ToX509DER()
|
||||
return string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: pemPubBlockType,
|
||||
Bytes: der,
|
||||
}))
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-256.
|
||||
func (p *PublicKey) VerifyBytes(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
if len(signature) != SignatureBytesSize {
|
||||
return false
|
||||
}
|
||||
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hasher := params.HashOrDefault(crypto.SHA256).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
var r, s secp256k1.ModNScalar
|
||||
r.SetByteSlice(signature[:32])
|
||||
s.SetByteSlice(signature[32:])
|
||||
|
||||
return ecdsa.NewSignature(&r, &s).Verify(hash, p.k)
|
||||
}
|
||||
|
||||
// The default signing hash is SHA-256.
|
||||
func (p *PublicKey) VerifyASN1(message, signature []byte, opts ...crypto.SigningOption) bool {
|
||||
params := crypto.CollectSigningOptions(opts)
|
||||
|
||||
hasher := params.HashOrDefault(crypto.SHA256).New()
|
||||
hasher.Write(message)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
sig, err := ecdsa.ParseDERSignature(signature)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return sig.Verify(hash, p.k)
|
||||
}
|
||||
|
||||
func must[T any](v T, err error) T {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
@@ -20,7 +20,7 @@ func GenerateKeyPair() (*PublicKey, *PrivateKey, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
pub := priv.Public().(*ecdh.PublicKey)
|
||||
return (*PublicKey)(pub), (*PrivateKey)(priv), nil
|
||||
return &PublicKey{k: pub}, &PrivateKey{k: priv}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -11,9 +11,11 @@ import (
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
)
|
||||
|
||||
var _ crypto.KeyExchangePrivateKey = (*PrivateKey)(nil)
|
||||
var _ crypto.PrivateKeyKeyExchange = (*PrivateKey)(nil)
|
||||
|
||||
type PrivateKey ecdh.PrivateKey
|
||||
type PrivateKey struct {
|
||||
k *ecdh.PrivateKey
|
||||
}
|
||||
|
||||
// PrivateKeyFromBytes converts a serialized private key to a PrivateKey.
|
||||
// This compact serialization format is the raw key material, without metadata or structure.
|
||||
@@ -24,7 +26,7 @@ func PrivateKeyFromBytes(b []byte) (*PrivateKey, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*PrivateKey)(priv), nil
|
||||
return &PrivateKey{k: priv}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromEd25519 converts an ed25519 private key to a x25519 private key.
|
||||
@@ -51,8 +53,11 @@ func PrivateKeyFromPKCS8DER(bytes []byte) (*PrivateKey, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdhPriv := priv.(*ecdh.PrivateKey)
|
||||
return (*PrivateKey)(ecdhPriv), nil
|
||||
ecdhPriv, ok := priv.(*ecdh.PrivateKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid private key type")
|
||||
}
|
||||
return &PrivateKey{k: ecdhPriv}, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
|
||||
@@ -69,22 +74,21 @@ func PrivateKeyFromPKCS8PEM(str string) (*PrivateKey, error) {
|
||||
|
||||
func (p *PrivateKey) Equal(other crypto.PrivateKey) bool {
|
||||
if other, ok := other.(*PrivateKey); ok {
|
||||
return (*ecdh.PrivateKey)(p).Equal((*ecdh.PrivateKey)(other))
|
||||
return p.k.Equal(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PrivateKey) Public() crypto.PublicKey {
|
||||
ecdhPub := (*ecdh.PrivateKey)(p).Public().(*ecdh.PublicKey)
|
||||
return (*PublicKey)(ecdhPub)
|
||||
return &PublicKey{k: p.k.Public().(*ecdh.PublicKey)}
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToBytes() []byte {
|
||||
return (*ecdh.PrivateKey)(p).Bytes()
|
||||
return p.k.Bytes()
|
||||
}
|
||||
|
||||
func (p *PrivateKey) ToPKCS8DER() []byte {
|
||||
res, _ := x509.MarshalPKCS8PrivateKey((*ecdh.PrivateKey)(p))
|
||||
res, _ := x509.MarshalPKCS8PrivateKey(p.k)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -105,7 +109,7 @@ func (p *PrivateKey) PublicKeyIsCompatible(remote crypto.PublicKey) bool {
|
||||
|
||||
func (p *PrivateKey) KeyExchange(remote crypto.PublicKey) ([]byte, error) {
|
||||
if local, ok := remote.(*PublicKey); ok {
|
||||
return (*ecdh.PrivateKey)(p).ECDH((*ecdh.PublicKey)(local))
|
||||
return p.k.ECDH(local.k)
|
||||
}
|
||||
return nil, fmt.Errorf("incompatible public key")
|
||||
}
|
||||
|
||||
@@ -8,13 +8,15 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
helpers "github.com/INFURA/go-did/crypto/_helpers"
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
helpers "github.com/INFURA/go-did/crypto/internal"
|
||||
)
|
||||
|
||||
var _ crypto.PublicKey = (*PublicKey)(nil)
|
||||
|
||||
type PublicKey ecdh.PublicKey
|
||||
type PublicKey struct {
|
||||
k *ecdh.PublicKey
|
||||
}
|
||||
|
||||
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
|
||||
// This compact serialization format is the raw key material, without metadata or structure.
|
||||
@@ -24,7 +26,7 @@ func PublicKeyFromBytes(b []byte) (*PublicKey, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*PublicKey)(pub), nil
|
||||
return &PublicKey{k: pub}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromEd25519 converts an ed25519 public key to a x25519 public key.
|
||||
@@ -101,8 +103,7 @@ func PublicKeyFromX509DER(bytes []byte) (*PublicKey, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdhPub := pub.(*ecdh.PublicKey)
|
||||
return (*PublicKey)(ecdhPub), nil
|
||||
return &PublicKey{k: pub.(*ecdh.PublicKey)}, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromX509PEM decodes an X.509 PEM (string) encoded public key.
|
||||
@@ -119,21 +120,21 @@ func PublicKeyFromX509PEM(str string) (*PublicKey, error) {
|
||||
|
||||
func (p *PublicKey) Equal(other crypto.PublicKey) bool {
|
||||
if other, ok := other.(*PublicKey); ok {
|
||||
return (*ecdh.PublicKey)(p).Equal((*ecdh.PublicKey)(other))
|
||||
return p.k.Equal(other.k)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToBytes() []byte {
|
||||
return (*ecdh.PublicKey)(p).Bytes()
|
||||
return p.k.Bytes()
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToPublicKeyMultibase() string {
|
||||
return helpers.PublicKeyMultibaseEncode(MultibaseCode, (*ecdh.PublicKey)(p).Bytes())
|
||||
return helpers.PublicKeyMultibaseEncode(MultibaseCode, p.k.Bytes())
|
||||
}
|
||||
|
||||
func (p *PublicKey) ToX509DER() []byte {
|
||||
res, _ := x509.MarshalPKIXPublicKey((*ecdh.PublicKey)(p))
|
||||
res, _ := x509.MarshalPKIXPublicKey(p.k)
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
_ "github.com/INFURA/go-did/methods/did-key"
|
||||
)
|
||||
|
||||
func ExampleSignature() {
|
||||
func Example_signature() {
|
||||
// errors need to be handled
|
||||
|
||||
// 1) Parse the DID string into a DID object
|
||||
@@ -32,7 +32,7 @@ func ExampleSignature() {
|
||||
// Output: Signature is valid, verified with method: Ed25519VerificationKey2020 did:key:z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse#z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse
|
||||
}
|
||||
|
||||
func ExampleKeyAgreement() {
|
||||
func Example_keyAgreement() {
|
||||
// errors need to be handled
|
||||
|
||||
// 1) We have a private key for Alice
|
||||
|
||||
@@ -8,11 +8,57 @@ import (
|
||||
|
||||
_ "github.com/INFURA/go-did/methods/did-key"
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
"github.com/INFURA/go-did/verifications/jsonwebkey"
|
||||
"github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
|
||||
func TestRoundTrip(t *testing.T) {
|
||||
strDoc := `
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
strDoc string
|
||||
assertion func(t *testing.T, doc *Document)
|
||||
}{
|
||||
{
|
||||
name: "ed25519",
|
||||
strDoc: ed25519Doc,
|
||||
assertion: func(t *testing.T, doc *Document) {
|
||||
require.Equal(t, "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", doc.ID())
|
||||
require.Equal(t, ed25519vm.Type2020, doc.Authentication()[0].Type())
|
||||
require.Equal(t, ed25519vm.Type2020, doc.Assertion()[0].Type())
|
||||
require.Equal(t, x25519vm.Type2020, doc.KeyAgreement()[0].Type())
|
||||
require.Equal(t, ed25519vm.Type2020, doc.CapabilityInvocation()[0].Type())
|
||||
require.Equal(t, ed25519vm.Type2020, doc.CapabilityDelegation()[0].Type())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsonWebKey",
|
||||
strDoc: jsonWebKeyDoc,
|
||||
assertion: func(t *testing.T, doc *Document) {
|
||||
require.Equal(t, "did:example:123", doc.ID())
|
||||
require.Len(t, doc.VerificationMethods(), 6)
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A"].Type())
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A"].Type())
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs"].Type())
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw"].Type())
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY"].Type())
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"].Type())
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
doc, err := FromJsonBytes([]byte(tc.strDoc))
|
||||
require.NoError(t, err)
|
||||
|
||||
tc.assertion(t, doc)
|
||||
|
||||
roundtrip, err := json.Marshal(doc)
|
||||
require.NoError(t, err)
|
||||
requireDocEqual(t, tc.strDoc, string(roundtrip))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const ed25519Doc = `
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
@@ -46,18 +92,136 @@ func TestRoundTrip(t *testing.T) {
|
||||
}]
|
||||
}
|
||||
`
|
||||
doc, err := FromJsonBytes([]byte(strDoc))
|
||||
require.NoError(t, err)
|
||||
|
||||
// basic testing
|
||||
require.Equal(t, "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", doc.ID())
|
||||
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)
|
||||
require.JSONEq(t, strDoc, string(roundtrip))
|
||||
const jsonWebKeyDoc = `
|
||||
{
|
||||
"@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1"],
|
||||
"id": "did:example:123",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "OKP",
|
||||
"crv": "Ed25519",
|
||||
"x": "VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "secp256k1",
|
||||
"x": "Z4Y3NNOxv0J6tCgqOBFnHnaZhJF6LdulT7z8A-2D5_8",
|
||||
"y": "i5a2NtJoUKXkLm6q8nOEu9WOkso1Ag6FTUT6k_LMnGk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "RSA",
|
||||
"e": "AQAB",
|
||||
"n": "omwsC1AqEk6whvxyOltCFWheSQvv1MExu5RLCMT4jVk9khJKv8JeMXWe3bWHatjPskdf2dlaGkW5QjtOnUKL742mvr4tCldKS3ULIaT1hJInMHHxj2gcubO6eEegACQ4QSu9LO0H-LM_L3DsRABB7Qja8HecpyuspW1Tu_DbqxcSnwendamwL52V17eKhlO4uXwv2HFlxufFHM0KmCJujIKyAxjD_m3q__IiHUVHD1tDIEvLPhG9Azsn3j95d-saIgZzPLhQFiKluGvsjrSkYU5pXVWIsV-B2jtLeeLC14XcYxWDUJ0qVopxkBvdlERcNtgF4dvW4X00EHj4vCljFw"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "38M1FDts7Oea7urmseiugGW7tWc3mLpJh6rKe7xINZ8",
|
||||
"y": "nDQW6XZ7b_u2Sy9slofYLlG03sOEoug3I0aAPQ0exs4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "GnLl6mDti7a2VUIZP5w6pcRX8q5nvEIgB3Q_5RI2p9F_QVsaAlDN7IG68Jn0dS_F",
|
||||
"y": "jq4QoAHKiIzezDp88s_cxSPXtuXYFliuCGndgU4Qp8l91xzD1spCmFIzQgVjqvcP"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "AVlZG23LyXYwlbjbGPMxZbHmJpDSu-IvpuKigEN2pzgWtSo--Rwd-n78nrWnZzeDc187Ln3qHlw5LRGrX4qgLQ-y",
|
||||
"y": "ANIbFeRdPHf1WYMCUjcPz-ZhecZFybOqLIJjVOlLETH7uPlyG0gEoMWnIZXhQVypPy_HtUiUzdnSEPAylYhHBTX2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"authentication": [
|
||||
"did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
// requireDocEqual compare two DID JSON document but ignore the ordering inside arrays of VerificationMethods
|
||||
func requireDocEqual(t *testing.T, expected, actual string) {
|
||||
propsExpected := map[string]json.RawMessage{}
|
||||
require.NoError(t, json.Unmarshal([]byte(expected), &propsExpected))
|
||||
propsActual := map[string]json.RawMessage{}
|
||||
require.NoError(t, json.Unmarshal([]byte(actual), &propsActual))
|
||||
|
||||
require.Equal(t, len(propsExpected), len(propsActual))
|
||||
|
||||
for k, v := range propsExpected {
|
||||
switch k {
|
||||
case "authentication",
|
||||
"assertionMethod",
|
||||
"capabilityDelegation",
|
||||
"capabilityInvocation",
|
||||
"verificationMethod":
|
||||
var arrayExpected, arrayActual any
|
||||
require.NoError(t, json.Unmarshal(propsExpected[k], &arrayExpected))
|
||||
require.NoError(t, json.Unmarshal(v, &arrayActual))
|
||||
require.ElementsMatch(t, arrayExpected, arrayActual, "--> on property \"%s\"", k)
|
||||
delete(propsExpected, k)
|
||||
delete(propsActual, k)
|
||||
default:
|
||||
require.JSONEq(t, string(v), string(propsActual[k]), "--> on property \"%s\"", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
go.mod
4
go.mod
@@ -5,6 +5,8 @@ go 1.23.0
|
||||
toolchain go1.23.1
|
||||
|
||||
require (
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
||||
github.com/mr-tron/base58 v1.1.0
|
||||
github.com/multiformats/go-multibase v0.2.0
|
||||
github.com/multiformats/go-varint v0.0.7
|
||||
github.com/stretchr/testify v1.10.0
|
||||
@@ -13,9 +15,9 @@ require (
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/mr-tron/base58 v1.1.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.0.3 // indirect
|
||||
github.com/multiformats/go-base36 v0.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
6
go.sum
6
go.sum
@@ -1,5 +1,9 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
|
||||
@@ -16,6 +20,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
@@ -14,7 +14,7 @@ type DID interface {
|
||||
|
||||
// Document resolves the DID into a DID Document usable for e.g. signature check.
|
||||
// This can be simply expanding the DID into a Document, or involve external resolution.
|
||||
Document() (Document, error)
|
||||
Document(opts ...ResolutionOption) (Document, error)
|
||||
|
||||
// String returns the string representation of the DID.
|
||||
String() string
|
||||
@@ -111,8 +111,8 @@ type VerificationMethodKeyAgreement interface {
|
||||
VerificationMethod
|
||||
|
||||
// PrivateKeyIsCompatible checks that the given PrivateKey is compatible with this method.
|
||||
PrivateKeyIsCompatible(local crypto.KeyExchangePrivateKey) bool
|
||||
PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool
|
||||
|
||||
// KeyExchange computes the shared key using the given PrivateKey.
|
||||
KeyExchange(local crypto.KeyExchangePrivateKey) ([]byte, error)
|
||||
KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ func (d document) MarshalJSON() ([]byte, error) {
|
||||
// Maybe it doesn't matter, but the spec contradicts itself.
|
||||
// See https://github.com/w3c-ccg/did-key-spec/issues/71
|
||||
|
||||
vms := []did.VerificationMethod{d.signature}
|
||||
if d.signature != did.VerificationMethod(d.keyAgreement) {
|
||||
vms = append(vms, d.keyAgreement)
|
||||
}
|
||||
|
||||
return json.Marshal(struct {
|
||||
Context []string `json:"@context"`
|
||||
ID string `json:"id"`
|
||||
@@ -28,17 +33,17 @@ func (d document) MarshalJSON() ([]byte, error) {
|
||||
VerificationMethod []did.VerificationMethod `json:"verificationMethod,omitempty"`
|
||||
Authentication []string `json:"authentication,omitempty"`
|
||||
AssertionMethod []string `json:"assertionMethod,omitempty"`
|
||||
KeyAgreement []did.VerificationMethod `json:"keyAgreement,omitempty"`
|
||||
KeyAgreement []string `json:"keyAgreement,omitempty"`
|
||||
CapabilityInvocation []string `json:"capabilityInvocation,omitempty"`
|
||||
CapabilityDelegation []string `json:"capabilityDelegation,omitempty"`
|
||||
}{
|
||||
Context: d.Context(),
|
||||
ID: d.id.String(),
|
||||
AlsoKnownAs: nil,
|
||||
VerificationMethod: []did.VerificationMethod{d.signature},
|
||||
VerificationMethod: vms,
|
||||
Authentication: []string{d.signature.ID()},
|
||||
AssertionMethod: []string{d.signature.ID()},
|
||||
KeyAgreement: []did.VerificationMethod{d.keyAgreement},
|
||||
KeyAgreement: []string{d.keyAgreement.ID()},
|
||||
CapabilityInvocation: []string{d.signature.ID()},
|
||||
CapabilityDelegation: []string{d.signature.ID()},
|
||||
})
|
||||
@@ -66,6 +71,11 @@ func (d document) AlsoKnownAs() []*url.URL {
|
||||
}
|
||||
|
||||
func (d document) VerificationMethods() map[string]did.VerificationMethod {
|
||||
if d.signature == did.VerificationMethod(d.keyAgreement) {
|
||||
return map[string]did.VerificationMethod{
|
||||
d.signature.ID(): d.signature,
|
||||
}
|
||||
}
|
||||
return map[string]did.VerificationMethod{
|
||||
d.signature.ID(): d.signature,
|
||||
d.keyAgreement.ID(): d.keyAgreement,
|
||||
|
||||
@@ -7,20 +7,19 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/methods/did-key/testvectors"
|
||||
)
|
||||
|
||||
func TestDocument(t *testing.T) {
|
||||
d, err := did.Parse("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK")
|
||||
require.NoError(t, err)
|
||||
|
||||
doc, err := d.Document()
|
||||
doc, err := d.Document(did.WithResolutionHintVerificationMethod("Ed25519VerificationKey2020"))
|
||||
require.NoError(t, err)
|
||||
|
||||
bytes, err := json.MarshalIndent(doc, "", " ")
|
||||
require.NoError(t, err)
|
||||
|
||||
// TODO: https://github.com/w3c-ccg/did-key-spec/issues/71
|
||||
|
||||
const expected = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
@@ -54,8 +53,113 @@ func TestDocument(t *testing.T) {
|
||||
}]
|
||||
}`
|
||||
|
||||
require.JSONEq(t, expected, string(bytes))
|
||||
requireDocEqual(t, expected, string(bytes))
|
||||
}
|
||||
|
||||
// TODO: test vectors:
|
||||
// https://github.com/w3c-ccg/did-key-spec/tree/main/test-vectors
|
||||
func TestVectors(t *testing.T) {
|
||||
for _, filename := range testvectors.AllFiles() {
|
||||
t.Run(filename, func(t *testing.T) {
|
||||
vectors, err := testvectors.LoadTestVectors(filename)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, vector := range vectors {
|
||||
t.Run(vector.DID, func(t *testing.T) {
|
||||
t.Log("hint is", vector.ResolutionHint)
|
||||
require.NotZero(t, vector.Document)
|
||||
require.NotZero(t, vector.Pub)
|
||||
require.NotZero(t, vector.Priv)
|
||||
|
||||
d, err := did.Parse(vector.DID)
|
||||
require.NoError(t, err)
|
||||
|
||||
var opts []did.ResolutionOption
|
||||
for _, hint := range vector.ResolutionHint {
|
||||
opts = append(opts, did.WithResolutionHintVerificationMethod(hint))
|
||||
}
|
||||
|
||||
doc, err := d.Document(opts...)
|
||||
require.NoError(t, err)
|
||||
bytes, err := json.MarshalIndent(doc, "", " ")
|
||||
require.NoError(t, err)
|
||||
requireDocEqual(t, vector.Document, string(bytes))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Some variations in the DID document are legal, so we can't just require.JSONEq() to compare two of them.
|
||||
// This function does its best to compare two documents, regardless of those variations.
|
||||
func requireDocEqual(t *testing.T, expected, actual string) {
|
||||
propsExpected := map[string]json.RawMessage{}
|
||||
err := json.Unmarshal([]byte(expected), &propsExpected)
|
||||
require.NoError(t, err)
|
||||
|
||||
propsActual := map[string]json.RawMessage{}
|
||||
err = json.Unmarshal([]byte(actual), &propsActual)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(propsExpected), len(propsActual))
|
||||
|
||||
// if a VerificationMethod is defined inline in the properties below, we move it to vmExpected and replace it with the VM ID
|
||||
var vmExpected []json.RawMessage
|
||||
err = json.Unmarshal(propsExpected["verificationMethod"], &vmExpected)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, s := range []string{"authentication", "assertionMethod", "keyAgreement", "capabilityInvocation", "capabilityDelegation"} {
|
||||
var vms []json.RawMessage
|
||||
err = json.Unmarshal(propsExpected[s], &vms)
|
||||
require.NoError(t, err)
|
||||
for _, vmBytes := range vms {
|
||||
vm := map[string]json.RawMessage{}
|
||||
if err := json.Unmarshal(vmBytes, &vm); err == nil {
|
||||
vmExpected = append(vmExpected, vmBytes)
|
||||
propsExpected[s] = append([]byte("[ "), append(vm["id"], []byte(" ]")...)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same for actual
|
||||
var vmActual []json.RawMessage
|
||||
err = json.Unmarshal(propsActual["verificationMethod"], &vmActual)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, s := range []string{"authentication", "assertionMethod", "keyAgreement", "capabilityInvocation", "capabilityDelegation"} {
|
||||
var vms []json.RawMessage
|
||||
err = json.Unmarshal(propsActual[s], &vms)
|
||||
require.NoError(t, err)
|
||||
for _, vmBytes := range vms {
|
||||
vm := map[string]json.RawMessage{}
|
||||
if err := json.Unmarshal(vmBytes, &vm); err == nil {
|
||||
vmActual = append(vmActual, vmBytes)
|
||||
propsActual[s] = append([]byte("[ "), append(vm["id"], []byte(" ]")...)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range propsExpected {
|
||||
switch k {
|
||||
case "verificationMethod":
|
||||
// Convert to interface{} slices to normalize JSON formatting
|
||||
expectedVMs := make([]interface{}, len(vmExpected))
|
||||
for i, vm := range vmExpected {
|
||||
var normalized interface{}
|
||||
err := json.Unmarshal(vm, &normalized)
|
||||
require.NoError(t, err)
|
||||
expectedVMs[i] = normalized
|
||||
}
|
||||
|
||||
actualVMs := make([]interface{}, len(vmActual))
|
||||
for i, vm := range vmActual {
|
||||
var normalized interface{}
|
||||
err := json.Unmarshal(vm, &normalized)
|
||||
require.NoError(t, err)
|
||||
actualVMs[i] = normalized
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, expectedVMs, actualVMs, "--> on property \"%s\"", k)
|
||||
default:
|
||||
require.JSONEq(t, string(v), string(propsActual[k]), "--> on property \"%s\"", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,19 @@ import (
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/_helpers"
|
||||
allkeys "github.com/INFURA/go-did/crypto/_allkeys"
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
"github.com/INFURA/go-did/crypto/p256"
|
||||
"github.com/INFURA/go-did/crypto/p384"
|
||||
"github.com/INFURA/go-did/crypto/p521"
|
||||
"github.com/INFURA/go-did/crypto/rsa"
|
||||
"github.com/INFURA/go-did/crypto/secp256k1"
|
||||
"github.com/INFURA/go-did/crypto/x25519"
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
"github.com/INFURA/go-did/verifications/jsonwebkey"
|
||||
"github.com/INFURA/go-did/verifications/multikey"
|
||||
p256vm "github.com/INFURA/go-did/verifications/p256"
|
||||
secp256k1vm "github.com/INFURA/go-did/verifications/secp256k1"
|
||||
"github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
|
||||
@@ -21,12 +28,11 @@ func init() {
|
||||
did.RegisterMethod("key", Decode)
|
||||
}
|
||||
|
||||
var _ did.DID = &DidKey{}
|
||||
var _ did.DID = DidKey{}
|
||||
|
||||
type DidKey struct {
|
||||
msi string // method-specific identifier, i.e. "12345" in "did:key:12345"
|
||||
signature did.VerificationMethodSignature
|
||||
keyAgreement did.VerificationMethodKeyAgreement
|
||||
pubkey crypto.PublicKey
|
||||
}
|
||||
|
||||
func Decode(identifier string) (did.DID, error) {
|
||||
@@ -38,57 +44,18 @@ func Decode(identifier string) (did.DID, error) {
|
||||
|
||||
msi := identifier[len(keyPrefix):]
|
||||
|
||||
code, bytes, err := helpers.PublicKeyMultibaseDecode(msi)
|
||||
pub, err := allkeys.PublicKeyFromPublicKeyMultibase(msi)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
|
||||
}
|
||||
|
||||
decoder, ok := decoders[code]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: unsupported did:key multicodec: 0x%x", did.ErrInvalidDid, code)
|
||||
}
|
||||
pub, err := decoder(bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
|
||||
}
|
||||
d, err := FromPublicKey(pub)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
|
||||
}
|
||||
return d, nil
|
||||
return DidKey{msi: msi, pubkey: pub}, nil
|
||||
}
|
||||
|
||||
var decoders = map[uint64]func(b []byte) (crypto.PublicKey, error){
|
||||
ed25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return ed25519.PublicKeyFromBytes(b) },
|
||||
p256.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p256.PublicKeyFromBytes(b) },
|
||||
x25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return x25519.PublicKeyFromBytes(b) },
|
||||
func FromPublicKey(pub crypto.PublicKey) did.DID {
|
||||
return DidKey{msi: pub.ToPublicKeyMultibase(), pubkey: pub}
|
||||
}
|
||||
|
||||
func FromPublicKey(pub crypto.PublicKey) (did.DID, error) {
|
||||
switch pub := pub.(type) {
|
||||
case ed25519.PublicKey:
|
||||
d := DidKey{msi: pub.ToPublicKeyMultibase()}
|
||||
d.signature = ed25519vm.NewVerificationKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, d.msi), pub, d)
|
||||
xpub, err := x25519.PublicKeyFromEd25519(pub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xmsi := xpub.ToPublicKeyMultibase()
|
||||
d.keyAgreement = x25519vm.NewKeyAgreementKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, xmsi), xpub, d)
|
||||
return d, nil
|
||||
case *p256.PublicKey:
|
||||
d := DidKey{msi: pub.ToPublicKeyMultibase()}
|
||||
mk := multikey.NewMultiKey(fmt.Sprintf("did:key:%s#%s", d.msi, d.msi), pub, d)
|
||||
d.signature = mk
|
||||
d.keyAgreement = mk
|
||||
return d, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported public key: %T", pub)
|
||||
}
|
||||
}
|
||||
|
||||
func FromPrivateKey(priv crypto.PrivateKey) (did.DID, error) {
|
||||
func FromPrivateKey(priv crypto.PrivateKey) did.DID {
|
||||
return FromPublicKey(priv.Public().(crypto.PublicKey))
|
||||
}
|
||||
|
||||
@@ -96,12 +63,92 @@ func (d DidKey) Method() string {
|
||||
return "key"
|
||||
}
|
||||
|
||||
func (d DidKey) Document() (did.Document, error) {
|
||||
return document{
|
||||
id: d,
|
||||
signature: d.signature,
|
||||
keyAgreement: d.keyAgreement,
|
||||
}, nil
|
||||
func (d DidKey) Document(opts ...did.ResolutionOption) (did.Document, error) {
|
||||
params := did.CollectResolutionOpts(opts)
|
||||
|
||||
doc := document{id: d}
|
||||
mainVmId := fmt.Sprintf("did:key:%s#%s", d.msi, d.msi)
|
||||
|
||||
switch pub := d.pubkey.(type) {
|
||||
case ed25519.PublicKey:
|
||||
xpub, err := x25519.PublicKeyFromEd25519(pub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xmsi := xpub.ToPublicKeyMultibase()
|
||||
xVmId := fmt.Sprintf("did:key:%s#%s", d.msi, xmsi)
|
||||
|
||||
switch {
|
||||
case params.HasVerificationMethodHint(jsonwebkey.Type):
|
||||
doc.signature = jsonwebkey.NewJsonWebKey2020(mainVmId, pub, d)
|
||||
doc.keyAgreement = jsonwebkey.NewJsonWebKey2020(xVmId, xpub, d)
|
||||
case params.HasVerificationMethodHint(multikey.Type):
|
||||
doc.signature = multikey.NewMultiKey(mainVmId, pub, d)
|
||||
doc.keyAgreement = multikey.NewMultiKey(xVmId, xpub, d)
|
||||
default:
|
||||
if params.HasVerificationMethodHint(ed25519vm.Type2018) {
|
||||
doc.signature = ed25519vm.NewVerificationKey2018(mainVmId, pub, d)
|
||||
}
|
||||
if params.HasVerificationMethodHint(x25519vm.Type2019) {
|
||||
doc.keyAgreement = x25519vm.NewKeyAgreementKey2019(xVmId, xpub, d)
|
||||
}
|
||||
if doc.signature == nil {
|
||||
doc.signature = ed25519vm.NewVerificationKey2020(mainVmId, pub, d)
|
||||
}
|
||||
if doc.keyAgreement == nil {
|
||||
doc.keyAgreement = x25519vm.NewKeyAgreementKey2020(xVmId, xpub, d)
|
||||
}
|
||||
}
|
||||
|
||||
case *p256.PublicKey:
|
||||
switch {
|
||||
case params.HasVerificationMethodHint(jsonwebkey.Type):
|
||||
jwk := jsonwebkey.NewJsonWebKey2020(mainVmId, pub, d)
|
||||
doc.signature = jwk
|
||||
doc.keyAgreement = jwk
|
||||
case params.HasVerificationMethodHint(p256vm.Type2021):
|
||||
vm := p256vm.NewKey2021(mainVmId, pub, d)
|
||||
doc.signature = vm
|
||||
doc.keyAgreement = vm
|
||||
default:
|
||||
mk := multikey.NewMultiKey(mainVmId, pub, d)
|
||||
doc.signature = mk
|
||||
doc.keyAgreement = mk
|
||||
}
|
||||
|
||||
case *secp256k1.PublicKey:
|
||||
switch {
|
||||
case params.HasVerificationMethodHint(jsonwebkey.Type):
|
||||
jwk := jsonwebkey.NewJsonWebKey2020(mainVmId, pub, d)
|
||||
doc.signature = jwk
|
||||
doc.keyAgreement = jwk
|
||||
case params.HasVerificationMethodHint(secp256k1vm.Type2019):
|
||||
vm := secp256k1vm.NewVerificationKey2019(mainVmId, pub, d)
|
||||
doc.signature = vm
|
||||
doc.keyAgreement = vm
|
||||
default:
|
||||
mk := multikey.NewMultiKey(mainVmId, pub, d)
|
||||
doc.signature = mk
|
||||
doc.keyAgreement = mk
|
||||
}
|
||||
|
||||
case *p384.PublicKey, *p521.PublicKey, *rsa.PublicKey:
|
||||
switch {
|
||||
case params.HasVerificationMethodHint(jsonwebkey.Type):
|
||||
jwk := jsonwebkey.NewJsonWebKey2020(mainVmId, pub, d)
|
||||
doc.signature = jwk
|
||||
doc.keyAgreement = jwk
|
||||
default:
|
||||
mk := multikey.NewMultiKey(mainVmId, pub, d)
|
||||
doc.signature = mk
|
||||
doc.keyAgreement = mk
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported public key: %T", pub)
|
||||
}
|
||||
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
func (d DidKey) String() string {
|
||||
@@ -116,5 +163,8 @@ func (d DidKey) Equal(d2 did.DID) bool {
|
||||
if d2, ok := d2.(DidKey); ok {
|
||||
return d.msi == d2.msi
|
||||
}
|
||||
if d2, ok := d2.(*DidKey); ok {
|
||||
return d.msi == d2.msi
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ func ExampleGenerateKeyPair() {
|
||||
fmt.Println("Private key:", base64.StdEncoding.EncodeToString(priv.ToBytes()))
|
||||
|
||||
// Make the associated did:key
|
||||
dk, err := didkey.FromPrivateKey(priv)
|
||||
handleErr(err)
|
||||
dk := didkey.FromPrivateKey(priv)
|
||||
fmt.Println("Did:", dk.String())
|
||||
|
||||
// Produce a signature
|
||||
@@ -56,6 +55,16 @@ func TestMustParseDIDKey(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestFromPublicKey(t *testing.T) {
|
||||
pub, _, err := ed25519.GenerateKeyPair()
|
||||
require.NoError(t, err)
|
||||
dk := didkey.FromPublicKey(pub)
|
||||
require.Equal(t, "did:key:"+pub.ToPublicKeyMultibase(), dk.String())
|
||||
doc, err := dk.Document()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, doc)
|
||||
}
|
||||
|
||||
func TestEquivalence(t *testing.T) {
|
||||
did0A, err := did.Parse("did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z")
|
||||
require.NoError(t, err)
|
||||
|
||||
231
methods/did-key/testvectors/bls12381.json
Normal file
231
methods/did-key/testvectors/bls12381.json
Normal file
@@ -0,0 +1,231 @@
|
||||
{
|
||||
"did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY": {
|
||||
"verificationKeyPair": {
|
||||
"id": "#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY",
|
||||
"type": "Bls12381G2Key2020",
|
||||
"controller": "did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY",
|
||||
"publicKeyBase58": "25EEkQtcLKsEzQ6JTo9cg4W7NHpaurn4Wg6LaNPFq6JQXnrP91SDviUz7KrJVMJd76CtAZFsRLYzvgX2JGxo2ccUHtuHk7ELCWwrkBDfrXCFVfqJKDootee9iVaF6NpdJtBE",
|
||||
"privateKeyBase58": "8TXrPTbhefHvcz2vkGsDLBZT2UMeemveLKbdh5JZCvvn"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/bls12381-2020/v1"
|
||||
],
|
||||
"id": "did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY",
|
||||
"type": "Bls12381G2Key2020",
|
||||
"controller": "did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY",
|
||||
"publicKeyBase58": "25EEkQtcLKsEzQ6JTo9cg4W7NHpaurn4Wg6LaNPFq6JQXnrP91SDviUz7KrJVMJd76CtAZFsRLYzvgX2JGxo2ccUHtuHk7ELCWwrkBDfrXCFVfqJKDootee9iVaF6NpdJtBE"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY": {
|
||||
"verificationKeyPair": {
|
||||
"id": "#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY",
|
||||
"type": "Bls12381G2Key2020",
|
||||
"controller": "did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY",
|
||||
"publicKeyBase58": "t5QqHdxR4C6QJWJAnk3qVd2DMr4MVFEefdP43i7fLbR5A2qJkE5bqgEtyzpNsDViGEsMKHMdpo7fKbPMhGihbfxz3Dv2Hw36XvprLHBA5DDFSphmy91oHQFdahQMei2HjoE",
|
||||
"privateKeyBase58": "URWBZN9g2ZfKVdAz1L8pvVwEBqCbGBozt4p8Cootb35"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/bls12381-2020/v1"
|
||||
],
|
||||
"id": "did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY",
|
||||
"type": "Bls12381G2Key2020",
|
||||
"controller": "did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY",
|
||||
"publicKeyBase58": "t5QqHdxR4C6QJWJAnk3qVd2DMr4MVFEefdP43i7fLbR5A2qJkE5bqgEtyzpNsDViGEsMKHMdpo7fKbPMhGihbfxz3Dv2Hw36XvprLHBA5DDFSphmy91oHQFdahQMei2HjoE"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW": {
|
||||
"verificationKeyPair": {
|
||||
"id": "#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW",
|
||||
"type": "Bls12381G2Key2020",
|
||||
"controller": "did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW",
|
||||
"publicKeyBase58": "25VFRgQEfbJ3Pit6Z3mnZbKPK9BdQYGwdmfdcmderjYZ12BFNQYeowjMN1AYKKKcacF3UH35ZNpBqCR8y8QLeeaGLL7UKdKLcFje3VQnosesDNHsU8jBvtvYmLJusxXsSUBC",
|
||||
"privateKeyBase58": "48FTGTBBhezV7Ldk5g392NSxP2hwgEgWiSZQkMoNri7E"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/bls12381-2020/v1"
|
||||
],
|
||||
"id": "did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW",
|
||||
"type": "Bls12381G2Key2020",
|
||||
"controller": "did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW",
|
||||
"publicKeyBase58": "25VFRgQEfbJ3Pit6Z3mnZbKPK9BdQYGwdmfdcmderjYZ12BFNQYeowjMN1AYKKKcacF3UH35ZNpBqCR8y8QLeeaGLL7UKdKLcFje3VQnosesDNHsU8jBvtvYmLJusxXsSUBC"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU": {
|
||||
"verificationKeyPair": {
|
||||
"id": "#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU",
|
||||
"type": "Bls12381G2Key2020",
|
||||
"controller": "did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU",
|
||||
"publicKeyBase58": "21LWABB5R6mqxvcU6LWMMt9yCAVyt8C1mHREs1EAX23fLcAEPMK4dWx59Jd6RpJ5geGt881vH9yPzZyC8WpHhS2g296mumPxJA3Aghp9jMoACE13rtTie8FYdgzgUw24eboA",
|
||||
"privateKeyBase58": "86rp8w6Q7zgDdKqYxZsdTyhZogzwbcR7wf3VQrhV3xLG"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/bls12381-2020/v1"
|
||||
],
|
||||
"id": "did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU",
|
||||
"type": "Bls12381G2Key2020",
|
||||
"controller": "did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU",
|
||||
"publicKeyBase58": "21LWABB5R6mqxvcU6LWMMt9yCAVyt8C1mHREs1EAX23fLcAEPMK4dWx59Jd6RpJ5geGt881vH9yPzZyC8WpHhS2g296mumPxJA3Aghp9jMoACE13rtTie8FYdgzgUw24eboA"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA": {
|
||||
"verificationKeyPair": {
|
||||
"id": "#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA",
|
||||
"type": "Bls12381G2Key2020",
|
||||
"controller": "did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA",
|
||||
"publicKeyBase58": "21XhJ3o4ZSgDgRoyP4Pp8agXMwLycuRa1U6fM4ZzJBxH3gJEQbiuwP3Qh2zNoofNrBKPqp3FgXxGvW84cFwMD29oA7Q9w3L8Sjcc3e9mZqFgs8iWxSsDNRcbQdoYtGaxu11r",
|
||||
"privateKeyBase58": "5LjJ3yibKGP4zKbNgqeiQ284g8LJYnbF7ZBve7Ke9qZ5"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/bls12381-2020/v1"
|
||||
],
|
||||
"id": "did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA",
|
||||
"type": "Bls12381G2Key2020",
|
||||
"controller": "did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA",
|
||||
"publicKeyBase58": "21XhJ3o4ZSgDgRoyP4Pp8agXMwLycuRa1U6fM4ZzJBxH3gJEQbiuwP3Qh2zNoofNrBKPqp3FgXxGvW84cFwMD29oA7Q9w3L8Sjcc3e9mZqFgs8iWxSsDNRcbQdoYtGaxu11r"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk": {
|
||||
"verificationKeyPair": {
|
||||
"id": "did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "BLS12381_G1",
|
||||
"x": "im0OQGMTkh4YEhAl16hQwUQTcOaRqIqThqtSwksFK7WaH6Qywypmc3VIDyydmYTe"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "BLS12381_G1",
|
||||
"x": "im0OQGMTkh4YEhAl16hQwUQTcOaRqIqThqtSwksFK7WaH6Qywypmc3VIDyydmYTe",
|
||||
"d": "S7Z1TuL05WHge8od0_mW8b3sRM747caCffsLwS6JZ-c"
|
||||
}
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "BLS12381_G1",
|
||||
"x": "im0OQGMTkh4YEhAl16hQwUQTcOaRqIqThqtSwksFK7WaH6Qywypmc3VIDyydmYTe"
|
||||
}
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
293
methods/did-key/testvectors/ed25519-x25519.json
Normal file
293
methods/did-key/testvectors/ed25519-x25519.json
Normal file
@@ -0,0 +1,293 @@
|
||||
{
|
||||
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": {
|
||||
"seed": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"verificationKeyPair": {
|
||||
"id": "#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||
"type": "Ed25519VerificationKey2018",
|
||||
"controller": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||
"publicKeyBase58": "4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS"
|
||||
},
|
||||
"keyAgreementKeyPair": {
|
||||
"id": "#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||
"publicKeyBase58": "7By6kV2t2d188odEM4ExAve1UithKT6dLva4dwsDT3ak",
|
||||
"privateKeyBase58": "6QN8DfuN9hjgHgPvLXqgzqYE3jRRGRrmJQZkd5tL8paR"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/ed25519-2018/v1",
|
||||
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||
],
|
||||
"id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||
"type": "Ed25519VerificationKey2018",
|
||||
"controller": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||
"publicKeyBase58": "4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS"
|
||||
},
|
||||
{
|
||||
"id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||
"publicKeyBase58": "7By6kV2t2d188odEM4ExAve1UithKT6dLva4dwsDT3ak"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG": {
|
||||
"seed": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"verificationKeyPair": {
|
||||
"id": "#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||
"type": "Ed25519VerificationKey2018",
|
||||
"controller": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||
"publicKeyBase58": "6ASf5EcmmEHTgDJ4X4ZT5vT6iHVJBXPg5AN5YoTCpGWt"
|
||||
},
|
||||
"keyAgreementKeyPair": {
|
||||
"id": "#z6LSrHyXiPBhUbvPUtyUCdf32sniiMGPTAesgHrtEa4FePtr",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||
"publicKeyBase58": "FcoNC5NqP9CePWbhfz95iHaEsCjGkZUioK9Ck7Qiw286",
|
||||
"privateKeyBase58": "HBTcN2MrXNRj9xF9oi8QqYyuEPv3JLLjQKuEgW9oxVKP"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/ed25519-2018/v1",
|
||||
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||
],
|
||||
"id": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||
"type": "Ed25519VerificationKey2018",
|
||||
"controller": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||
"publicKeyBase58": "6ASf5EcmmEHTgDJ4X4ZT5vT6iHVJBXPg5AN5YoTCpGWt"
|
||||
},
|
||||
{
|
||||
"id": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6LSrHyXiPBhUbvPUtyUCdf32sniiMGPTAesgHrtEa4FePtr",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||
"publicKeyBase58": "FcoNC5NqP9CePWbhfz95iHaEsCjGkZUioK9Ck7Qiw286"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6LSrHyXiPBhUbvPUtyUCdf32sniiMGPTAesgHrtEa4FePtr"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf": {
|
||||
"seed": "0000000000000000000000000000000000000000000000000000000000000002",
|
||||
"verificationKeyPair": {
|
||||
"id": "#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||
"type": "Ed25519VerificationKey2018",
|
||||
"controller": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||
"publicKeyBase58": "8pM1DN3RiT8vbom5u1sNryaNT1nyL8CTTW3b5PwWXRBH"
|
||||
},
|
||||
"keyAgreementKeyPair": {
|
||||
"id": "#z6LSkkqoZRC34AEpbkhZCqLDcHQVAxuLpQ7kC8XCXMVUfvjE",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||
"publicKeyBase58": "A5fe37PAxhX5WNKngBpGHhC1KpNE7nwbK9oX2tqwxYxU",
|
||||
"privateKeyBase58": "ACa4PPJ1LnPNq1iwS33V3Akh7WtnC71WkKFZ9ccM6sX2"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/ed25519-2018/v1",
|
||||
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||
],
|
||||
"id": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||
"type": "Ed25519VerificationKey2018",
|
||||
"controller": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||
"publicKeyBase58": "8pM1DN3RiT8vbom5u1sNryaNT1nyL8CTTW3b5PwWXRBH"
|
||||
},
|
||||
{
|
||||
"id": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6LSkkqoZRC34AEpbkhZCqLDcHQVAxuLpQ7kC8XCXMVUfvjE",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||
"publicKeyBase58": "A5fe37PAxhX5WNKngBpGHhC1KpNE7nwbK9oX2tqwxYxU"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6LSkkqoZRC34AEpbkhZCqLDcHQVAxuLpQ7kC8XCXMVUfvjE"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ": {
|
||||
"seed": "0000000000000000000000000000000000000000000000000000000000000003",
|
||||
"verificationKeyPair": {
|
||||
"id": "#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||
"type": "Ed25519VerificationKey2018",
|
||||
"controller": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||
"publicKeyBase58": "HPYVwAQmskwT1qEEeRzhoomyfyupJGASQQtCXSNG8XS2"
|
||||
},
|
||||
"keyAgreementKeyPair": {
|
||||
"id": "#z6LSiUo6AEDat8Ze4nQzDo67SGuHLLwsUGkxndHGUjsywHow",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||
"publicKeyBase58": "7ocvdvQinfqtyQ3Dh9aA7ggoVCQkmfaoueZazHETDv3B",
|
||||
"privateKeyBase58": "FZrzd1osCnbK6y6MJzMBW1RcVfL524sNKhSbqRwMuwHT"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/ed25519-2018/v1",
|
||||
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||
],
|
||||
"id": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||
"type": "Ed25519VerificationKey2018",
|
||||
"controller": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||
"publicKeyBase58": "HPYVwAQmskwT1qEEeRzhoomyfyupJGASQQtCXSNG8XS2"
|
||||
},
|
||||
{
|
||||
"id": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6LSiUo6AEDat8Ze4nQzDo67SGuHLLwsUGkxndHGUjsywHow",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||
"publicKeyBase58": "7ocvdvQinfqtyQ3Dh9aA7ggoVCQkmfaoueZazHETDv3B"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6LSiUo6AEDat8Ze4nQzDo67SGuHLLwsUGkxndHGUjsywHow"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU": {
|
||||
"seed": "0000000000000000000000000000000000000000000000000000000000000005",
|
||||
"verificationKeyPair": {
|
||||
"id": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||
"publicKeyJwk": {
|
||||
"kty": "OKP",
|
||||
"crv": "Ed25519",
|
||||
"x": "_eT7oDCtAC98L31MMx9J0T-w7HR-zuvsY08f9MvKne8"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "OKP",
|
||||
"crv": "Ed25519",
|
||||
"x": "_eT7oDCtAC98L31MMx9J0T-w7HR-zuvsY08f9MvKne8",
|
||||
"d": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU"
|
||||
}
|
||||
},
|
||||
"keyAgreementKeyPair": {
|
||||
"id": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6LSmArkPSdTKjEESsExHRrSwUzYUHgDuWDewXc4nocasvFU",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||
"publicKeyJwk": {
|
||||
"kty": "OKP",
|
||||
"crv": "X25519",
|
||||
"x": "jRIz3oriXDNZmnb35XQb7K1UIlz3ae1ao1YSqLeBXHs"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "OKP",
|
||||
"crv": "X25519",
|
||||
"x": "jRIz3oriXDNZmnb35XQb7K1UIlz3ae1ao1YSqLeBXHs",
|
||||
"d": "aEAAB3VBFPCQtgF3N__wRiXhMOgeiRGstpPC3gnJ1Eo"
|
||||
}
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||
"publicKeyJwk": {
|
||||
"kty": "OKP",
|
||||
"crv": "Ed25519",
|
||||
"x": "_eT7oDCtAC98L31MMx9J0T-w7HR-zuvsY08f9MvKne8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6LSmArkPSdTKjEESsExHRrSwUzYUHgDuWDewXc4nocasvFU",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||
"publicKeyJwk": {
|
||||
"kty": "OKP",
|
||||
"crv": "X25519",
|
||||
"x": "jRIz3oriXDNZmnb35XQb7K1UIlz3ae1ao1YSqLeBXHs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6LSmArkPSdTKjEESsExHRrSwUzYUHgDuWDewXc4nocasvFU"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
371
methods/did-key/testvectors/nist-curves.json
Normal file
371
methods/did-key/testvectors/nist-curves.json
Normal file
@@ -0,0 +1,371 @@
|
||||
{
|
||||
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv": {
|
||||
"verificationMethod": {
|
||||
"id": "#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns",
|
||||
"y": "efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns",
|
||||
"y": "efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM",
|
||||
"d": "gPh-VvVS8MbvKQ9LSVVmfnxnKjHn4Tqj0bmbpehRlpc"
|
||||
}
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns",
|
||||
"y": "efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM"
|
||||
}
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169": {
|
||||
"verificationMethod": {
|
||||
"id": "#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "fyNYMN0976ci7xqiSdag3buk-ZCwgXU4kz9XNkBlNUI",
|
||||
"y": "hW2ojTNfH7Jbi8--CJUo3OCbH3y5n91g-IMA9MLMbTU"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "fyNYMN0976ci7xqiSdag3buk-ZCwgXU4kz9XNkBlNUI",
|
||||
"y": "hW2ojTNfH7Jbi8--CJUo3OCbH3y5n91g-IMA9MLMbTU",
|
||||
"d": "YjRs6vNvw4sYrzVVY8ipkEpDAD9PFqw1sUnvPRMA-WI"
|
||||
}
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "fyNYMN0976ci7xqiSdag3buk-ZCwgXU4kz9XNkBlNUI",
|
||||
"y": "hW2ojTNfH7Jbi8--CJUo3OCbH3y5n91g-IMA9MLMbTU"
|
||||
}
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9": {
|
||||
"verificationMethod": {
|
||||
"id": "#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc",
|
||||
"y": "y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc",
|
||||
"y": "y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv",
|
||||
"d": "hAyGZNj9031guBCdpAOaZkO-E5m-LKLYnMIq0-msrp8JLctseaOeNTHmP3uKVWwX"
|
||||
}
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc",
|
||||
"y": "y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv"
|
||||
}
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54": {
|
||||
"verificationMethod": {
|
||||
"id": "#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "CA-iNoHDg1lL8pvX3d1uvExzVfCz7Rn6tW781Ub8K5MrDf2IMPyL0RTDiaLHC1JT",
|
||||
"y": "Kpnrn8DkXUD3ge4mFxi-DKr0DYO2KuJdwNBrhzLRtfMa3WFMZBiPKUPfJj8dYNl_"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "CA-iNoHDg1lL8pvX3d1uvExzVfCz7Rn6tW781Ub8K5MrDf2IMPyL0RTDiaLHC1JT",
|
||||
"y": "Kpnrn8DkXUD3ge4mFxi-DKr0DYO2KuJdwNBrhzLRtfMa3WFMZBiPKUPfJj8dYNl_",
|
||||
"d": "Xe1HHeh-UsrJPRNLR_Y06VTrWpZYBXi7a7kiRqCgwnAOlJZPwE-xzL3DIIVMavAL"
|
||||
}
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "CA-iNoHDg1lL8pvX3d1uvExzVfCz7Rn6tW781Ub8K5MrDf2IMPyL0RTDiaLHC1JT",
|
||||
"y": "Kpnrn8DkXUD3ge4mFxi-DKr0DYO2KuJdwNBrhzLRtfMa3WFMZBiPKUPfJj8dYNl_"
|
||||
}
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7": {
|
||||
"verificationMethod": {
|
||||
"id": "#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS",
|
||||
"y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS",
|
||||
"y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC",
|
||||
"d": "AHwRaNaGs0jkj_pT6PK2aHep7dJK-yxyoL2bIfVRAceq1baxoiFDo3W14c8E2YZn1k5S53r4a11flhQdaB5guJ_X"
|
||||
}
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS",
|
||||
"y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC"
|
||||
}
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f": {
|
||||
"verificationMethod": {
|
||||
"id": "#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "AQgyFy6EwH3_u_KXPw8aTXTY7WSVytmbuJeFpq4U6LipxtSmBJe_jjRzms9qubnwm_fGoHMQlvQ1vzS2YLusR2V0",
|
||||
"y": "Ab06MCcgoG7dM2I-VppdLV1k3lDoeHMvyYqHVfP05Ep2O7Zu0Qwd6IVzfZi9K0KMDud22wdnGUpUtFukZo0EeO15"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "AQgyFy6EwH3_u_KXPw8aTXTY7WSVytmbuJeFpq4U6LipxtSmBJe_jjRzms9qubnwm_fGoHMQlvQ1vzS2YLusR2V0",
|
||||
"y": "Ab06MCcgoG7dM2I-VppdLV1k3lDoeHMvyYqHVfP05Ep2O7Zu0Qwd6IVzfZi9K0KMDud22wdnGUpUtFukZo0EeO15",
|
||||
"d": "AbheZ-AA58LP4BpopCGCLH8ZoMdkdJaVOS6KK2NNmDCisr5_Ifxl-qcunrkOJ0CSauA4LJyNbCWcy28Bo6zgHTXQ"
|
||||
}
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "AQgyFy6EwH3_u_KXPw8aTXTY7WSVytmbuJeFpq4U6LipxtSmBJe_jjRzms9qubnwm_fGoHMQlvQ1vzS2YLusR2V0",
|
||||
"y": "Ab06MCcgoG7dM2I-VppdLV1k3lDoeHMvyYqHVfP05Ep2O7Zu0Qwd6IVzfZi9K0KMDud22wdnGUpUtFukZo0EeO15"
|
||||
}
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb": {
|
||||
"verificationMethod": {
|
||||
"id": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||
"type": "P256Key2021",
|
||||
"controller": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||
"publicKeyBase58": "ekVhkcBFq3w7jULLkBVye6PwaTuMbhJYuzwFnNcgQAPV",
|
||||
"privateKeyBase58": "9p4VRzdmhsnq869vQjVCTrRry7u4TtfRxhvBFJTGU2Cp"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/multikey-2021/v1"
|
||||
],
|
||||
"id": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||
"type": "P256Key2021",
|
||||
"controller": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||
"publicKeyBase58": "ekVhkcBFq3w7jULLkBVye6PwaTuMbhJYuzwFnNcgQAPV"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
106
methods/did-key/testvectors/rsa.json
Normal file
106
methods/did-key/testvectors/rsa.json
Normal file
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i": {
|
||||
"publicKeyJwk": {
|
||||
"kty": "RSA",
|
||||
"n": "sbX82NTV6IylxCh7MfV4hlyvaniCajuP97GyOqSvTmoEdBOflFvZ06kR_9D6ctt45Fk6hskfnag2GG69NALVH2o4RCR6tQiLRpKcMRtDYE_thEmfBvDzm_VVkOIYfxu-Ipuo9J_S5XDNDjczx2v-3oDh5-CIHkU46hvFeCvpUS-L8TJSbgX0kjVk_m4eIb9wh63rtmD6Uz_KBtCo5mmR4TEtcLZKYdqMp3wCjN-TlgHiz_4oVXWbHUefCEe8rFnX1iQnpDHU49_SaXQoud1jCaexFn25n-Aa8f8bc5Vm-5SeRwidHa6ErvEhTvf1dz6GoNPp2iRvm-wJ1gxwWJEYPQ",
|
||||
"e": "AQAB"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "RSA",
|
||||
"n": "sbX82NTV6IylxCh7MfV4hlyvaniCajuP97GyOqSvTmoEdBOflFvZ06kR_9D6ctt45Fk6hskfnag2GG69NALVH2o4RCR6tQiLRpKcMRtDYE_thEmfBvDzm_VVkOIYfxu-Ipuo9J_S5XDNDjczx2v-3oDh5-CIHkU46hvFeCvpUS-L8TJSbgX0kjVk_m4eIb9wh63rtmD6Uz_KBtCo5mmR4TEtcLZKYdqMp3wCjN-TlgHiz_4oVXWbHUefCEe8rFnX1iQnpDHU49_SaXQoud1jCaexFn25n-Aa8f8bc5Vm-5SeRwidHa6ErvEhTvf1dz6GoNPp2iRvm-wJ1gxwWJEYPQ",
|
||||
"e": "AQAB",
|
||||
"d": "Eym3sT4KLwBzo5pl5nY83-hAti92iLQRizkrKe22RbNi9Y1kKOBatdtGaJqFVztZZu5ERGKNuTd5VdsjJeekSbXviVGRtdHNCvgmRZlWA5261AgIUPxMmKW062GmGJbKQvscFfziBgHK6tyDBd8cZavqMFHi-7ilMYF7IsFBcJKM85x_30pnfd4YwhGQIc9hzv238aOwYKg8c-MzYhEVUnL273jaiLVlfZWQ5ca-GXJHmdOb_Y4fE5gpXfPFBseqleXsMp0VuXxCEsN30LIJHYscdPtbzLD3LFbuMJglFbQqYqssqymILGqJ7Tc2mB2LmXevfqRWz5D7A_K1WzvuoQ",
|
||||
"p": "3CWT55Vc9CmUKavtV11fwwCU3lha99eRsl7Yo6HJLudpKV8zJ5bojTPqrowAjiHxyz3ITPCY3WgSX_q1n_4093U51rYermMfHQmrY_l7EgwxvNNMdsH4uMwHhz5vUNks6svtmkJL4JwQe8HPimHMdruCrPZKs0gajO59uNL-0rk",
|
||||
"q": "zqcpEWpGAeJS0ZzTElrClcl6iQaElAV-KcOVqSOSm25FA2_QE7Px9FTGTrPDBivH5P9iT7SgAWwPypYiCJeDxZ_Rt1FbQvR0gfhzp9_eZJERd4BPaHdcNoXQXVgqezTxbJha064iJhYKHI72zB4rsBS5o4n7qWvqUSXNMYdV_6U",
|
||||
"dp": "gcUE8rZxHNyFoiretWktUd2943Nh7Eb-c47FVW_BEA0JSIH9vZCPdOztogaVLTOFPLEmqXQKKDl422sGNVG8F0La3V5tp452gL96cGxXx8O4bf6ATGD7JLPgnDCJnbbna2Daptv9rmFQtiMBHCmaRUMzPJHSZuxR-lF7er-lxsE",
|
||||
"dq": "Id2bCVOVLXHdiKReor9k7A8cmaAL0gYkasu2lwVRXU9w1-NXAiOXHydVaEhlSXmbRJflkJJVNmZzIAwCf830tko-oAAhKJPPFA2XRoeVdn2fkynf2YrV_cloICP2skI23kkJeW8sAXnTJmL3ZvP6zNxYn8hZCaa5u5qqSdeX7FE",
|
||||
"qi": "WKIToXXnjl7GDbz7jCNbX9nWYOE5BDNzVmwiVOnyGoTZfwJ_qtgizj7pOapxi6dT9S9mMavmeAi6LAsEe1WUWtaKSNhbNh0PUGGXlXHGlhkS8jI1ot0e-scrHAuACE567YQ4VurpNorPKtZ5UENXIn74DEmt4l5m6902VF3X5Wo"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i",
|
||||
"publicKeyJwk": {
|
||||
"kty": "RSA",
|
||||
"n": "sbX82NTV6IylxCh7MfV4hlyvaniCajuP97GyOqSvTmoEdBOflFvZ06kR_9D6ctt45Fk6hskfnag2GG69NALVH2o4RCR6tQiLRpKcMRtDYE_thEmfBvDzm_VVkOIYfxu-Ipuo9J_S5XDNDjczx2v-3oDh5-CIHkU46hvFeCvpUS-L8TJSbgX0kjVk_m4eIb9wh63rtmD6Uz_KBtCo5mmR4TEtcLZKYdqMp3wCjN-TlgHiz_4oVXWbHUefCEe8rFnX1iQnpDHU49_SaXQoud1jCaexFn25n-Aa8f8bc5Vm-5SeRwidHa6ErvEhTvf1dz6GoNPp2iRvm-wJ1gxwWJEYPQ",
|
||||
"e": "AQAB"
|
||||
}
|
||||
}
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2": {
|
||||
"publicKeyJwk": {
|
||||
"kty": "RSA",
|
||||
"n": "qMCkFFRFWtzUyZeK8mgJdyM6SEQcXC5E6JwCRVDld-jlJs8sXNOE_vliexq34wZRQ4hk53-JPFlvZ_QjRgIxdUxSMiZ3S5hlNVvvRaue6SMakA9ugQhnfXaWORro0UbPuHLms-bg5StDP8-8tIezu9c1H1FjwPcdbV6rAvKhyhnsM10qP3v2CPbdE0q3FOsihoKuTelImtO110E7N6fLn4U3EYbC4OyViqlrP1o_1M-R-tiM1cb4pD7XKJnIs6ryZdfOQSPBJwjNqSdN6Py_tdrFgPDTyacSSdpTVADOM2IMAoYbhV1N5APhnjOHBRFyKkF1HffQKpmXQLBqvUNNjuhmpVKWBtrTdcCKrglFXiw0cKGHKxIirjmiOlB_HYHg5UdosyE3_1Txct2U7-WBB6QXak1UgxCzgKYBDI8UPA0RlkUuHHP_Zg0fVXrXIInHO04MYxUeSps5qqyP6dJBu_v_BDn3zUq6LYFwJ_-xsU7zbrKYB4jaRlHPoCj_eDC-rSA2uQ4KXHBB8_aAqNFC9ukWxc26Ifz9dF968DLuL30bi-ZAa2oUh492Pw1bg89J7i4qTsOOfpQvGyDV7TGhKuUG3Hbumfr2w16S-_3EI2RIyd1nYsflE6ZmCkZQMG_lwDAFXaqfyGKEDouJuja4XH8r4fGWeGTrozIoniXT1HU",
|
||||
"e": "AQAB"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "RSA",
|
||||
"n": "qMCkFFRFWtzUyZeK8mgJdyM6SEQcXC5E6JwCRVDld-jlJs8sXNOE_vliexq34wZRQ4hk53-JPFlvZ_QjRgIxdUxSMiZ3S5hlNVvvRaue6SMakA9ugQhnfXaWORro0UbPuHLms-bg5StDP8-8tIezu9c1H1FjwPcdbV6rAvKhyhnsM10qP3v2CPbdE0q3FOsihoKuTelImtO110E7N6fLn4U3EYbC4OyViqlrP1o_1M-R-tiM1cb4pD7XKJnIs6ryZdfOQSPBJwjNqSdN6Py_tdrFgPDTyacSSdpTVADOM2IMAoYbhV1N5APhnjOHBRFyKkF1HffQKpmXQLBqvUNNjuhmpVKWBtrTdcCKrglFXiw0cKGHKxIirjmiOlB_HYHg5UdosyE3_1Txct2U7-WBB6QXak1UgxCzgKYBDI8UPA0RlkUuHHP_Zg0fVXrXIInHO04MYxUeSps5qqyP6dJBu_v_BDn3zUq6LYFwJ_-xsU7zbrKYB4jaRlHPoCj_eDC-rSA2uQ4KXHBB8_aAqNFC9ukWxc26Ifz9dF968DLuL30bi-ZAa2oUh492Pw1bg89J7i4qTsOOfpQvGyDV7TGhKuUG3Hbumfr2w16S-_3EI2RIyd1nYsflE6ZmCkZQMG_lwDAFXaqfyGKEDouJuja4XH8r4fGWeGTrozIoniXT1HU",
|
||||
"e": "AQAB",
|
||||
"d": "TMq1H-clVG7PihkjCqJbRFLMj9wmx6_qfauYwPBKK-HYfWujdW5vxBO6Q-jpqy7RxhiISmxYCBVuw_BuKMqQtR8Q_G9StBzaWYjHfn3Vp6Poz4umLqOjbI2NWNks_ybpGbd30oAK8V5ZkO04ozJpkN4i92hzK3mIc5-z1HiTNUPMn6cStab0VCn6em_ylltV774CEcRJ3OLgid7OUspRt_rID3qyreYbOulTu5WXHIGEnZDzrciIlz1dbcVldpUhD0VAP5ZErD2uUP5oztBNcTTn0YBF8CrOALuQVdaz_t_sNS3P0kWeT1eQ0QwDskO5Hw-Aey2tFeWk1bQyLoQ1A0jsw8mDbkO2zrGfJoxmVBkueTK-q64_n1kV7W1aeJFRj4NwEWmwcrs8GSOGOn38fGB_Y3Kci04qvD6L0QZbFkAVzcJracnxbTdHCEX0jsAAPbYC8M_8PyrPJvPC4IAAWTRrSRbysb7r7viRf4A1vTK9VT7uYyxj7Kzx2cU12d9QBXYfdQ2744bUE7HqN-Vh2rHvv2l5v6vzBRoZ5_OhHHVeUYwC9LouE9lSVAObbFM-Qe1SvzbbwN91LziI7UzUc_xMAEiNwt6PpnIAWAhdvSRawEllTwUcn89udHd5UhiAcm-RQOqXIdA9Aly6d8TT8R1p-ZnQ_gbZyBZeS39AuvU",
|
||||
"p": "1p4cypsJeTyVXXc5bQpvzVenPy78OHXtGcFQnbTjW8x1GsvJ-rlHAcjUImd44pgNQNe-iYpeUg3KqfONeedNgQCFd8kP7GoVAd45mEvsGBXvjoCXOBMQlsf8UU_hm_LKhVvTvTmMGoudnNv5qYNDMCGJGzwoG-aSvROlIoXzHmDnusZ-hKsDxM9j0PPz21t99Y_Fr30Oq3FIWXPVmLYmfyZYQkxm9a9WNMkqRbwJuMwGI6V9ABsQ1dW_KJzp_aEBbJLcDr9DsWhm9ErLeAlzyaDYEai6wCtKm9em4LDwCbKhJq3hWEp1sIG-hwx1sk7N4i-b8lBijjEQE-dbSQxUlw",
|
||||
"q": "yUqMejfrttGujadj7Uf7q91KM7nbQGny4TjD-CqibcFE-s2_DExCgP1wfhUPfJr2uPQDIe4g12uaNoa5GbCSDaQwEmQpurC_5mazt-z-_tbI24hoPQm5Hq67fZz-jDE_3OccLPLIWtajJqmxHbbB5VqskMuXo8KDxPRfBQBhykmb9_5M8pY2ggZOV4shCUn5E9nOnvibvw5Wx4CBtWUtca4rhpd3mVen1d8xCe4xTG_ni_w1lwdxzU1GmRFqgTuZWzL0r2FKzJg7hju1SOEe4tKMxQ-xs2HyNaMM__SLsNmS3lsYZ8r2hqcjEMQQZI0T_O-3BjIpyg986P8j055E0w",
|
||||
"dp": "DujzJRw6P0L3OYQT6EBmXgSt6NTRzvZaX4SvnhU4CmOc6xynTpTamwQhwLYhjtRzb0LNyO5k-RxeLQpvlL1-A-1OWHEOeyUvim6u36a-ozm659KFLu8cIu2H2PpMuTHX4gXsIuRBmIKEk6YwpRcqbsiVpt-6BZ4yKZKY0Vou9rhSwQYTOhJLc7vYumaIVX_4szumxzdP8pcvKI_EkhRtfj3iudBnAsCIo6gqGKgkoMMD1iwkEALRW5m66w5jrywlVi6pvRiKkmOna2da1V8KvUJAYJGxT7JyP3tu64M_Wd0gFvjTg_fAT1_kJau27YlOAl2-Xso43poH_OoAzIVfxw",
|
||||
"dq": "XI6Z76z9BxB9mgcpTLc3wzw63XQNnB3bn7JRcjBwhdVD2at3uLjsL5HaAy-98kbzQfJ56kUr9sI0o_Po8yYc0ob3z80c3wpdAx2gb-dbDWVH8KJVhBOPestPzR--cEpJGlNuwkBU3mgplyKaHZamq8a46M-lB5jurEbN1mfpj3GvdSYKzdVCdSFfLqP76eCI1pblinW4b-6w-oVdn0JJ1icHPpkxVmJW-2Hok69iHcqrBtRO9AZpTsTEvKekeI4mIyhYGLi9AzzQyhV0c3GImTXFoutng5t7GyzBUoRpI0W4YeQzYa6TEzGRTylIfGPemATF_OReENp0TlLbb3gsHw",
|
||||
"qi": "m7uZk4AsOfJ1V2RY8lmEF518toCV7juKuS_b_OUx8B0dRG0_kbF1cH-Tmrgsya3bwkYx5HeZG81rX7SRjh-0nVPOMW3tGqU5U9f59DXqvOItJIJ6wvWvWXnuna2-NstYCotFQWadIKjk4wjEKj-a4NJt4D_F4csyeyqWOH2DiUFzBGGxxdEoD5t_HEeNXuWQ6-SiV0x5ZVMln3TSh7IOMl70Smm8HcQF5mOsWg3N0wIg-yffxPrs6r15TRuW1MfT-bZk2GLrtHF1TkIoT1e00jWK4eBl2oRxiJGONUBMTEHV85Fr0yztnA99AgHnrMbE_4ehvev4h5DEWvFyFuJN_g"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2",
|
||||
"publicKeyJwk": {
|
||||
"kty": "RSA",
|
||||
"n": "qMCkFFRFWtzUyZeK8mgJdyM6SEQcXC5E6JwCRVDld-jlJs8sXNOE_vliexq34wZRQ4hk53-JPFlvZ_QjRgIxdUxSMiZ3S5hlNVvvRaue6SMakA9ugQhnfXaWORro0UbPuHLms-bg5StDP8-8tIezu9c1H1FjwPcdbV6rAvKhyhnsM10qP3v2CPbdE0q3FOsihoKuTelImtO110E7N6fLn4U3EYbC4OyViqlrP1o_1M-R-tiM1cb4pD7XKJnIs6ryZdfOQSPBJwjNqSdN6Py_tdrFgPDTyacSSdpTVADOM2IMAoYbhV1N5APhnjOHBRFyKkF1HffQKpmXQLBqvUNNjuhmpVKWBtrTdcCKrglFXiw0cKGHKxIirjmiOlB_HYHg5UdosyE3_1Txct2U7-WBB6QXak1UgxCzgKYBDI8UPA0RlkUuHHP_Zg0fVXrXIInHO04MYxUeSps5qqyP6dJBu_v_BDn3zUq6LYFwJ_-xsU7zbrKYB4jaRlHPoCj_eDC-rSA2uQ4KXHBB8_aAqNFC9ukWxc26Ifz9dF968DLuL30bi-ZAa2oUh492Pw1bg89J7i4qTsOOfpQvGyDV7TGhKuUG3Hbumfr2w16S-_3EI2RIyd1nYsflE6ZmCkZQMG_lwDAFXaqfyGKEDouJuja4XH8r4fGWeGTrozIoniXT1HU",
|
||||
"e": "AQAB"
|
||||
}
|
||||
}
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2"
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
257
methods/did-key/testvectors/secp256k1.json
Normal file
257
methods/did-key/testvectors/secp256k1.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme": {
|
||||
"seed": "9085d2bef69286a6cbb51623c8fa258629945cd55ca705cc4e66700396894e0c",
|
||||
"verificationKeyPair": {
|
||||
"id": "#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
|
||||
"publicKeyBase58": "23o6Sau8NxxzXcgSc3PLcNxrzrZpbLeBn1izfv3jbKhuv",
|
||||
"privateKeyBase58": "AjA4cyPUbbfW5wr6iZeRbJLhgH3qDt6q6LMkRw36KpxT"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||
],
|
||||
"id": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
|
||||
"publicKeyBase58": "23o6Sau8NxxzXcgSc3PLcNxrzrZpbLeBn1izfv3jbKhuv"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2": {
|
||||
"seed": "f0f4df55a2b3ff13051ea814a8f24ad00f2e469af73c363ac7e9fb999a9072ed",
|
||||
"verificationKeyPair": {
|
||||
"id": "#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2",
|
||||
"publicKeyBase58": "291KzQhqCPC18PqH83XKhxv1HdqrdnxyS7dh15t2uNRzJ",
|
||||
"privateKeyBase58": "HDbR1D5W3CoNbUKYzUbHH2PRF1atshtVupXgXTQhNB9E"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||
],
|
||||
"id": "did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2",
|
||||
"publicKeyBase58": "291KzQhqCPC18PqH83XKhxv1HdqrdnxyS7dh15t2uNRzJ"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N": {
|
||||
"seed": "6b0b91287ae3348f8c2f2552d766f30e3604867e34adc37ccbb74a8e6b893e02",
|
||||
"verificationKeyPair": {
|
||||
"id": "#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N",
|
||||
"publicKeyBase58": "oesQ92MLiAkt2pjBcJFbW7H4DvzKJv22cotjYbmC2JEe",
|
||||
"privateKeyBase58": "8CrrWVdzDnvaS7vS5dd2HetFSebwEN46XEFrNDdtWZSZ"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||
],
|
||||
"id": "did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N",
|
||||
"publicKeyBase58": "oesQ92MLiAkt2pjBcJFbW7H4DvzKJv22cotjYbmC2JEe"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy": {
|
||||
"seed": "c0a6a7c560d37d7ba81ecee9543721ff48fea3e0fb827d42c1868226540fac15",
|
||||
"verificationKeyPair": {
|
||||
"id": "#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||
"publicKeyBase58": "pg3p1vprqePgUoqfAQ1TTgxhL6zLYhHyzooR1pqLxo9F",
|
||||
"privateKeyBase58": "Dy2fnt8ba4NmbRBXas9bo1BtYgpYFr6ThpFhJbuA3PRn"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||
],
|
||||
"id": "did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||
"publicKeyBase58": "pg3p1vprqePgUoqfAQ1TTgxhL6zLYhHyzooR1pqLxo9F"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj": {
|
||||
"seed": "175a232d440be1e0788f25488a73d9416c04b6f924bea6354bf05dd2f1a75133",
|
||||
"verificationKeyPair": {
|
||||
"id": "#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj",
|
||||
"publicKeyBase58": "24waDFAUAS16UpZwQQTXVEAmm17rQRjadjuAeBDW8aqL1",
|
||||
"privateKeyBase58": "2aA6WgZnPiVMBX3LvKSTg3KaFKyzfKpvEacixB3yyTgv"
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||
],
|
||||
"id": "did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj",
|
||||
"publicKeyBase58": "24waDFAUAS16UpZwQQTXVEAmm17rQRjadjuAeBDW8aqL1"
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj"
|
||||
]
|
||||
}
|
||||
},
|
||||
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS": {
|
||||
"verificationKeyPair": {
|
||||
"id": "did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "secp256k1",
|
||||
"x": "TEIJN9vnTq1EXMkqzo7yN_867-foKc2pREv45Fw_QA8",
|
||||
"y": "9yiymlzdxKCiRbYq7p-ArRB-C1ytjHE-eb7RDTi6rVc"
|
||||
},
|
||||
"privateKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "secp256k1",
|
||||
"x": "TEIJN9vnTq1EXMkqzo7yN_867-foKc2pREv45Fw_QA8",
|
||||
"y": "9yiymlzdxKCiRbYq7p-ArRB-C1ytjHE-eb7RDTi6rVc",
|
||||
"d": "J5yKm7OXFsXDEutteGYeT0CAfQJwIlHLSYkQxKtgiyo"
|
||||
}
|
||||
},
|
||||
"didDocument": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "secp256k1",
|
||||
"x": "TEIJN9vnTq1EXMkqzo7yN_867-foKc2pREv45Fw_QA8",
|
||||
"y": "9yiymlzdxKCiRbYq7p-ArRB-C1ytjHE-eb7RDTi6rVc"
|
||||
}
|
||||
}
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS"
|
||||
],
|
||||
"authentication": [
|
||||
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS"
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
240
methods/did-key/testvectors/vectors.go
Normal file
240
methods/did-key/testvectors/vectors.go
Normal file
@@ -0,0 +1,240 @@
|
||||
package testvectors
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
"github.com/INFURA/go-did/crypto/jwk"
|
||||
"github.com/INFURA/go-did/crypto/p256"
|
||||
"github.com/INFURA/go-did/crypto/secp256k1"
|
||||
ed25519vm "github.com/INFURA/go-did/verifications/ed25519"
|
||||
"github.com/INFURA/go-did/verifications/jsonwebkey"
|
||||
p256vm "github.com/INFURA/go-did/verifications/p256"
|
||||
secp256k1vm "github.com/INFURA/go-did/verifications/secp256k1"
|
||||
)
|
||||
|
||||
// Origin: https://github.com/w3c-ccg/did-key-spec/tree/main/test-vectors
|
||||
// See also: https://github.com/w3c-ccg/did-key-spec/pull/73
|
||||
|
||||
//go:embed *.json
|
||||
var testVectorFiles embed.FS
|
||||
|
||||
type Vector struct {
|
||||
DID string
|
||||
Pub crypto.PublicKey
|
||||
Priv crypto.PrivateKey
|
||||
Document string
|
||||
|
||||
// Those test vectors are done in a way that, for example, if the input is a JWK the expected verification method
|
||||
// is JsonWebKey2020. This field collects those hints so that the future resolution matches the expected document.
|
||||
ResolutionHint []string
|
||||
}
|
||||
|
||||
func AllFiles() []string {
|
||||
files, err := testVectorFiles.ReadDir(".")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var res []string
|
||||
for _, f := range files {
|
||||
// filter some
|
||||
switch {
|
||||
case strings.HasPrefix(f.Name(), "bls"): // BLS is not supported
|
||||
case strings.HasPrefix(f.Name(), "x25519"): // this file has a complete different structure
|
||||
default:
|
||||
res = append(res, f.Name())
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func LoadTestVectors(filename string) ([]Vector, error) {
|
||||
data, err := testVectorFiles.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []Vector
|
||||
|
||||
var vectorsData map[string]map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &vectorsData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range vectorsData {
|
||||
vect := Vector{DID: k}
|
||||
vect.Document = string(v["didDocument"])
|
||||
|
||||
// naked JWK
|
||||
if v["publicKeyJwk"] != nil {
|
||||
var pub jwk.PublicJwk
|
||||
if err = json.Unmarshal(v["publicKeyJwk"], &pub); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.Pub = pub.Pubkey
|
||||
var priv jwk.PrivateJwk
|
||||
if err = json.Unmarshal(v["privateKeyJwk"], &priv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.Priv = priv.Privkey
|
||||
vect.ResolutionHint = append(vect.ResolutionHint, jsonwebkey.Type)
|
||||
}
|
||||
|
||||
if v["verificationMethod"] != nil {
|
||||
var vm map[string]json.RawMessage
|
||||
if err = json.Unmarshal(v["verificationMethod"], &vm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var vmType string
|
||||
if err = json.Unmarshal(vm["type"], &vmType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.ResolutionHint = append(vect.ResolutionHint, vmType)
|
||||
|
||||
if vm["publicKeyJwk"] != nil {
|
||||
var pub jwk.PublicJwk
|
||||
if err = json.Unmarshal(vm["publicKeyJwk"], &pub); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.Pub = pub.Pubkey
|
||||
var priv jwk.PrivateJwk
|
||||
if err = json.Unmarshal(vm["privateKeyJwk"], &priv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.Priv = priv.Privkey
|
||||
}
|
||||
|
||||
var pubBytes []byte
|
||||
if vm["publicKeyBase58"] != nil {
|
||||
var pubB58 string
|
||||
if err = json.Unmarshal(vm["publicKeyBase58"], &pubB58); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubBytes, err = base58.DecodeAlphabet(pubB58, base58.BTCAlphabet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var privBytes []byte
|
||||
if vm["privateKeyBase58"] != nil {
|
||||
var privB58 string
|
||||
if err = json.Unmarshal(vm["privateKeyBase58"], &privB58); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privBytes, err = base58.DecodeAlphabet(privB58, base58.BTCAlphabet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
switch vmType {
|
||||
case p256vm.Type2021:
|
||||
vect.Pub, err = p256.PublicKeyFromBytes(pubBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.Priv, err = p256.PrivateKeyFromBytes(privBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if v["verificationKeyPair"] != nil {
|
||||
var vkp map[string]json.RawMessage
|
||||
if err = json.Unmarshal(v["verificationKeyPair"], &vkp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var vmType string
|
||||
if err = json.Unmarshal(vkp["type"], &vmType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.ResolutionHint = append(vect.ResolutionHint, vmType)
|
||||
|
||||
var pubBytes []byte
|
||||
if vkp["publicKeyBase58"] != nil {
|
||||
var pubB58 string
|
||||
if err = json.Unmarshal(vkp["publicKeyBase58"], &pubB58); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubBytes, err = base58.DecodeAlphabet(pubB58, base58.BTCAlphabet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var privBytes []byte
|
||||
if vkp["privateKeyBase58"] != nil {
|
||||
var privB58 string
|
||||
if err = json.Unmarshal(vkp["privateKeyBase58"], &privB58); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privBytes, err = base58.DecodeAlphabet(privB58, base58.BTCAlphabet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
switch vmType {
|
||||
case secp256k1vm.Type2019:
|
||||
vect.Pub, err = secp256k1.PublicKeyFromBytes(pubBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.Priv, err = secp256k1.PrivateKeyFromBytes(privBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case ed25519vm.Type2018:
|
||||
vect.Pub, err = ed25519.PublicKeyFromBytes(pubBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seed, err := hex.DecodeString(strings.Trim(string(v["seed"]), "\""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.Priv, err = ed25519.PrivateKeyFromSeed(seed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case jsonwebkey.Type:
|
||||
var pub jwk.PublicJwk
|
||||
if err = json.Unmarshal(vkp["publicKeyJwk"], &pub); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.Pub = pub.Pubkey
|
||||
var priv jwk.PrivateJwk
|
||||
if err = json.Unmarshal(vkp["privateKeyJwk"], &priv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.Priv = priv.Privkey
|
||||
vect.ResolutionHint = append(vect.ResolutionHint, jsonwebkey.Type)
|
||||
}
|
||||
}
|
||||
|
||||
if v["keyAgreementKeyPair"] != nil {
|
||||
var kakp map[string]json.RawMessage
|
||||
if err = json.Unmarshal(v["keyAgreementKeyPair"], &kakp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var vmType string
|
||||
if err = json.Unmarshal(kakp["type"], &vmType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vect.ResolutionHint = append(vect.ResolutionHint, vmType)
|
||||
}
|
||||
|
||||
res = append(res, vect)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
80
methods/did-key/testvectors/x25519.json
Normal file
80
methods/did-key/testvectors/x25519.json
Normal file
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"didDocument": {
|
||||
"did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||
],
|
||||
"id": "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F",
|
||||
"publicKeyBase58": "4Dy8E9UaZscuPUf2GLxV44RCNL7oxmEXXkgWXaug1WKV"
|
||||
}
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F"
|
||||
]
|
||||
},
|
||||
"did:key:z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||
],
|
||||
"id": "did:key:z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha#z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha",
|
||||
"publicKeyBase58": "J3PiFeuSyLugy4DKn87TwK5cnruRgPtxouzXUqg99Avp"
|
||||
}
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha#z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha"
|
||||
]
|
||||
},
|
||||
"did:key:z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||
],
|
||||
"id": "did:key:z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ#z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ",
|
||||
"publicKeyBase58": "CgTbngDMe7yHHfxPMvhpaFRpFoQWKgXAgwenJj8PsFDe"
|
||||
}
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ#z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ"
|
||||
]
|
||||
},
|
||||
"did:key:z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/jws-2020/v1"
|
||||
],
|
||||
"id": "did:key:z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:key:z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz#z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:key:z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz",
|
||||
"publicKeyJwk": {
|
||||
"kty": "OKP",
|
||||
"crv": "X25519",
|
||||
"x": "467ap28wHJGEXJAb4mLrokqq8A-txA_KmoQTcj31XzU"
|
||||
}
|
||||
}
|
||||
],
|
||||
"keyAgreement": [
|
||||
"did:key:z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz#z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
41
options.go
Normal file
41
options.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package did
|
||||
|
||||
type ResolutionOpts struct {
|
||||
hintVerificationMethod []string
|
||||
}
|
||||
|
||||
func (opts *ResolutionOpts) HasVerificationMethodHint(hint string) bool {
|
||||
for _, h := range opts.hintVerificationMethod {
|
||||
if h == hint {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CollectResolutionOpts(opts []ResolutionOption) ResolutionOpts {
|
||||
res := ResolutionOpts{}
|
||||
for _, opt := range opts {
|
||||
opt(&res)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type ResolutionOption func(opts *ResolutionOpts)
|
||||
|
||||
// WithResolutionHintVerificationMethod adds a hint for the type of verification method to be used
|
||||
// when resolving and constructing the DID Document, if possible.
|
||||
// Hints are expected to be VerificationMethod string types, like ed25519vm.Type.
|
||||
func WithResolutionHintVerificationMethod(hint string) ResolutionOption {
|
||||
return func(opts *ResolutionOpts) {
|
||||
if len(hint) == 0 {
|
||||
return
|
||||
}
|
||||
for _, s := range opts.hintVerificationMethod {
|
||||
if s == hint {
|
||||
return
|
||||
}
|
||||
}
|
||||
opts.hintVerificationMethod = append(opts.hintVerificationMethod, hint)
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ 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 crypto.KeyExchangePrivateKey) ([]byte, VerificationMethodKeyAgreement, error) {
|
||||
func FindMatchingKeyAgreement(methods []VerificationMethodKeyAgreement, priv crypto.PrivateKeyKeyExchange) ([]byte, VerificationMethodKeyAgreement, error) {
|
||||
for _, method := range methods {
|
||||
if method.PrivateKeyIsCompatible(priv) {
|
||||
key, err := method.KeyExchange(priv)
|
||||
|
||||
102
verifications/ed25519/VerificationKey2018.go
Normal file
102
verifications/ed25519/VerificationKey2018.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package ed25519vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
)
|
||||
|
||||
// Specification: https://w3c-ccg.github.io/lds-ed25519-2018/
|
||||
|
||||
const (
|
||||
JsonLdContext2018 = "https://w3id.org/security/suites/ed25519-2018/v1"
|
||||
Type2018 = "Ed25519VerificationKey2018"
|
||||
)
|
||||
|
||||
var _ did.VerificationMethodSignature = &VerificationKey2018{}
|
||||
|
||||
type VerificationKey2018 struct {
|
||||
id string
|
||||
pubkey ed25519.PublicKey
|
||||
controller string
|
||||
}
|
||||
|
||||
func NewVerificationKey2018(id string, pubkey ed25519.PublicKey, controller did.DID) *VerificationKey2018 {
|
||||
return &VerificationKey2018{
|
||||
id: id,
|
||||
pubkey: pubkey,
|
||||
controller: controller.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (v VerificationKey2018) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Controller string `json:"controller"`
|
||||
PublicKeyBase58 string `json:"publicKeyBase58"`
|
||||
}{
|
||||
ID: v.ID(),
|
||||
Type: v.Type(),
|
||||
Controller: v.Controller(),
|
||||
PublicKeyBase58: base58.Encode(v.pubkey.ToBytes()),
|
||||
})
|
||||
}
|
||||
|
||||
func (v *VerificationKey2018) UnmarshalJSON(bytes []byte) error {
|
||||
aux := struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Controller string `json:"controller"`
|
||||
PublicKeyBase58 string `json:"publicKeyBase58"`
|
||||
}{}
|
||||
err := json.Unmarshal(bytes, &aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.Type != v.Type() {
|
||||
return errors.New("invalid type")
|
||||
}
|
||||
v.id = aux.ID
|
||||
if len(v.id) == 0 {
|
||||
return errors.New("invalid id")
|
||||
}
|
||||
pubBytes, err := base58.Decode(aux.PublicKeyBase58)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyBase58: %w", err)
|
||||
}
|
||||
v.pubkey, err = ed25519.PublicKeyFromBytes(pubBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyBase58: %w", err)
|
||||
}
|
||||
v.controller = aux.Controller
|
||||
if !did.HasValidDIDSyntax(v.controller) {
|
||||
return errors.New("invalid controller")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v VerificationKey2018) ID() string {
|
||||
return v.id
|
||||
}
|
||||
|
||||
func (v VerificationKey2018) Type() string {
|
||||
return Type2018
|
||||
}
|
||||
|
||||
func (v VerificationKey2018) Controller() string {
|
||||
return v.controller
|
||||
}
|
||||
|
||||
func (v VerificationKey2018) JsonLdContext() string {
|
||||
return JsonLdContext2018
|
||||
}
|
||||
|
||||
func (v VerificationKey2018) Verify(data []byte, sig []byte) (bool, error) {
|
||||
return v.pubkey.VerifyBytes(data, sig), nil
|
||||
}
|
||||
27
verifications/ed25519/VerificationKey2018_test.go
Normal file
27
verifications/ed25519/VerificationKey2018_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package ed25519vm_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
ed25519vm "github.com/INFURA/go-did/verifications/ed25519"
|
||||
)
|
||||
|
||||
func TestJsonRoundTrip2018(t *testing.T) {
|
||||
data := `{
|
||||
"id": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||
"type": "Ed25519VerificationKey2018",
|
||||
"controller": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||
"publicKeyBase58": "6ASf5EcmmEHTgDJ4X4ZT5vT6iHVJBXPg5AN5YoTCpGWt"
|
||||
}`
|
||||
|
||||
var vk ed25519vm.VerificationKey2018
|
||||
err := json.Unmarshal([]byte(data), &vk)
|
||||
require.NoError(t, err)
|
||||
|
||||
bytes, err := json.Marshal(vk)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, data, string(bytes))
|
||||
}
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
// Specification: https://w3c.github.io/cg-reports/credentials/CG-FINAL-di-eddsa-2020-20220724/
|
||||
|
||||
const (
|
||||
JsonLdContext = "https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
Type = "Ed25519VerificationKey2020"
|
||||
JsonLdContext2020 = "https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
Type2020 = "Ed25519VerificationKey2020"
|
||||
)
|
||||
|
||||
var _ did.VerificationMethodSignature = &VerificationKey2020{}
|
||||
@@ -80,7 +80,7 @@ func (v VerificationKey2020) ID() string {
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) Type() string {
|
||||
return Type
|
||||
return Type2020
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) Controller() string {
|
||||
@@ -88,7 +88,7 @@ func (v VerificationKey2020) Controller() string {
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) JsonLdContext() string {
|
||||
return JsonLdContext
|
||||
return JsonLdContext2020
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) Verify(data []byte, sig []byte) (bool, error) {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
)
|
||||
|
||||
func TestJsonRoundTrip(t *testing.T) {
|
||||
func TestJsonRoundTrip2020(t *testing.T) {
|
||||
data := `{
|
||||
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
|
||||
"type": "Ed25519VerificationKey2020",
|
||||
@@ -30,7 +30,7 @@ func TestJsonRoundTrip(t *testing.T) {
|
||||
require.JSONEq(t, data, string(bytes))
|
||||
}
|
||||
|
||||
func TestSignature(t *testing.T) {
|
||||
func TestSignature2020(t *testing.T) {
|
||||
// test vector from https://datatracker.ietf.org/doc/html/rfc8032#section-7.1
|
||||
|
||||
pkHex := "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025"
|
||||
|
||||
@@ -6,7 +6,10 @@ import (
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
"github.com/INFURA/go-did/verifications/jsonwebkey"
|
||||
"github.com/INFURA/go-did/verifications/multikey"
|
||||
p256vm "github.com/INFURA/go-did/verifications/p256"
|
||||
secp256k1vm "github.com/INFURA/go-did/verifications/secp256k1"
|
||||
"github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
|
||||
@@ -20,12 +23,22 @@ func UnmarshalJSON(data []byte) (did.VerificationMethod, error) {
|
||||
|
||||
var res did.VerificationMethod
|
||||
switch aux.Type {
|
||||
case ed25519vm.Type:
|
||||
case ed25519vm.Type2018:
|
||||
res = &ed25519vm.VerificationKey2018{}
|
||||
case ed25519vm.Type2020:
|
||||
res = &ed25519vm.VerificationKey2020{}
|
||||
case multikey.Type:
|
||||
res = &multikey.MultiKey{}
|
||||
case x25519vm.Type:
|
||||
case p256vm.Type2021:
|
||||
res = &p256vm.Key2021{}
|
||||
case secp256k1vm.Type2019:
|
||||
res = &secp256k1vm.VerificationKey2019{}
|
||||
case x25519vm.Type2019:
|
||||
res = &x25519vm.KeyAgreementKey2019{}
|
||||
case x25519vm.Type2020:
|
||||
res = &x25519vm.KeyAgreementKey2020{}
|
||||
case jsonwebkey.Type:
|
||||
res = &jsonwebkey.JsonWebKey2020{}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown verification type: %s", aux.Type)
|
||||
}
|
||||
|
||||
109
verifications/jsonwebkey/JsonWebKey2020.go
Normal file
109
verifications/jsonwebkey/JsonWebKey2020.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package jsonwebkey
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/jwk"
|
||||
)
|
||||
|
||||
// Specification:
|
||||
// - https://www.w3.org/TR/vc-jws-2020/
|
||||
// - https://w3c-ccg.github.io/lds-jws2020/
|
||||
|
||||
const (
|
||||
JsonLdContext = "https://w3id.org/security/suites/jws-2020/v1"
|
||||
Type = "JsonWebKey2020"
|
||||
)
|
||||
|
||||
var _ did.VerificationMethodSignature = &JsonWebKey2020{}
|
||||
var _ did.VerificationMethodKeyAgreement = &JsonWebKey2020{}
|
||||
|
||||
type JsonWebKey2020 struct {
|
||||
id string
|
||||
pubkey crypto.PublicKey
|
||||
controller string
|
||||
}
|
||||
|
||||
func NewJsonWebKey2020(id string, pubkey crypto.PublicKey, controller did.DID) *JsonWebKey2020 {
|
||||
return &JsonWebKey2020{
|
||||
id: id,
|
||||
pubkey: pubkey,
|
||||
controller: controller.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (j JsonWebKey2020) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Controller string `json:"controller"`
|
||||
PublicKeyJWK jwk.PublicJwk `json:"publicKeyJwk"`
|
||||
}{
|
||||
ID: j.ID(),
|
||||
Type: j.Type(),
|
||||
Controller: j.Controller(),
|
||||
PublicKeyJWK: jwk.PublicJwk{Pubkey: j.pubkey},
|
||||
})
|
||||
}
|
||||
|
||||
func (j *JsonWebKey2020) UnmarshalJSON(bytes []byte) error {
|
||||
aux := struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Controller string `json:"controller"`
|
||||
PublicKeyJWK jwk.PublicJwk `json:"publicKeyJwk"`
|
||||
}{}
|
||||
err := json.Unmarshal(bytes, &aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.Type != j.Type() {
|
||||
return errors.New("invalid type")
|
||||
}
|
||||
j.id = aux.ID
|
||||
if len(j.id) == 0 {
|
||||
return errors.New("invalid id")
|
||||
}
|
||||
j.controller = aux.Controller
|
||||
if !did.HasValidDIDSyntax(j.controller) {
|
||||
return errors.New("invalid controller")
|
||||
}
|
||||
|
||||
j.pubkey = aux.PublicKeyJWK.Pubkey
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j JsonWebKey2020) ID() string {
|
||||
return j.id
|
||||
}
|
||||
|
||||
func (j JsonWebKey2020) Type() string {
|
||||
return Type
|
||||
}
|
||||
|
||||
func (j JsonWebKey2020) Controller() string {
|
||||
return j.controller
|
||||
}
|
||||
|
||||
func (j JsonWebKey2020) JsonLdContext() string {
|
||||
return JsonLdContext
|
||||
}
|
||||
|
||||
func (j JsonWebKey2020) Verify(data []byte, sig []byte) (bool, error) {
|
||||
if pub, ok := j.pubkey.(crypto.PublicKeySigningBytes); ok {
|
||||
return pub.VerifyBytes(data, sig), nil
|
||||
}
|
||||
return false, errors.New("not a signing public key")
|
||||
}
|
||||
|
||||
func (j JsonWebKey2020) PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool {
|
||||
return local.PublicKeyIsCompatible(j.pubkey)
|
||||
}
|
||||
|
||||
func (j JsonWebKey2020) KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error) {
|
||||
return local.KeyExchange(j.pubkey)
|
||||
}
|
||||
102
verifications/jsonwebkey/JsonWebKey2020_test.go
Normal file
102
verifications/jsonwebkey/JsonWebKey2020_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package jsonwebkey
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestJsonRoundTrip(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
str string
|
||||
}{
|
||||
{
|
||||
name: "did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
str: `{
|
||||
"id": "did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "OKP",
|
||||
"crv": "Ed25519",
|
||||
"x": "VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ"
|
||||
}}`,
|
||||
},
|
||||
{
|
||||
name: "did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
str: `{
|
||||
"id": "did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "secp256k1",
|
||||
"x": "Z4Y3NNOxv0J6tCgqOBFnHnaZhJF6LdulT7z8A-2D5_8",
|
||||
"y": "i5a2NtJoUKXkLm6q8nOEu9WOkso1Ag6FTUT6k_LMnGk"
|
||||
}}`,
|
||||
},
|
||||
{
|
||||
name: "did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
str: `{
|
||||
"id": "did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "RSA",
|
||||
"e": "AQAB",
|
||||
"n": "omwsC1AqEk6whvxyOltCFWheSQvv1MExu5RLCMT4jVk9khJKv8JeMXWe3bWHatjPskdf2dlaGkW5QjtOnUKL742mvr4tCldKS3ULIaT1hJInMHHxj2gcubO6eEegACQ4QSu9LO0H-LM_L3DsRABB7Qja8HecpyuspW1Tu_DbqxcSnwendamwL52V17eKhlO4uXwv2HFlxufFHM0KmCJujIKyAxjD_m3q__IiHUVHD1tDIEvLPhG9Azsn3j95d-saIgZzPLhQFiKluGvsjrSkYU5pXVWIsV-B2jtLeeLC14XcYxWDUJ0qVopxkBvdlERcNtgF4dvW4X00EHj4vCljFw"
|
||||
}}`,
|
||||
},
|
||||
{
|
||||
name: "did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
str: `{
|
||||
"id": "did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "38M1FDts7Oea7urmseiugGW7tWc3mLpJh6rKe7xINZ8",
|
||||
"y": "nDQW6XZ7b_u2Sy9slofYLlG03sOEoug3I0aAPQ0exs4"
|
||||
}}`,
|
||||
},
|
||||
{
|
||||
name: "did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
str: `{
|
||||
"id": "did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "GnLl6mDti7a2VUIZP5w6pcRX8q5nvEIgB3Q_5RI2p9F_QVsaAlDN7IG68Jn0dS_F",
|
||||
"y": "jq4QoAHKiIzezDp88s_cxSPXtuXYFliuCGndgU4Qp8l91xzD1spCmFIzQgVjqvcP"
|
||||
}}`,
|
||||
},
|
||||
{
|
||||
name: "did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E",
|
||||
str: `{
|
||||
"id": "did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "AVlZG23LyXYwlbjbGPMxZbHmJpDSu-IvpuKigEN2pzgWtSo--Rwd-n78nrWnZzeDc187Ln3qHlw5LRGrX4qgLQ-y",
|
||||
"y": "ANIbFeRdPHf1WYMCUjcPz-ZhecZFybOqLIJjVOlLETH7uPlyG0gEoMWnIZXhQVypPy_HtUiUzdnSEPAylYhHBTX2"
|
||||
}}`,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var jwk JsonWebKey2020
|
||||
err := json.Unmarshal([]byte(tc.str), &jwk)
|
||||
require.NoError(t, err)
|
||||
|
||||
bytes, err := json.Marshal(jwk)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, tc.str, string(bytes))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,7 @@ import (
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
helpers "github.com/INFURA/go-did/crypto/_helpers"
|
||||
"github.com/INFURA/go-did/crypto/ed25519"
|
||||
"github.com/INFURA/go-did/crypto/p256"
|
||||
"github.com/INFURA/go-did/crypto/x25519"
|
||||
allkeys "github.com/INFURA/go-did/crypto/_allkeys"
|
||||
)
|
||||
|
||||
// Specification: https://www.w3.org/TR/cid-1.0/#Multikey
|
||||
@@ -69,31 +66,17 @@ func (m *MultiKey) UnmarshalJSON(bytes []byte) error {
|
||||
if len(m.id) == 0 {
|
||||
return errors.New("invalid id")
|
||||
}
|
||||
|
||||
code, pubBytes, err := helpers.PublicKeyMultibaseDecode(aux.PublicKeyMultibase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyMultibase: %w", err)
|
||||
}
|
||||
decoder, ok := decoders[code]
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported publicKeyMultibase code: %d", code)
|
||||
}
|
||||
m.pubkey, err = decoder(pubBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyMultibase: %w", err)
|
||||
}
|
||||
|
||||
m.controller = aux.Controller
|
||||
if !did.HasValidDIDSyntax(m.controller) {
|
||||
return errors.New("invalid controller")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var decoders = map[uint64]func(b []byte) (crypto.PublicKey, error){
|
||||
ed25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return ed25519.PublicKeyFromBytes(b) },
|
||||
p256.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p256.PublicKeyFromBytes(b) },
|
||||
x25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return x25519.PublicKeyFromBytes(b) },
|
||||
m.pubkey, err = allkeys.PublicKeyFromPublicKeyMultibase(aux.PublicKeyMultibase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyMultibase: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m MultiKey) ID() string {
|
||||
@@ -113,16 +96,16 @@ func (m MultiKey) JsonLdContext() string {
|
||||
}
|
||||
|
||||
func (m MultiKey) Verify(data []byte, sig []byte) (bool, error) {
|
||||
if pub, ok := m.pubkey.(crypto.SigningPublicKey); ok {
|
||||
if pub, ok := m.pubkey.(crypto.PublicKeySigningBytes); ok {
|
||||
return pub.VerifyBytes(data, sig), nil
|
||||
}
|
||||
return false, errors.New("not a signing public key")
|
||||
}
|
||||
|
||||
func (m MultiKey) PrivateKeyIsCompatible(local crypto.KeyExchangePrivateKey) bool {
|
||||
func (m MultiKey) PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool {
|
||||
return local.PublicKeyIsCompatible(m.pubkey)
|
||||
}
|
||||
|
||||
func (m MultiKey) KeyExchange(local crypto.KeyExchangePrivateKey) ([]byte, error) {
|
||||
func (m MultiKey) KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error) {
|
||||
return local.KeyExchange(m.pubkey)
|
||||
}
|
||||
|
||||
114
verifications/p256/key2021.go
Normal file
114
verifications/p256/key2021.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package p256vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/p256"
|
||||
)
|
||||
|
||||
// Specification: missing
|
||||
|
||||
const (
|
||||
JsonLdContext2021 = "https://w3id.org/security/suites/multikey-2021/v1"
|
||||
Type2021 = "P256Key2021"
|
||||
)
|
||||
|
||||
var _ did.VerificationMethodSignature = &Key2021{}
|
||||
var _ did.VerificationMethodKeyAgreement = &Key2021{}
|
||||
|
||||
type Key2021 struct {
|
||||
id string
|
||||
pubkey *p256.PublicKey
|
||||
controller string
|
||||
}
|
||||
|
||||
func NewKey2021(id string, pubkey *p256.PublicKey, controller did.DID) *Key2021 {
|
||||
return &Key2021{
|
||||
id: id,
|
||||
pubkey: pubkey,
|
||||
controller: controller.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (m Key2021) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Controller string `json:"controller"`
|
||||
PublicKeyBase58 string `json:"publicKeyBase58"`
|
||||
}{
|
||||
ID: m.ID(),
|
||||
Type: m.Type(),
|
||||
Controller: m.Controller(),
|
||||
PublicKeyBase58: base58.Encode(m.pubkey.ToBytes()),
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Key2021) UnmarshalJSON(bytes []byte) error {
|
||||
aux := struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Controller string `json:"controller"`
|
||||
PublicKeyBase58 string `json:"publicKeyBase58"`
|
||||
}{}
|
||||
err := json.Unmarshal(bytes, &aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.Type != m.Type() {
|
||||
return errors.New("invalid type")
|
||||
}
|
||||
m.id = aux.ID
|
||||
if len(m.id) == 0 {
|
||||
return errors.New("invalid id")
|
||||
}
|
||||
m.controller = aux.Controller
|
||||
if !did.HasValidDIDSyntax(m.controller) {
|
||||
return errors.New("invalid controller")
|
||||
}
|
||||
|
||||
pubBytes, err := base58.Decode(aux.PublicKeyBase58)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyBase58: %w", err)
|
||||
}
|
||||
m.pubkey, err = p256.PublicKeyFromBytes(pubBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyBase58: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Key2021) ID() string {
|
||||
return m.id
|
||||
}
|
||||
|
||||
func (m Key2021) Type() string {
|
||||
return Type2021
|
||||
}
|
||||
|
||||
func (m Key2021) Controller() string {
|
||||
return m.controller
|
||||
}
|
||||
|
||||
func (m Key2021) JsonLdContext() string {
|
||||
return JsonLdContext2021
|
||||
}
|
||||
|
||||
func (m Key2021) Verify(data []byte, sig []byte) (bool, error) {
|
||||
return m.pubkey.VerifyBytes(data, sig), nil
|
||||
}
|
||||
|
||||
func (m Key2021) PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool {
|
||||
return local.PublicKeyIsCompatible(m.pubkey)
|
||||
}
|
||||
|
||||
func (m Key2021) KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error) {
|
||||
return local.KeyExchange(m.pubkey)
|
||||
}
|
||||
27
verifications/p256/key2021_test.go
Normal file
27
verifications/p256/key2021_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package p256vm_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
p256vm "github.com/INFURA/go-did/verifications/p256"
|
||||
)
|
||||
|
||||
func TestJsonRoundTrip(t *testing.T) {
|
||||
data := `{
|
||||
"id": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||
"type": "P256Key2021",
|
||||
"controller": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||
"publicKeyBase58": "ekVhkcBFq3w7jULLkBVye6PwaTuMbhJYuzwFnNcgQAPV"
|
||||
}`
|
||||
|
||||
var mk p256vm.Key2021
|
||||
err := json.Unmarshal([]byte(data), &mk)
|
||||
require.NoError(t, err)
|
||||
|
||||
bytes, err := json.Marshal(mk)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, data, string(bytes))
|
||||
}
|
||||
114
verifications/secp256k1/VerificationKey2019.go
Normal file
114
verifications/secp256k1/VerificationKey2019.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package secp256k1vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/secp256k1"
|
||||
)
|
||||
|
||||
// Specification: https://w3c-ccg.github.io/lds-ecdsa-secp256k1-2019/
|
||||
|
||||
const (
|
||||
JsonLdContext = "https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||
Type2019 = "EcdsaSecp256k1VerificationKey2019"
|
||||
)
|
||||
|
||||
var _ did.VerificationMethodSignature = &VerificationKey2019{}
|
||||
var _ did.VerificationMethodKeyAgreement = &VerificationKey2019{}
|
||||
|
||||
type VerificationKey2019 struct {
|
||||
id string
|
||||
pubkey *secp256k1.PublicKey
|
||||
controller string
|
||||
}
|
||||
|
||||
func NewVerificationKey2019(id string, pubkey *secp256k1.PublicKey, controller did.DID) *VerificationKey2019 {
|
||||
return &VerificationKey2019{
|
||||
id: id,
|
||||
pubkey: pubkey,
|
||||
controller: controller.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (vm VerificationKey2019) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Controller string `json:"controller"`
|
||||
PublicKeyBase58 string `json:"publicKeyBase58"`
|
||||
}{
|
||||
ID: vm.ID(),
|
||||
Type: vm.Type(),
|
||||
Controller: vm.Controller(),
|
||||
PublicKeyBase58: base58.Encode(vm.pubkey.ToBytes()),
|
||||
})
|
||||
}
|
||||
|
||||
func (vm *VerificationKey2019) UnmarshalJSON(bytes []byte) error {
|
||||
aux := struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Controller string `json:"controller"`
|
||||
PublicKeyBase58 string `json:"publicKeyBase58"`
|
||||
}{}
|
||||
err := json.Unmarshal(bytes, &aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.Type != vm.Type() {
|
||||
return errors.New("invalid type")
|
||||
}
|
||||
vm.id = aux.ID
|
||||
if len(vm.id) == 0 {
|
||||
return errors.New("invalid id")
|
||||
}
|
||||
vm.controller = aux.Controller
|
||||
if !did.HasValidDIDSyntax(vm.controller) {
|
||||
return errors.New("invalid controller")
|
||||
}
|
||||
|
||||
pubBytes, err := base58.Decode(aux.PublicKeyBase58)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyBase58: %w", err)
|
||||
}
|
||||
vm.pubkey, err = secp256k1.PublicKeyFromBytes(pubBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyBase58: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vm VerificationKey2019) ID() string {
|
||||
return vm.id
|
||||
}
|
||||
|
||||
func (vm VerificationKey2019) Type() string {
|
||||
return Type2019
|
||||
}
|
||||
|
||||
func (vm VerificationKey2019) Controller() string {
|
||||
return vm.controller
|
||||
}
|
||||
|
||||
func (vm VerificationKey2019) JsonLdContext() string {
|
||||
return JsonLdContext
|
||||
}
|
||||
|
||||
func (vm VerificationKey2019) Verify(data []byte, sig []byte) (bool, error) {
|
||||
return vm.pubkey.VerifyBytes(data, sig), nil
|
||||
}
|
||||
|
||||
func (vm VerificationKey2019) PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool {
|
||||
return local.PublicKeyIsCompatible(vm.pubkey)
|
||||
}
|
||||
|
||||
func (vm VerificationKey2019) KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error) {
|
||||
return local.KeyExchange(vm.pubkey)
|
||||
}
|
||||
27
verifications/secp256k1/VerificationKey2019_test.go
Normal file
27
verifications/secp256k1/VerificationKey2019_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package secp256k1vm_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
secp256k1vm "github.com/INFURA/go-did/verifications/secp256k1"
|
||||
)
|
||||
|
||||
func TestJsonRoundTrip(t *testing.T) {
|
||||
data := `{
|
||||
"id": "did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||
"controller": "did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||
"publicKeyBase58": "pg3p1vprqePgUoqfAQ1TTgxhL6zLYhHyzooR1pqLxo9F"
|
||||
}`
|
||||
|
||||
var mk secp256k1vm.VerificationKey2019
|
||||
err := json.Unmarshal([]byte(data), &mk)
|
||||
require.NoError(t, err)
|
||||
|
||||
bytes, err := json.Marshal(mk)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, data, string(bytes))
|
||||
}
|
||||
107
verifications/x25519/KeyAgreementKey2019.go
Normal file
107
verifications/x25519/KeyAgreementKey2019.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package x25519vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/crypto"
|
||||
"github.com/INFURA/go-did/crypto/x25519"
|
||||
)
|
||||
|
||||
// Specification: https://github.com/digitalbazaar/x25519-key-agreement-key-2019
|
||||
|
||||
const (
|
||||
JsonLdContext2019 = "https://w3id.org/security/suites/x25519-2019/v1"
|
||||
Type2019 = "X25519KeyAgreementKey2019"
|
||||
)
|
||||
|
||||
var _ did.VerificationMethodKeyAgreement = &KeyAgreementKey2019{}
|
||||
|
||||
type KeyAgreementKey2019 struct {
|
||||
id string
|
||||
pubkey *x25519.PublicKey
|
||||
controller string
|
||||
}
|
||||
|
||||
func NewKeyAgreementKey2019(id string, pubkey *x25519.PublicKey, controller did.DID) *KeyAgreementKey2019 {
|
||||
return &KeyAgreementKey2019{
|
||||
id: id,
|
||||
pubkey: pubkey,
|
||||
controller: controller.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2019) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Controller string `json:"controller"`
|
||||
PublicKeyBase58 string `json:"publicKeyBase58"`
|
||||
}{
|
||||
ID: k.ID(),
|
||||
Type: k.Type(),
|
||||
Controller: k.Controller(),
|
||||
PublicKeyBase58: base58.Encode(k.pubkey.ToBytes()),
|
||||
})
|
||||
}
|
||||
|
||||
func (k *KeyAgreementKey2019) UnmarshalJSON(bytes []byte) error {
|
||||
aux := struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Controller string `json:"controller"`
|
||||
PublicKeyBase58 string `json:"publicKeyBase58"`
|
||||
}{}
|
||||
err := json.Unmarshal(bytes, &aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.Type != k.Type() {
|
||||
return errors.New("invalid type")
|
||||
}
|
||||
k.id = aux.ID
|
||||
if len(k.id) == 0 {
|
||||
return errors.New("invalid id")
|
||||
}
|
||||
pubBytes, err := base58.Decode(aux.PublicKeyBase58)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyBase58: %w", err)
|
||||
}
|
||||
k.pubkey, err = x25519.PublicKeyFromBytes(pubBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyBase58: %w", err)
|
||||
}
|
||||
k.controller = aux.Controller
|
||||
if !did.HasValidDIDSyntax(k.controller) {
|
||||
return errors.New("invalid controller")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2019) ID() string {
|
||||
return k.id
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2019) Type() string {
|
||||
return Type2019
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2019) Controller() string {
|
||||
return k.controller
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2019) JsonLdContext() string {
|
||||
return JsonLdContext2019
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2019) PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool {
|
||||
return local.PublicKeyIsCompatible(k.pubkey)
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2019) KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error) {
|
||||
return local.KeyExchange(k.pubkey)
|
||||
}
|
||||
27
verifications/x25519/KeyAgreementKey2019_test.go
Normal file
27
verifications/x25519/KeyAgreementKey2019_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package x25519vm_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
x25519vm "github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
|
||||
func TestJsonRoundTrip2019(t *testing.T) {
|
||||
data := `{
|
||||
"id": "#z6LSkkqoZRC34AEpbkhZCqLDcHQVAxuLpQ7kC8XCXMVUfvjE",
|
||||
"type": "X25519KeyAgreementKey2019",
|
||||
"controller": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||
"publicKeyBase58": "A5fe37PAxhX5WNKngBpGHhC1KpNE7nwbK9oX2tqwxYxU"
|
||||
}`
|
||||
|
||||
var vm x25519vm.KeyAgreementKey2019
|
||||
err := json.Unmarshal([]byte(data), &vm)
|
||||
require.NoError(t, err)
|
||||
|
||||
bytes, err := json.Marshal(vm)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, data, string(bytes))
|
||||
}
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
// Specification: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519
|
||||
|
||||
const (
|
||||
JsonLdContext = "https://w3id.org/security/suites/x25519-2020/v1"
|
||||
Type = "X25519KeyAgreementKey2020"
|
||||
JsonLdContext2020 = "https://w3id.org/security/suites/x25519-2020/v1"
|
||||
Type2020 = "X25519KeyAgreementKey2020"
|
||||
)
|
||||
|
||||
var _ did.VerificationMethodKeyAgreement = &KeyAgreementKey2020{}
|
||||
@@ -81,7 +81,7 @@ func (k KeyAgreementKey2020) ID() string {
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) Type() string {
|
||||
return Type
|
||||
return Type2020
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) Controller() string {
|
||||
@@ -89,13 +89,13 @@ func (k KeyAgreementKey2020) Controller() string {
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) JsonLdContext() string {
|
||||
return JsonLdContext
|
||||
return JsonLdContext2020
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) PrivateKeyIsCompatible(local crypto.KeyExchangePrivateKey) bool {
|
||||
func (k KeyAgreementKey2020) PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool {
|
||||
return local.PublicKeyIsCompatible(k.pubkey)
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) KeyExchange(local crypto.KeyExchangePrivateKey) ([]byte, error) {
|
||||
func (k KeyAgreementKey2020) KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error) {
|
||||
return local.KeyExchange(k.pubkey)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
x25519vm "github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
|
||||
func TestJsonRoundTrip(t *testing.T) {
|
||||
func TestJsonRoundTrip2020(t *testing.T) {
|
||||
data := `{
|
||||
"id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW",
|
||||
"type": "X25519KeyAgreementKey2020",
|
||||
|
||||
Reference in New Issue
Block a user