2 Commits

Author SHA1 Message Date
Michael Muré
4bfdc4bf2e "bytes", PKCS8/X509 x DER/PEM serialization for every keys 2025-06-18 19:04:17 +02:00
Michael Muré
e15cc2295a WIP: P-256 support, serialization improvements 2025-06-18 14:21:17 +02:00
105 changed files with 1295 additions and 9540 deletions

10
.github/Repo.toml vendored
View File

@@ -1,10 +0,0 @@
[scopes]
ci = [".github/workflows"]
config = [".github", "go.mod", "go.sum", ".gitignore"]
docs = ["Readme.md", "crypto/Readme.md", "design.md", "LICENSE.md"]
tests = ["didtest", "verifiers/did-key/testvectors"]
core = ["*.go"]
crypto = ["crypto"]
verifiers = ["verifiers"]
controller = ["controller"]
document = ["document"]

140
Readme.md
View File

@@ -1,20 +1,20 @@
<div align="center"> <div align="center">
<h1 align="center">go-did-it</h1> <h1 align="center">go-did</h1>
<p> <p>
<a href="https://code.sonr.org/go/did-it/tags"> <a href="https://github.com/INFURA/go-did/tags">
<img alt="GitHub Tag" src="https://img.shields.io/github/v/tag/MetaMask/go-did-it"> <img alt="GitHub Tag" src="https://img.shields.io/github/v/tag/INFURA/go-did">
</a> </a>
<a href="https://code.sonr.org/go/did-it/actions?query="> <a href="https://github.com/INFURA/go-did/actions?query=">
<img src="https://code.sonr.org/go/did-it/actions/workflows/gotest.yml/badge.svg" alt="Build Status"> <img src="https://github.com/INFURA/go-did/actions/workflows/gotest.yml/badge.svg" alt="Build Status">
</a> </a>
<a href="https://MetaMask.github.io/go-did-it/dev/bench/"> <a href="https://INFURA.github.io/go-did/dev/bench/">
<img alt="Go benchmarks" src="https://img.shields.io/badge/Benchmarks-go-blue"> <img alt="Go benchmarks" src="https://img.shields.io/badge/Benchmarks-go-blue">
</a> </a>
<a href="https://code.sonr.org/go/did-it/blob/v1/LICENSE.md"> <a href="https://github.com/INFURA/go-did/blob/v1/LICENSE.md">
<img alt="Apache 2.0 + MIT License" src="https://img.shields.io/badge/License-Apache--2.0+MIT-green"> <img alt="Apache 2.0 + MIT License" src="https://img.shields.io/badge/License-Apache--2.0+MIT-green">
</a> </a>
<a href="https://pkg.go.dev/code.sonr.org/go/did-it"> <a href="https://pkg.go.dev/github.com/INFURA/go-did">
<img src="https://img.shields.io/badge/Docs-godoc-blue" alt="Docs"> <img src="https://img.shields.io/badge/Docs-godoc-blue" alt="Docs">
</a> </a>
</p> </p>
@@ -27,126 +27,10 @@ This is an implementation of Decentralized Identifiers (DIDs) in go. It differs
- **support producing and using DIDs**: unlike some others, this all-in-one implementation is meant to create, manipulate and handle DIDs - **support producing and using DIDs**: unlike some others, this all-in-one implementation is meant to create, manipulate and handle DIDs
- **extensible**: you can easily register your custom DID method - **extensible**: you can easily register your custom DID method
Built with ❤️ by [Consensys](https://consensys.io/). DID spec concepts:
## Concepts ![DID spec concepts](resources/did_brief_architecture_overview.svg)
![`go-did-it` concepts](.github/concepts.png) `go-did` concepts:
## Installation ![`go-did` concepts](resources/go-did%20concepts.png)
```bash
go get code.sonr.org/go/did-it
```
## Usage
### Signature verification
On the verifier (~server) side, you can parse and resolve DIDs and perform signature verification:
```go
package main
import (
"encoding/base64"
"fmt"
"code.sonr.org/go/did-it"
// 0) Import the methods you want to support
_ "code.sonr.org/go/did-it/verifiers/did-key"
)
func main() {
// 1) Parse the DID string into a DID object
d, _ := did.Parse("did:key:z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse")
// 2) Resolve to the DID Document
doc, _ := d.Document()
// 3) Use the appropriate set of verification methods (ex: verify a signature for authentication purpose)
sig, _ := base64.StdEncoding.DecodeString("nhpkr5a7juUM2eDpDRSJVdEE++0SYqaZXHtuvyafVFUx8zsOdDSrij+vHmd/ARwUOmi/ysmSD+b3K9WTBtmmBQ==")
if ok, method := did.TryAllVerify(doc.Authentication(), []byte("message"), sig); ok {
fmt.Println("Signature is valid, verified with method:", method.Type(), method.ID())
} else {
fmt.Println("Signature is invalid")
}
// Output: Signature is valid, verified with method: Ed25519VerificationKey2020 did:key:z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse#z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse
}
```
### Key agreement
You can also compute a shared secret to bootstrap an encrypted communication protocol.
> **⚠️ Security Warning**: The shared secret returned by key agreement should NOT be used directly as an encryption key. It must be processed through a Key Derivation Function (KDF) such as HKDF before being used in cryptographic protocols. Using the raw shared secret directly can lead to security vulnerabilities.
```go
package main
import (
"encoding/base64"
"fmt"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/crypto/x25519"
// 0) Import the methods you want to support
_ "code.sonr.org/go/did-it/verifiers/did-key"
)
func main() {
// 1) We have a private key for Alice
privAliceBytes, _ := base64.StdEncoding.DecodeString("fNOf3xWjFZYGYWixorM5+JR+u/2Udnc9Zw5+9rSvjqo=")
privAlice, _ := x25519.PrivateKeyFromBytes(privAliceBytes)
// 2) We resolve the DID Document for Bob
dBob, _ := did.Parse("did:key:z6MkgRNXpJRbEE6FoXhT8KWHwJo4KyzFo1FdSEFpRLh5vuXZ")
docBob, _ := dBob.Document()
// 3) We perform the key agreement
key, method, _ := did.FindMatchingKeyAgreement(docBob.KeyAgreement(), privAlice)
fmt.Println("Shared key:", base64.StdEncoding.EncodeToString(key))
fmt.Println("Verification method used:", method.Type(), method.ID())
// Output: Shared key: 7G1qwS/gn5W1hxBtObHc3F0jA7m2vuXkLJJ32yBuHVQ=
// Verification method used: X25519KeyAgreementKey2020 did:key:z6MkgRNXpJRbEE6FoXhT8KWHwJo4KyzFo1FdSEFpRLh5vuXZ#z6LSjeQx2VkXz8yirhrYJv8uicu9BBaeYU3Q1D9sFBovhmPF
}
```
## Features
### Supported DID Methods
| Method | Controller | Verifier | Description |
|-----------|------------|----------|----------------------------------------------------|
| `did:key` | ✅ | ✅ | Self-contained DIDs based on public keys |
| `did:web` | ❌ | ✅ | DID document resolved with HTTP |
| `did:plc` | ❌ | ✅ | Bluesky's DID with rotation and a public directory |
### Supported Verification Method Types
| Type | Use Case |
|-------------------------------------|--------------------------|
| `EcdsaSecp256k1VerificationKey2019` | secp256k1 signatures |
| `Ed25519VerificationKey2018` | Ed25519 signatures |
| `Ed25519VerificationKey2020` | Ed25519 signatures |
| `JsonWebKey2020` | All supported algorithms |
| `Multikey` | All supported algorithms |
| `P256Key2021` | P-256 signatures |
| `X25519KeyAgreementKey2020` | X25519 key agreement |
### Supported Cryptographic Algorithms
| Algorithm | Signature Format | Public Key Formats | Private Key Formats | Key Agreement |
|-----------------|-------------------|-------------------------------------|---------------------------|----------------|
| Ed25519 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ (via X25519) |
| ECDSA P-256 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ |
| ECDSA P-384 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ |
| ECDSA P-521 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ |
| ECDSA secp256k1 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ |
| RSA | PKCS#1 v1.5 ASN.1 | X.509 DER/PEM, Multibase | PKCS#8 DER/PEM | ❌ |
| X25519 | ❌ | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ |

View File

@@ -1,15 +0,0 @@
package didkeyctl
import (
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/crypto"
didkey "code.sonr.org/go/did-it/verifiers/did-key"
)
func FromPublicKey(pub crypto.PublicKey) did.DID {
return didkey.FromPublicKey(pub)
}
func FromPrivateKey(priv crypto.PrivateKey) did.DID {
return didkey.FromPrivateKey(priv)
}

View File

@@ -1,93 +0,0 @@
# Crypto package
This crypto package is a thin ergonomic layer on top of the normal golang crypto packages or `x/crypto`.
It aims to solve the following problems with the standard crypto packages:
- different algorithms have different APIs and ergonomics, which makes it hard to use them interchangeably
- occasionally, it's quite hard to figure out how to do simple tasks (like encoding/decoding keys)
- it's still necessary to make some educated choices (e.g. which hash function to use for signatures)
- sometimes features are left out (e.g. ed25519 to X25519 for key exchange, secp256k1...)
- some hash functions are not available in the standard library with no easy way to extend it (e.g. KECCAK-256)
To do so, this package provides and implements a set of shared interfaces for all algorithms. As not all algorithms
support all features (e.g. RSA keys don't support key exchange), some interfaces are optionally implemented.
An additional benefit of shared interfaces is that a shared test suite can be written to test all algorithms, which this
package does.
Note: this is not a dig or a criticism of the golang crypto packages, just an attempt to make them easier to use.
## Example
```go
// This example demonstrates how to use the crypto package without going over all the features.
// We will use P-256 keys, but they all work the same way (although not all have all the features).
// 0: Generate a key pair
pubAlice, privAlice, err := p256.GenerateKeyPair()
handleErr(err)
// 1: Serialize a key, read it back, verify it's the same
privAliceBytes := privAlice.ToPKCS8DER()
privAlice2, err := p256.PrivateKeyFromPKCS8DER(privAliceBytes)
handleErr(err)
fmt.Println("Keys are equals:", privAlice.Equal(privAlice2))
// 2: Sign a message, verify the signature.
// Signatures can be made in raw bytes (SignToBytes) or ASN.1 DER format (SignToASN1).
msg := []byte("hello world")
sig, err := privAlice.SignToBytes(msg)
handleErr(err)
fmt.Println("Signature is valid:", pubAlice.VerifyBytes(msg, sig))
// 3: Signatures are done with an opinionated default configuration, but you can override it.
// For example, the default hash function for P-256 is SHA-256, but you can use SHA-384 instead.
opts := []crypto.SigningOption{crypto.WithSigningHash(crypto.SHA384)}
sig384, err := privAlice.SignToBytes(msg, opts...)
handleErr(err)
fmt.Println("Signature is valid (SHA-384):", pubAlice.VerifyBytes(msg, sig384, opts...))
// 4: Key exchange: generate a second key-pair and compute a shared secret.
// ⚠️ Security Warning: The shared secret returned by key agreement should NOT be used directly as an encryption key.
// It must be processed through a Key Derivation Function (KDF) such as HKDF before being used in cryptographic protocols.
// Using the raw shared secret directly can lead to security vulnerabilities.
pubBob, privBob, err := p256.GenerateKeyPair()
handleErr(err)
shared1, err := privAlice.KeyExchange(pubBob)
handleErr(err)
shared2, err := privBob.KeyExchange(pubAlice)
handleErr(err)
fmt.Println("Shared secrets are identical:", bytes.Equal(shared1, shared2))
// 5: Bonus: one very annoying thing in cryptographic protocols is that the other side needs to know the configuration
// you used for your signature. Having defaults or implied config only work sor far.
// To solve this problem, this package integrates varsig: a format to describe the signing configuration. This varsig
// can be attached to the signature, and the other side doesn't have to guess any more. Here is how it works:
varsigBytes := privAlice.Varsig(opts...).Encode()
fmt.Println("Varsig:", base64.StdEncoding.EncodeToString(varsigBytes))
sig, err = privAlice.SignToBytes(msg, opts...)
handleErr(err)
varsigDecoded, err := varsig.Decode(varsigBytes)
handleErr(err)
fmt.Println("Signature with varsig is valid:", pubAlice.VerifyBytes(msg, sig, crypto.WithVarsig(varsigDecoded)))
// Output:
// Keys are equals: true
// Signature is valid: true
// Signature is valid (SHA-384): true
// Shared secrets are identical: true
// Varsig: NAHsAYAkIF8=
// Signature with varsig is valid: true
```
## Supported Cryptographic Algorithms
| Algorithm | Signature Format | Public Key Formats | Private Key Formats | Key Agreement |
|-----------------|-------------------|-------------------------------------|---------------------------|----------------|
| Ed25519 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ (via X25519) |
| ECDSA P-256 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ |
| ECDSA P-384 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ |
| ECDSA P-521 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ |
| ECDSA secp256k1 | Raw bytes, ASN.1 | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ |
| RSA | PKCS#1 v1.5 ASN.1 | X.509 DER/PEM, Multibase | PKCS#8 DER/PEM | ❌ |
| X25519 | ❌ | Raw bytes, X.509 DER/PEM, Multibase | Raw bytes, PKCS#8 DER/PEM | ✅ |

View File

@@ -1,38 +0,0 @@
package allkeys
import (
"fmt"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/ed25519"
helpers "code.sonr.org/go/did-it/crypto/internal"
"code.sonr.org/go/did-it/crypto/p256"
"code.sonr.org/go/did-it/crypto/p384"
"code.sonr.org/go/did-it/crypto/p521"
"code.sonr.org/go/did-it/crypto/rsa"
"code.sonr.org/go/did-it/crypto/secp256k1"
"code.sonr.org/go/did-it/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)
}

View File

@@ -1,624 +0,0 @@
package testsuite
import (
"fmt"
"strings"
"testing"
"text/tabwriter"
mbase "github.com/multiformats/go-multibase"
"github.com/multiformats/go-varint"
"github.com/stretchr/testify/require"
"github.com/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/crypto"
)
// TestHarness describes a keypair implementation for the test/bench suites to operate on.
type TestHarness[PubT crypto.PublicKey, PrivT crypto.PrivateKey] struct {
Name string
GenerateKeyPair func() (PubT, PrivT, error)
PublicKeyFromBytes func(b []byte) (PubT, error)
PublicKeyFromPublicKeyMultibase func(multibase string) (PubT, error)
PublicKeyFromX509DER func(bytes []byte) (PubT, error)
PublicKeyFromX509PEM func(str string) (PubT, error)
PrivateKeyFromBytes func(b []byte) (PrivT, error)
PrivateKeyFromPKCS8DER func(bytes []byte) (PrivT, error)
PrivateKeyFromPKCS8PEM func(str string) (PrivT, error)
MultibaseCode uint64
DefaultHash crypto.Hash
OtherHashes []crypto.Hash
PublicKeyBytesSize int
PrivateKeyBytesSize int
SignatureBytesSize int
}
// TestSuite runs a set of tests against a keypair implementation.
func TestSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](t *testing.T, harness TestHarness[PubT, PrivT]) {
stats := struct {
bytesPubSize int
bytesPrivSize int
x509DerPubSize int
pkcs8DerPrivSize int
x509PemPubSize int
pkcs8PemPrivSize int
sigRawSize int
sigAsn1Size int
}{}
t.Cleanup(func() {
out := strings.Builder{}
out.WriteString("\nKeypairs (in bytes):\n")
w := tabwriter.NewWriter(&out, 0, 0, 3, ' ', 0)
_, _ = fmt.Fprintln(w, "\tPublic key\tPrivate key")
_, _ = fmt.Fprintf(w, "Bytes\t%v\t%v\n", stats.bytesPubSize, stats.bytesPrivSize)
_, _ = fmt.Fprintf(w, "DER (pub:x509, priv:PKCS#8)\t%v\t%v\n", stats.x509DerPubSize, stats.pkcs8DerPrivSize)
_, _ = fmt.Fprintf(w, "PEM (pub:x509, priv:PKCS#8)\t%v\t%v\n", stats.x509PemPubSize, stats.pkcs8PemPrivSize)
_ = w.Flush()
out.WriteString("\nSignatures (in bytes):\n")
w.Init(&out, 0, 0, 3, ' ', 0)
_, _ = fmt.Fprintln(w, "Raw bytes\tASN.1")
_, _ = fmt.Fprintf(w, "%v\t%v\n", stats.sigRawSize, stats.sigAsn1Size)
_ = w.Flush()
t.Logf("Test result for %s:\n%s\n", harness.Name, out.String())
})
t.Run("GenerateKeyPair", func(t *testing.T) {
pub, priv, err := harness.GenerateKeyPair()
require.NoError(t, err)
require.NotNil(t, pub)
require.NotNil(t, priv)
require.True(t, pub.Equal(priv.Public()))
})
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)
require.True(t, pub1.Equal(pub1))
require.True(t, priv1.Equal(priv1))
require.False(t, pub1.Equal(pub2))
require.False(t, priv1.Equal(priv2))
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(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 := 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 = privTb.ToBytes()
stats.bytesPrivSize = len(bytes)
rtPriv, err := harness.PrivateKeyFromBytes(bytes)
require.NoError(t, err)
require.True(t, priv.Equal(rtPriv))
require.Equal(t, harness.PrivateKeyBytesSize, len(bytes))
})
t.Run("MultibaseRoundTrip", func(t *testing.T) {
pub, _, err := harness.GenerateKeyPair()
require.NoError(t, err)
mb := pub.ToPublicKeyMultibase()
rt, err := harness.PublicKeyFromPublicKeyMultibase(mb)
require.NoError(t, err)
require.Equal(t, pub, rt)
encoding, bytes, err := mbase.Decode(mb)
require.NoError(t, err)
require.Equal(t, mbase.Base58BTC, int32(encoding)) // according to the DID spec
code, _, err := varint.FromUvarint(bytes)
require.NoError(t, err)
require.Equal(t, harness.MultibaseCode, code)
})
t.Run("PublicKeyX509RoundTrip", func(t *testing.T) {
pub, _, err := harness.GenerateKeyPair()
require.NoError(t, err)
der := pub.ToX509DER()
stats.x509DerPubSize = len(der)
rt, err := harness.PublicKeyFromX509DER(der)
require.NoError(t, err)
require.True(t, pub.Equal(rt))
pem := pub.ToX509PEM()
stats.x509PemPubSize = len(pem)
rt, err = harness.PublicKeyFromX509PEM(pem)
require.NoError(t, err)
require.True(t, pub.Equal(rt))
})
t.Run("PrivateKeyPKCS8RoundTrip", func(t *testing.T) {
pub, priv, err := harness.GenerateKeyPair()
require.NoError(t, err)
der := priv.ToPKCS8DER()
stats.pkcs8DerPrivSize = len(der)
rt, err := harness.PrivateKeyFromPKCS8DER(der)
require.NoError(t, err)
require.True(t, priv.Equal(rt))
require.True(t, pub.Equal(rt.Public()))
pem := priv.ToPKCS8PEM()
stats.pkcs8PemPrivSize = len(pem)
rt, err = harness.PrivateKeyFromPKCS8PEM(pem)
require.NoError(t, err)
require.True(t, priv.Equal(rt))
require.True(t, pub.Equal(rt.Public()))
})
t.Run("Signature", func(t *testing.T) {
pub, priv, err := harness.GenerateKeyPair()
require.NoError(t, err)
type testcase struct {
name string
signer func(msg []byte, opts ...crypto.SigningOption) ([]byte, error)
verifier func(msg []byte, sig []byte, opts ...crypto.SigningOption) bool
varsig func(opts ...crypto.SigningOption) varsig.Varsig
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,
varsig: spriv.Varsig,
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,
varsig: spriv.Varsig,
stats: &stats.sigAsn1Size,
defaultHash: harness.DefaultHash,
otherHashes: harness.OtherHashes,
})
})
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
msg := []byte("message")
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)
vsig := tc.varsig()
require.Equal(t, harness.DefaultHash.ToVarsigHash(), vsig.Hash())
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, sigNoParams, crypto.WithVarsig(vsig))
require.True(t, valid)
valid = tc.verifier(msg, sigDefault)
require.True(t, valid)
valid = tc.verifier(msg, sigDefault, crypto.WithVarsig(vsig))
require.True(t, valid)
valid = tc.verifier([]byte("wrong message"), sigNoParams)
require.False(t, valid)
valid = tc.verifier([]byte("wrong message"), sigNoParams, crypto.WithVarsig(vsig))
require.False(t, valid)
valid = tc.verifier([]byte("wrong message"), sigDefault)
require.False(t, valid)
valid = tc.verifier([]byte("wrong message"), sigDefault, crypto.WithVarsig(vsig))
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)
vsig := tc.varsig(crypto.WithSigningHash(hash))
require.Equal(t, hash.ToVarsigHash(), vsig.Hash())
valid := tc.verifier(msg, sig, crypto.WithSigningHash(hash))
require.True(t, valid)
valid = tc.verifier(msg, sig, crypto.WithVarsig(vsig))
require.True(t, valid)
valid = tc.verifier([]byte("wrong message"), sig)
require.False(t, valid)
valid = tc.verifier([]byte("wrong message"), sig, crypto.WithVarsig(vsig))
require.False(t, valid)
})
}
}
})
t.Run("KeyExchange", func(t *testing.T) {
pub1, priv1, err := harness.GenerateKeyPair()
require.NoError(t, err)
pub2, priv2, err := harness.GenerateKeyPair()
require.NoError(t, err)
pub3, _, err := harness.GenerateKeyPair()
require.NoError(t, err)
kePriv1, ok := crypto.PrivateKey(priv1).(crypto.PrivateKeyKeyExchange)
if !ok {
t.Skip("Key exchange is not implemented")
}
kePriv2 := crypto.PrivateKey(priv2).(crypto.PrivateKeyKeyExchange)
// TODO: test with incompatible public keys
require.True(t, kePriv1.PublicKeyIsCompatible(pub2))
require.True(t, kePriv2.PublicKeyIsCompatible(pub1))
// 1 --> 2
kA, err := kePriv1.KeyExchange(pub2)
require.NoError(t, err)
require.NotEmpty(t, kA)
// 2 --> 1
kB, err := kePriv2.KeyExchange(pub1)
require.NoError(t, err)
require.NotEmpty(t, kB)
// 2 --> 3
kC, err := kePriv2.KeyExchange(pub3)
require.NoError(t, err)
require.NotEmpty(t, kC)
require.Equal(t, kA, kB)
require.NotEqual(t, kB, kC)
})
}
// BenchSuite runs a set of benchmarks on a keypair implementation.
func BenchSuite[PubT crypto.PublicKey, PrivT crypto.PrivateKey](b *testing.B, harness TestHarness[PubT, PrivT]) {
b.Run("GenerateKeyPair", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _, _ = harness.GenerateKeyPair()
}
})
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++ {
_ = pubTb.ToBytes()
}
})
b.Run("PubFromBytes", func(b *testing.B) {
pub, _, err := harness.GenerateKeyPair()
require.NoError(b, err)
pubTb := (crypto.PublicKey(pub)).(crypto.PublicKeyToBytes)
buf := pubTb.ToBytes()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = harness.PublicKeyFromBytes(buf)
}
})
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++ {
_ = privTb.ToBytes()
}
})
b.Run("PrivFromBytes", func(b *testing.B) {
_, priv, err := harness.GenerateKeyPair()
require.NoError(b, err)
privTb := (crypto.PrivateKey(priv)).(crypto.PrivateKeyToBytes)
buf := privTb.ToBytes()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = harness.PrivateKeyFromBytes(buf)
}
})
})
b.Run("DER", func(b *testing.B) {
b.Run("PubToDER", func(b *testing.B) {
pub, _, err := harness.GenerateKeyPair()
require.NoError(b, err)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = pub.ToX509DER()
}
})
b.Run("PubFromDER", func(b *testing.B) {
pub, _, err := harness.GenerateKeyPair()
require.NoError(b, err)
buf := pub.ToX509DER()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = harness.PublicKeyFromX509DER(buf)
}
})
b.Run("PrivToDER", func(b *testing.B) {
_, priv, err := harness.GenerateKeyPair()
require.NoError(b, err)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = priv.ToPKCS8DER()
}
})
b.Run("PrivFromDER", func(b *testing.B) {
_, priv, err := harness.GenerateKeyPair()
require.NoError(b, err)
buf := priv.ToPKCS8DER()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = harness.PrivateKeyFromPKCS8DER(buf)
}
})
})
b.Run("PEM", func(b *testing.B) {
b.Run("PubToPEM", func(b *testing.B) {
pub, _, err := harness.GenerateKeyPair()
require.NoError(b, err)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = pub.ToX509PEM()
}
})
b.Run("PubFromPEM", func(b *testing.B) {
pub, _, err := harness.GenerateKeyPair()
require.NoError(b, err)
buf := pub.ToX509PEM()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = harness.PublicKeyFromX509PEM(buf)
}
})
b.Run("PrivToPEM", func(b *testing.B) {
_, priv, err := harness.GenerateKeyPair()
require.NoError(b, err)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = priv.ToPKCS8PEM()
}
})
b.Run("PrivFromPEM", func(b *testing.B) {
_, priv, err := harness.GenerateKeyPair()
require.NoError(b, err)
buf := priv.ToPKCS8PEM()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = harness.PrivateKeyFromPKCS8PEM(buf)
}
})
})
b.Run("Signatures", func(b *testing.B) {
b.Run("Sign to Bytes signature", func(b *testing.B) {
if !pubImplements[PubT, crypto.PublicKeySigningBytes]() {
b.Skip("Signature to bytes is not implemented")
}
_, priv, err := harness.GenerateKeyPair()
require.NoError(b, err)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningBytes)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = spriv.SignToBytes([]byte("message"))
}
})
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.PublicKeySigningBytes)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningBytes)
sig, err := spriv.SignToBytes([]byte("message"))
require.NoError(b, err)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
spub.VerifyBytes([]byte("message"), sig)
}
})
b.Run("Verify from varsig 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.PublicKeySigningBytes)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningBytes)
sig, err := spriv.SignToBytes([]byte("message"))
require.NoError(b, err)
vsig := spriv.Varsig()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
spub.VerifyBytes([]byte("message"), sig, crypto.WithVarsig(vsig))
}
})
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.PrivateKeySigningASN1)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = spriv.SignToASN1([]byte("message"))
}
})
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.PublicKeySigningASN1)
spriv := (crypto.PrivateKey(priv)).(crypto.PrivateKeySigningASN1)
sig, err := spriv.SignToASN1([]byte("message"))
require.NoError(b, err)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
spub.VerifyASN1([]byte("message"), sig)
}
})
})
b.Run("Key exchange", func(b *testing.B) {
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.PrivateKeyKeyExchange)
pub2, _, err := harness.GenerateKeyPair()
require.NoError(b, err)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = kePriv1.KeyExchange(pub2)
}
})
})
}
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
}

