marginally more working
This commit is contained in:
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
out
|
||||||
|
gen
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.test
|
||||||
|
*.out
|
||||||
|
go.work
|
||||||
|
go.work.sum
|
||||||
|
.env
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package did_key
|
package did_key
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/INFURA/go-did"
|
"github.com/INFURA/go-did"
|
||||||
@@ -14,17 +15,38 @@ type document struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d document) MarshalJSON() ([]byte, error) {
|
func (d document) MarshalJSON() ([]byte, error) {
|
||||||
// TODO implement me
|
return json.Marshal(struct {
|
||||||
panic("implement me")
|
Context []string `json:"@context"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
AlsoKnownAs []string `json:"alsoKnownAs,omitempty"`
|
||||||
|
Controller string `json:"controller,omitempty"`
|
||||||
|
VerificationMethod []did.VerificationMethod `json:"verificationMethod,omitempty"`
|
||||||
|
Authentication []string `json:"authentication,omitempty"`
|
||||||
|
AssertionMethod []string `json:"assertionMethod,omitempty"`
|
||||||
|
KeyAgreement []string `json:"keyAgreement,omitempty"`
|
||||||
|
CapabilityInvocation []string `json:"capabilityInvocation,omitempty"`
|
||||||
|
CapabilityDelegation []string `json:"capabilityDelegation,omitempty"`
|
||||||
|
}{
|
||||||
|
Context: []string{did.JsonLdContext, d.verification.JsonLdContext()},
|
||||||
|
ID: d.id.String(),
|
||||||
|
AlsoKnownAs: nil,
|
||||||
|
Controller: d.id.String(),
|
||||||
|
VerificationMethod: []did.VerificationMethod{d.verification},
|
||||||
|
Authentication: []string{d.verification.ID()},
|
||||||
|
AssertionMethod: []string{d.verification.ID()},
|
||||||
|
KeyAgreement: []string{d.verification.ID()},
|
||||||
|
CapabilityInvocation: []string{d.verification.ID()},
|
||||||
|
CapabilityDelegation: []string{d.verification.ID()},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d document) ID() did.DID {
|
func (d document) ID() did.DID {
|
||||||
return d.id
|
return d.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d document) Controller() did.DID {
|
func (d document) Controllers() []did.DID {
|
||||||
// no external controller possible for did:key
|
// no external controller possible for did:key
|
||||||
return d.id
|
return []did.DID{d.id}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d document) AlsoKnownAs() []url.URL {
|
func (d document) AlsoKnownAs() []url.URL {
|
||||||
|
|||||||
@@ -3,46 +3,53 @@ package did_key
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
mbase "github.com/multiformats/go-multibase"
|
mbase "github.com/multiformats/go-multibase"
|
||||||
varint "github.com/multiformats/go-varint"
|
"github.com/multiformats/go-varint"
|
||||||
|
|
||||||
"github.com/INFURA/go-did"
|
"github.com/INFURA/go-did"
|
||||||
|
"github.com/INFURA/go-did/verifications/ed25519"
|
||||||
)
|
)
|
||||||
|
|
||||||
type multicodecCode uint64
|
// Specification: https://w3c-ccg.github.io/did-method-key/
|
||||||
|
|
||||||
// Signature algorithms from the [did:key specification]
|
|
||||||
//
|
|
||||||
// [did:key specification]: https://w3c-ccg.github.io/did-method-key/#signature-method-creation-algorithm
|
|
||||||
const (
|
|
||||||
X25519 multicodecCode = 0xec
|
|
||||||
Ed25519 multicodecCode = 0xed
|
|
||||||
P256 multicodecCode = 0x1200
|
|
||||||
P384 multicodecCode = 0x1201
|
|
||||||
P521 multicodecCode = 0x1202
|
|
||||||
Secp256k1 multicodecCode = 0xe7
|
|
||||||
RSA multicodecCode = 0x1205
|
|
||||||
)
|
|
||||||
|
|
||||||
func Decode(identifier string) (did.DID, error) {
|
func Decode(identifier string) (did.DID, error) {
|
||||||
// baseCodec, bytes, err := mbase.Decode(identifier)
|
const keyPrefix = "did:key:"
|
||||||
_, bytes, err := mbase.Decode(identifier)
|
|
||||||
|
if !strings.HasPrefix(identifier, keyPrefix) {
|
||||||
|
return nil, fmt.Errorf("must start with 'did:key'")
|
||||||
|
}
|
||||||
|
|
||||||
|
baseCodec, bytes, err := mbase.Decode(identifier[len(keyPrefix):])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
|
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
|
||||||
}
|
}
|
||||||
// if baseCodec != mbase.Base58BTC {
|
// the specification enforces that encoding
|
||||||
// return nil, fmt.Errorf("%w: not Base58BTC encoded", did.ErrInvalidDid)
|
if baseCodec != mbase.Base58BTC {
|
||||||
// }
|
return nil, fmt.Errorf("%w: not Base58BTC encoded", did.ErrInvalidDid)
|
||||||
code, _, err := varint.FromUvarint(bytes)
|
}
|
||||||
|
code, read, err := varint.FromUvarint(bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
|
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
|
||||||
}
|
}
|
||||||
switch multicodecCode(code) {
|
|
||||||
case Ed25519, P256, Secp256k1, RSA:
|
d := DidKey{identifier: identifier}
|
||||||
return DidKey{bytes: string(bytes), code: multicodecCode(code)}, nil
|
|
||||||
|
switch code {
|
||||||
|
case ed25519.MultibaseCode:
|
||||||
|
d.verification, err = ed25519.NewVerificationKey2020(identifier, bytes[read:], d)
|
||||||
|
// case P256: // TODO
|
||||||
|
// case Secp256k1: // TODO
|
||||||
|
// case RSA: // TODO
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("%w: unsupported did:key multicodec: 0x%x", did.ErrInvalidDid, code)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("%w: unsupported did:key multicodec: 0x%x", did.ErrInvalidDid, code)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -52,9 +59,8 @@ func init() {
|
|||||||
var _ did.DID = &DidKey{}
|
var _ did.DID = &DidKey{}
|
||||||
|
|
||||||
type DidKey struct {
|
type DidKey struct {
|
||||||
// TODO: store a verification method instead
|
identifier string // cached value
|
||||||
code multicodecCode
|
verification did.VerificationMethod
|
||||||
bytes string // as string instead of []byte to allow the == operator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d DidKey) Method() string {
|
func (d DidKey) Method() string {
|
||||||
@@ -74,11 +80,12 @@ func (d DidKey) Fragment() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d DidKey) Document() (did.Document, error) {
|
func (d DidKey) Document() (did.Document, error) {
|
||||||
// TODO implement me
|
return document{
|
||||||
panic("implement me")
|
id: d,
|
||||||
|
verification: d.verification,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d DidKey) String() string {
|
func (d DidKey) String() string {
|
||||||
key, _ := mbase.Encode(mbase.Base58BTC, []byte(d.bytes))
|
return d.identifier
|
||||||
return "did:key:" + key
|
|
||||||
}
|
}
|
||||||
|
|||||||
54
did.go
54
did.go
@@ -6,16 +6,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Decoder is a function decoding an identifier ("foo" in "did:example:foo") into a DID.
|
const JsonLdContext = "https://www.w3.org/ns/did/v1"
|
||||||
|
|
||||||
|
// Decoder is a function decoding a DID string representation ("did:example:foo") into a DID.
|
||||||
type Decoder func(identifier string) (DID, error)
|
type Decoder func(identifier string) (DID, error)
|
||||||
|
|
||||||
var (
|
// RegisterMethod registers a DID decoder for a given DID method.
|
||||||
decodersMu sync.RWMutex
|
// Method must be the DID method (for example, "key" in did:key).
|
||||||
decoders = map[string]Decoder{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterMethod registers a DID decoder for a given DID method..
|
|
||||||
// Method must be the DID method (for example "key" in did:key).
|
|
||||||
func RegisterMethod(method string, decoder Decoder) {
|
func RegisterMethod(method string, decoder Decoder) {
|
||||||
decodersMu.Lock()
|
decodersMu.Lock()
|
||||||
defer decodersMu.Unlock()
|
defer decodersMu.Unlock()
|
||||||
@@ -28,24 +25,21 @@ func RegisterMethod(method string, decoder Decoder) {
|
|||||||
decoders[method] = decoder
|
decoders[method] = decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse returns the DID from the string representation or an error if
|
// Parse attempts to decode a DID from its string representation.
|
||||||
// the prefix and method are incorrect, if an unknown encryption algorithm
|
func Parse(identifier string) (DID, error) {
|
||||||
// is specified or if the method-specific-identifier's bytes don't
|
|
||||||
// represent a public key for the specified encryption algorithm.
|
|
||||||
func Parse(str string) (DID, error) {
|
|
||||||
decodersMu.RLock()
|
decodersMu.RLock()
|
||||||
defer decodersMu.RUnlock()
|
defer decodersMu.RUnlock()
|
||||||
|
|
||||||
if !strings.HasPrefix(str, "did:") {
|
if !strings.HasPrefix(identifier, "did:") {
|
||||||
return nil, fmt.Errorf("%w: must start with \"did:\"", ErrInvalidDid)
|
return nil, fmt.Errorf("%w: must start with \"did:\"", ErrInvalidDid)
|
||||||
}
|
}
|
||||||
|
|
||||||
method, identifier, ok := strings.Cut(str[len("did:"):], ":")
|
method, suffix, ok := strings.Cut(identifier[len("did:"):], ":")
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("%w: must have a method and an identifier", ErrInvalidDid)
|
return nil, fmt.Errorf("%w: must have a method and an identifier", ErrInvalidDid)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !checkIdentifier(identifier) {
|
if !checkSuffix(suffix) {
|
||||||
return nil, fmt.Errorf("%w: invalid identifier characters", ErrInvalidDid)
|
return nil, fmt.Errorf("%w: invalid identifier characters", ErrInvalidDid)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,14 +52,27 @@ func Parse(str string) (DID, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MustParse is like Parse but panics instead of returning an error.
|
// MustParse is like Parse but panics instead of returning an error.
|
||||||
func MustParse(str string) DID {
|
func MustParse(identifier string) DID {
|
||||||
did, err := Parse(str)
|
did, err := Parse(identifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return did
|
return did
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasValidSyntax tells if the given string representation conforms to DID syntax.
|
||||||
|
// This does NOT verify that the method is supported by this library.
|
||||||
|
func HasValidSyntax(identifier string) bool {
|
||||||
|
if !strings.HasPrefix(identifier, "did:") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
method, suffix, ok := strings.Cut(identifier[len("did:"):], ":")
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return checkMethod(method) && checkSuffix(suffix)
|
||||||
|
}
|
||||||
|
|
||||||
func checkMethod(method string) bool {
|
func checkMethod(method string) bool {
|
||||||
if len(method) == 0 {
|
if len(method) == 0 {
|
||||||
return false
|
return false
|
||||||
@@ -80,14 +87,19 @@ func checkMethod(method string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkIdentifier(identifier string) bool {
|
func checkSuffix(suffix string) bool {
|
||||||
if len(identifier) == 0 {
|
if len(suffix) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// TODO
|
// TODO
|
||||||
// for _, char := range identifier {
|
// for _, char := range suffix {
|
||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
decodersMu sync.RWMutex
|
||||||
|
decoders = map[string]Decoder{}
|
||||||
|
)
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DID interface { // --> implementation for each DID type: key, pkh ..
|
// DID is a decoded (i.e. from a string) Decentralized Identifiers.
|
||||||
|
type DID interface {
|
||||||
Method() string
|
Method() string
|
||||||
Path() string
|
Path() string
|
||||||
Query() url.Values
|
Query() url.Values
|
||||||
@@ -15,13 +16,15 @@ type DID interface { // --> implementation for each DID type: key, pkh ..
|
|||||||
String() string // return the full DID URL, with path, query, fragment
|
String() string // return the full DID URL, with path, query, fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
type Document interface { // --> compact implementation, get serialized into json only if necessary
|
// Document is the interface for a DID document. It represents the "resolved" state of a DID.
|
||||||
|
type Document interface {
|
||||||
json.Marshaler
|
json.Marshaler
|
||||||
|
|
||||||
// ID is the identifier of the Document, which is the DID itself.
|
// ID is the identifier of the Document, which is the DID itself.
|
||||||
ID() DID
|
ID() DID
|
||||||
// Controller is the DID that is authorized to make changes to the Document. It's often the same as ID.
|
|
||||||
Controller() DID
|
// Controllers is the set of DID that is authorized to make changes to the Document. It's often the same as ID.
|
||||||
|
Controllers() []DID
|
||||||
|
|
||||||
// AlsoKnownAs returns an optional set of URL describing ???TODO
|
// AlsoKnownAs returns an optional set of URL describing ???TODO
|
||||||
AlsoKnownAs() []url.URL
|
AlsoKnownAs() []url.URL
|
||||||
@@ -55,18 +58,26 @@ type Document interface { // --> compact implementation, get serialized into jso
|
|||||||
// https://www.w3.org/TR/did-extensions-properties/#service-types
|
// https://www.w3.org/TR/did-extensions-properties/#service-types
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationMethod interface { // --> implementation for each method
|
// VerificationMethod is a common interface for a cryptographic signature verification method.
|
||||||
|
// For example, Ed25519VerificationKey2020 implements the Ed25519 signature verification.
|
||||||
|
type VerificationMethod interface {
|
||||||
json.Marshaler
|
json.Marshaler
|
||||||
json.Unmarshaler
|
json.Unmarshaler
|
||||||
|
|
||||||
// ID is a string identifier for the VerificationMethod
|
// ID is a string identifier for the VerificationMethod. It can be referenced in a Document.
|
||||||
ID() string
|
ID() string
|
||||||
|
|
||||||
// Type is a string identifier of a verification method.
|
// Type is a string identifier of a verification method.
|
||||||
// See https://www.w3.org/TR/did-extensions-properties/#verification-method-types
|
// See https://www.w3.org/TR/did-extensions-properties/#verification-method-types
|
||||||
Type() string
|
Type() string
|
||||||
// ???? TODO
|
|
||||||
Controller() DID
|
|
||||||
|
|
||||||
// Verify that 'sig' is the signed hash of 'data'
|
// Controller is a DID able to control the VerificationMethod.
|
||||||
|
// This is not necessarily the same as for DID itself or the Document.
|
||||||
|
Controller() string
|
||||||
|
|
||||||
|
// JsonLdContext reports the JSON-LD context definition required for this verification method.
|
||||||
|
JsonLdContext() string
|
||||||
|
|
||||||
|
// Verify checks that 'sig' is a valid signature of 'data'.
|
||||||
Verify(data []byte, sig []byte) bool
|
Verify(data []byte, sig []byte) bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
package Ed25519
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ed25519"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/INFURA/go-did"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ did.VerificationMethod = &VerificationKey2020{}
|
|
||||||
|
|
||||||
type VerificationKey2020 struct {
|
|
||||||
id string
|
|
||||||
pubkey ed25519.PublicKey
|
|
||||||
controller did.DID
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewVerificationKey2020(id string, pubkey []byte, controller did.DID) (*VerificationKey2020, error) {
|
|
||||||
if len(pubkey) != ed25519.PublicKeySize {
|
|
||||||
return nil, errors.New("invalid ed25519 public key size")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &VerificationKey2020{
|
|
||||||
id: id,
|
|
||||||
pubkey: pubkey,
|
|
||||||
controller: controller,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v VerificationKey2020) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Controller string `json:"controller"`
|
|
||||||
PublicKeyMultibase string `json:"publicKeyMultibase"`
|
|
||||||
}{
|
|
||||||
ID: v.ID(),
|
|
||||||
Type: v.Type(),
|
|
||||||
Controller: v.Controller().String(),
|
|
||||||
PublicKeyMultibase: v,
|
|
||||||
})
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
{
|
|
||||||
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
|
|
||||||
"type": "Ed25519VerificationKey2020",
|
|
||||||
"controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
|
|
||||||
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v VerificationKey2020) UnmarshalJSON(bytes []byte) error {
|
|
||||||
// TODO implement me
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v VerificationKey2020) ID() string {
|
|
||||||
return v.id
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v VerificationKey2020) Type() string {
|
|
||||||
return "Ed25519VerificationKey2020"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v VerificationKey2020) Controller() did.DID {
|
|
||||||
return v.controller
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v VerificationKey2020) Verify(data []byte, sig []byte) bool {
|
|
||||||
return ed25519.Verify(v.pubkey, data, sig)
|
|
||||||
}
|
|
||||||
132
verifications/ed25519/VerificationKey2020.go
Normal file
132
verifications/ed25519/VerificationKey2020.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package ed25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
mbase "github.com/multiformats/go-multibase"
|
||||||
|
"github.com/multiformats/go-varint"
|
||||||
|
|
||||||
|
"github.com/INFURA/go-did"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MultibaseCode = uint64(0xed)
|
||||||
|
JsonLdContext = "https://w3id.org/security/suites/ed25519-2020/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ did.VerificationMethod = &VerificationKey2020{}
|
||||||
|
|
||||||
|
type VerificationKey2020 struct {
|
||||||
|
id string
|
||||||
|
pubkey ed25519.PublicKey
|
||||||
|
controller string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVerificationKey2020(id string, pubkey []byte, controller did.DID) (*VerificationKey2020, error) {
|
||||||
|
if len(pubkey) != ed25519.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) {
|
||||||
|
return json.Marshal(struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Controller string `json:"controller"`
|
||||||
|
PublicKeyMultibase string `json:"publicKeyMultibase"`
|
||||||
|
}{
|
||||||
|
ID: v.ID(),
|
||||||
|
Type: v.Type(),
|
||||||
|
Controller: v.Controller(),
|
||||||
|
PublicKeyMultibase: encodePubkey(v.pubkey),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *VerificationKey2020) 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 != v.Type() {
|
||||||
|
return errors.New("invalid type")
|
||||||
|
}
|
||||||
|
v.id = aux.ID
|
||||||
|
if len(v.id) == 0 {
|
||||||
|
return errors.New("invalid id")
|
||||||
|
}
|
||||||
|
v.pubkey, err = decodePubkey(aux.PublicKeyMultibase)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid publicKeyMultibase: %w", err)
|
||||||
|
}
|
||||||
|
v.controller = aux.Controller
|
||||||
|
if !did.HasValidSyntax(v.controller) {
|
||||||
|
return errors.New("invalid controller")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VerificationKey2020) ID() string {
|
||||||
|
return v.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VerificationKey2020) Type() string {
|
||||||
|
return "Ed25519VerificationKey2020"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VerificationKey2020) Controller() string {
|
||||||
|
return v.controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VerificationKey2020) JsonLdContext() string {
|
||||||
|
return JsonLdContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VerificationKey2020) Verify(data []byte, sig []byte) bool {
|
||||||
|
return ed25519.Verify(v.pubkey, data, sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodePubkey(pubkey ed25519.PublicKey) string {
|
||||||
|
// can only fail with an invalid encoding, but it's hardcoded
|
||||||
|
bytes, _ := mbase.Encode(mbase.Base58BTC, append(varint.ToUvarint(MultibaseCode), pubkey...))
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePubkey(encoded string) (ed25519.PublicKey, error) {
|
||||||
|
baseCodec, bytes, err := mbase.Decode(encoded)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// the specification enforces that encoding
|
||||||
|
if baseCodec != mbase.Base58BTC {
|
||||||
|
return nil, fmt.Errorf("not Base58BTC encoded")
|
||||||
|
}
|
||||||
|
code, read, err := varint.FromUvarint(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if code != MultibaseCode {
|
||||||
|
return nil, fmt.Errorf("invalid code")
|
||||||
|
}
|
||||||
|
if read != 2 {
|
||||||
|
return nil, fmt.Errorf("unexpected multibase")
|
||||||
|
}
|
||||||
|
if len(bytes)-read != ed25519.PublicKeySize {
|
||||||
|
return nil, fmt.Errorf("invalid ed25519 public key size")
|
||||||
|
}
|
||||||
|
return bytes[read:], nil
|
||||||
|
}
|
||||||
28
verifications/ed25519/VerificationKey2020_test.go
Normal file
28
verifications/ed25519/VerificationKey2020_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package ed25519_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
_ "github.com/INFURA/go-did/did-key"
|
||||||
|
"github.com/INFURA/go-did/verifications/ed25519"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestJson(t *testing.T) {
|
||||||
|
data := `{
|
||||||
|
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
|
||||||
|
"type": "Ed25519VerificationKey2020",
|
||||||
|
"controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
|
||||||
|
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
|
||||||
|
}`
|
||||||
|
|
||||||
|
var vm ed25519.VerificationKey2020
|
||||||
|
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))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user