Compare commits
2 Commits
master
...
p256-seria
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bfdc4bf2e | ||
|
|
e15cc2295a |
10
.github/Repo.toml
vendored
10
.github/Repo.toml
vendored
@@ -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
140
Readme.md
@@ -1,20 +1,20 @@
|
||||
<div align="center">
|
||||
<h1 align="center">go-did-it</h1>
|
||||
<h1 align="center">go-did</h1>
|
||||
|
||||
<p>
|
||||
<a href="https://code.sonr.org/go/did-it/tags">
|
||||
<img alt="GitHub Tag" src="https://img.shields.io/github/v/tag/MetaMask/go-did-it">
|
||||
<a href="https://github.com/INFURA/go-did/tags">
|
||||
<img alt="GitHub Tag" src="https://img.shields.io/github/v/tag/INFURA/go-did">
|
||||
</a>
|
||||
<a href="https://code.sonr.org/go/did-it/actions?query=">
|
||||
<img src="https://code.sonr.org/go/did-it/actions/workflows/gotest.yml/badge.svg" alt="Build Status">
|
||||
<a href="https://github.com/INFURA/go-did/actions?query=">
|
||||
<img src="https://github.com/INFURA/go-did/actions/workflows/gotest.yml/badge.svg" alt="Build Status">
|
||||
</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">
|
||||
</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">
|
||||
</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">
|
||||
</a>
|
||||
</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
|
||||
- **extensible**: you can easily register your custom DID method
|
||||
|
||||
Built with ❤️ by [Consensys](https://consensys.io/).
|
||||
DID spec concepts:
|
||||
|
||||
## Concepts
|
||||

|
||||
|
||||

|
||||
`go-did` concepts:
|
||||
|
||||
## Installation
|
||||
|
||||
```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 | ✅ |
|
||||

|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 | ✅ |
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
171
crypto/hash.go
171
crypto/hash.go
@@ -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,
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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"])
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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"])
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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}
|
||||
)
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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()))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
21
design.md
21
design.md
@@ -6,14 +6,31 @@ General:
|
||||
- coding style should be clean, straightforward and documented, in a similar fashion as go-ucan.
|
||||
- keep the dependencies minimal, favor the standard go libraries
|
||||
- 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
|
||||
- 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
|
||||
|
||||
DIDs:
|
||||
- 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:
|
||||
- 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.
|
||||
- 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...)
|
||||
|
||||
## Minimal target features
|
||||
|
||||
Methods:
|
||||
- did:key
|
||||
- did:pkh
|
||||
|
||||
Verification Methods:
|
||||
- ed25519
|
||||
- x25519
|
||||
- secp256k1
|
||||
- p256
|
||||
- p384
|
||||
4
did.go
4
did.go
@@ -7,10 +7,6 @@ import (
|
||||
"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"
|
||||
|
||||
// Decoder is a function decoding a DID string representation ("did:example:foo") into a DID.
|
||||
|
||||
18
did_test.go
18
did_test.go
@@ -7,12 +7,12 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"code.sonr.org/go/did-it"
|
||||
"code.sonr.org/go/did-it/crypto/x25519"
|
||||
_ "code.sonr.org/go/did-it/verifiers/did-key"
|
||||
"github.com/INFURA/go-did"
|
||||
_ "github.com/INFURA/go-did/methods/did-key"
|
||||
"github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
|
||||
func Example_signature() {
|
||||
func ExampleSignature() {
|
||||
// errors need to be handled
|
||||
|
||||
// 1) Parse the DID string into a DID object
|
||||
@@ -21,9 +21,9 @@ func Example_signature() {
|
||||
// 2) Resolve to the DID 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==")
|
||||
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())
|
||||
} else {
|
||||
fmt.Println("Signature is invalid")
|
||||
@@ -32,7 +32,7 @@ func Example_signature() {
|
||||
// Output: Signature is valid, verified with method: Ed25519VerificationKey2020 did:key:z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse#z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse
|
||||
}
|
||||
|
||||
func Example_keyAgreement() {
|
||||
func ExampleKeyAgreement() {
|
||||
// errors need to be handled
|
||||
|
||||
// 1) We have a private key for Alice
|
||||
@@ -92,7 +92,7 @@ func TestHasValidDIDSyntax(t *testing.T) {
|
||||
|
||||
func BenchmarkHasValidDIDSyntax(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
for i := 0; i < b.N; i++ {
|
||||
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) {
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
for i := 0; i < b.N; i++ {
|
||||
did.HasValidDidUrlSyntax("did:example:123456789abcdefghi/path/to/resource?key=value#section1")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
"code.sonr.org/go/did-it"
|
||||
verifications "code.sonr.org/go/did-it/verifiers/_methods"
|
||||
"github.com/INFURA/go-did"
|
||||
"github.com/INFURA/go-did/verifications"
|
||||
)
|
||||
|
||||
var _ did.Document = &Document{}
|
||||
@@ -26,7 +26,6 @@ type Document struct {
|
||||
keyAgreement []did.VerificationMethodKeyAgreement
|
||||
capabilityInvocation []did.VerificationMethodSignature
|
||||
capabilityDelegation []did.VerificationMethodSignature
|
||||
services did.Services
|
||||
}
|
||||
|
||||
type aux struct {
|
||||
@@ -40,7 +39,6 @@ type aux struct {
|
||||
KeyAgreement []json.RawMessage `json:"keyAgreement,omitempty"`
|
||||
CapabilityInvocation []json.RawMessage `json:"capabilityInvocation,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.
|
||||
@@ -71,7 +69,6 @@ func fromAux(aux *aux) (*Document, error) {
|
||||
res := Document{
|
||||
context: aux.Context,
|
||||
id: aux.Id,
|
||||
services: aux.Services,
|
||||
}
|
||||
|
||||
// id
|
||||
@@ -194,7 +191,7 @@ func resolveVerificationMethods[T did.VerificationMethod](doc *Document, msgs []
|
||||
func (d Document) MarshalJSON() ([]byte, error) {
|
||||
var err error
|
||||
|
||||
data := aux{Context: d.context, Id: d.id, Services: d.services}
|
||||
data := aux{Context: d.context, Id: d.id}
|
||||
|
||||
// alsoKnownAs
|
||||
data.AlsoKnownAs = make([]string, len(d.alsoKnownAs))
|
||||
@@ -312,7 +309,3 @@ func (d Document) CapabilityInvocation() []did.VerificationMethodSignature {
|
||||
func (d Document) CapabilityDelegation() []did.VerificationMethodSignature {
|
||||
return d.capabilityDelegation
|
||||
}
|
||||
|
||||
func (d Document) Services() did.Services {
|
||||
return d.services
|
||||
}
|
||||
|
||||
@@ -6,68 +6,13 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"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/x25519"
|
||||
_ "code.sonr.org/go/did-it/verifiers/did-key"
|
||||
_ "github.com/INFURA/go-did/methods/did-key"
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
"github.com/INFURA/go-did/verifications/x25519"
|
||||
)
|
||||
|
||||
func TestRoundTrip(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
strDoc string
|
||||
assertion func(t *testing.T, doc *Document)
|
||||
}{
|
||||
{
|
||||
name: "ed25519",
|
||||
strDoc: ed25519Doc,
|
||||
assertion: func(t *testing.T, doc *Document) {
|
||||
require.Equal(t, "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", doc.ID())
|
||||
require.Equal(t, ed25519vm.Type2020, doc.Authentication()[0].Type())
|
||||
require.Equal(t, ed25519vm.Type2020, doc.Assertion()[0].Type())
|
||||
require.Equal(t, x25519vm.Type2020, doc.KeyAgreement()[0].Type())
|
||||
require.Equal(t, ed25519vm.Type2020, doc.CapabilityInvocation()[0].Type())
|
||||
require.Equal(t, ed25519vm.Type2020, doc.CapabilityDelegation()[0].Type())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsonWebKey",
|
||||
strDoc: jsonWebKeyDoc,
|
||||
assertion: func(t *testing.T, doc *Document) {
|
||||
require.Equal(t, "did:example:123", doc.ID())
|
||||
require.Len(t, doc.VerificationMethods(), 6)
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A"].Type())
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A"].Type())
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs"].Type())
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw"].Type())
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY"].Type())
|
||||
require.Equal(t, jsonwebkey.Type, doc.verificationMethods["did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"].Type())
|
||||
},
|
||||
},
|
||||
{
|
||||
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 = `
|
||||
strDoc := `
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
@@ -101,162 +46,18 @@ const ed25519Doc = `
|
||||
}]
|
||||
}
|
||||
`
|
||||
doc, err := FromJsonBytes([]byte(strDoc))
|
||||
require.NoError(t, err)
|
||||
|
||||
const jsonWebKeyDoc = `
|
||||
{
|
||||
"@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1"],
|
||||
"id": "did:example:123",
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": "did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "OKP",
|
||||
"crv": "Ed25519",
|
||||
"x": "VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "secp256k1",
|
||||
"x": "Z4Y3NNOxv0J6tCgqOBFnHnaZhJF6LdulT7z8A-2D5_8",
|
||||
"y": "i5a2NtJoUKXkLm6q8nOEu9WOkso1Ag6FTUT6k_LMnGk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "RSA",
|
||||
"e": "AQAB",
|
||||
"n": "omwsC1AqEk6whvxyOltCFWheSQvv1MExu5RLCMT4jVk9khJKv8JeMXWe3bWHatjPskdf2dlaGkW5QjtOnUKL742mvr4tCldKS3ULIaT1hJInMHHxj2gcubO6eEegACQ4QSu9LO0H-LM_L3DsRABB7Qja8HecpyuspW1Tu_DbqxcSnwendamwL52V17eKhlO4uXwv2HFlxufFHM0KmCJujIKyAxjD_m3q__IiHUVHD1tDIEvLPhG9Azsn3j95d-saIgZzPLhQFiKluGvsjrSkYU5pXVWIsV-B2jtLeeLC14XcYxWDUJ0qVopxkBvdlERcNtgF4dvW4X00EHj4vCljFw"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "38M1FDts7Oea7urmseiugGW7tWc3mLpJh6rKe7xINZ8",
|
||||
"y": "nDQW6XZ7b_u2Sy9slofYLlG03sOEoug3I0aAPQ0exs4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-384",
|
||||
"x": "GnLl6mDti7a2VUIZP5w6pcRX8q5nvEIgB3Q_5RI2p9F_QVsaAlDN7IG68Jn0dS_F",
|
||||
"y": "jq4QoAHKiIzezDp88s_cxSPXtuXYFliuCGndgU4Qp8l91xzD1spCmFIzQgVjqvcP"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E",
|
||||
"type": "JsonWebKey2020",
|
||||
"controller": "did:example:123",
|
||||
"publicKeyJwk": {
|
||||
"kty": "EC",
|
||||
"crv": "P-521",
|
||||
"x": "AVlZG23LyXYwlbjbGPMxZbHmJpDSu-IvpuKigEN2pzgWtSo--Rwd-n78nrWnZzeDc187Ln3qHlw5LRGrX4qgLQ-y",
|
||||
"y": "ANIbFeRdPHf1WYMCUjcPz-ZhecZFybOqLIJjVOlLETH7uPlyG0gEoMWnIZXhQVypPy_HtUiUzdnSEPAylYhHBTX2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"authentication": [
|
||||
"did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"
|
||||
],
|
||||
"assertionMethod": [
|
||||
"did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"
|
||||
],
|
||||
"capabilityDelegation": [
|
||||
"did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"
|
||||
],
|
||||
"capabilityInvocation": [
|
||||
"did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
|
||||
"did:example:123#4SZ-StXrp5Yd4_4rxHVTCYTHyt4zyPfN1fIuYsm6k3A",
|
||||
"did:example:123#n4cQ-I_WkHMcwXBJa7IHkYu8CMfdNcZKnKsOrnHLpFs",
|
||||
"did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
|
||||
"did:example:123#8wgRfY3sWmzoeAL-78-oALNvNj67ZlQxd1ss_NX1hZY",
|
||||
"did:example:123#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
// basic testing
|
||||
require.Equal(t, "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", doc.ID())
|
||||
require.Equal(t, ed25519.Type, doc.Authentication()[0].Type())
|
||||
require.Equal(t, ed25519.Type, doc.Assertion()[0].Type())
|
||||
require.Equal(t, x25519.Type, doc.KeyAgreement()[0].Type())
|
||||
require.Equal(t, ed25519.Type, doc.CapabilityInvocation()[0].Type())
|
||||
require.Equal(t, ed25519.Type, doc.CapabilityDelegation()[0].Type())
|
||||
|
||||
roundtrip, err := json.Marshal(doc)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, strDoc, string(roundtrip))
|
||||
}
|
||||
|
||||
@@ -16,7 +16,4 @@ var (
|
||||
var (
|
||||
// ErrNotFound indicates that the DID resolver was unable to find the DID document for the given DID.
|
||||
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
12
go.mod
@@ -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 (
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
||||
github.com/mr-tron/base58 v1.1.0
|
||||
github.com/multiformats/go-multibase v0.2.0
|
||||
github.com/multiformats/go-varint v0.0.7
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/ucan-wg/go-varsig v1.0.0
|
||||
golang.org/x/crypto v0.45.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/mr-tron/base58 v1.1.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.0.3 // indirect
|
||||
github.com/multiformats/go-base36 v0.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
10
go.sum
10
go.sum
@@ -1,9 +1,5 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
|
||||
@@ -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/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package did
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"code.sonr.org/go/did-it/crypto"
|
||||
)
|
||||
|
||||
// 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.
|
||||
// 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() 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() []string
|
||||
|
||||
// AlsoKnownAs returns an optional set of URL describing different identifier for the DID subject,
|
||||
// for different purpose or different time.
|
||||
// AlsoKnownAs returns an optional set of URL describing ???TODO
|
||||
AlsoKnownAs() []*url.URL
|
||||
|
||||
// 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.
|
||||
CapabilityDelegation() []VerificationMethodSignature
|
||||
|
||||
// Services are means of communicating or interacting with the DID subject or associated entities
|
||||
// via one or more endpoints. Examples include discovery services, agent services, social networking
|
||||
// services, file storage services, and verifiable credential repository services.
|
||||
Services() Services
|
||||
// TODO: Service
|
||||
// https://www.w3.org/TR/did-extensions-properties/#service-types
|
||||
}
|
||||
|
||||
// VerificationMethod is a common interface for a cryptographic signature verification method.
|
||||
@@ -104,11 +100,8 @@ type VerificationMethod interface {
|
||||
type VerificationMethodSignature interface {
|
||||
VerificationMethod
|
||||
|
||||
// VerifyBytes checks that 'sig' is a valid "raw bytes" signature of 'data'.
|
||||
VerifyBytes(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error)
|
||||
|
||||
// VerifyASN1 checks that 'sig' is a valid ASN.1 signature of 'data'.
|
||||
VerifyASN1(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error)
|
||||
// Verify checks that 'sig' is a valid signature of 'data'.
|
||||
Verify(data []byte, sig []byte) bool
|
||||
}
|
||||
|
||||
// VerificationMethodKeyAgreement is a VerificationMethod implementing a shared key agreement.
|
||||
@@ -117,8 +110,20 @@ type VerificationMethodKeyAgreement interface {
|
||||
VerificationMethod
|
||||
|
||||
// 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.
|
||||
KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error)
|
||||
// ECDH computes the shared key using the given PrivateKey.
|
||||
ECDH(local PrivateKey) ([]byte, error)
|
||||
}
|
||||
|
||||
// Below are the interfaces for crypto.PublicKey and crypto.PrivateKey in the go standard library.
|
||||
// They are not defined there for compatibility reasons, so we need to define them here.
|
||||
|
||||
type PublicKey interface {
|
||||
Equal(x crypto.PublicKey) bool
|
||||
}
|
||||
|
||||
type PrivateKey interface {
|
||||
Public() crypto.PublicKey
|
||||
Equal(x crypto.PrivateKey) bool
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"code.sonr.org/go/did-it"
|
||||
"github.com/INFURA/go-did"
|
||||
)
|
||||
|
||||
var _ did.Document = &document{}
|
||||
|
||||
type document struct {
|
||||
id string
|
||||
id did.DID
|
||||
signature did.VerificationMethodSignature
|
||||
keyAgreement did.VerificationMethodKeyAgreement
|
||||
}
|
||||
@@ -20,11 +20,6 @@ func (d document) MarshalJSON() ([]byte, error) {
|
||||
// Maybe it doesn't matter, but the spec contradicts itself.
|
||||
// See https://github.com/w3c-ccg/did-key-spec/issues/71
|
||||
|
||||
vms := []did.VerificationMethod{d.signature}
|
||||
if d.signature != did.VerificationMethod(d.keyAgreement) {
|
||||
vms = append(vms, d.keyAgreement)
|
||||
}
|
||||
|
||||
return json.Marshal(struct {
|
||||
Context []string `json:"@context"`
|
||||
ID string `json:"id"`
|
||||
@@ -33,17 +28,17 @@ func (d document) MarshalJSON() ([]byte, error) {
|
||||
VerificationMethod []did.VerificationMethod `json:"verificationMethod,omitempty"`
|
||||
Authentication []string `json:"authentication,omitempty"`
|
||||
AssertionMethod []string `json:"assertionMethod,omitempty"`
|
||||
KeyAgreement []string `json:"keyAgreement,omitempty"`
|
||||
KeyAgreement []did.VerificationMethod `json:"keyAgreement,omitempty"`
|
||||
CapabilityInvocation []string `json:"capabilityInvocation,omitempty"`
|
||||
CapabilityDelegation []string `json:"capabilityDelegation,omitempty"`
|
||||
}{
|
||||
Context: d.Context(),
|
||||
ID: d.id,
|
||||
ID: d.id.String(),
|
||||
AlsoKnownAs: nil,
|
||||
VerificationMethod: vms,
|
||||
VerificationMethod: []did.VerificationMethod{d.signature},
|
||||
Authentication: []string{d.signature.ID()},
|
||||
AssertionMethod: []string{d.signature.ID()},
|
||||
KeyAgreement: []string{d.keyAgreement.ID()},
|
||||
KeyAgreement: []did.VerificationMethod{d.keyAgreement},
|
||||
CapabilityInvocation: []string{d.signature.ID()},
|
||||
CapabilityDelegation: []string{d.signature.ID()},
|
||||
})
|
||||
@@ -58,7 +53,7 @@ func (d document) Context() []string {
|
||||
}
|
||||
|
||||
func (d document) ID() string {
|
||||
return d.id
|
||||
return d.id.String()
|
||||
}
|
||||
|
||||
func (d document) Controllers() []string {
|
||||
@@ -71,11 +66,6 @@ func (d document) AlsoKnownAs() []*url.URL {
|
||||
}
|
||||
|
||||
func (d document) VerificationMethods() map[string]did.VerificationMethod {
|
||||
if d.signature == did.VerificationMethod(d.keyAgreement) {
|
||||
return map[string]did.VerificationMethod{
|
||||
d.signature.ID(): d.signature,
|
||||
}
|
||||
}
|
||||
return map[string]did.VerificationMethod{
|
||||
d.signature.ID(): d.signature,
|
||||
d.keyAgreement.ID(): d.keyAgreement,
|
||||
@@ -102,10 +92,6 @@ func (d document) CapabilityDelegation() []did.VerificationMethodSignature {
|
||||
return []did.VerificationMethodSignature{d.signature}
|
||||
}
|
||||
|
||||
func (d document) Services() did.Services {
|
||||
return nil
|
||||
}
|
||||
|
||||
func stringSet(values ...string) []string {
|
||||
res := make([]string, 0, len(values))
|
||||
loop:
|
||||
61
methods/did-key/document_test.go
Normal file
61
methods/did-key/document_test.go
Normal 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
121
methods/did-key/key.go
Normal 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
|
||||
}
|
||||
@@ -7,32 +7,38 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"code.sonr.org/go/did-it"
|
||||
"code.sonr.org/go/did-it/crypto/ed25519"
|
||||
didkey "code.sonr.org/go/did-it/verifiers/did-key"
|
||||
"github.com/INFURA/go-did"
|
||||
didkey "github.com/INFURA/go-did/methods/did-key"
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
)
|
||||
|
||||
func ExampleGenerateKeyPair() {
|
||||
// Generate a key pair
|
||||
pub, priv, err := ed25519.GenerateKeyPair()
|
||||
handleErr(err)
|
||||
fmt.Println("Public key:", pub.ToPublicKeyMultibase())
|
||||
fmt.Println("Private key:", base64.StdEncoding.EncodeToString(priv.ToBytes()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Public key:", ed25519.PublicKeyToMultibase(pub))
|
||||
fmt.Println("Private key:", base64.StdEncoding.EncodeToString(priv))
|
||||
|
||||
// Make the associated did:key
|
||||
dk := didkey.FromPrivateKey(priv)
|
||||
dk, err := didkey.FromPrivateKey(priv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Did:", dk.String())
|
||||
|
||||
// Produce a signature
|
||||
msg := []byte("message")
|
||||
sig, err := priv.SignToBytes(msg)
|
||||
handleErr(err)
|
||||
sig := ed25519.Sign(priv, msg)
|
||||
fmt.Println("Signature:", base64.StdEncoding.EncodeToString(sig))
|
||||
|
||||
// Resolve the DID and verify a signature
|
||||
doc, err := dk.Document()
|
||||
handleErr(err)
|
||||
ok, _ := did.TryAllVerifyBytes(doc.Authentication(), msg, sig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ok, _ := did.TryAllVerify(doc.Authentication(), msg, sig)
|
||||
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) {
|
||||
did0A, err := did.Parse("did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z")
|
||||
require.NoError(t, err)
|
||||
@@ -76,9 +72,3 @@ func TestEquivalence(t *testing.T) {
|
||||
require.True(t, did0A.Equal(did0B))
|
||||
require.False(t, did0A.Equal(did1))
|
||||
}
|
||||
|
||||
func handleErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
81
options.go
81
options.go
@@ -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
|
||||
}
|
||||
}
|
||||
3
resources/did_brief_architecture_overview.svg
Normal file
3
resources/did_brief_architecture_overview.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 202 KiB |
155
service.go
155
service.go
@@ -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)
|
||||
}
|
||||
107
service_test.go
107
service_test.go
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
24
utilities.go
24
utilities.go
@@ -2,28 +2,14 @@ package did
|
||||
|
||||
import (
|
||||
"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.
|
||||
// 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 {
|
||||
if valid, err := method.VerifyBytes(data, sig, opts...); err == nil && valid {
|
||||
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 {
|
||||
if method.Verify(data, sig) {
|
||||
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.
|
||||
// It returns the shared key as well as the selected method.
|
||||
// If no matching method is found, it returns an error.
|
||||
func FindMatchingKeyAgreement(methods []VerificationMethodKeyAgreement, priv crypto.PrivateKeyKeyExchange) ([]byte, VerificationMethodKeyAgreement, error) {
|
||||
func FindMatchingKeyAgreement(methods []VerificationMethodKeyAgreement, priv PrivateKey) ([]byte, VerificationMethodKeyAgreement, error) {
|
||||
for _, method := range methods {
|
||||
if method.PrivateKeyIsCompatible(priv) {
|
||||
key, err := method.KeyExchange(priv)
|
||||
key, err := method.ECDH(priv)
|
||||
return key, method, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,39 @@
|
||||
package ed25519vm
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"code.sonr.org/go/did-it"
|
||||
"code.sonr.org/go/did-it/crypto"
|
||||
"code.sonr.org/go/did-it/crypto/ed25519"
|
||||
"github.com/INFURA/go-did"
|
||||
)
|
||||
|
||||
// Specification: https://w3c.github.io/cg-reports/credentials/CG-FINAL-di-eddsa-2020-20220724/
|
||||
|
||||
const (
|
||||
JsonLdContext2020 = "https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
Type2020 = "Ed25519VerificationKey2020"
|
||||
JsonLdContext = "https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
Type = "Ed25519VerificationKey2020"
|
||||
)
|
||||
|
||||
var _ did.VerificationMethodSignature = &VerificationKey2020{}
|
||||
|
||||
type VerificationKey2020 struct {
|
||||
id string
|
||||
pubkey ed25519.PublicKey
|
||||
pubkey PublicKey
|
||||
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{
|
||||
id: id,
|
||||
pubkey: pubkey,
|
||||
controller: controller.String(),
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) MarshalJSON() ([]byte, error) {
|
||||
@@ -43,7 +46,7 @@ func (v VerificationKey2020) MarshalJSON() ([]byte, error) {
|
||||
ID: v.ID(),
|
||||
Type: v.Type(),
|
||||
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 {
|
||||
return errors.New("invalid id")
|
||||
}
|
||||
v.pubkey, err = ed25519.PublicKeyFromPublicKeyMultibase(aux.PublicKeyMultibase)
|
||||
v.pubkey, err = PublicKeyFromMultibase(aux.PublicKeyMultibase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyMultibase: %w", err)
|
||||
}
|
||||
@@ -81,7 +84,7 @@ func (v VerificationKey2020) ID() string {
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) Type() string {
|
||||
return Type2020
|
||||
return Type
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) Controller() string {
|
||||
@@ -89,13 +92,9 @@ func (v VerificationKey2020) Controller() string {
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) JsonLdContext() string {
|
||||
return JsonLdContext2020
|
||||
return JsonLdContext
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) VerifyBytes(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
|
||||
return v.pubkey.VerifyBytes(data, sig, opts...), nil
|
||||
}
|
||||
|
||||
func (v VerificationKey2020) VerifyASN1(data []byte, sig []byte, opts ...crypto.SigningOption) (bool, error) {
|
||||
return v.pubkey.VerifyASN1(data, sig, opts...), nil
|
||||
func (v VerificationKey2020) Verify(data []byte, sig []byte) bool {
|
||||
return ed25519.Verify(v.pubkey, data, sig)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package ed25519vm_test
|
||||
package ed25519_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
@@ -7,13 +7,12 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"code.sonr.org/go/did-it"
|
||||
"code.sonr.org/go/did-it/crypto/ed25519"
|
||||
ed25519vm "code.sonr.org/go/did-it/verifiers/_methods/ed25519"
|
||||
_ "code.sonr.org/go/did-it/verifiers/did-key"
|
||||
"github.com/INFURA/go-did"
|
||||
_ "github.com/INFURA/go-did/methods/did-key"
|
||||
"github.com/INFURA/go-did/verifications/ed25519"
|
||||
)
|
||||
|
||||
func TestJsonRoundTrip2020(t *testing.T) {
|
||||
func TestJsonRoundTrip(t *testing.T) {
|
||||
data := `{
|
||||
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
|
||||
"type": "Ed25519VerificationKey2020",
|
||||
@@ -21,7 +20,7 @@ func TestJsonRoundTrip2020(t *testing.T) {
|
||||
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
|
||||
}`
|
||||
|
||||
var vk ed25519vm.VerificationKey2020
|
||||
var vk ed25519.VerificationKey2020
|
||||
err := json.Unmarshal([]byte(data), &vk)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -30,7 +29,7 @@ func TestJsonRoundTrip2020(t *testing.T) {
|
||||
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
|
||||
|
||||
pkHex := "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025"
|
||||
@@ -38,9 +37,10 @@ func TestSignature2020(t *testing.T) {
|
||||
pk, err := ed25519.PublicKeyFromBytes(pkBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
contDid := "did:key:" + pk.ToPublicKeyMultibase()
|
||||
contDid := "did:key:" + ed25519.PublicKeyToMultibase(pk)
|
||||
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 {
|
||||
name string
|
||||
@@ -77,9 +77,7 @@ func TestSignature2020(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
valid, err := vk.Verify(tc.data, tc.signature)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.valid, valid)
|
||||
require.Equal(t, tc.valid, vk.Verify(tc.data, tc.signature))
|
||||
})
|
||||
}
|
||||
}
|
||||
171
verifications/ed25519/key.go
Normal file
171
verifications/ed25519/key.go
Normal 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)
|
||||
}
|
||||
89
verifications/ed25519/key_test.go
Normal file
89
verifications/ed25519/key_test.go
Normal 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"))
|
||||
//
|
||||
// }
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"github.com/multiformats/go-varint"
|
||||
)
|
||||
|
||||
// PublicKeyMultibaseDecode is a helper for decoding multibase public keys.
|
||||
func PublicKeyMultibaseDecode(multibase string) (uint64, []byte, error) {
|
||||
// MultibaseDecode is a helper for decoding multibase public keys.
|
||||
func MultibaseDecode(multibase string) (uint64, []byte, error) {
|
||||
baseCodec, bytes, err := mbase.Decode(multibase)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
@@ -27,8 +27,8 @@ func PublicKeyMultibaseDecode(multibase string) (uint64, []byte, error) {
|
||||
return code, bytes[read:], nil
|
||||
}
|
||||
|
||||
// PublicKeyMultibaseEncode is a helper for encoding multibase public keys.
|
||||
func PublicKeyMultibaseEncode(code uint64, bytes []byte) string {
|
||||
// MultibaseEncode is a helper for encoding multibase public keys.
|
||||
func MultibaseEncode(code uint64, bytes []byte) string {
|
||||
// can only fail with an invalid encoding, but it's hardcoded
|
||||
res, _ := mbase.Encode(mbase.Base58BTC, append(varint.ToUvarint(code), bytes...))
|
||||
return res
|
||||
34
verifications/json.go
Normal file
34
verifications/json.go
Normal 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
188
verifications/p256/key.go
Normal 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,
|
||||
}))
|
||||
}
|
||||
74
verifications/p256/key_test.go
Normal file
74
verifications/p256/key_test.go
Normal 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)
|
||||
}
|
||||
@@ -1,36 +1,39 @@
|
||||
package x25519vm
|
||||
package x25519
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"code.sonr.org/go/did-it"
|
||||
"code.sonr.org/go/did-it/crypto"
|
||||
"code.sonr.org/go/did-it/crypto/x25519"
|
||||
"github.com/INFURA/go-did"
|
||||
)
|
||||
|
||||
// Specification: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519
|
||||
|
||||
const (
|
||||
JsonLdContext2020 = "https://w3id.org/security/suites/x25519-2020/v1"
|
||||
Type2020 = "X25519KeyAgreementKey2020"
|
||||
JsonLdContext = "https://w3id.org/security/suites/x25519-2020/v1"
|
||||
Type = "X25519KeyAgreementKey2020"
|
||||
)
|
||||
|
||||
var _ did.VerificationMethodKeyAgreement = &KeyAgreementKey2020{}
|
||||
|
||||
type KeyAgreementKey2020 struct {
|
||||
id string
|
||||
pubkey *x25519.PublicKey
|
||||
pubkey PublicKey
|
||||
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{
|
||||
id: id,
|
||||
pubkey: pubkey,
|
||||
controller: controller.String(),
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) MarshalJSON() ([]byte, error) {
|
||||
@@ -43,7 +46,7 @@ func (k KeyAgreementKey2020) MarshalJSON() ([]byte, error) {
|
||||
ID: k.ID(),
|
||||
Type: k.Type(),
|
||||
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 {
|
||||
return errors.New("invalid id")
|
||||
}
|
||||
k.pubkey, err = x25519.PublicKeyFromPublicKeyMultibase(aux.PublicKeyMultibase)
|
||||
k.pubkey, err = PublicKeyFromMultibase(aux.PublicKeyMultibase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid publicKeyMultibase: %w", err)
|
||||
}
|
||||
@@ -81,7 +84,7 @@ func (k KeyAgreementKey2020) ID() string {
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) Type() string {
|
||||
return Type2020
|
||||
return Type
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) Controller() string {
|
||||
@@ -89,13 +92,24 @@ func (k KeyAgreementKey2020) Controller() string {
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) JsonLdContext() string {
|
||||
return JsonLdContext2020
|
||||
return JsonLdContext
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) PrivateKeyIsCompatible(local crypto.PrivateKeyKeyExchange) bool {
|
||||
return local.PublicKeyIsCompatible(k.pubkey)
|
||||
func (k KeyAgreementKey2020) PrivateKeyIsCompatible(local did.PrivateKey) bool {
|
||||
_, ok := local.(PrivateKey)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (k KeyAgreementKey2020) KeyExchange(local crypto.PrivateKeyKeyExchange) ([]byte, error) {
|
||||
return local.KeyExchange(k.pubkey)
|
||||
func (k KeyAgreementKey2020) ECDH(local did.PrivateKey) ([]byte, error) {
|
||||
cast, ok := local.(PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("private key type doesn't match the public key type")
|
||||
}
|
||||
if cast == nil {
|
||||
return nil, errors.New("invalid private key")
|
||||
}
|
||||
if k.pubkey.Curve() != cast.Curve() {
|
||||
return nil, errors.New("key curves don't match")
|
||||
}
|
||||
return cast.ECDH(k.pubkey)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package x25519vm_test
|
||||
package x25519_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
|
||||
"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 := `{
|
||||
"id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW",
|
||||
"type": "X25519KeyAgreementKey2020",
|
||||
@@ -17,7 +17,7 @@ func TestJsonRoundTrip2020(t *testing.T) {
|
||||
"publicKeyMultibase": "z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW"
|
||||
}`
|
||||
|
||||
var vm x25519vm.KeyAgreementKey2020
|
||||
var vm x25519.KeyAgreementKey2020
|
||||
err := json.Unmarshal([]byte(data), &vm)
|
||||
require.NoError(t, err)
|
||||
|
||||
246
verifications/x25519/key.go
Normal file
246
verifications/x25519/key.go
Normal 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
|
||||
}
|
||||
122
verifications/x25519/key_test.go
Normal file
122
verifications/x25519/key_test.go
Normal 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()))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user