View File

@@ -1,30 +0,0 @@
package ed25519
import (
"crypto/ed25519"
"crypto/rand"
)
const (
// PublicKeyBytesSize is the size, in bytes, of public keys in raw bytes.
PublicKeyBytesSize = ed25519.PublicKeySize
// PrivateKeyBytesSize is the size, in bytes, of private keys in raw bytes.
PrivateKeyBytesSize = ed25519.PrivateKeySize
// SignatureBytesSize is the size, in bytes, of signatures in raw bytes.
SignatureBytesSize = ed25519.SignatureSize
MultibaseCode = uint64(0xed)
)
func GenerateKeyPair() (PublicKey, PrivateKey, error) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return PublicKey{}, PrivateKey{}, err
}
return PublicKey{k: pub}, PrivateKey{k: priv}, nil
}
const (
pemPubBlockType = "PUBLIC KEY"
pemPrivBlockType = "PRIVATE KEY"
)

View File

@@ -1,34 +0,0 @@
package ed25519
import (
"testing"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/_testsuite"
)
var harness = testsuite.TestHarness[PublicKey, PrivateKey]{
Name: "ed25519",
GenerateKeyPair: GenerateKeyPair,
PublicKeyFromBytes: PublicKeyFromBytes,
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
PublicKeyFromX509DER: PublicKeyFromX509DER,
PublicKeyFromX509PEM: PublicKeyFromX509PEM,
PrivateKeyFromBytes: PrivateKeyFromBytes,
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
MultibaseCode: MultibaseCode,
DefaultHash: crypto.SHA512,
OtherHashes: nil,
PublicKeyBytesSize: PublicKeyBytesSize,
PrivateKeyBytesSize: PrivateKeyBytesSize,
SignatureBytesSize: SignatureBytesSize,
}
func TestSuite(t *testing.T) {
testsuite.TestSuite(t, harness)
}
func BenchmarkSuite(b *testing.B) {
testsuite.BenchSuite(b, harness)
}

View File

@@ -1,134 +0,0 @@
package ed25519
import (
"crypto/ed25519"
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/ucan-wg/go-varsig"
"golang.org/x/crypto/cryptobyte"
"code.sonr.org/go/did-it/crypto"
)
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
var _ crypto.PrivateKeyToBytes = &PrivateKey{}
type PrivateKey struct {
k ed25519.PrivateKey
}
// PrivateKeyFromBytes converts a serialized private key to a PrivateKey.
// This compact serialization format is the raw key material, without metadata or structure.
// It returns an error if the slice is not the right size.
func PrivateKeyFromBytes(b []byte) (PrivateKey, error) {
if len(b) != PrivateKeyBytesSize {
return PrivateKey{}, fmt.Errorf("invalid ed25519 private key size")
}
// make a copy
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
}
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.
func PrivateKeyFromPKCS8PEM(str string) (PrivateKey, error) {
block, _ := pem.Decode([]byte(str))
if block == nil {
return PrivateKey{}, fmt.Errorf("failed to decode PEM block")
}
if block.Type != pemPrivBlockType {
return PrivateKey{}, 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 {
return PublicKey{k: p.k.Public().(ed25519.PublicKey)}
}
func (p PrivateKey) Varsig(opts ...crypto.SigningOption) varsig.Varsig {
params := crypto.CollectSigningOptions(opts)
return varsig.NewEdDSAVarsig(varsig.CurveEd25519, params.HashOrDefault(crypto.SHA512).ToVarsigHash(), params.PayloadEncoding())
}
func (p PrivateKey) SignToBytes(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
params := crypto.CollectSigningOptions(opts)
hash := params.HashOrDefault(crypto.SHA512)
if 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, opts ...crypto.SigningOption) ([]byte, error) {
params := crypto.CollectSigningOptions(opts)
hash := params.HashOrDefault(crypto.SHA512)
if 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)
return b.Bytes()
}
func (p PrivateKey) ToBytes() []byte {
// Copy the private key to a fixed size buffer that can get allocated on the
// caller's stack after inlining.
var buf [PrivateKeyBytesSize]byte
return append(buf[:0], p.k...)
}
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,
}))
}
// Seed returns the private key seed corresponding to priv. It is provided for
// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
// in this package.
func (p PrivateKey) Seed() []byte {
return p.k.Seed()
}

View File

@@ -1,143 +0,0 @@
package ed25519
import (
"crypto/ed25519"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"fmt"
"github.com/ucan-wg/go-varsig"
"golang.org/x/crypto/cryptobyte"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/internal"
)
var _ crypto.PublicKeySigningBytes = PublicKey{}
var _ crypto.PublicKeySigningASN1 = PublicKey{}
var _ crypto.PublicKeyToBytes = PublicKey{}
type PublicKey struct {
k ed25519.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 PublicKey{}, fmt.Errorf("invalid ed25519 public key size")
}
// make a copy
return PublicKey{k: append([]byte{}, b...)}, nil
}
// PublicKeyFromPublicKeyMultibase decodes the public key from its PublicKeyMultibase form
func PublicKeyFromPublicKeyMultibase(multibase string) (PublicKey, error) {
code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase)
if err != nil {
return PublicKey{}, err
}
if code != MultibaseCode {
return PublicKey{}, fmt.Errorf("invalid code")
}
if len(bytes) != PublicKeyBytesSize {
return PublicKey{}, fmt.Errorf("invalid ed25519 public key size")
}
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 PublicKey{}, err
}
return PublicKey{k: pub.(ed25519.PublicKey)}, 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 PublicKey{}, fmt.Errorf("failed to decode PEM block")
}
if block.Type != pemPubBlockType {
return PublicKey{}, fmt.Errorf("incorrect PEM block type")
}
return PublicKeyFromX509DER(block.Bytes)
}
func (p PublicKey) ToBytes() []byte {
// Copy the private key to a fixed size buffer that can get allocated on the
// caller's stack after inlining.
var buf [PublicKeyBytesSize]byte
return append(buf[:0], p.k...)
}
func (p PublicKey) ToPublicKeyMultibase() string {
return helpers.PublicKeyMultibaseEncode(MultibaseCode, p.k)
}
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,
}))
}
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) VerifyBytes(message, signature []byte, opts ...crypto.SigningOption) bool {
params := crypto.CollectSigningOptions(opts)
if !params.VarsigMatch(varsig.AlgorithmEdDSA, uint64(varsig.CurveEd25519), 0) {
return false
}
if params.HashOrDefault(crypto.SHA512) != 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, opts ...crypto.SigningOption) bool {
params := crypto.CollectSigningOptions(opts)
if !params.VarsigMatch(varsig.AlgorithmEdDSA, uint64(varsig.CurveEd25519), 0) {
return false
}
if params.HashOrDefault(crypto.SHA512) != crypto.SHA512 {
// ed25519 does not support custom hash functions
return false
}
var s cryptobyte.String = signature
var bitString asn1.BitString
if !s.ReadASN1BitString(&bitString) {
return false
}
if bitString.BitLength != SignatureBytesSize*8 {
return false
}
return ed25519.Verify(p.k, message, bitString.Bytes)
}

View File

@@ -1,79 +0,0 @@
package crypto_test
import (
"bytes"
"encoding/base64"
"fmt"
"github.com/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/p256"
)
func Example() {
// This example demonstrates how to use the crypto package without going over all the features.
// We will use P-256 keys, but they all work the same way (although not all have all the features).
// 0: Generate a key pair
pubAlice, privAlice, err := p256.GenerateKeyPair()
handleErr(err)
// 1: Serialize a key, read it back, verify it's the same
privAliceBytes := privAlice.ToPKCS8DER()
privAlice2, err := p256.PrivateKeyFromPKCS8DER(privAliceBytes)
handleErr(err)
fmt.Println("Keys are equals:", privAlice.Equal(privAlice2))
// 2: Sign a message, verify the signature.
// Signatures can be made in raw bytes (SignToBytes) or ASN.1 DER format (SignToASN1).
msg := []byte("hello world")
sig, err := privAlice.SignToBytes(msg)
handleErr(err)
fmt.Println("Signature is valid:", pubAlice.VerifyBytes(msg, sig))
// 3: Signatures are done with an opinionated default configuration, but you can override it.
// For example, the default hash function for P-256 is SHA-256, but you can use SHA-384 instead.
opts := []crypto.SigningOption{crypto.WithSigningHash(crypto.SHA384)}
sig384, err := privAlice.SignToBytes(msg, opts...)
handleErr(err)
fmt.Println("Signature is valid (SHA-384):", pubAlice.VerifyBytes(msg, sig384, opts...))
// 4: Key exchange: generate a second key-pair and compute a shared secret.
// ⚠️ Security Warning: The shared secret returned by key agreement should NOT be used directly as an encryption key.
// It must be processed through a Key Derivation Function (KDF) such as HKDF before being used in cryptographic protocols.
// Using the raw shared secret directly can lead to security vulnerabilities.
pubBob, privBob, err := p256.GenerateKeyPair()
handleErr(err)
shared1, err := privAlice.KeyExchange(pubBob)
handleErr(err)
shared2, err := privBob.KeyExchange(pubAlice)
handleErr(err)
fmt.Println("Shared secrets are identical:", bytes.Equal(shared1, shared2))
// 5: Bonus: one very annoying thing in cryptographic protocols is that the other side needs to know the configuration
// you used for your signature. Having defaults or implied config only work sor far.
// To solve this problem, this package integrates varsig: a format to describe the signing configuration. This varsig
// can be attached to the signature, and the other side doesn't have to guess any more. Here is how it works:
varsigBytes := privAlice.Varsig(opts...).Encode()
fmt.Println("Varsig:", base64.StdEncoding.EncodeToString(varsigBytes))
sig, err = privAlice.SignToBytes(msg, opts...)
handleErr(err)
varsigDecoded, err := varsig.Decode(varsigBytes)
handleErr(err)
fmt.Println("Signature with varsig is valid:", pubAlice.VerifyBytes(msg, sig, crypto.WithVarsig(varsigDecoded)))
// Output:
// Keys are equals: true
// Signature is valid: true
// Signature is valid (SHA-384): true
// Shared secrets are identical: true
// Varsig: NAHsAYAkIF8=
// Signature with varsig is valid: true
}
func handleErr(err error) {
if err != nil {
panic(err)
}
}

View File

@@ -1,171 +0,0 @@
package crypto
import (
stdcrypto "crypto"
"hash"
"strconv"
"github.com/ucan-wg/go-varsig"
"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.
// Hash is similar to crypto.Hash but can be extended with more values.
type Hash uint
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
)
// HashFunc simply returns the value of h so that [Hash] implements [SignerOpts].
func (h Hash) HashFunc() Hash {
return h
}
// String returns the name of the hash function.
func (h Hash) String() string {
if h < maxStdHash {
return stdcrypto.Hash(h).String()
}
if h > maxStdHash && h < maxHash {
return hashNames[h-maxStdHash-1]
}
panic("requested hash #" + strconv.Itoa(int(h)) + " is unavailable")
}
// 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 := hashFns[h-maxStdHash-1]
if f != nil {
return f()
}
}
panic("requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
}
// ToVarsigHash returns the corresponding varsig.Hash value.
func (h Hash) ToVarsigHash() varsig.Hash {
if h == MD5SHA1 {
panic("no multihash/multicodec value exists for MD5+SHA1")
}
if h < maxHash {
return hashVarsigs[h]
}
panic("requested hash #" + strconv.Itoa(int(h)) + " is unavailable")
}
// FromVarsigHash converts a varsig.Hash value to the corresponding Hash value.
func FromVarsigHash(h varsig.Hash) Hash {
switch h {
case varsig.HashMd4:
return MD4
case varsig.HashMd5:
return MD5
case varsig.HashSha1:
return SHA1
case varsig.HashSha2_224:
return SHA224
case varsig.HashSha2_256:
return SHA256
case varsig.HashSha2_384:
return SHA384
case varsig.HashSha2_512:
return SHA512
case varsig.HashRipemd_160:
return RIPEMD160
case varsig.HashSha3_224:
return SHA3_224
case varsig.HashSha3_256:
return SHA3_256
case varsig.HashSha3_384:
return SHA3_384
case varsig.HashSha3_512:
return SHA3_512
case varsig.HashSha512_224:
return SHA512_224
case varsig.HashSha512_256:
return SHA512_256
case varsig.HashBlake2s_256:
return BLAKE2s_256
case varsig.HashBlake2b_256:
return BLAKE2b_256
case varsig.HashBlake2b_384:
return BLAKE2b_384
case varsig.HashBlake2b_512:
return BLAKE2b_512
case varsig.HashKeccak_256:
return KECCAK_256
case varsig.HashKeccak_512:
return KECCAK_512
default:
panic("varsig " + strconv.Itoa(int(h)) + " is not supported")
}
}
var hashNames = []string{
"Keccak-256",
"Keccak-512",
}
var hashFns = []func() hash.Hash{
sha3.NewLegacyKeccak256,
sha3.NewLegacyKeccak512,
}
var hashVarsigs = []varsig.Hash{
0, // undef
varsig.HashMd4,
varsig.HashMd5,
varsig.HashSha1,
varsig.HashSha2_224,
varsig.HashSha2_256,
varsig.HashSha2_384,
varsig.HashSha2_512,
0, // missing MD5SHA1
varsig.HashRipemd_160,
varsig.HashSha3_224,
varsig.HashSha3_256,
varsig.HashSha3_384,
varsig.HashSha3_512,
varsig.HashSha512_224,
varsig.HashSha512_256,
varsig.HashBlake2s_256,
varsig.HashBlake2b_256,
varsig.HashBlake2b_384,
varsig.HashBlake2b_512,
0, // maxStdHash
varsig.HashKeccak_256,
varsig.HashKeccak_512,
}

