test(did): split the test vector reading code in a separate file/package
tests are much cleaner and explicit now
This commit is contained in:
@@ -1,26 +1,19 @@
|
|||||||
// go:build jwx_es256k
|
//go:build jwx_es256k
|
||||||
|
|
||||||
package did_test
|
package did_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
|
||||||
"github.com/lestrrat-go/jwx/v2/jwk"
|
|
||||||
"github.com/libp2p/go-libp2p/core/crypto"
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
"github.com/mr-tron/base58"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/did"
|
"github.com/ucan-wg/go-ucan/did"
|
||||||
|
"github.com/ucan-wg/go-ucan/did/testvectors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestDidKeyVectors executes tests read from the [test vector files] provided
|
// TestDidKeyVectors executes tests read from the [test vector files] provided
|
||||||
@@ -67,177 +60,25 @@ func TestDidKeyVectors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadTestVectors(t *testing.T, filename string) vectors {
|
func loadTestVectors(t *testing.T, filename string) testvectors.Vectors {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
data, err := os.ReadFile(filepath.Join("testdata", filename))
|
data, err := os.ReadFile(filepath.Join("testvectors", filename))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var vs vectors
|
var vs testvectors.Vectors
|
||||||
|
|
||||||
require.NoError(t, json.Unmarshal(data, &vs))
|
require.NoError(t, json.Unmarshal(data, &vs))
|
||||||
|
|
||||||
return vs
|
return vs
|
||||||
}
|
}
|
||||||
|
|
||||||
func vectorPubKey(t *testing.T, v vector) crypto.PubKey {
|
func vectorPubKey(t *testing.T, v testvectors.Vector) crypto.PubKey {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
pubKey, err := v.pubKey()
|
pubKey, err := v.PubKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotZero(t, pubKey)
|
require.NotZero(t, pubKey)
|
||||||
|
|
||||||
return pubKey
|
return pubKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func vectorType(t *testing.T, v vector) string {
|
|
||||||
vectorType, err := v.pubKeyType()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
return vectorType
|
|
||||||
}
|
|
||||||
|
|
||||||
type vectors map[string]vector
|
|
||||||
|
|
||||||
// This is pretty gross but the structure allows the repeated verifier,
|
|
||||||
// PublicKeyJwk and PublicKeyBase58 account for the fact that the test
|
|
||||||
// files are very inconsistent.
|
|
||||||
type vector struct {
|
|
||||||
VerificationKeyPair verifier
|
|
||||||
VerificationMethod verifier
|
|
||||||
PublicKeyJwk json.RawMessage
|
|
||||||
DidDocument json.RawMessage // TODO: if we start producing DID documents, we should test this too
|
|
||||||
}
|
|
||||||
|
|
||||||
type verifier struct {
|
|
||||||
ID string
|
|
||||||
Type string
|
|
||||||
PublicKeyBase58 string
|
|
||||||
PublicKeyJwk json.RawMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v vector) pubKey() (crypto.PubKey, error) {
|
|
||||||
// If the public key is in base58
|
|
||||||
if pubB58 := v.pubKeyBase58(); len(pubB58) > 0 {
|
|
||||||
pubBytes, err := base58.Decode(pubB58)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := v.pubKeyType()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var unmarshaler crypto.PubKeyUnmarshaller
|
|
||||||
|
|
||||||
switch t {
|
|
||||||
case "Ed25519VerificationKey2018":
|
|
||||||
unmarshaler = crypto.UnmarshalEd25519PublicKey
|
|
||||||
case "EcdsaSecp256k1VerificationKey2019":
|
|
||||||
unmarshaler = crypto.UnmarshalSecp256k1PublicKey
|
|
||||||
// This is weak as it assumes the P256 curve - that's all the vectors contain (for now)
|
|
||||||
case "P256Key2021":
|
|
||||||
unmarshaler = compressedEcdsaPublicKeyUnmarshaler
|
|
||||||
default:
|
|
||||||
return nil, errors.New("failed to resolve unmarshaler")
|
|
||||||
}
|
|
||||||
|
|
||||||
return unmarshaler(pubBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the public key is in a JWK
|
|
||||||
if pubJwk := v.pubKeyJwk(); len(pubJwk) > 0 {
|
|
||||||
key, err := jwk.ParseKey(pubJwk)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var a any
|
|
||||||
|
|
||||||
if err := key.Raw(&a); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch a.(type) {
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
epub := a.(*ecdsa.PublicKey)
|
|
||||||
|
|
||||||
if epub.Curve == secp256k1.S256() {
|
|
||||||
bytes := append([]byte{0x04}, append(epub.X.Bytes(), epub.Y.Bytes()...)...)
|
|
||||||
|
|
||||||
return crypto.UnmarshalSecp256k1PublicKey(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
asn1, err := x509.MarshalPKIXPublicKey(epub)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.UnmarshalECDSAPublicKey(asn1)
|
|
||||||
case ed25519.PublicKey:
|
|
||||||
return crypto.UnmarshalEd25519PublicKey(a.(ed25519.PublicKey))
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
asn1, err := x509.MarshalPKIXPublicKey(a.(*rsa.PublicKey))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.UnmarshalRsaPublicKey(asn1)
|
|
||||||
default:
|
|
||||||
return nil, errors.New("unsupported key type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't find a public key at all
|
|
||||||
return nil, errors.New("vector's public key not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v vector) pubKeyBase58() string {
|
|
||||||
if len(v.VerificationKeyPair.PublicKeyBase58) > 0 {
|
|
||||||
return v.VerificationKeyPair.PublicKeyBase58
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.VerificationMethod.PublicKeyBase58
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v vector) pubKeyJwk() json.RawMessage {
|
|
||||||
if len(v.VerificationKeyPair.PublicKeyJwk) > 0 {
|
|
||||||
return v.VerificationKeyPair.PublicKeyJwk
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.VerificationMethod.PublicKeyJwk) > 0 {
|
|
||||||
return v.VerificationMethod.PublicKeyJwk
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.PublicKeyJwk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v vector) pubKeyType() (string, error) {
|
|
||||||
if len(v.VerificationKeyPair.Type) > 0 {
|
|
||||||
return v.VerificationKeyPair.Type, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.VerificationMethod.Type) > 0 {
|
|
||||||
return v.VerificationMethod.Type, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", errors.New("vector's type not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func compressedEcdsaPublicKeyUnmarshaler(data []byte) (crypto.PubKey, error) {
|
|
||||||
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), data)
|
|
||||||
|
|
||||||
ecdsaPublicKey := ecdsa.PublicKey{
|
|
||||||
Curve: elliptic.P256(),
|
|
||||||
X: x,
|
|
||||||
Y: y,
|
|
||||||
}
|
|
||||||
|
|
||||||
asn1, err := x509.MarshalPKIXPublicKey(&ecdsaPublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.UnmarshalECDSAPublicKey(asn1)
|
|
||||||
}
|
|
||||||
|
|||||||
163
did/testvectors/vectors.go
Normal file
163
did/testvectors/vectors.go
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
//go:build jwx_es256k
|
||||||
|
|
||||||
|
package testvectors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
|
"github.com/lestrrat-go/jwx/v2/jwk"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/mr-tron/base58"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Vectors map[string]Vector
|
||||||
|
|
||||||
|
// This is pretty gross but the structure allows the repeated Verifier,
|
||||||
|
// PublicKeyJwk and PublicKeyBase58 account for the fact that the test
|
||||||
|
// files are very inconsistent.
|
||||||
|
type Vector struct {
|
||||||
|
VerificationKeyPair Verifier
|
||||||
|
VerificationMethod Verifier
|
||||||
|
PublicKeyJwk json.RawMessage
|
||||||
|
DidDocument json.RawMessage // TODO: if we start producing DID documents, we should test this too
|
||||||
|
}
|
||||||
|
|
||||||
|
type Verifier struct {
|
||||||
|
ID string
|
||||||
|
Type string
|
||||||
|
PublicKeyBase58 string
|
||||||
|
PublicKeyJwk json.RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vector) PubKey() (crypto.PubKey, error) {
|
||||||
|
// If the public key is in base58
|
||||||
|
if pubB58 := v.PubKeyBase58(); len(pubB58) > 0 {
|
||||||
|
pubBytes, err := base58.Decode(pubB58)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := v.PubKeyType()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var unmarshaler crypto.PubKeyUnmarshaller
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "Ed25519VerificationKey2018":
|
||||||
|
unmarshaler = crypto.UnmarshalEd25519PublicKey
|
||||||
|
case "EcdsaSecp256k1VerificationKey2019":
|
||||||
|
unmarshaler = crypto.UnmarshalSecp256k1PublicKey
|
||||||
|
// This is weak as it assumes the P256 curve - that's all the vectors contain (for now)
|
||||||
|
case "P256Key2021":
|
||||||
|
unmarshaler = compressedEcdsaPublicKeyUnmarshaler
|
||||||
|
default:
|
||||||
|
return nil, errors.New("failed to resolve unmarshaler")
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshaler(pubBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the public key is in a JWK
|
||||||
|
if pubJwk := v.PubKeyJwk(); len(pubJwk) > 0 {
|
||||||
|
key, err := jwk.ParseKey(pubJwk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var a any
|
||||||
|
|
||||||
|
if err := key.Raw(&a); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch a.(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
epub := a.(*ecdsa.PublicKey)
|
||||||
|
|
||||||
|
if epub.Curve == secp256k1.S256() {
|
||||||
|
bytes := append([]byte{0x04}, append(epub.X.Bytes(), epub.Y.Bytes()...)...)
|
||||||
|
|
||||||
|
return crypto.UnmarshalSecp256k1PublicKey(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
asn1, err := x509.MarshalPKIXPublicKey(epub)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.UnmarshalECDSAPublicKey(asn1)
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
return crypto.UnmarshalEd25519PublicKey(a.(ed25519.PublicKey))
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
asn1, err := x509.MarshalPKIXPublicKey(a.(*rsa.PublicKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.UnmarshalRsaPublicKey(asn1)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported key type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't find a public key at all
|
||||||
|
return nil, errors.New("vector's public key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vector) PubKeyBase58() string {
|
||||||
|
if len(v.VerificationKeyPair.PublicKeyBase58) > 0 {
|
||||||
|
return v.VerificationKeyPair.PublicKeyBase58
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.VerificationMethod.PublicKeyBase58
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vector) PubKeyJwk() json.RawMessage {
|
||||||
|
if len(v.VerificationKeyPair.PublicKeyJwk) > 0 {
|
||||||
|
return v.VerificationKeyPair.PublicKeyJwk
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(v.VerificationMethod.PublicKeyJwk) > 0 {
|
||||||
|
return v.VerificationMethod.PublicKeyJwk
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.PublicKeyJwk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vector) PubKeyType() (string, error) {
|
||||||
|
if len(v.VerificationKeyPair.Type) > 0 {
|
||||||
|
return v.VerificationKeyPair.Type, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(v.VerificationMethod.Type) > 0 {
|
||||||
|
return v.VerificationMethod.Type, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("vector's type not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressedEcdsaPublicKeyUnmarshaler(data []byte) (crypto.PubKey, error) {
|
||||||
|
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), data)
|
||||||
|
|
||||||
|
ecdsaPublicKey := ecdsa.PublicKey{
|
||||||
|
Curve: elliptic.P256(),
|
||||||
|
X: x,
|
||||||
|
Y: y,
|
||||||
|
}
|
||||||
|
|
||||||
|
asn1, err := x509.MarshalPKIXPublicKey(&ecdsaPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.UnmarshalECDSAPublicKey(asn1)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user