View File

@@ -1,37 +0,0 @@
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)
})
}

View File

@@ -1,217 +0,0 @@
package jwk
import (
"encoding/base64"
"encoding/json"
"fmt"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/ed25519"
"code.sonr.org/go/did-it/crypto/p256"
"code.sonr.org/go/did-it/crypto/p384"
"code.sonr.org/go/did-it/crypto/p521"
"code.sonr.org/go/did-it/crypto/rsa"
"code.sonr.org/go/did-it/crypto/secp256k1"
"code.sonr.org/go/did-it/crypto/x25519"
)
// PrivateJwk is a JWK holding a private key
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"])
}
}

View File

@@ -1,114 +0,0 @@
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))
})
}
}

View File

@@ -1,180 +0,0 @@
package jwk
import (
"encoding/base64"
"encoding/json"
"fmt"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/ed25519"
"code.sonr.org/go/did-it/crypto/p256"
"code.sonr.org/go/did-it/crypto/p384"
"code.sonr.org/go/did-it/crypto/p521"
"code.sonr.org/go/did-it/crypto/rsa"
"code.sonr.org/go/did-it/crypto/secp256k1"
"code.sonr.org/go/did-it/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)
// PublicJwk is a JWK holding a public key
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"])
}
}

View File

@@ -1,88 +0,0 @@
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))
})
}
}

View File

@@ -1,96 +0,0 @@
package crypto
import (
"github.com/ucan-wg/go-varsig"
)
// SigningOpts contains the resulting signature configuration.
type SigningOpts struct {
hash Hash
payloadEncoding varsig.PayloadEncoding
// if WithVarsig is used
algo varsig.Algorithm
curve uint64
keyLen uint64
}
// CollectSigningOptions collects the signing options into a SigningOpts.
func CollectSigningOptions(opts []SigningOption) SigningOpts {
res := SigningOpts{}
for _, opt := range opts {
opt(&res)
}
return res
}
// HashOrDefault returns the hash algorithm to be used for signatures, or the default if not specified.
func (opts SigningOpts) HashOrDefault(_default Hash) Hash {
if opts.hash == 0 {
return _default
}
return opts.hash
}
// PayloadEncoding returns the encoding used on the message before signing it.
func (opts SigningOpts) PayloadEncoding() varsig.PayloadEncoding {
if opts.payloadEncoding == 0 {
return varsig.PayloadEncodingVerbatim
}
return opts.payloadEncoding
}
// VarsigMatch returns true if the given varsig parameters match the signing options.
func (opts SigningOpts) VarsigMatch(algo varsig.Algorithm, curve uint64, keyLength uint64) bool {
// This is relatively ugly, but we get cyclic import otherwise
switch opts.algo {
case 0:
// not varsig to compare
return true
case varsig.AlgorithmECDSA:
return algo == varsig.AlgorithmECDSA && opts.curve == curve
case varsig.AlgorithmEdDSA:
return algo == varsig.AlgorithmEdDSA && opts.curve == curve
case varsig.AlgorithmRSA:
return algo == varsig.AlgorithmRSA && opts.keyLen == keyLength
default:
panic("unreachable")
}
}
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
}
}
// WithPayloadEncoding specify the encoding that was used on the message before signing it.
// This will be included in the resulting varsig.
func WithPayloadEncoding(encoding varsig.PayloadEncoding) SigningOption {
return func(opts *SigningOpts) {
opts.payloadEncoding = encoding
}
}
// WithVarsig configure the signing or verification parameters from a varsig.
// If you use WithVarsig, you should NOT use other options.
func WithVarsig(vsig varsig.Varsig) SigningOption {
return func(opts *SigningOpts) {
opts.payloadEncoding = vsig.PayloadEncoding()
opts.hash = FromVarsigHash(vsig.Hash())
opts.algo = vsig.Algorithm()
switch vsig := vsig.(type) {
case varsig.EdDSAVarsig:
opts.curve = uint64(vsig.Curve())
case varsig.ECDSAVarsig:
opts.curve = uint64(vsig.Curve())
case varsig.RSAVarsig:
opts.keyLen = vsig.KeyLength()
default:
panic("unreachable")
}
}
}

View File

@@ -1,35 +0,0 @@
package p256
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(0x1200)
// coordinateSize is the size, in bytes, of one coordinate in the elliptic curve.
coordinateSize = 32
)
func GenerateKeyPair() (*PublicKey, *PrivateKey, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), 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"
)

View File

@@ -1,59 +0,0 @@
package p256
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/require"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/_testsuite"
)
var harness = testsuite.TestHarness[*PublicKey, *PrivateKey]{
Name: "p256",
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.SHA224, crypto.SHA384, 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)
}
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))
}

View File

@@ -1,163 +0,0 @@
package p256
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/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-256 private key size")
}
res := &PrivateKey{
k: &ecdsa.PrivateKey{
D: new(big.Int).SetBytes(b),
PublicKey: ecdsa.PublicKey{Curve: elliptic.P256()},
},
}
// 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-256.
func (p *PrivateKey) Varsig(opts ...crypto.SigningOption) varsig.Varsig {
params := crypto.CollectSigningOptions(opts)
return varsig.NewECDSAVarsig(varsig.CurveP256, params.HashOrDefault(crypto.SHA256).ToVarsigHash(), params.PayloadEncoding())
}
// 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)
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-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)
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")
}

View File

@@ -1,162 +0,0 @@
package p256
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/crypto"
helpers "code.sonr.org/go/did-it/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-256 public key size")
}
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), b)
if x == nil {
return nil, fmt.Errorf("invalid P-256 public key")
}
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
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.P256(), p.k.X, p.k.Y)
}
func (p *PublicKey) ToPublicKeyMultibase() string {
bytes := elliptic.MarshalCompressed(elliptic.P256(), 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-256.
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-256.
func (p *PublicKey) VerifyASN1(message, signature []byte, opts ...crypto.SigningOption) bool {
params := crypto.CollectSigningOptions(opts)
if !params.VarsigMatch(varsig.AlgorithmECDSA, uint64(varsig.CurveP256), 0) {
return false
}
hasher := params.HashOrDefault(crypto.SHA256).New()
hasher.Write(message)
hash := hasher.Sum(nil)
return ecdsa.VerifyASN1(p.k, hash[:], signature)
}

View File

@@ -1,35 +0,0 @@
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"
)

View File

@@ -1,34 +0,0 @@
package p384
import (
"testing"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/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)
}

View File

@@ -1,163 +0,0 @@
package p384
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/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) Varsig(opts ...crypto.SigningOption) varsig.Varsig {
params := crypto.CollectSigningOptions(opts)
return varsig.NewECDSAVarsig(varsig.CurveP384, params.HashOrDefault(crypto.SHA384).ToVarsigHash(), params.PayloadEncoding())
}
// 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")
}

View File

@@ -1,162 +0,0 @@
package p384
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/crypto"
helpers "code.sonr.org/go/did-it/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)
if !params.VarsigMatch(varsig.AlgorithmECDSA, uint64(varsig.CurveP384), 0) {
return false
}
hasher := params.HashOrDefault(crypto.SHA384).New()
hasher.Write(message)
hash := hasher.Sum(nil)
return ecdsa.VerifyASN1(p.k, hash[:], signature)
}

View File

@@ -1,35 +0,0 @@
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"
)

View File

@@ -1,34 +0,0 @@
package p521
import (
"testing"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/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)
}

View File

@@ -1,163 +0,0 @@
package p521
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/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) Varsig(opts ...crypto.SigningOption) varsig.Varsig {
params := crypto.CollectSigningOptions(opts)
return varsig.NewECDSAVarsig(varsig.CurveP521, params.HashOrDefault(crypto.SHA512).ToVarsigHash(), params.PayloadEncoding())
}
// 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")
}

View File

@@ -1,162 +0,0 @@
package p521
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/crypto"
helpers "code.sonr.org/go/did-it/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)
if !params.VarsigMatch(varsig.AlgorithmECDSA, uint64(varsig.CurveP521), 0) {
return false
}
hasher := params.HashOrDefault(crypto.SHA512).New()
hasher.Write(message)
hash := hasher.Sum(nil)
return ecdsa.VerifyASN1(p.k, hash[:], signature)
}

View File

@@ -1,58 +0,0 @@
package crypto
import "github.com/ucan-wg/go-varsig"
type PrivateKey interface {
// Equal returns true if other is the same PrivateKey
Equal(other PrivateKey) bool
// Public returns the matching PublicKey.
Public() PublicKey
// ToPKCS8DER serializes the PrivateKey into the PKCS#8 DER (binary) format.
ToPKCS8DER() []byte
// ToPKCS8PEM serializes the PrivateKey into the PKCS#8 PEM (string) format.
ToPKCS8PEM() string
}
type PrivateKeyToBytes interface {
PrivateKey
// 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
}
type PrivateKeySigningBytes interface {
PrivateKey
// Varsig returns the varsig.Varsig corresponding to the given parameters and private key.
Varsig(opts ...SigningOption) varsig.Varsig
// 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, opts ...SigningOption) ([]byte, error)
}
type PrivateKeySigningASN1 interface {
PrivateKey
// Varsig returns the varsig.Varsig corresponding to the given parameters and private key.
Varsig(opts ...SigningOption) varsig.Varsig
// 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.
PublicKeyIsCompatible(remote PublicKey) bool
// KeyExchange computes the shared key using the given PublicKey.
KeyExchange(remote PublicKey) ([]byte, error)
}

View File

@@ -1,55 +0,0 @@
// Package crypto is a thin ergonomic layer on top of the normal golang crypto packages or `x/crypto`.
//
// It aims to solve the following problems with the standard crypto packages:
// - different algorithms have different APIs and ergonomics, which makes it hard to use them interchangeably
// - occasionally, it's quite hard to figure out how to do simple tasks (like encoding/decoding keys)
// - it's still necessary to make some educated choices (e.g. which hash function to use for signatures)
// - sometimes features are left out (e.g. ed25519 to X25519 for key exchange, secp256k1...)
// - some hash functions are not available in the standard library with no easy way to extend it (e.g. KECCAK-256)
//
// To do so, this package provides and implements a set of shared interfaces for all algorithms. As not all algorithms
// support all features (e.g. RSA keys don't support key exchange), some interfaces are optionally implemented.
//
// An additional benefit of shared interfaces is that a shared test suite can be written to test all algorithms, which this
// package does.
package crypto
type PublicKey interface {
// Equal returns true if other is the same PublicKey
Equal(other PublicKey) bool
// ToPublicKeyMultibase format the PublicKey into a string compatible with a PublicKeyMultibase field
// in a DID Document.
ToPublicKeyMultibase() string
// ToX509DER serializes the PublicKey into the X.509 DER (binary) format.
ToX509DER() []byte
// ToX509PEM serializes the PublicKey into the X.509 PEM (string) format.
ToX509PEM() string
}
type 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
}

View File

@@ -1,43 +0,0 @@
package rsa
import (
"crypto/rand"
"crypto/rsa"
"fmt"
"code.sonr.org/go/did-it/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
}
}

View File

@@ -1,164 +0,0 @@
package rsa
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/require"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/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))
}

View File

@@ -1,176 +0,0 @@
package rsa
import (
stdcrypto "crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/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) KeyLength() uint64 {
return uint64((p.k.N.BitLen() + 7) / 8) // Round up to the nearest byte
}
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,
}))
}
// 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) Varsig(opts ...crypto.SigningOption) varsig.Varsig {
params := crypto.CollectSigningOptions(opts)
hashCode := params.HashOrDefault(defaultSigHash(p.k.N.BitLen()))
return varsig.NewRSAVarsig(hashCode.ToVarsigHash(), uint64(p.KeyLength()), params.PayloadEncoding())
}
// 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)
}

View File

@@ -1,154 +0,0 @@
package rsa
import (
stdcrypto "crypto"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/crypto"
helpers "code.sonr.org/go/did-it/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) KeyLength() uint64 {
return uint64((p.k.N.BitLen() + 7) / 8) // Round up to the nearest byte
}
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)
if !params.VarsigMatch(varsig.AlgorithmRSA, 0, p.KeyLength()) {
return false
}
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
}

View File

@@ -1,43 +0,0 @@
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}
)

View File

@@ -1,105 +0,0 @@
package secp256k1
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/require"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/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))
}

View File

@@ -1,226 +0,0 @@
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/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/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) Varsig(opts ...crypto.SigningOption) varsig.Varsig {
params := crypto.CollectSigningOptions(opts)
return varsig.NewECDSAVarsig(varsig.CurveSecp256k1, params.HashOrDefault(crypto.SHA256).ToVarsigHash(), params.PayloadEncoding())
}
// 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")
}

View File

@@ -1,220 +0,0 @@
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/ucan-wg/go-varsig"
"code.sonr.org/go/did-it/crypto"
helpers "code.sonr.org/go/did-it/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)
if !params.VarsigMatch(varsig.AlgorithmECDSA, uint64(varsig.CurveSecp256k1), 0) {
return false
}
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)
if !params.VarsigMatch(varsig.AlgorithmECDSA, uint64(varsig.CurveSecp256k1), 0) {
return false
}
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
}

View File

@@ -1,29 +0,0 @@
package x25519
import (
"crypto/ecdh"
"crypto/rand"
)
const (
// PublicKeyBytesSize is the size, in bytes, of public keys in raw bytes.
PublicKeyBytesSize = 32
// PrivateKeyBytesSize is the size, in bytes, of private keys in raw bytes.
PrivateKeyBytesSize = 32
MultibaseCode = uint64(0xec)
)
func GenerateKeyPair() (*PublicKey, *PrivateKey, error) {
priv, err := ecdh.X25519().GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
pub := priv.Public().(*ecdh.PublicKey)
return &PublicKey{k: pub}, &PrivateKey{k: priv}, nil
}
const (
pemPubBlockType = "PUBLIC KEY"
pemPrivBlockType = "PRIVATE KEY"
)

View File

@@ -1,71 +0,0 @@
package x25519
import (
"testing"
"github.com/stretchr/testify/require"
"code.sonr.org/go/did-it/crypto/_testsuite"
"code.sonr.org/go/did-it/crypto/ed25519"
)
var harness = testsuite.TestHarness[*PublicKey, *PrivateKey]{
Name: "x25519",
GenerateKeyPair: GenerateKeyPair,
PublicKeyFromBytes: PublicKeyFromBytes,
PublicKeyFromPublicKeyMultibase: PublicKeyFromPublicKeyMultibase,
PublicKeyFromX509DER: PublicKeyFromX509DER,
PublicKeyFromX509PEM: PublicKeyFromX509PEM,
PrivateKeyFromBytes: PrivateKeyFromBytes,
PrivateKeyFromPKCS8DER: PrivateKeyFromPKCS8DER,
PrivateKeyFromPKCS8PEM: PrivateKeyFromPKCS8PEM,
MultibaseCode: MultibaseCode,
PublicKeyBytesSize: PublicKeyBytesSize,
PrivateKeyBytesSize: PrivateKeyBytesSize,
SignatureBytesSize: -1,
}
func TestSuite(t *testing.T) {
testsuite.TestSuite(t, harness)
}
func BenchmarkSuite(b *testing.B) {
testsuite.BenchSuite(b, harness)
}
func TestEd25519ToX25519(t *testing.T) {
// Known pubkey ed25519 --> x25519
for _, tc := range []struct {
pubEdMultibase string
pubXMultibase string
}{
{
// From https://w3c-ccg.github.io/did-key-spec/#ed25519-with-x25519
pubEdMultibase: "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
pubXMultibase: "z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p",
},
} {
t.Run(tc.pubEdMultibase, func(t *testing.T) {
pubEd, err := ed25519.PublicKeyFromPublicKeyMultibase(tc.pubEdMultibase)
require.NoError(t, err)
pubX, err := PublicKeyFromEd25519(pubEd)
require.NoError(t, err)
require.Equal(t, tc.pubXMultibase, pubX.ToPublicKeyMultibase())
})
}
// Check that ed25519 --> x25519 match for pubkeys and privkeys
t.Run("ed25519 --> x25519 priv+pub are matching", func(t *testing.T) {
for i := 0; i < 10; i++ {
pubEd, privEd, err := ed25519.GenerateKeyPair()
require.NoError(t, err)
pubX, err := PublicKeyFromEd25519(pubEd)
require.NoError(t, err)
privX, err := PrivateKeyFromEd25519(privEd)
require.NoError(t, err)
require.True(t, pubX.Equal(privX.Public()))
}
})
}

View File

@@ -1,115 +0,0 @@
package x25519
import (
"crypto/ecdh"
"crypto/sha512"
"crypto/x509"
"encoding/pem"
"fmt"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/ed25519"
)
var _ crypto.PrivateKeyKeyExchange = (*PrivateKey)(nil)
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.
// It errors if len(privateKey) is not [PrivateKeyBytesSize].
func PrivateKeyFromBytes(b []byte) (*PrivateKey, error) {
// this already check the size of b
priv, err := ecdh.X25519().NewPrivateKey(b)
if err != nil {
return nil, err
}
return &PrivateKey{k: priv}, nil
}
// PrivateKeyFromEd25519 converts an ed25519 private key to a x25519 private key.
// It errors if the slice is not the right size.
//
// This function is based on the algorithm described in https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm#name-curve25519
func PrivateKeyFromEd25519(priv ed25519.PrivateKey) (*PrivateKey, error) {
// get the 32-byte seed (first half of the private key)
seed := priv.Seed()
h := sha512.Sum512(seed)
// clamp as per the X25519 spec
h[0] &= 248
h[31] &= 127
h[31] |= 64
return PrivateKeyFromBytes(h[:32])
}
// 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
}
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.
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 {
return &PublicKey{k: p.k.Public().(*ecdh.PublicKey)}
}
func (p *PrivateKey) ToBytes() []byte {
return p.k.Bytes()
}
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,
}))
}
func (p *PrivateKey) PublicKeyIsCompatible(remote crypto.PublicKey) bool {
if _, ok := remote.(*PublicKey); ok {
return true
}
return false
}
func (p *PrivateKey) KeyExchange(remote crypto.PublicKey) ([]byte, error) {
if local, ok := remote.(*PublicKey); ok {
return p.k.ECDH(local.k)
}
return nil, fmt.Errorf("incompatible public key")
}

View File

@@ -1,155 +0,0 @@
package x25519
import (
"crypto/ecdh"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/ed25519"
helpers "code.sonr.org/go/did-it/crypto/internal"
)
var _ crypto.PublicKey = (*PublicKey)(nil)
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.
// It errors if the slice is not the right size.
func PublicKeyFromBytes(b []byte) (*PublicKey, error) {
pub, err := ecdh.X25519().NewPublicKey(b)
if err != nil {
return nil, err
}
return &PublicKey{k: pub}, nil
}
// PublicKeyFromEd25519 converts an ed25519 public key to a x25519 public key.
// It errors if the slice is not the right size.
//
// This function is based on the algorithm described in https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm#name-curve25519
func PublicKeyFromEd25519(pub ed25519.PublicKey) (*PublicKey, error) {
// Conversion formula is u = (1 + y) / (1 - y) (mod p)
// See https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm#name-ecdh-with-montgomery-coordi
pubBytes := pub.ToBytes()
// Clear the sign bit (MSB of last byte)
// This is because ed25519 serialize as bytes with 255 bit for Y, and one bit for the sign.
// We only want Y, and the sign is irrelevant for the conversion.
pubBytes[ed25519.PublicKeyBytesSize-1] &= 0x7F
// ed25519 are little-endian, but big.Int expects big-endian
// See https://www.rfc-editor.org/rfc/rfc8032
y := new(big.Int).SetBytes(reverseBytes(pubBytes))
one := big.NewInt(1)
negOne := big.NewInt(-1)
if y.Cmp(one) == 0 || y.Cmp(negOne) == 0 {
return nil, fmt.Errorf("x25519 undefined for this public key")
}
// p = 2^255-19
//
// Equivalent to:
// two := big.NewInt(2)
// exp := big.NewInt(255)
// p := new(big.Int).Exp(two, exp, nil)
// p.Sub(p, big.NewInt(19))
//
p := new(big.Int).SetBytes([]byte{
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed,
})
onePlusY := new(big.Int).Add(one, y)
oneMinusY := new(big.Int).Sub(one, y)
oneMinusYInv := new(big.Int).ModInverse(oneMinusY, p)
u := new(big.Int).Mul(onePlusY, oneMinusYInv)
u.Mod(u, p)
// make sure we get 32 bytes, pad if necessary
uBytes := u.Bytes()
res := make([]byte, PublicKeyBytesSize)
copy(res[PublicKeyBytesSize-len(uBytes):], uBytes)
// x25519 are little-endian, but big.Int gives us big-endian.
// See https://www.ietf.org/rfc/rfc7748.txt
return PublicKeyFromBytes(reverseBytes(res))
}
// 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
}
return &PublicKey{k: pub.(*ecdh.PublicKey)}, 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) 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 p.k.Bytes()
}
func (p *PublicKey) ToPublicKeyMultibase() string {
return helpers.PublicKeyMultibaseEncode(MultibaseCode, p.k.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,
}))
}
func reverseBytes(b []byte) []byte {
r := make([]byte, len(b))
for i := 0; i < len(b); i++ {
r[i] = b[len(b)-1-i]
}
return r
}

View File

@@ -6,14 +6,31 @@ General:
- coding style should be clean, straightforward and documented, in a similar fashion as go-ucan. - coding style should be clean, straightforward and documented, in a similar fashion as go-ucan.
- keep the dependencies minimal, favor the standard go libraries - keep the dependencies minimal, favor the standard go libraries
- code should be decently tested and profiled - code should be decently tested and profiled
- specifications and test vectors origins used MUST be referenced in a comment - specifications and test vectors used MUST be referenced in a comment
- if something differs from a specification, it should be documented and explained - if something differs from a specification, it should be documented and explained
- generally, follow the existing structure of did:key, ed25519 or x25519
- consider how an average user will read and understand your code, rather than how you read it - consider how an average user will read and understand your code, rather than how you read it
DIDs: DIDs:
- DID and document structs are minimal/lightweight and get expanded into the relevant interface (DID, Document). - DID and document structs are minimal/lightweight and get expanded into the relevant interface (DID, Document).
- They get expanded when marshalling into JSON but not otherwise. DID Documents are for out-of-process communication, not the normal path. - They get flattened when marshalling into JSON but not otherwise. DID Documents are for out-of-process communication, not the normal path.
- this library should also have a generic Document struct, to accept arbitrary DID documents in JSON format
Crypto: Crypto:
- each type of crypto handling should be self-contained in the relevant verification method package (e.g. everything ed25519 is in /verifications/ed25519). This includes the JSON (un)marshalling of the VerificationMethod.
- a user of the library shouldn't have to know or care about the underlying crypto to use it "server side" (signature verification, key agreement). Thus, it should be abstracted behind the VerificationMethod interfaces. - a user of the library shouldn't have to know or care about the underlying crypto to use it "server side" (signature verification, key agreement). Thus, it should be abstracted behind the VerificationMethod interfaces.
- for the same reason, each of those packages should expose or alias the relevant types (ex: PublicKey/PrivateKey in /verifications/ed25519) to expose a regular way to work with crypto primitives, as well as allowing behind the scene upgrades.
- for each, we should expose some generally useful functions to handle private keys (generation, marshalling...) - for each, we should expose some generally useful functions to handle private keys (generation, marshalling...)
## Minimal target features
Methods:
- did:key
- did:pkh
Verification Methods:
- ed25519
- x25519
- secp256k1
- p256
- p384

4
did.go
View File

@@ -7,10 +7,6 @@ import (
"sync" "sync"
) )
// Specifications:
// - https://www.w3.org/TR/did-1.0/
// - https://www.w3.org/TR/did-1.1/
const JsonLdContext = "https://www.w3.org/ns/did/v1" const JsonLdContext = "https://www.w3.org/ns/did/v1"
// Decoder is a function decoding a DID string representation ("did:example:foo") into a DID. // Decoder is a function decoding a DID string representation ("did:example:foo") into a DID.

View File

@@ -7,12 +7,12 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"code.sonr.org/go/did-it" "github.com/INFURA/go-did"
"code.sonr.org/go/did-it/crypto/x25519" _ "github.com/INFURA/go-did/methods/did-key"
_ "code.sonr.org/go/did-it/verifiers/did-key" "github.com/INFURA/go-did/verifications/x25519"
) )
func Example_signature() { func ExampleSignature() {
// errors need to be handled // errors need to be handled
// 1) Parse the DID string into a DID object // 1) Parse the DID string into a DID object
@@ -21,9 +21,9 @@ func Example_signature() {
// 2) Resolve to the DID Document // 2) Resolve to the DID Document
doc, _ := d.Document() doc, _ := d.Document()
// 3) Use the appropriate set of verification methods (ex: verify a signature for authentication purpose) // 3) Use the appropriate verification method (ex: verify a signature for authentication purpose)
sig, _ := base64.StdEncoding.DecodeString("nhpkr5a7juUM2eDpDRSJVdEE++0SYqaZXHtuvyafVFUx8zsOdDSrij+vHmd/ARwUOmi/ysmSD+b3K9WTBtmmBQ==") sig, _ := base64.StdEncoding.DecodeString("nhpkr5a7juUM2eDpDRSJVdEE++0SYqaZXHtuvyafVFUx8zsOdDSrij+vHmd/ARwUOmi/ysmSD+b3K9WTBtmmBQ==")
if ok, method := did.TryAllVerifyBytes(doc.Authentication(), []byte("message"), sig); ok { if ok, method := did.TryAllVerify(doc.Authentication(), []byte("message"), sig); ok {
fmt.Println("Signature is valid, verified with method:", method.Type(), method.ID()) fmt.Println("Signature is valid, verified with method:", method.Type(), method.ID())
} else { } else {
fmt.Println("Signature is invalid") fmt.Println("Signature is invalid")
@@ -32,7 +32,7 @@ func Example_signature() {
// Output: Signature is valid, verified with method: Ed25519VerificationKey2020 did:key:z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse#z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse // Output: Signature is valid, verified with method: Ed25519VerificationKey2020 did:key:z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse#z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse
} }
func Example_keyAgreement() { func ExampleKeyAgreement() {
// errors need to be handled // errors need to be handled
// 1) We have a private key for Alice // 1) We have a private key for Alice
@@ -92,7 +92,7 @@ func TestHasValidDIDSyntax(t *testing.T) {
func BenchmarkHasValidDIDSyntax(b *testing.B) { func BenchmarkHasValidDIDSyntax(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for b.Loop() { for i := 0; i < b.N; i++ {
did.HasValidDIDSyntax("did:example:abc:def:ghi:jkl%20mno%3Apqr%3Astuv") did.HasValidDIDSyntax("did:example:abc:def:ghi:jkl%20mno%3Apqr%3Astuv")
} }
} }
@@ -132,7 +132,7 @@ func TestHasValidDidUrlSyntax(t *testing.T) {
func BenchmarkHasValidDidUrlSyntax(b *testing.B) { func BenchmarkHasValidDidUrlSyntax(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for b.Loop() { for i := 0; i < b.N; i++ {
did.HasValidDidUrlSyntax("did:example:123456789abcdefghi/path/to/resource?key=value#section1") did.HasValidDidUrlSyntax("did:example:123456789abcdefghi/path/to/resource?key=value#section1")
} }
} }

View File

@@ -1,131 +0,0 @@
// Package didtest provides Personas that can be used for testing. Each
// Persona has a name, crypto.PrivKey and associated crypto.PubKey and
// did.DID.
package didtest
import (
"encoding/base64"
"fmt"
"code.sonr.org/go/did-it"
didkeyctl "code.sonr.org/go/did-it/controller/did-key"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/ed25519"
)
const (
// all are ed25519 as base64
alicePrivKeyB64 = "zth/9cTSUVwlLzfEWwLCcOkaEmjrRGPOI6mOJksWAYZ3Toe7ymxAzDeiseyxbmEpJ81qYM3dZ8XrXqgonnTTEw=="
bobPrivKeyB64 = "+p1REV3MkUnLhUMbFe9RcSsmo33TT/FO85yaV+c6fiYJCBsdiwfMwodlkzSAG3sHQIuZj8qnJ678oJucYy7WEg=="
carolPrivKeyB64 = "aSu3vTwE7z3pXaTaAhVLeizuqnZUJZQHTCSLMLxyZh5LDoZQn80uoQgMEdsbOhR+zIqrjBn5WviGurDkKYVfug=="
danPrivKeyB64 = "s1zM1av6og3o0UMNbEs/RyezS7Nk/jbSYL2Z+xPEw9Cho/KuEAa75Sf4yJHclLwpKXNucbrZ2scE8Iy8K05KWQ=="
erinPrivKeyB64 = "+qHpaAR3iivWMEl+pkXmq+uJeHtqFiY++XOXtZ9Tu/WPABCO+eRFrTCLJykJEzAPGFmkJF8HQ7DMwOH7Ry3Aqw=="
frankPrivKeyB64 = "4k/1N0+Fq73DxmNbGis9PY2KgKxWmtDWhmi1E6sBLuGd7DS0TWjCn1Xa3lXkY49mFszMjhWC+V6DCBf7R68u4Q=="
)
// Persona is a generic participant used for cryptographic testing.
type Persona int
// The provided Personas were selected from the first few generic
// participants listed in this [table].
//
// [table]: https://en.wikipedia.org/wiki/Alice_and_Bob#Cryptographic_systems
const (
PersonaAlice Persona = iota + 1
PersonaBob
PersonaCarol
PersonaDan
PersonaErin
PersonaFrank
)
var privKeys map[Persona]crypto.PrivateKeySigningBytes
func init() {
privKeys = make(map[Persona]crypto.PrivateKeySigningBytes, 6)
for persona, pB64 := range privKeyB64() {
privBytes, err := base64.StdEncoding.DecodeString(pB64)
if err != nil {
return
}
privKey, err := ed25519.PrivateKeyFromBytes(privBytes)
if err != nil {
return
}
privKeys[persona] = privKey
}
}
// DID returns a did.DID based on the Persona's Ed25519 public key.
func (p Persona) DID() did.DID {
return didkeyctl.FromPrivateKey(p.PrivKey())
}
// Name returns the username of the Persona.
func (p Persona) Name() string {
name, ok := map[Persona]string{
PersonaAlice: "Alice",
PersonaBob: "Bob",
PersonaCarol: "Carol",
PersonaDan: "Dan",
PersonaErin: "Erin",
PersonaFrank: "Frank",
}[p]
if !ok {
panic(fmt.Sprintf("Unknown persona: %v", p))
}
return name
}
// PrivKey returns the Ed25519 private key for the Persona.
func (p Persona) PrivKey() crypto.PrivateKeySigningBytes {
res, ok := privKeys[p]
if !ok {
panic(fmt.Sprintf("Unknown persona: %v", p))
}
return res
}
// PubKey returns the Ed25519 public key for the Persona.
func (p Persona) PubKey() crypto.PublicKey {
return p.PrivKey().Public()
}
func privKeyB64() map[Persona]string {
return map[Persona]string{
PersonaAlice: alicePrivKeyB64,
PersonaBob: bobPrivKeyB64,
PersonaCarol: carolPrivKeyB64,
PersonaDan: danPrivKeyB64,
PersonaErin: erinPrivKeyB64,
PersonaFrank: frankPrivKeyB64,
}
}
// Personas returns an (alphabetically) ordered list of the defined
// Persona values.
func Personas() []Persona {
return []Persona{
PersonaAlice,
PersonaBob,
PersonaCarol,
PersonaDan,
PersonaErin,
PersonaFrank,
}
}
// DidToName retrieve the persona's name from its DID.
func DidToName(d did.DID) string {
return map[did.DID]string{
PersonaAlice.DID(): "Alice",
PersonaBob.DID(): "Bob",
PersonaCarol.DID(): "Carol",
PersonaDan.DID(): "Dan",
PersonaErin.DID(): "Erin",
PersonaFrank.DID(): "Frank",
}[d]
}

View File

@@ -7,8 +7,8 @@ import (
"io" "io"
"net/url" "net/url"
"code.sonr.org/go/did-it" "github.com/INFURA/go-did"
verifications "code.sonr.org/go/did-it/verifiers/_methods" "github.com/INFURA/go-did/verifications"
) )
var _ did.Document = &Document{} var _ did.Document = &Document{}
@@ -26,7 +26,6 @@ type Document struct {
keyAgreement []did.VerificationMethodKeyAgreement keyAgreement []did.VerificationMethodKeyAgreement
capabilityInvocation []did.VerificationMethodSignature capabilityInvocation []did.VerificationMethodSignature
capabilityDelegation []did.VerificationMethodSignature capabilityDelegation []did.VerificationMethodSignature
services did.Services
} }
type aux struct { type aux struct {
@@ -40,7 +39,6 @@ type aux struct {
KeyAgreement []json.RawMessage `json:"keyAgreement,omitempty"` KeyAgreement []json.RawMessage `json:"keyAgreement,omitempty"`
CapabilityInvocation []json.RawMessage `json:"capabilityInvocation,omitempty"` CapabilityInvocation []json.RawMessage `json:"capabilityInvocation,omitempty"`
CapabilityDelegation []json.RawMessage `json:"capabilityDelegation,omitempty"` CapabilityDelegation []json.RawMessage `json:"capabilityDelegation,omitempty"`
Services did.Services `json:"service,omitempty"`
} }
// FromJsonReader decodes an arbitrary Json DID Document into a usable did.Document. // FromJsonReader decodes an arbitrary Json DID Document into a usable did.Document.
@@ -69,9 +67,8 @@ func FromJsonBytes(data []byte) (*Document, error) {
func fromAux(aux *aux) (*Document, error) { func fromAux(aux *aux) (*Document, error) {
var err error var err error
res := Document{ res := Document{
context: aux.Context, context: aux.Context,
id: aux.Id, id: aux.Id,
services: aux.Services,
} }
// id // id
@@ -194,7 +191,7 @@ func resolveVerificationMethods[T did.VerificationMethod](doc *Document, msgs []
func (d Document) MarshalJSON() ([]byte, error) { func (d Document) MarshalJSON() ([]byte, error) {
var err error var err error
data := aux{Context: d.context, Id: d.id, Services: d.services} data := aux{Context: d.context, Id: d.id}
// alsoKnownAs // alsoKnownAs
data.AlsoKnownAs = make([]string, len(d.alsoKnownAs)) data.AlsoKnownAs = make([]string, len(d.alsoKnownAs))
@@ -312,7 +309,3 @@ func (d Document) CapabilityInvocation() []did.VerificationMethodSignature {
func (d Document) CapabilityDelegation() []did.VerificationMethodSignature { func (d Document) CapabilityDelegation() []did.VerificationMethodSignature {
return d.capabilityDelegation return d.capabilityDelegation
} }
func (d Document) Services() did.Services {
return d.services
}

View File

@@ -6,68 +6,13 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"code.sonr.org/go/did-it/verifiers/_methods/ed25519" _ "github.com/INFURA/go-did/methods/did-key"
"code.sonr.org/go/did-it/verifiers/_methods/jsonwebkey" "github.com/INFURA/go-did/verifications/ed25519"
"code.sonr.org/go/did-it/verifiers/_methods/x25519" "github.com/INFURA/go-did/verifications/x25519"
_ "code.sonr.org/go/did-it/verifiers/did-key"
) )
func TestRoundTrip(t *testing.T) { func TestRoundTrip(t *testing.T) {
for _, tc := range []struct { strDoc := `
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())
},
},
{
name: "plc",
strDoc: plcDoc,
assertion: func(t *testing.T, doc *Document) {
require.Equal(t, "did:plc:ewvi7nxzyoun6zhxrhs64oiz", doc.ID())
require.Len(t, doc.VerificationMethods(), 1)
require.Len(t, doc.Services(), 1)
},
},
} {
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": [ "@context": [
"https://www.w3.org/ns/did/v1", "https://www.w3.org/ns/did/v1",
@@ -101,162 +46,18 @@ const ed25519Doc = `
}] }]
} }
` `
doc, err := FromJsonBytes([]byte(strDoc))
require.NoError(t, err)
const jsonWebKeyDoc = ` // basic testing
{ require.Equal(t, "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", doc.ID())
"@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1"], require.Equal(t, ed25519.Type, doc.Authentication()[0].Type())
"id": "did:example:123", require.Equal(t, ed25519.Type, doc.Assertion()[0].Type())
"verificationMethod": [ require.Equal(t, x25519.Type, doc.KeyAgreement()[0].Type())
{ require.Equal(t, ed25519.Type, doc.CapabilityInvocation()[0].Type())
"id": "did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A", require.Equal(t, ed25519.Type, doc.CapabilityDelegation()[0].Type())
"type": "JsonWebKey2020",
"controller": "did:example:123", roundtrip, err := json.Marshal(doc)
"publicKeyJwk": { require.NoError(t, err)
"kty": "OKP", require.JSONEq(t, strDoc, string(roundtrip))
"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"
]
}
`
const plcDoc = `{
"@context":[
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/multikey/v1"
],
"id":"did:plc:ewvi7nxzyoun6zhxrhs64oiz",
"alsoKnownAs":[
"at://atproto.com"
],
"verificationMethod":[
{
"id":"did:plc:ewvi7nxzyoun6zhxrhs64oiz#atproto",
"type":"Multikey",
"controller":"did:plc:ewvi7nxzyoun6zhxrhs64oiz",
"publicKeyMultibase":"zQ3shunBKsXixLxKtC5qeSG9E4J5RkGN57im31pcTzbNQnm5w"
}
],
"service":[
{
"id":"#atproto_pds",
"type":"AtprotoPersonalDataServer",
"serviceEndpoint":"https://enoki.us-east.host.bsky.network"
}
]
}`
// 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)
}
}
} }

View File

@@ -16,7 +16,4 @@ var (
var ( var (
// ErrNotFound indicates that the DID resolver was unable to find the DID document for the given DID. // ErrNotFound indicates that the DID resolver was unable to find the DID document for the given DID.
ErrNotFound = fmt.Errorf("did not found") ErrNotFound = fmt.Errorf("did not found")
// ErrResolutionFailure indicates that the DID resolver failed to resolve the DID, in a way that is not ErrNotFound
ErrResolutionFailure = fmt.Errorf("resolution failure")
) )

12
go.mod
View File

@@ -1,24 +1,20 @@
module code.sonr.org/go/did-it module github.com/INFURA/go-did
go 1.24.4 go 1.23.0
toolchain go1.24.5 toolchain go1.23.1
require ( 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-multibase v0.2.0
github.com/multiformats/go-varint v0.0.7 github.com/multiformats/go-varint v0.0.7
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/ucan-wg/go-varsig v1.0.0
golang.org/x/crypto v0.45.0
) )
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect 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-base32 v0.0.3 // indirect
github.com/multiformats/go-base36 v0.1.0 // indirect github.com/multiformats/go-base36 v0.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.38.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

10
go.sum
View File

@@ -1,9 +1,5 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/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 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= 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= github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
@@ -18,12 +14,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ucan-wg/go-varsig v1.0.0 h1:Hrc437Zg+B5Eoajg+qZQZI3Q3ocPyjlnp3/Bz9ZnlWw=
github.com/ucan-wg/go-varsig v1.0.0/go.mod h1:Sakln6IPooDPH+ClQ0VvR09TuwUhHcfLqcPiPkMZGh0=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -1,10 +1,9 @@
package did package did
import ( import (
"crypto"
"encoding/json" "encoding/json"
"net/url" "net/url"
"code.sonr.org/go/did-it/crypto"
) )
// DID is a decoded (i.e. from a string) Decentralized Identifier. // DID is a decoded (i.e. from a string) Decentralized Identifier.
@@ -14,7 +13,7 @@ type DID interface {
// Document resolves the DID into a DID Document usable for e.g. signature check. // 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. // This can be simply expanding the DID into a Document, or involve external resolution.
Document(opts ...ResolutionOption) (Document, error) Document() (Document, error)
// String returns the string representation of the DID. // String returns the string representation of the DID.
String() string String() string
@@ -42,8 +41,7 @@ type Document interface {
// Controllers is the set of DID that is authorized to make changes to the Document. It's often the same as ID. // Controllers is the set of DID that is authorized to make changes to the Document. It's often the same as ID.
Controllers() []string Controllers() []string
// AlsoKnownAs returns an optional set of URL describing different identifier for the DID subject, // AlsoKnownAs returns an optional set of URL describing ???TODO
// for different purpose or different time.
AlsoKnownAs() []*url.URL AlsoKnownAs() []*url.URL
// VerificationMethods returns all the VerificationMethod known in the document. // VerificationMethods returns all the VerificationMethod known in the document.
@@ -71,10 +69,8 @@ type Document interface {
// capability to another party, such as delegating the authority to access a specific HTTP API to a subordinate. // capability to another party, such as delegating the authority to access a specific HTTP API to a subordinate.
CapabilityDelegation() []VerificationMethodSignature CapabilityDelegation() []VerificationMethodSignature
// Services are means of communicating or interacting with the DID subject or associated entities // TODO: Service
// via one or more endpoints. Examples include discovery services, agent services, social networking // https://www.w3.org/TR/did-extensions-properties/#service-types
// services, file storage services, and verifiable credential repository services.
Services() Services
} }
// VerificationMethod is a common interface for a cryptographic signature verification method. // VerificationMethod is a common interface for a cryptographic signature verification method.
@@ -104,11 +100,8 @@ type VerificationMethod interface {
type VerificationMethodSignature interface { type VerificationMethodSignature interface {
VerificationMethod VerificationMethod
// VerifyBytes checks that 'sig' is a valid "raw bytes" signature of 'data'. // Verify checks that 'sig' is a valid signature of 'data'.
VerifyBytes(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) Verify(data []byte, sig []byte) bool
// VerifyASN1 checks that 'sig' is a valid ASN.1 signature of 'data'.
VerifyASN1(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error)
} }
// VerificationMethodKeyAgreement is a VerificationMethod implementing a shared key agreement. // VerificationMethodKeyAgreement is a VerificationMethod implementing a shared key agreement.
@@ -117,8 +110,20 @@ type VerificationMethodKeyAgreement interface {
VerificationMethod VerificationMethod
// PrivateKeyIsCompatible checks that the given PrivateKey is compatible with this method. // PrivateKeyIsCompatible checks that the given PrivateKey is compatible with this method.
PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool PrivateKeyIsCompatible(local PrivateKey) bool
// KeyExchange computes the shared key using the given PrivateKey. // ECDH computes the shared key using the given PrivateKey.
KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error) ECDH(local PrivateKey) ([]byte, error)
}
// Below are the interfaces for crypto.PublicKey and crypto.PrivateKey in the go standard library.
// They are not defined there for compatibility reasons, so we need to define them here.
type PublicKey interface {
Equal(x crypto.PublicKey) bool
}
type PrivateKey interface {
Public() crypto.PublicKey
Equal(x crypto.PrivateKey) bool
} }

View File

@@ -4,13 +4,13 @@ import (
"encoding/json" "encoding/json"
"net/url" "net/url"
"code.sonr.org/go/did-it" "github.com/INFURA/go-did"
) )
var _ did.Document = &document{} var _ did.Document = &document{}
type document struct { type document struct {
id string id did.DID
signature did.VerificationMethodSignature signature did.VerificationMethodSignature
keyAgreement did.VerificationMethodKeyAgreement keyAgreement did.VerificationMethodKeyAgreement
} }
@@ -20,11 +20,6 @@ func (d document) MarshalJSON() ([]byte, error) {
// Maybe it doesn't matter, but the spec contradicts itself. // Maybe it doesn't matter, but the spec contradicts itself.
// See https://github.com/w3c-ccg/did-key-spec/issues/71 // 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 { return json.Marshal(struct {
Context []string `json:"@context"` Context []string `json:"@context"`
ID string `json:"id"` ID string `json:"id"`
@@ -33,17 +28,17 @@ func (d document) MarshalJSON() ([]byte, error) {
VerificationMethod []did.VerificationMethod `json:"verificationMethod,omitempty"` VerificationMethod []did.VerificationMethod `json:"verificationMethod,omitempty"`
Authentication []string `json:"authentication,omitempty"` Authentication []string `json:"authentication,omitempty"`
AssertionMethod []string `json:"assertionMethod,omitempty"` AssertionMethod []string `json:"assertionMethod,omitempty"`
KeyAgreement []string `json:"keyAgreement,omitempty"` KeyAgreement []did.VerificationMethod `json:"keyAgreement,omitempty"`
CapabilityInvocation []string `json:"capabilityInvocation,omitempty"` CapabilityInvocation []string `json:"capabilityInvocation,omitempty"`
CapabilityDelegation []string `json:"capabilityDelegation,omitempty"` CapabilityDelegation []string `json:"capabilityDelegation,omitempty"`
}{ }{
Context: d.Context(), Context: d.Context(),
ID: d.id, ID: d.id.String(),
AlsoKnownAs: nil, AlsoKnownAs: nil,
VerificationMethod: vms, VerificationMethod: []did.VerificationMethod{d.signature},
Authentication: []string{d.signature.ID()}, Authentication: []string{d.signature.ID()},
AssertionMethod: []string{d.signature.ID()}, AssertionMethod: []string{d.signature.ID()},
KeyAgreement: []string{d.keyAgreement.ID()}, KeyAgreement: []did.VerificationMethod{d.keyAgreement},
CapabilityInvocation: []string{d.signature.ID()}, CapabilityInvocation: []string{d.signature.ID()},
CapabilityDelegation: []string{d.signature.ID()}, CapabilityDelegation: []string{d.signature.ID()},
}) })
@@ -58,7 +53,7 @@ func (d document) Context() []string {
} }
func (d document) ID() string { func (d document) ID() string {
return d.id return d.id.String()
} }
func (d document) Controllers() []string { func (d document) Controllers() []string {
@@ -71,11 +66,6 @@ func (d document) AlsoKnownAs() []*url.URL {
} }
func (d document) VerificationMethods() map[string]did.VerificationMethod { 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{ return map[string]did.VerificationMethod{
d.signature.ID(): d.signature, d.signature.ID(): d.signature,
d.keyAgreement.ID(): d.keyAgreement, d.keyAgreement.ID(): d.keyAgreement,
@@ -102,10 +92,6 @@ func (d document) CapabilityDelegation() []did.VerificationMethodSignature {
return []did.VerificationMethodSignature{d.signature} return []did.VerificationMethodSignature{d.signature}
} }
func (d document) Services() did.Services {
return nil
}
func stringSet(values ...string) []string { func stringSet(values ...string) []string {
res := make([]string, 0, len(values)) res := make([]string, 0, len(values))
loop: loop:

View File

@@ -0,0 +1,61 @@
package didkey
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"github.com/INFURA/go-did"
)
func TestDocument(t *testing.T) {
d, err := did.Parse("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK")
require.NoError(t, err)
doc, err := d.Document()
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",
"https://w3id.org/security/suites/ed25519-2020/v1",
"https://w3id.org/security/suites/x25519-2020/v1"
],
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"verificationMethod": [{
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"type": "Ed25519VerificationKey2020",
"controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
}],
"authentication": [
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
],
"assertionMethod": [
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
],
"capabilityDelegation": [
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
],
"capabilityInvocation": [
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
],
"keyAgreement": [{
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p",
"type": "X25519KeyAgreementKey2020",
"controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"publicKeyMultibase": "z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p"
}]
}`
require.JSONEq(t, expected, string(bytes))
}
// TODO: test vectors:
// https://github.com/w3c-ccg/did-key-spec/tree/main/test-vectors

121
methods/did-key/key.go Normal file
View File

@@ -0,0 +1,121 @@
package didkey
import (
"fmt"
"strings"
mbase "github.com/multiformats/go-multibase"
"github.com/multiformats/go-varint"
"github.com/INFURA/go-did"
"github.com/INFURA/go-did/verifications/ed25519"
"github.com/INFURA/go-did/verifications/x25519"
)
// Specification: https://w3c-ccg.github.io/did-method-key/
func init() {
did.RegisterMethod("key", Decode)
}
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
}
func Decode(identifier string) (did.DID, error) {
const keyPrefix = "did:key:"
if !strings.HasPrefix(identifier, keyPrefix) {
return nil, fmt.Errorf("must start with 'did:key'")
}
msi := identifier[len(keyPrefix):]
baseCodec, bytes, err := mbase.Decode(msi)
if err != nil {
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
}
// the specification enforces that encoding
if baseCodec != mbase.Base58BTC {
return nil, fmt.Errorf("%w: not Base58BTC encoded", did.ErrInvalidDid)
}
code, read, err := varint.FromUvarint(bytes)
if err != nil {
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
}
switch code {
case ed25519.MultibaseCode:
pub, err := ed25519.PublicKeyFromBytes(bytes[read:])
if err != nil {
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
}
return FromPublicKey(pub)
// case P256: // TODO
// case Secp256k1: // TODO
// case RSA: // TODO
}
return nil, fmt.Errorf("%w: unsupported did:key multicodec: 0x%x", did.ErrInvalidDid, code)
}
func FromPublicKey(pub did.PublicKey) (did.DID, error) {
var err error
switch pub := pub.(type) {
case ed25519.PublicKey:
d := DidKey{msi: ed25519.PublicKeyToMultibase(pub)}
d.signature, err = ed25519.NewVerificationKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, d.msi), pub, d)
if err != nil {
return nil, err
}
xpub, err := x25519.PublicKeyFromEd25519(pub)
if err != nil {
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
}
xmsi := x25519.PublicKeyToMultibase(xpub)
d.keyAgreement, err = x25519.NewKeyAgreementKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, xmsi), xpub, d)
if err != nil {
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
}
return d, nil
default:
return nil, fmt.Errorf("unsupported public key: %T", pub)
}
}
func FromPrivateKey(priv did.PrivateKey) (did.DID, error) {
return FromPublicKey(priv.Public().(did.PublicKey))
}
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) String() string {
return fmt.Sprintf("did:key:%s", d.msi)
}
func (d DidKey) ResolutionIsExpensive() bool {
return false
}
func (d DidKey) Equal(d2 did.DID) bool {
if d2, ok := d2.(DidKey); ok {
return d.msi == d2.msi
}
return false
}

View File

@@ -7,32 +7,38 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"code.sonr.org/go/did-it" "github.com/INFURA/go-did"
"code.sonr.org/go/did-it/crypto/ed25519" didkey "github.com/INFURA/go-did/methods/did-key"
didkey "code.sonr.org/go/did-it/verifiers/did-key" "github.com/INFURA/go-did/verifications/ed25519"
) )
func ExampleGenerateKeyPair() { func ExampleGenerateKeyPair() {
// Generate a key pair // Generate a key pair
pub, priv, err := ed25519.GenerateKeyPair() pub, priv, err := ed25519.GenerateKeyPair()
handleErr(err) if err != nil {
fmt.Println("Public key:", pub.ToPublicKeyMultibase()) panic(err)
fmt.Println("Private key:", base64.StdEncoding.EncodeToString(priv.ToBytes())) }
fmt.Println("Public key:", ed25519.PublicKeyToMultibase(pub))
fmt.Println("Private key:", base64.StdEncoding.EncodeToString(priv))
// Make the associated did:key // Make the associated did:key
dk := didkey.FromPrivateKey(priv) dk, err := didkey.FromPrivateKey(priv)
if err != nil {
panic(err)
}
fmt.Println("Did:", dk.String()) fmt.Println("Did:", dk.String())
// Produce a signature // Produce a signature
msg := []byte("message") msg := []byte("message")
sig, err := priv.SignToBytes(msg) sig := ed25519.Sign(priv, msg)
handleErr(err)
fmt.Println("Signature:", base64.StdEncoding.EncodeToString(sig)) fmt.Println("Signature:", base64.StdEncoding.EncodeToString(sig))
// Resolve the DID and verify a signature // Resolve the DID and verify a signature
doc, err := dk.Document() doc, err := dk.Document()
handleErr(err) if err != nil {
ok, _ := did.TryAllVerifyBytes(doc.Authentication(), msg, sig) panic(err)
}
ok, _ := did.TryAllVerify(doc.Authentication(), msg, sig)
fmt.Println("Signature verified:", ok) fmt.Println("Signature verified:", ok)
} }
@@ -55,16 +61,6 @@ 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) { func TestEquivalence(t *testing.T) {
did0A, err := did.Parse("did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z") did0A, err := did.Parse("did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z")
require.NoError(t, err) require.NoError(t, err)
@@ -76,9 +72,3 @@ func TestEquivalence(t *testing.T) {
require.True(t, did0A.Equal(did0B)) require.True(t, did0A.Equal(did0B))
require.False(t, did0A.Equal(did1)) require.False(t, did0A.Equal(did1))
} }
func handleErr(err error) {
if err != nil {
panic(err)
}
}

View File

@@ -1,81 +0,0 @@
package did
import (
"context"
"net/http"
)
type ResolutionOpts struct {
ctx context.Context
hintVerificationMethod []string
client HttpClient
}
func (opts *ResolutionOpts) Context() context.Context {
if opts.ctx != nil {
return opts.ctx
}
return context.Background()
}
func (opts *ResolutionOpts) HasVerificationMethodHint(hint string) bool {
for _, h := range opts.hintVerificationMethod {
if h == hint {
return true
}
}
return false
}
func (opts *ResolutionOpts) HttpClient() HttpClient {
if opts.client != nil {
return opts.client
}
return http.DefaultClient
}
func CollectResolutionOpts(opts []ResolutionOption) ResolutionOpts {
res := ResolutionOpts{}
for _, opt := range opts {
opt(&res)
}
return res
}
type ResolutionOption func(opts *ResolutionOpts)
// WithResolutionContext provides a go context to use for the resolution.
// This context can be used for deadline or cancellation.
func WithResolutionContext(ctx context.Context) ResolutionOption {
return func(opts *ResolutionOpts) {
opts.ctx = ctx
}
}
// 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)
}
}
type HttpClient interface {
Do(req *http.Request) (*http.Response, error)
}
// WithHttpClient provides an HttpClient to be used during resolution.
func WithHttpClient(client HttpClient) ResolutionOption {
return func(opts *ResolutionOpts) {
opts.client = client
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 202 KiB

View File

@@ -1,155 +0,0 @@
package did
import (
"encoding/json"
"fmt"
)
// Specification: https://www.w3.org/TR/cid-1.0/#services
// List of service types and their fields: https://www.w3.org/TR/did-extensions-properties/#service-types
// Services is a collection of Service.
type Services []Service
// ServiceById retrieves a Service from the Services slice by its id.
// Returns the Service and true if found, otherwise returns an empty Service and false.
func (ss Services) ServiceById(id string) (Service, bool) {
for _, s := range ss {
if s.Id == id {
return s, true
}
}
return Service{}, false
}
// ServiceByType returns zero or one Service matching the given type.
// If there is more than one service for that type, the first match is returned.
func (ss Services) ServiceByType(_type string) (Service, bool) {
for _, s := range ss {
if s.HasType(_type) {
return s, true
}
}
return Service{}, false
}
// Service is a means of communicating or interacting with the DID subject or associated entities
// via one or more service endpoints.
// It can have one or more types.
type Service struct {
Id string
Types []string
Endpoints []any // either strEndpoint or mapEndpoint
}
func (s Service) HasType(_type string) bool {
for _, t := range s.Types {
if t == _type {
return true
}
}
return false
}
func (s Service) MarshalJSON() ([]byte, error) {
var aux struct {
Id string `json:"id"`
Type any `json:"type"`
Endpoint any `json:"serviceEndpoint"`
}
aux.Id = s.Id
switch len(s.Types) {
case 0:
return nil, fmt.Errorf("service type is required")
case 1:
aux.Type = s.Types[0]
default:
aux.Type = s.Types
}
switch len(s.Endpoints) {
case 0:
return nil, fmt.Errorf("service endpoint is required")
case 1:
aux.Endpoint = s.Endpoints[0]
default:
aux.Endpoint = s.Endpoints
}
return json.Marshal(aux)
}
func (s *Service) UnmarshalJSON(bytes []byte) error {
var aux struct {
Id string `json:"id"`
Type json.RawMessage `json:"type"`
Endpoint json.RawMessage `json:"serviceEndpoint"`
}
err := json.Unmarshal(bytes, &aux)
if err != nil {
return err
}
if len(aux.Id) == 0 {
return fmt.Errorf("service id is required")
}
s.Id = aux.Id
s.Types, err = unmarshalSingleOrArray[string](aux.Type)
if err != nil {
return err
}
if len(s.Types) == 0 {
return fmt.Errorf("service type is required")
}
for _, _type := range s.Types {
if len(_type) == 0 {
return fmt.Errorf("invalid service type: must not be empty string")
}
}
s.Endpoints, err = unmarshalSingleOrArray[any](aux.Endpoint)
if err != nil {
return err
}
if len(s.Endpoints) == 0 {
return fmt.Errorf("service endpoint is required")
}
for i, endpoint := range s.Endpoints {
switch endpoint := endpoint.(type) {
case string:
s.Endpoints[i] = StrEndpoint(endpoint)
case map[string]any:
s.Endpoints[i] = MapEndpoint(endpoint)
default:
return fmt.Errorf("endpoint must be %T or %T", StrEndpoint(""), MapEndpoint{})
}
}
return nil
}
type StrEndpoint string
type MapEndpoint map[string]any
func unmarshalSingleOrArray[T any](data json.RawMessage) ([]T, error) {
if data == nil {
return nil, nil
}
var single T
if err := json.Unmarshal(data, &single); err == nil {
return []T{single}, nil
}
var array []T
if err := json.Unmarshal(data, &array); err == nil {
return array, nil
}
return nil, fmt.Errorf("must be %T or array of %T", single, single)
}

View File

@@ -1,107 +0,0 @@
package did
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func TestServicesJsonRountrip(t *testing.T) {
tests := []struct {
name string
input string
}{
{
name: "LinkedDomains",
input: `[
{
"id":"did:example:123#foo",
"type": "LinkedDomains",
"serviceEndpoint": {
"origins": ["https://foo.example.com", "https://identity.foundation"]
}
},
{
"id":"did:example:123#bar",
"type": "LinkedDomains",
"serviceEndpoint": "https://bar.example.com"
}
]`,
},
{
name: "LinkedVerifiablePresentation",
input: `[
{
"id": "did:example:123#foo",
"type": "LinkedVerifiablePresentation",
"serviceEndpoint": "https://bar.example.com/verifiable-presentation.jsonld"
},
{
"id": "did:example:123#baz",
"type": "LinkedVerifiablePresentation",
"serviceEndpoint": "ipfs://bafybeihkoviema7g3gxyt6la7vd5ho32ictqbilu3wnlo3rs7ewhnp7lly/verifiable-presentation.jwt"
}
]`,
},
{
name: "WotThing",
input: `[{
"id": "did:example:wotdiscoveryexample#td",
"type": "WotThing",
"serviceEndpoint":
"https://wot.example.com/.well-known/wot"
}]`,
},
{
name: "multi types",
input: `[
{
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#node",
"type": [
"DIDCommMessaging",
"CredentialRepositoryService",
"RevocationList2020Status",
"TrustRegistryService"
],
"serviceEndpoint": "https://node.blockchain-network.com/api/v1"
}
]`,
},
{
name: "multi types, map values",
input: `[
{
"id": "did:web:wallet.example.com#wallet-service",
"type": [
"VerifiableCredentialService",
"OpenIdConnectVersion1.0Service",
"DIDCommMessaging",
"CredentialRepositoryService"
],
"serviceEndpoint": {
"credentialIssue": "https://wallet.example.com/credentials/issue",
"credentialVerify": "https://wallet.example.com/credentials/verify",
"credentialStore": "https://wallet.example.com/vault",
"oidcAuth": "https://wallet.example.com/auth",
"oidcToken": "https://wallet.example.com/token",
"didcommInbox": "https://wallet.example.com/didcomm/inbox",
"didcommOutbox": "https://wallet.example.com/didcomm/outbox"
}
}
]`,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var services []Service
err := json.Unmarshal([]byte(tc.input), &services)
require.NoError(t, err)
rt, err := json.Marshal(services)
require.NoError(t, err)
require.JSONEq(t, tc.input, string(rt))
})
}
}

View File

@@ -2,28 +2,14 @@ package did
import ( import (
"fmt" "fmt"
"code.sonr.org/go/did-it/crypto"
) )
// TryAllVerifyBytes tries to verify the signature as bytes with all the methods in the slice. // TryAllVerify tries to verify the signature with all the methods in the slice.
// It returns true if the signature is verified, and the method that verified it. // It returns true if the signature is verified, and the method that verified it.
// If no method verifies the signature, it returns false and nil. // If no method verifies the signature, it returns false and nil.
func TryAllVerifyBytes(methods []VerificationMethodSignature, data []byte, sig []byte, opts ...crypto.SigningOption) (bool, VerificationMethodSignature) { func TryAllVerify(methods []VerificationMethodSignature, data []byte, sig []byte) (bool, VerificationMethodSignature) {
for _, method := range methods { for _, method := range methods {
if valid, err := method.VerifyBytes(data, sig, opts...); err == nil && valid { if method.Verify(data, sig) {
return true, method
}
}
return false, nil
}
// TryAllVerifyASN1 tries to verify the signature as ASN.1 with all the methods in the slice.
// It returns true if the signature is verified, and the method that verified it.
// If no method verifies the signature, it returns false and nil.
func TryAllVerifyASN1(methods []VerificationMethodSignature, data []byte, sig []byte, opts ...crypto.SigningOption) (bool, VerificationMethodSignature) {
for _, method := range methods {
if valid, err := method.VerifyASN1(data, sig, opts...); err == nil && valid {
return true, method return true, method
} }
} }
@@ -33,10 +19,10 @@ func TryAllVerifyASN1(methods []VerificationMethodSignature, data []byte, sig []
// FindMatchingKeyAgreement tries to find a matching key agreement method for the given private key type. // 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. // It returns the shared key as well as the selected method.
// If no matching method is found, it returns an error. // If no matching method is found, it returns an error.
func FindMatchingKeyAgreement(methods []VerificationMethodKeyAgreement, priv crypto.PrivateKeyKeyExchange) ([]byte, VerificationMethodKeyAgreement, error) { func FindMatchingKeyAgreement(methods []VerificationMethodKeyAgreement, priv PrivateKey) ([]byte, VerificationMethodKeyAgreement, error) {
for _, method := range methods { for _, method := range methods {
if method.PrivateKeyIsCompatible(priv) { if method.PrivateKeyIsCompatible(priv) {
key, err := method.KeyExchange(priv) key, err := method.ECDH(priv)
return key, method, err return key, method, err
} }
} }

View File

@@ -1,36 +1,39 @@
package ed25519vm package ed25519
import ( import (
"crypto/ed25519"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"code.sonr.org/go/did-it" "github.com/INFURA/go-did"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/ed25519"
) )
// Specification: https://w3c.github.io/cg-reports/credentials/CG-FINAL-di-eddsa-2020-20220724/ // Specification: https://w3c.github.io/cg-reports/credentials/CG-FINAL-di-eddsa-2020-20220724/
const ( const (
JsonLdContext2020 = "https://w3id.org/security/suites/ed25519-2020/v1" JsonLdContext = "https://w3id.org/security/suites/ed25519-2020/v1"
Type2020 = "Ed25519VerificationKey2020" Type = "Ed25519VerificationKey2020"
) )
var _ did.VerificationMethodSignature = &VerificationKey2020{} var _ did.VerificationMethodSignature = &VerificationKey2020{}
type VerificationKey2020 struct { type VerificationKey2020 struct {
id string id string
pubkey ed25519.PublicKey pubkey PublicKey
controller string controller string
} }
func NewVerificationKey2020(id string, pubkey ed25519.PublicKey, controller did.DID) *VerificationKey2020 { func NewVerificationKey2020(id string, pubkey PublicKey, controller did.DID) (*VerificationKey2020, error) {
if len(pubkey) != PublicKeySize {
return nil, errors.New("invalid ed25519 public key size")
}
return &VerificationKey2020{ return &VerificationKey2020{
id: id, id: id,
pubkey: pubkey, pubkey: pubkey,
controller: controller.String(), controller: controller.String(),
} }, nil
} }
func (v VerificationKey2020) MarshalJSON() ([]byte, error) { func (v VerificationKey2020) MarshalJSON() ([]byte, error) {
@@ -43,7 +46,7 @@ func (v VerificationKey2020) MarshalJSON() ([]byte, error) {
ID: v.ID(), ID: v.ID(),
Type: v.Type(), Type: v.Type(),
Controller: v.Controller(), Controller: v.Controller(),
PublicKeyMultibase: v.pubkey.ToPublicKeyMultibase(), PublicKeyMultibase: PublicKeyToMultibase(v.pubkey),
}) })
} }
@@ -65,7 +68,7 @@ func (v *VerificationKey2020) UnmarshalJSON(bytes []byte) error {
if len(v.id) == 0 { if len(v.id) == 0 {
return errors.New("invalid id") return errors.New("invalid id")
} }
v.pubkey, err = ed25519.PublicKeyFromPublicKeyMultibase(aux.PublicKeyMultibase) v.pubkey, err = PublicKeyFromMultibase(aux.PublicKeyMultibase)
if err != nil { if err != nil {
return fmt.Errorf("invalid publicKeyMultibase: %w", err) return fmt.Errorf("invalid publicKeyMultibase: %w", err)
} }
@@ -81,7 +84,7 @@ func (v VerificationKey2020) ID() string {
} }
func (v VerificationKey2020) Type() string { func (v VerificationKey2020) Type() string {
return Type2020 return Type
} }
func (v VerificationKey2020) Controller() string { func (v VerificationKey2020) Controller() string {
@@ -89,13 +92,9 @@ func (v VerificationKey2020) Controller() string {
} }
func (v VerificationKey2020) JsonLdContext() string { func (v VerificationKey2020) JsonLdContext() string {
return JsonLdContext2020 return JsonLdContext
} }
func (v VerificationKey2020) VerifyBytes(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) { func (v VerificationKey2020) Verify(data []byte, sig []byte) bool {
return v.pubkey.VerifyBytes(data, sig, opts...), nil return ed25519.Verify(v.pubkey, data, sig)
}
func (v VerificationKey2020) VerifyASN1(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
return v.pubkey.VerifyASN1(data, sig, opts...), nil
} }

View File

@@ -1,4 +1,4 @@
package ed25519vm_test package ed25519_test
import ( import (
"encoding/hex" "encoding/hex"
@@ -7,13 +7,12 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"code.sonr.org/go/did-it" "github.com/INFURA/go-did"
"code.sonr.org/go/did-it/crypto/ed25519" _ "github.com/INFURA/go-did/methods/did-key"
ed25519vm "code.sonr.org/go/did-it/verifiers/_methods/ed25519" "github.com/INFURA/go-did/verifications/ed25519"
_ "code.sonr.org/go/did-it/verifiers/did-key"
) )
func TestJsonRoundTrip2020(t *testing.T) { func TestJsonRoundTrip(t *testing.T) {
data := `{ data := `{
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", "id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"type": "Ed25519VerificationKey2020", "type": "Ed25519VerificationKey2020",
@@ -21,7 +20,7 @@ func TestJsonRoundTrip2020(t *testing.T) {
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK" "publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
}` }`
var vk ed25519vm.VerificationKey2020 var vk ed25519.VerificationKey2020
err := json.Unmarshal([]byte(data), &vk) err := json.Unmarshal([]byte(data), &vk)
require.NoError(t, err) require.NoError(t, err)
@@ -30,7 +29,7 @@ func TestJsonRoundTrip2020(t *testing.T) {
require.JSONEq(t, data, string(bytes)) require.JSONEq(t, data, string(bytes))
} }
func TestSignature2020(t *testing.T) { func TestVerify(t *testing.T) {
// test vector from https://datatracker.ietf.org/doc/html/rfc8032#section-7.1 // test vector from https://datatracker.ietf.org/doc/html/rfc8032#section-7.1
pkHex := "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025" pkHex := "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025"
@@ -38,9 +37,10 @@ func TestSignature2020(t *testing.T) {
pk, err := ed25519.PublicKeyFromBytes(pkBytes) pk, err := ed25519.PublicKeyFromBytes(pkBytes)
require.NoError(t, err) require.NoError(t, err)
contDid := "did:key:" + pk.ToPublicKeyMultibase() contDid := "did:key:" + ed25519.PublicKeyToMultibase(pk)
controller := did.MustParse(contDid) controller := did.MustParse(contDid)
vk := ed25519vm.NewVerificationKey2020("foo", pk, controller) vk, err := ed25519.NewVerificationKey2020("foo", pk, controller)
require.NoError(t, err)
for _, tc := range []struct { for _, tc := range []struct {
name string name string
@@ -77,9 +77,7 @@ func TestSignature2020(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
valid, err := vk.Verify(tc.data, tc.signature) require.Equal(t, tc.valid, vk.Verify(tc.data, tc.signature))
require.NoError(t, err)
require.Equal(t, tc.valid, valid)
}) })
} }
} }

View File

@@ -0,0 +1,171 @@
package ed25519
import (
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/INFURA/go-did/verifications/internal"
)
type PublicKey = ed25519.PublicKey
type PrivateKey = ed25519.PrivateKey
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = ed25519.PublicKeySize
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = ed25519.PrivateKeySize
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = ed25519.SignatureSize
MultibaseCode = uint64(0xed)
)
func GenerateKeyPair() (PublicKey, PrivateKey, error) {
return ed25519.GenerateKey(rand.Reader)
}
// 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) != PublicKeySize {
return nil, fmt.Errorf("invalid ed25519 public key size")
}
// make a copy
return PublicKey(append([]byte{}, b...)), nil
}
// PublicKeyToBytes converts a public key to a byte slice.
// This compact serialization format is the raw key material, without metadata or structure.
func PublicKeyToBytes(pub PublicKey) []byte {
// Copy the private key to a fixed size buffer that can get allocated on the
// caller's stack after inlining.
var buf [PublicKeySize]byte
return append(buf[:0], pub...)
}
// PublicKeyFromMultibase decodes the public key from its Multibase form
func PublicKeyFromMultibase(multibase string) (PublicKey, error) {
code, bytes, err := helpers.MultibaseDecode(multibase)
if err != nil {
return nil, err
}
if code != MultibaseCode {
return nil, fmt.Errorf("invalid code")
}
if len(bytes) != PublicKeySize {
return nil, fmt.Errorf("invalid ed25519 public key size")
}
return PublicKeyFromBytes(bytes)
}
// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase
func PublicKeyToMultibase(pub PublicKey) string {
return helpers.MultibaseEncode(MultibaseCode, pub)
}
// 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
}
return pub.(PublicKey), nil
}
// PublicKeyToX509DER encodes the public key into the X.509 DER (binary) format.
func PublicKeyToX509DER(pub PublicKey) []byte {
res, _ := x509.MarshalPKIXPublicKey(pub)
return res
}
const pemPubBlockType = "PUBLIC KEY"
// 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)
}
// PublicKeyToX509PEM encodes the public key into the X.509 PEM (binary) format.
func PublicKeyToX509PEM(pub PublicKey) string {
der := PublicKeyToX509DER(pub)
return string(pem.EncodeToMemory(&pem.Block{
Type: pemPubBlockType,
Bytes: der,
}))
}
// PrivateKeyFromBytes converts a serialized private 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) != PrivateKeySize {
return nil, fmt.Errorf("invalid ed25519 private key size")
}
// make a copy
return append([]byte{}, b...), nil
}
// PrivateKeyToBytes converts a private key to a byte slice.
// This compact serialization format is the raw key material, without metadata or structure.
func PrivateKeyToBytes(priv PrivateKey) []byte {
// Copy the private key to a fixed size buffer that can get allocated on the
// caller's stack after inlining.
var buf [PrivateKeySize]byte
return append(buf[:0], priv...)
}
// 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
}
return priv.(PrivateKey), nil
}
// PrivateKeyToPKCS8DER encodes the private key into the PKCS#8 DER (binary) format.
func PrivateKeyToPKCS8DER(priv PrivateKey) []byte {
res, _ := x509.MarshalPKCS8PrivateKey(priv)
return res
}
const pemPrivBlockType = "PRIVATE KEY"
// 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)
}
// PrivateKeyToPKCS8PEM encodes the private key into the PKCS#8 PEM (binary) format.
func PrivateKeyToPKCS8PEM(priv PrivateKey) string {
der := PrivateKeyToPKCS8DER(priv)
return string(pem.EncodeToMemory(&pem.Block{
Type: pemPrivBlockType,
Bytes: der,
}))
}
// Sign signs the message with privateKey and returns a signature.
// It will panic if len(privateKey) is not [PrivateKeySize].
func Sign(privateKey PrivateKey, message []byte) []byte {
return ed25519.Sign(privateKey, message)
}

View File

@@ -0,0 +1,89 @@
package ed25519_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/INFURA/go-did/verifications/ed25519"
)
func TestGenerateKey(t *testing.T) {
pub, priv, err := ed25519.GenerateKeyPair()
require.NoError(t, err)
require.NotNil(t, pub)
require.NotNil(t, priv)
require.True(t, pub.Equal(priv.Public()))
}
func TestBytesRoundTrip(t *testing.T) {
pub, priv, err := ed25519.GenerateKeyPair()
require.NoError(t, err)
bytes := ed25519.PublicKeyToBytes(pub)
fmt.Println("pub", len(bytes))
rtPub, err := ed25519.PublicKeyFromBytes(bytes)
require.NoError(t, err)
require.True(t, pub.Equal(rtPub))
bytes = ed25519.PrivateKeyToBytes(priv)
fmt.Println("priv", len(bytes))
rtPriv, err := ed25519.PrivateKeyFromBytes(bytes)
require.NoError(t, err)
require.True(t, priv.Equal(rtPriv))
}
func TestMultibaseRoundTrip(t *testing.T) {
pub, _, err := ed25519.GenerateKeyPair()
require.NoError(t, err)
mb := ed25519.PublicKeyToMultibase(pub)
rt, err := ed25519.PublicKeyFromMultibase(mb)
require.NoError(t, err)
require.Equal(t, pub, rt)
}
func TestPublicKeyX509RoundTrip(t *testing.T) {
pub, _, err := ed25519.GenerateKeyPair()
require.NoError(t, err)
der := ed25519.PublicKeyToX509DER(pub)
fmt.Println("der", len(der))
rt, err := ed25519.PublicKeyFromX509DER(der)
require.NoError(t, err)
require.True(t, pub.Equal(rt))
pem := ed25519.PublicKeyToX509PEM(pub)
fmt.Println("pem", len(pem))
rt, err = ed25519.PublicKeyFromX509PEM(pem)
require.NoError(t, err)
require.True(t, pub.Equal(rt))
}
func TestPrivateKeyPKCS8RoundTrip(t *testing.T) {
pub, priv, err := ed25519.GenerateKeyPair()
require.NoError(t, err)
der := ed25519.PrivateKeyToPKCS8DER(priv)
fmt.Println("der", len(der))
rt, err := ed25519.PrivateKeyFromPKCS8DER(der)
require.NoError(t, err)
require.True(t, priv.Equal(rt))
require.True(t, pub.Equal(rt.Public()))
pem := ed25519.PrivateKeyToPKCS8PEM(priv)
fmt.Println("pem", len(pem))
rt, err = ed25519.PrivateKeyFromPKCS8PEM(pem)
require.NoError(t, err)
require.True(t, priv.Equal(rt))
require.True(t, pub.Equal(rt.Public()))
}
// func TestSignature(t *testing.T) {
// pub, priv, err := ed25519.GenerateKeyPair()
// require.NoError(t, err)
//
// sig := ed25519.Sign(priv, []byte("message"))
//
// }

View File

@@ -7,8 +7,8 @@ import (
"github.com/multiformats/go-varint" "github.com/multiformats/go-varint"
) )
// PublicKeyMultibaseDecode is a helper for decoding multibase public keys. // MultibaseDecode is a helper for decoding multibase public keys.
func PublicKeyMultibaseDecode(multibase string) (uint64, []byte, error) { func MultibaseDecode(multibase string) (uint64, []byte, error) {
baseCodec, bytes, err := mbase.Decode(multibase) baseCodec, bytes, err := mbase.Decode(multibase)
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
@@ -27,8 +27,8 @@ func PublicKeyMultibaseDecode(multibase string) (uint64, []byte, error) {
return code, bytes[read:], nil return code, bytes[read:], nil
} }
// PublicKeyMultibaseEncode is a helper for encoding multibase public keys. // MultibaseEncode is a helper for encoding multibase public keys.
func PublicKeyMultibaseEncode(code uint64, bytes []byte) string { func MultibaseEncode(code uint64, bytes []byte) string {
// can only fail with an invalid encoding, but it's hardcoded // can only fail with an invalid encoding, but it's hardcoded
res, _ := mbase.Encode(mbase.Base58BTC, append(varint.ToUvarint(code), bytes...)) res, _ := mbase.Encode(mbase.Base58BTC, append(varint.ToUvarint(code), bytes...))
return res return res

34
verifications/json.go Normal file
View File

@@ -0,0 +1,34 @@
package verifications
import (
"encoding/json"
"fmt"
"github.com/INFURA/go-did"
"github.com/INFURA/go-did/verifications/ed25519"
"github.com/INFURA/go-did/verifications/x25519"
)
func UnmarshalJSON(data []byte) (did.VerificationMethod, error) {
var aux struct {
Type string
}
if err := json.Unmarshal(data, &aux); err != nil {
return nil, err
}
var res did.VerificationMethod
switch aux.Type {
case ed25519.Type:
res = &ed25519.VerificationKey2020{}
case x25519.Type:
res = &x25519.KeyAgreementKey2020{}
default:
return nil, fmt.Errorf("unknown verification type: %s", aux.Type)
}
if err := json.Unmarshal(data, &res); err != nil {
return nil, err
}
return res, nil
}

188
verifications/p256/key.go Normal file
View File

@@ -0,0 +1,188 @@
package p256
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
helpers "github.com/INFURA/go-did/verifications/internal"
)
type PublicKey = *ecdsa.PublicKey
type PrivateKey = *ecdsa.PrivateKey
const (
// TODO
PublicKeySize = 33
PrivateKeySize = 32
SignatureSize = 123456
MultibaseCode = uint64(0x1200)
)
func GenerateKeyPair() (PublicKey, PrivateKey, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, err
}
return priv.Public().(PublicKey), priv, nil
}
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
// 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) != PublicKeySize {
return nil, fmt.Errorf("invalid P-256 public key size")
}
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), b)
if x == nil {
return nil, fmt.Errorf("invalid P-256 public key")
}
return &ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}, nil
// if len(b) != PublicKeySize {
// return nil, fmt.Errorf("invalid P-256 public key size")
// }
// x := new(big.Int).SetBytes(b[:PublicKeySize/2])
// y := new(big.Int).SetBytes(b[PublicKeySize/2:])
// return &ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}, nil
}
// PublicKeyToBytes converts a public key to a byte slice.
// This compact serialization format is the raw key material, without metadata or structure.
func PublicKeyToBytes(pub PublicKey) []byte {
return elliptic.MarshalCompressed(elliptic.P256(), pub.X, pub.Y)
// // fixed size buffer that can get allocated on the caller's stack after inlining.
// var buf [PublicKeySize]byte
// pub.X.FillBytes(buf[:PublicKeySize/2])
// pub.Y.FillBytes(buf[PublicKeySize/2:])
// return buf[:]
}
// PublicKeyFromMultibase decodes the public key from its Multibase form
func PublicKeyFromMultibase(multibase string) (PublicKey, error) {
code, bytes, err := helpers.MultibaseDecode(multibase)
if err != nil {
return nil, err
}
if code != MultibaseCode {
return nil, fmt.Errorf("invalid code")
}
return PublicKeyFromBytes(bytes)
}
// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase
func PublicKeyToMultibase(pub PublicKey) string {
bytes := elliptic.MarshalCompressed(elliptic.P256(), pub.X, pub.Y)
return helpers.MultibaseEncode(MultibaseCode, bytes)
}
// 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
}
return pub.(PublicKey), nil
}
// PublicKeyToX509DER encodes the public key into the X.509 DER (binary) format.
func PublicKeyToX509DER(pub PublicKey) []byte {
res, _ := x509.MarshalPKIXPublicKey(pub)
return res
}
const pemPubBlockType = "PUBLIC KEY"
// 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)
}
// PublicKeyToX509PEM encodes the public key into the X.509 PEM (binary) format.
func PublicKeyToX509PEM(pub PublicKey) string {
der := PublicKeyToX509DER(pub)
return string(pem.EncodeToMemory(&pem.Block{
Type: pemPubBlockType,
Bytes: der,
}))
}
// 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) != PrivateKeySize {
return nil, fmt.Errorf("invalid P-256 private key size")
}
res := &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)
return res, nil
}
// PrivateKeyToBytes converts a private key to a byte slice.
// This compact serialization format is the raw key material, without metadata or structure.
func PrivateKeyToBytes(priv PrivateKey) []byte {
// fixed size buffer that can get allocated on the caller's stack after inlining.
var buf [PrivateKeySize]byte
priv.D.FillBytes(buf[:])
return buf[:]
}
// 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
}
return priv.(PrivateKey), nil
}
// PrivateKeyToPKCS8DER encodes the private key into the PKCS#8 DER (binary) format.
func PrivateKeyToPKCS8DER(priv PrivateKey) []byte {
res, _ := x509.MarshalPKCS8PrivateKey(priv)
return res
}
const pemPrivBlockType = "PRIVATE KEY"
// 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)
}
// PrivateKeyToPKCS8PEM encodes the private key into the PKCS#8 PEM (binary) format.
func PrivateKeyToPKCS8PEM(priv PrivateKey) string {
der := PrivateKeyToPKCS8DER(priv)
return string(pem.EncodeToMemory(&pem.Block{
Type: pemPrivBlockType,
Bytes: der,
}))
}

View File

@@ -0,0 +1,74 @@
package p256_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/INFURA/go-did/verifications/p256"
)
func TestGenerateKey(t *testing.T) {
pub, priv, err := p256.GenerateKeyPair()
require.NoError(t, err)
require.NotNil(t, pub)
require.NotNil(t, priv)
require.True(t, pub.Equal(priv.Public()))
}
func TestBytesRoundTrip(t *testing.T) {
pub, priv, err := p256.GenerateKeyPair()
require.NoError(t, err)
bytes := p256.PublicKeyToBytes(pub)
rtPub, err := p256.PublicKeyFromBytes(bytes)
require.NoError(t, err)
require.True(t, pub.Equal(rtPub))
bytes = p256.PrivateKeyToBytes(priv)
rtPriv, err := p256.PrivateKeyFromBytes(bytes)
require.NoError(t, err)
require.True(t, priv.Equal(rtPriv))
}
func TestPublicKeyX509RoundTrip(t *testing.T) {
pub, _, err := p256.GenerateKeyPair()
require.NoError(t, err)
der := p256.PublicKeyToX509DER(pub)
rt, err := p256.PublicKeyFromX509DER(der)
require.NoError(t, err)
require.True(t, pub.Equal(rt))
pem := p256.PublicKeyToX509PEM(pub)
rt, err = p256.PublicKeyFromX509PEM(pem)
require.NoError(t, err)
require.True(t, pub.Equal(rt))
}
func TestPrivateKeyPKCS8RoundTrip(t *testing.T) {
pub, priv, err := p256.GenerateKeyPair()
require.NoError(t, err)
der := p256.PrivateKeyToPKCS8DER(priv)
rt, err := p256.PrivateKeyFromPKCS8DER(der)
require.NoError(t, err)
require.True(t, priv.Equal(rt))
require.True(t, pub.Equal(rt.Public()))
pem := p256.PrivateKeyToPKCS8PEM(priv)
rt, err = p256.PrivateKeyFromPKCS8PEM(pem)
require.NoError(t, err)
require.True(t, priv.Equal(rt))
require.True(t, pub.Equal(rt.Public()))
}
func TestMultibaseRoundTrip(t *testing.T) {
pub, _, err := p256.GenerateKeyPair()
require.NoError(t, err)
mb := p256.PublicKeyToMultibase(pub)
rt, err := p256.PublicKeyFromMultibase(mb)
require.NoError(t, err)
require.Equal(t, pub, rt)
}

View File

@@ -1,36 +1,39 @@
package x25519vm package x25519
import ( import (
"crypto/ecdh"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"code.sonr.org/go/did-it" "github.com/INFURA/go-did"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/x25519"
) )
// Specification: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 // Specification: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519
const ( const (
JsonLdContext2020 = "https://w3id.org/security/suites/x25519-2020/v1" JsonLdContext = "https://w3id.org/security/suites/x25519-2020/v1"
Type2020 = "X25519KeyAgreementKey2020" Type = "X25519KeyAgreementKey2020"
) )
var _ did.VerificationMethodKeyAgreement = &KeyAgreementKey2020{} var _ did.VerificationMethodKeyAgreement = &KeyAgreementKey2020{}
type KeyAgreementKey2020 struct { type KeyAgreementKey2020 struct {
id string id string
pubkey *x25519.PublicKey pubkey PublicKey
controller string controller string
} }
func NewKeyAgreementKey2020(id string, pubkey *x25519.PublicKey, controller did.DID) *KeyAgreementKey2020 { func NewKeyAgreementKey2020(id string, pubkey PublicKey, controller did.DID) (*KeyAgreementKey2020, error) {
if pubkey.Curve() != ecdh.X25519() {
return nil, errors.New("x25519 key curve must be X25519")
}
return &KeyAgreementKey2020{ return &KeyAgreementKey2020{
id: id, id: id,
pubkey: pubkey, pubkey: pubkey,
controller: controller.String(), controller: controller.String(),
} }, nil
} }
func (k KeyAgreementKey2020) MarshalJSON() ([]byte, error) { func (k KeyAgreementKey2020) MarshalJSON() ([]byte, error) {
@@ -43,7 +46,7 @@ func (k KeyAgreementKey2020) MarshalJSON() ([]byte, error) {
ID: k.ID(), ID: k.ID(),
Type: k.Type(), Type: k.Type(),
Controller: k.Controller(), Controller: k.Controller(),
PublicKeyMultibase: k.pubkey.ToPublicKeyMultibase(), PublicKeyMultibase: PublicKeyToMultibase(k.pubkey),
}) })
} }
@@ -65,7 +68,7 @@ func (k *KeyAgreementKey2020) UnmarshalJSON(bytes []byte) error {
if len(k.id) == 0 { if len(k.id) == 0 {
return errors.New("invalid id") return errors.New("invalid id")
} }
k.pubkey, err = x25519.PublicKeyFromPublicKeyMultibase(aux.PublicKeyMultibase) k.pubkey, err = PublicKeyFromMultibase(aux.PublicKeyMultibase)
if err != nil { if err != nil {
return fmt.Errorf("invalid publicKeyMultibase: %w", err) return fmt.Errorf("invalid publicKeyMultibase: %w", err)
} }
@@ -81,7 +84,7 @@ func (k KeyAgreementKey2020) ID() string {
} }
func (k KeyAgreementKey2020) Type() string { func (k KeyAgreementKey2020) Type() string {
return Type2020 return Type
} }
func (k KeyAgreementKey2020) Controller() string { func (k KeyAgreementKey2020) Controller() string {
@@ -89,13 +92,24 @@ func (k KeyAgreementKey2020) Controller() string {
} }
func (k KeyAgreementKey2020) JsonLdContext() string { func (k KeyAgreementKey2020) JsonLdContext() string {
return JsonLdContext2020 return JsonLdContext
} }
func (k KeyAgreementKey2020) PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool { func (k KeyAgreementKey2020) PrivateKeyIsCompatible(local did.PrivateKey) bool {
return local.PublicKeyIsCompatible(k.pubkey) _, ok := local.(PrivateKey)
return ok
} }
func (k KeyAgreementKey2020) KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error) { func (k KeyAgreementKey2020) ECDH(local did.PrivateKey) ([]byte, error) {
return local.KeyExchange(k.pubkey) cast, ok := local.(PrivateKey)
if !ok {
return nil, errors.New("private key type doesn't match the public key type")
}
if cast == nil {
return nil, errors.New("invalid private key")
}
if k.pubkey.Curve() != cast.Curve() {
return nil, errors.New("key curves don't match")
}
return cast.ECDH(k.pubkey)
} }

View File

@@ -1,4 +1,4 @@
package x25519vm_test package x25519_test
import ( import (
"encoding/json" "encoding/json"
@@ -6,10 +6,10 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"code.sonr.org/go/did-it/verifiers/_methods/x25519" "github.com/INFURA/go-did/verifications/x25519"
) )
func TestJsonRoundTrip2020(t *testing.T) { func TestJsonRoundTrip(t *testing.T) {
data := `{ data := `{
"id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW", "id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW",
"type": "X25519KeyAgreementKey2020", "type": "X25519KeyAgreementKey2020",
@@ -17,7 +17,7 @@ func TestJsonRoundTrip2020(t *testing.T) {
"publicKeyMultibase": "z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW" "publicKeyMultibase": "z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW"
}` }`
var vm x25519vm.KeyAgreementKey2020 var vm x25519.KeyAgreementKey2020
err := json.Unmarshal([]byte(data), &vm) err := json.Unmarshal([]byte(data), &vm)
require.NoError(t, err) require.NoError(t, err)

246
verifications/x25519/key.go Normal file
View File

@@ -0,0 +1,246 @@
package x25519
import (
"crypto/ecdh"
"crypto/rand"
"crypto/sha512"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/INFURA/go-did/verifications/ed25519"
helpers "github.com/INFURA/go-did/verifications/internal"
)
type PublicKey = *ecdh.PublicKey
type PrivateKey = *ecdh.PrivateKey
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 32
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 32
MultibaseCode = uint64(0xec)
)
func GenerateKeyPair() (PublicKey, PrivateKey, error) {
priv, err := ecdh.X25519().GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
return priv.Public().(PublicKey), priv, nil
}
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
// 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) {
return ecdh.X25519().NewPublicKey(b)
}
// PublicKeyToBytes converts a public key to a byte slice.
// This compact serialization format is the raw key material, without metadata or structure.
func PublicKeyToBytes(pub PublicKey) []byte {
return pub.Bytes()
}
// PublicKeyFromEd25519 converts an ed25519 public key to a x25519 public key.
// It errors if the slice is not the right size.
//
// This function is based on the algorithm described in https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm#name-curve25519
func PublicKeyFromEd25519(pub ed25519.PublicKey) (PublicKey, error) {
// Conversion formula is u = (1 + y) / (1 - y) (mod p)
// See https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm#name-ecdh-with-montgomery-coordi
if len(pub) != ed25519.PublicKeySize {
return nil, fmt.Errorf("invalid ed25519 public key size")
}
// Make a copy and clear the sign bit (MSB of last byte)
// This is because ed25519 serialize as bytes with 255 bit for Y, and one bit for the sign.
// We only want Y, and the sign is irrelevant for the conversion.
pubCopy := make([]byte, ed25519.PublicKeySize)
copy(pubCopy, pub)
pubCopy[ed25519.PublicKeySize-1] &= 0x7F
// ed25519 are little-endian, but big.Int expects big-endian
// See https://www.rfc-editor.org/rfc/rfc8032
y := new(big.Int).SetBytes(reverseBytes(pubCopy))
one := big.NewInt(1)
negOne := big.NewInt(-1)
if y.Cmp(one) == 0 || y.Cmp(negOne) == 0 {
return nil, fmt.Errorf("x25519 undefined for this public key")
}
// p = 2^255-19
//
// Equivalent to:
// two := big.NewInt(2)
// exp := big.NewInt(255)
// p := new(big.Int).Exp(two, exp, nil)
// p.Sub(p, big.NewInt(19))
//
p := new(big.Int).SetBytes([]byte{
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed,
})
onePlusY := new(big.Int).Add(one, y)
oneMinusY := new(big.Int).Sub(one, y)
oneMinusYInv := new(big.Int).ModInverse(oneMinusY, p)
u := new(big.Int).Mul(onePlusY, oneMinusYInv)
u.Mod(u, p)
// make sure we get 32 bytes, pad if necessary
uBytes := u.Bytes()
res := make([]byte, PublicKeySize)
copy(res[PublicKeySize-len(uBytes):], uBytes)
// x25519 are little-endian, but big.Int gives us big-endian.
// See https://www.ietf.org/rfc/rfc7748.txt
return ecdh.X25519().NewPublicKey(reverseBytes(res))
}
// PublicKeyFromMultibase decodes the public key from its Multibase form
func PublicKeyFromMultibase(multibase string) (PublicKey, error) {
code, bytes, err := helpers.MultibaseDecode(multibase)
if err != nil {
return nil, err
}
if code != MultibaseCode {
return nil, fmt.Errorf("invalid code")
}
return PublicKeyFromBytes(bytes)
}
// PublicKeyToMultibase encodes the public key in a suitable way for publicKeyMultibase
func PublicKeyToMultibase(pub PublicKey) string {
return helpers.MultibaseEncode(MultibaseCode, pub.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
}
return pub.(PublicKey), nil
}
// PublicKeyToX509DER encodes the public key into the X.509 DER (binary) format.
func PublicKeyToX509DER(pub PublicKey) []byte {
res, _ := x509.MarshalPKIXPublicKey(pub)
return res
}
const pemPubBlockType = "PUBLIC KEY"
// 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)
}
// PublicKeyToX509PEM encodes the public key into the X.509 PEM (binary) format.
func PublicKeyToX509PEM(pub PublicKey) string {
der := PublicKeyToX509DER(pub)
return string(pem.EncodeToMemory(&pem.Block{
Type: pemPubBlockType,
Bytes: der,
}))
}
// PrivateKeyFromBytes converts a serialized private key to a PrivateKey.
// This compact serialization format is the raw key material, without metadata or structure.
// It errors if len(privateKey) is not [PrivateKeySize].
func PrivateKeyFromBytes(b []byte) (PrivateKey, error) {
// this already check the size of b
return ecdh.X25519().NewPrivateKey(b)
}
// PrivateKeyToBytes converts a private key to a byte slice.
// This compact serialization format is the raw key material, without metadata or structure.
func PrivateKeyToBytes(priv PrivateKey) []byte {
return priv.Bytes()
}
// PrivateKeyFromEd25519 converts an ed25519 private key to a x25519 private key.
// It errors if the slice is not the right size.
//
// This function is based on the algorithm described in https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm#name-curve25519
func PrivateKeyFromEd25519(priv ed25519.PrivateKey) (PrivateKey, error) {
if len(priv) != ed25519.PrivateKeySize {
return nil, fmt.Errorf("invalid ed25519 private key size")
}
// get the 32-byte seed (first half of the private key)
seed := priv.Seed()
h := sha512.Sum512(seed)
// clamp as per the X25519 spec
h[0] &= 248
h[31] &= 127
h[31] |= 64
return ecdh.X25519().NewPrivateKey(h[:32])
}
// 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
}
return priv.(PrivateKey), nil
}
// PrivateKeyToPKCS8DER encodes the private key into the PKCS#8 DER (binary) format.
func PrivateKeyToPKCS8DER(priv PrivateKey) []byte {
res, _ := x509.MarshalPKCS8PrivateKey(priv)
return res
}
const pemPrivBlockType = "PRIVATE KEY"
// 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)
}
// PrivateKeyToPKCS8PEM encodes the private key into the PKCS#8 PEM (binary) format.
func PrivateKeyToPKCS8PEM(priv PrivateKey) string {
der := PrivateKeyToPKCS8DER(priv)
return string(pem.EncodeToMemory(&pem.Block{
Type: pemPrivBlockType,
Bytes: der,
}))
}
func reverseBytes(b []byte) []byte {
r := make([]byte, len(b))
for i := 0; i < len(b); i++ {
r[i] = b[len(b)-1-i]
}
return r
}

View File

@@ -0,0 +1,122 @@
package x25519_test
import (
"crypto/ecdh"
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/INFURA/go-did/verifications/ed25519"
"github.com/INFURA/go-did/verifications/x25519"
)
func TestGenerateKey(t *testing.T) {
pub, priv, err := x25519.GenerateKeyPair()
require.NoError(t, err)
require.NotNil(t, pub)
require.NotNil(t, priv)
require.Equal(t, ecdh.X25519(), pub.Curve())
require.Equal(t, ecdh.X25519(), priv.Curve())
require.True(t, pub.Equal(priv.Public()))
}
func TestBytesRoundTrip(t *testing.T) {
pub, priv, err := x25519.GenerateKeyPair()
require.NoError(t, err)
bytes := x25519.PublicKeyToBytes(pub)
fmt.Println("pub", len(bytes))
rtPub, err := x25519.PublicKeyFromBytes(bytes)
require.NoError(t, err)
require.True(t, pub.Equal(rtPub))
bytes = x25519.PrivateKeyToBytes(priv)
fmt.Println("priv", len(bytes))
rtPriv, err := x25519.PrivateKeyFromBytes(bytes)
require.NoError(t, err)
require.True(t, priv.Equal(rtPriv))
}
func TestMultibaseRoundTrip(t *testing.T) {
pub, _, err := x25519.GenerateKeyPair()
require.NoError(t, err)
mb := x25519.PublicKeyToMultibase(pub)
rt, err := x25519.PublicKeyFromMultibase(mb)
require.NoError(t, err)
require.Equal(t, pub, rt)
}
func TestPublicKeyX509RoundTrip(t *testing.T) {
pub, _, err := ed25519.GenerateKeyPair()
require.NoError(t, err)
der := ed25519.PublicKeyToX509DER(pub)
fmt.Println("der", len(der))
rt, err := ed25519.PublicKeyFromX509DER(der)
require.NoError(t, err)
require.True(t, pub.Equal(rt))
pem := ed25519.PublicKeyToX509PEM(pub)
fmt.Println("pem", len(pem))
rt, err = ed25519.PublicKeyFromX509PEM(pem)
require.NoError(t, err)
require.True(t, pub.Equal(rt))
}
func TestPrivateKeyPKCS8RoundTrip(t *testing.T) {
pub, priv, err := ed25519.GenerateKeyPair()
require.NoError(t, err)
der := ed25519.PrivateKeyToPKCS8DER(priv)
fmt.Println("der", len(der))
rt, err := ed25519.PrivateKeyFromPKCS8DER(der)
require.NoError(t, err)
require.True(t, priv.Equal(rt))
require.True(t, pub.Equal(rt.Public()))
pem := ed25519.PrivateKeyToPKCS8PEM(priv)
fmt.Println("pem", len(pem))
rt, err = ed25519.PrivateKeyFromPKCS8PEM(pem)
require.NoError(t, err)
require.True(t, priv.Equal(rt))
require.True(t, pub.Equal(rt.Public()))
}
func TestEd25519ToX25519(t *testing.T) {
// Known pubkey ed25519 --> x25519
for _, tc := range []struct {
pubEdMultibase string
pubXMultibase string
}{
{
// From https://w3c-ccg.github.io/did-key-spec/#ed25519-with-x25519
pubEdMultibase: "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
pubXMultibase: "z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p",
},
} {
t.Run(tc.pubEdMultibase, func(t *testing.T) {
pubEd, err := ed25519.PublicKeyFromMultibase(tc.pubEdMultibase)
require.NoError(t, err)
pubX, err := x25519.PublicKeyFromEd25519(pubEd)
require.NoError(t, err)
require.Equal(t, tc.pubXMultibase, x25519.PublicKeyToMultibase(pubX))
})
}
// Check that ed25519 --> x25519 match for pubkeys and privkeys
t.Run("ed25519 --> x25519 priv+pub are matching", func(t *testing.T) {
for i := 0; i < 10; i++ {
pubEd, privEd, err := ed25519.GenerateKeyPair()
require.NoError(t, err)
pubX, err := x25519.PublicKeyFromEd25519(pubEd)
require.NoError(t, err)
privX, err := x25519.PrivateKeyFromEd25519(privEd)
require.NoError(t, err)
require.True(t, pubX.Equal(privX.PublicKey()))
}
})
}

View File

@@ -1,107 +0,0 @@
package ed25519vm
import (
"encoding/json"
"errors"
"fmt"
"github.com/mr-tron/base58"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/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) VerifyBytes(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
return v.pubkey.VerifyBytes(data, sig, opts...), nil
}
func (v VerificationKey2018) VerifyASN1(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
return v.pubkey.VerifyASN1(data, sig, opts...), nil
}

View File

@@ -1,27 +0,0 @@
package ed25519vm_test
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"code.sonr.org/go/did-it/verifiers/_methods/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))
}

View File

@@ -1,50 +0,0 @@
package methods
import (
"encoding/json"
"fmt"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/verifiers/_methods/ed25519"
"code.sonr.org/go/did-it/verifiers/_methods/jsonwebkey"
"code.sonr.org/go/did-it/verifiers/_methods/multikey"
p256vm "code.sonr.org/go/did-it/verifiers/_methods/p256"
secp256k1vm "code.sonr.org/go/did-it/verifiers/_methods/secp256k1"
"code.sonr.org/go/did-it/verifiers/_methods/x25519"
)
func UnmarshalJSON(data []byte) (did.VerificationMethod, error) {
var aux struct {
Type string
}
if err := json.Unmarshal(data, &aux); err != nil {
return nil, err
}
var res did.VerificationMethod
switch aux.Type {
case ed25519vm.Type2018:
res = &ed25519vm.VerificationKey2018{}
case ed25519vm.Type2020:
res = &ed25519vm.VerificationKey2020{}
case multikey.Type:
res = &multikey.MultiKey{}
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)
}
if err := json.Unmarshal(data, &res); err != nil {
return nil, err
}
return res, nil
}

View File

@@ -1,116 +0,0 @@
package jsonwebkey
import (
"encoding/json"
"errors"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/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) VerifyBytes(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
if pub, ok := j.pubkey.(crypto.PublicKeySigningBytes); ok {
return pub.VerifyBytes(data, sig, opts...), nil
}
return false, errors.New("not a signing public key")
}
func (j JsonWebKey2020) VerifyASN1(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
if pub, ok := j.pubkey.(crypto.PublicKeySigningASN1); ok {
return pub.VerifyASN1(data, sig, opts...), 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)
}

View File

@@ -1,102 +0,0 @@
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))
})
}
}

View File

@@ -1,119 +0,0 @@
package multikey
import (
"encoding/json"
"errors"
"fmt"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/crypto"
allkeys "code.sonr.org/go/did-it/crypto/_allkeys"
)
// Specification: https://www.w3.org/TR/cid-1.0/#Multikey
const (
// This is apparently the right context despite the spec above saying otherwise.
JsonLdContext = "https://w3id.org/security/multikey/v1"
Type = "Multikey"
)
var _ did.VerificationMethodSignature = &MultiKey{}
var _ did.VerificationMethodKeyAgreement = &MultiKey{}
type MultiKey struct {
id string
pubkey crypto.PublicKey
controller string
}
func NewMultiKey(id string, pubkey crypto.PublicKey, controller did.DID) *MultiKey {
return &MultiKey{
id: id,
pubkey: pubkey,
controller: controller.String(),
}
}
func (m MultiKey) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
ID string `json:"id"`
Type string `json:"type"`
Controller string `json:"controller"`
PublicKeyMultibase string `json:"publicKeyMultibase"`
}{
ID: m.ID(),
Type: m.Type(),
Controller: m.Controller(),
PublicKeyMultibase: m.pubkey.ToPublicKeyMultibase(),
})
}
func (m *MultiKey) UnmarshalJSON(bytes []byte) error {
aux := struct {
ID string `json:"id"`
Type string `json:"type"`
Controller string `json:"controller"`
PublicKeyMultibase string `json:"publicKeyMultibase"`
}{}
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")
}
m.pubkey, err = allkeys.PublicKeyFromPublicKeyMultibase(aux.PublicKeyMultibase)
if err != nil {
return fmt.Errorf("invalid publicKeyMultibase: %w", err)
}
return nil
}
func (m MultiKey) ID() string {
return m.id
}
func (m MultiKey) Type() string {
return Type
}
func (m MultiKey) Controller() string {
return m.controller
}
func (m MultiKey) JsonLdContext() string {
return JsonLdContext
}
func (m MultiKey) VerifyBytes(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
if pub, ok := m.pubkey.(crypto.PublicKeySigningBytes); ok {
return pub.VerifyBytes(data, sig, opts...), nil
}
return false, errors.New("not a signing public key")
}
func (m MultiKey) VerifyASN1(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
if pub, ok := m.pubkey.(crypto.PublicKeySigningASN1); ok {
return pub.VerifyASN1(data, sig, opts...), nil
}
return false, errors.New("not a signing public key")
}
func (m MultiKey) PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool {
return local.PublicKeyIsCompatible(m.pubkey)
}
func (m MultiKey) KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error) {
return local.KeyExchange(m.pubkey)
}

View File

@@ -1,29 +0,0 @@
package multikey_test
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
_ "code.sonr.org/go/did-it/verifiers/did-key"
"code.sonr.org/go/did-it/verifiers/_methods/multikey"
)
func TestJsonRoundTrip(t *testing.T) {
data := `{
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"type": "Multikey",
"controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
}`
var mk multikey.MultiKey
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))
}

View File

@@ -1,118 +0,0 @@
package p256vm
import (
"encoding/json"
"errors"
"fmt"
"github.com/mr-tron/base58"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/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) VerifyBytes(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
return m.pubkey.VerifyBytes(data, sig, opts...), nil
}
func (m Key2021) VerifyASN1(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
return m.pubkey.VerifyASN1(data, sig, opts...), 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)
}

View File

@@ -1,27 +0,0 @@
package p256vm_test
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"code.sonr.org/go/did-it/verifiers/_methods/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))
}

View File

@@ -1,118 +0,0 @@
package secp256k1vm
import (
"encoding/json"
"errors"
"fmt"
"github.com/mr-tron/base58"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/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) VerifyBytes(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
return vm.pubkey.VerifyBytes(data, sig, opts...), nil
}
func (vm VerificationKey2019) VerifyASN1(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
return vm.pubkey.VerifyASN1(data, sig, opts...), 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)
}

View File

@@ -1,27 +0,0 @@
package secp256k1vm_test
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"code.sonr.org/go/did-it/verifiers/_methods/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))
}

View File

@@ -1,107 +0,0 @@
package x25519vm
import (
"encoding/json"
"errors"
"fmt"
"github.com/mr-tron/base58"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/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)
}

View File

@@ -1,27 +0,0 @@
package x25519vm_test
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"code.sonr.org/go/did-it/verifiers/_methods/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))
}

View File

@@ -1,165 +0,0 @@
package didkey
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/verifiers/did-key/testvectors"
)
func TestDocument(t *testing.T) {
d, err := did.Parse("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK")
require.NoError(t, err)
doc, err := d.Document(did.WithResolutionHintVerificationMethod("Ed25519VerificationKey2020"))
require.NoError(t, err)
bytes, err := json.MarshalIndent(doc, "", " ")
require.NoError(t, err)
const expected = `{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/ed25519-2020/v1",
"https://w3id.org/security/suites/x25519-2020/v1"
],
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"verificationMethod": [{
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"type": "Ed25519VerificationKey2020",
"controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
}],
"authentication": [
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
],
"assertionMethod": [
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
],
"capabilityDelegation": [
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
],
"capabilityInvocation": [
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
],
"keyAgreement": [{
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p",
"type": "X25519KeyAgreementKey2020",
"controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"publicKeyMultibase": "z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p"
}]
}`
requireDocEqual(t, expected, string(bytes))
}
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)
}
}
}

View File

@@ -1,170 +0,0 @@
package didkey
import (
"fmt"
"strings"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/crypto"
allkeys "code.sonr.org/go/did-it/crypto/_allkeys"
"code.sonr.org/go/did-it/crypto/ed25519"
"code.sonr.org/go/did-it/crypto/p256"
"code.sonr.org/go/did-it/crypto/p384"
"code.sonr.org/go/did-it/crypto/p521"
"code.sonr.org/go/did-it/crypto/rsa"
"code.sonr.org/go/did-it/crypto/secp256k1"
"code.sonr.org/go/did-it/crypto/x25519"
"code.sonr.org/go/did-it/verifiers/_methods/ed25519"
"code.sonr.org/go/did-it/verifiers/_methods/jsonwebkey"
"code.sonr.org/go/did-it/verifiers/_methods/multikey"
"code.sonr.org/go/did-it/verifiers/_methods/p256"
"code.sonr.org/go/did-it/verifiers/_methods/secp256k1"
"code.sonr.org/go/did-it/verifiers/_methods/x25519"
)
// Specification: https://w3c-ccg.github.io/did-method-key/
func init() {
did.RegisterMethod("key", Decode)
}
var _ did.DID = DidKey{}
type DidKey struct {
msi string // method-specific identifier, i.e. "12345" in "did:key:12345"
pubkey crypto.PublicKey
}
func Decode(identifier string) (did.DID, error) {
const keyPrefix = "did:key:"
if !strings.HasPrefix(identifier, keyPrefix) {
return nil, fmt.Errorf("%w: must start with 'did:key'", did.ErrInvalidDid)
}
msi := identifier[len(keyPrefix):]
pub, err := allkeys.PublicKeyFromPublicKeyMultibase(msi)
if err != nil {
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
}
return DidKey{msi: msi, pubkey: pub}, nil
}
func FromPublicKey(pub crypto.PublicKey) did.DID {
return DidKey{msi: pub.ToPublicKeyMultibase(), pubkey: pub}
}
func FromPrivateKey(priv crypto.PrivateKey) did.DID {
return FromPublicKey(priv.Public().(crypto.PublicKey))
}
func (d DidKey) Method() string {
return "key"
}
func (d DidKey) Document(opts ...did.ResolutionOption) (did.Document, error) {
params := did.CollectResolutionOpts(opts)
doc := document{id: d.String()}
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 {
return fmt.Sprintf("did:key:%s", d.msi)
}
func (d DidKey) ResolutionIsExpensive() bool {
return false
}
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
}

View File

@@ -1,231 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,293 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,371 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,106 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,257 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,240 +0,0 @@
package testvectors
import (
"embed"
"encoding/hex"
"encoding/json"
"strings"
"github.com/mr-tron/base58"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/ed25519"
"code.sonr.org/go/did-it/crypto/jwk"
"code.sonr.org/go/did-it/crypto/p256"
"code.sonr.org/go/did-it/crypto/secp256k1"
"code.sonr.org/go/did-it/verifiers/_methods/ed25519"
"code.sonr.org/go/did-it/verifiers/_methods/jsonwebkey"
"code.sonr.org/go/did-it/verifiers/_methods/p256"
"code.sonr.org/go/did-it/verifiers/_methods/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
}

View File

@@ -1,80 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,98 +0,0 @@
package didplc
import (
"encoding/json"
"net/url"
"code.sonr.org/go/did-it"
)
var _ did.Document = &document{}
type document struct {
id string
alsoKnownAs []*url.URL
signatures []did.VerificationMethodSignature
services did.Services
}
func (d document) MarshalJSON() ([]byte, error) {
akas := make([]string, len(d.alsoKnownAs))
for i, aka := range d.alsoKnownAs {
akas[i] = aka.String()
}
return json.Marshal(struct {
Context []string `json:"@context"`
ID string `json:"id"`
AlsoKnownAs []string `json:"alsoKnownAs,omitempty"`
Controller string `json:"controller,omitempty"`
VerificationMethod []did.VerificationMethodSignature `json:"verificationMethod,omitempty"`
Services did.Services `json:"service,omitempty"`
}{
Context: d.Context(),
ID: d.id,
AlsoKnownAs: akas,
VerificationMethod: d.signatures,
Services: d.services,
})
}
func (d document) Context() []string {
res := make([]string, 0, 1+len(d.signatures))
res = append(res, did.JsonLdContext)
loop:
for _, method := range d.signatures {
for _, item := range res {
if method.JsonLdContext() == item {
continue loop
}
}
res = append(res, method.JsonLdContext())
}
return res
}
func (d document) ID() string {
return d.id
}
func (d document) Controllers() []string {
return nil
}
func (d document) AlsoKnownAs() []*url.URL {
return d.alsoKnownAs
}
func (d document) VerificationMethods() map[string]did.VerificationMethod {
res := make(map[string]did.VerificationMethod)
for _, signature := range d.signatures {
res[signature.ID()] = signature
}
return res
}
func (d document) Authentication() []did.VerificationMethodSignature {
return d.signatures
}
func (d document) Assertion() []did.VerificationMethodSignature {
return d.signatures
}
func (d document) KeyAgreement() []did.VerificationMethodKeyAgreement {
return nil
}
func (d document) CapabilityInvocation() []did.VerificationMethodSignature {
return d.signatures
}
func (d document) CapabilityDelegation() []did.VerificationMethodSignature {
return d.signatures
}
func (d document) Services() did.Services {
return d.services
}

View File

@@ -1,74 +0,0 @@
package didplc
import (
"encoding/json"
"io"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/require"
"code.sonr.org/go/did-it"
)
func TestDocument(t *testing.T) {
// current resolved /data for did:plc:ewvi7nxzyoun6zhxrhs64oiz
resolvedData := `{"did":"did:plc:ewvi7nxzyoun6zhxrhs64oiz","verificationMethods":{"atproto":"did:key:zQ3shunBKsXixLxKtC5qeSG9E4J5RkGN57im31pcTzbNQnm5w"},"rotationKeys":["did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg","did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK"],"alsoKnownAs":["at://atproto.com"],"services":{"atproto_pds":{"type":"AtprotoPersonalDataServer","endpoint":"https://enoki.us-east.host.bsky.network"}}}`
// as resolved by https://plc.directory/did:plc:ewvi7nxzyoun6zhxrhs64oiz
// the original json had an additional
// "https://w3id.org/security/suites/secp256k1-2019/v1" context that
// I removed as it's just wrong
expectedJson := `
{
"@context":[
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/multikey/v1"
],
"id":"did:plc:ewvi7nxzyoun6zhxrhs64oiz",
"alsoKnownAs":[
"at://atproto.com"
],
"verificationMethod":[
{
"id":"did:plc:ewvi7nxzyoun6zhxrhs64oiz#atproto",
"type":"Multikey",
"controller":"did:plc:ewvi7nxzyoun6zhxrhs64oiz",
"publicKeyMultibase":"zQ3shunBKsXixLxKtC5qeSG9E4J5RkGN57im31pcTzbNQnm5w"
}
],
"service":[
{
"id":"#atproto_pds",
"type":"AtprotoPersonalDataServer",
"serviceEndpoint":"https://enoki.us-east.host.bsky.network"
}
]
}
`
mockClient := &MockHTTPClient{resp: resolvedData}
d, err := did.Parse("did:plc:ewvi7nxzyoun6zhxrhs64oiz")
require.NoError(t, err)
doc, err := d.Document(did.WithHttpClient(mockClient))
require.NoError(t, err)
docBytes, err := json.Marshal(doc)
require.NoError(t, err)
require.JSONEq(t, expectedJson, string(docBytes))
}
type MockHTTPClient struct {
resp string
}
func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(strings.NewReader(m.resp)),
}, nil
}

Some files were not shown because too many files have changed in this diff Show More