aboutsummaryrefslogtreecommitdiff
path: root/internal/verify/verifier_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/verify/verifier_test.go')
-rw-r--r--internal/verify/verifier_test.go183
1 files changed, 183 insertions, 0 deletions
diff --git a/internal/verify/verifier_test.go b/internal/verify/verifier_test.go
new file mode 100644
index 0000000..0b38411
--- /dev/null
+++ b/internal/verify/verifier_test.go
@@ -0,0 +1,183 @@
+package verify
+
+import (
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/base64"
+ "encoding/json"
+ "testing"
+
+ "github.com/Fuwn/plutia/internal/config"
+ "github.com/Fuwn/plutia/internal/types"
+ "github.com/decred/dcrd/dcrec/secp256k1/v4"
+ secpECDSA "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
+ "github.com/fxamacker/cbor/v2"
+ "github.com/mr-tron/base58"
+)
+
+func TestVerifyOperationValidSignature(t *testing.T) {
+ pub, priv, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatalf("generate key: %v", err)
+ }
+ payloadDoc := []byte(`{"did":"did:plc:alice","didDoc":{"id":"did:plc:alice"}}`)
+ sig := ed25519.Sign(priv, payloadDoc)
+ opJSON := map[string]any{
+ "did": "did:plc:alice",
+ "didDoc": map[string]any{"id": "did:plc:alice"},
+ "publicKey": base64.RawURLEncoding.EncodeToString(pub),
+ "sigPayload": base64.RawURLEncoding.EncodeToString(payloadDoc),
+ "sig": base64.RawURLEncoding.EncodeToString(sig),
+ }
+ raw, _ := json.Marshal(opJSON)
+ op, err := types.ParseOperation(types.ExportRecord{
+ Seq: 1,
+ DID: "did:plc:alice",
+ Operation: raw,
+ })
+ if err != nil {
+ t.Fatalf("parse operation: %v", err)
+ }
+ v := New(config.VerifyFull)
+ if err := v.VerifyOperation(op, nil); err != nil {
+ t.Fatalf("verify operation: %v", err)
+ }
+}
+
+func TestVerifyOperationPrevMismatch(t *testing.T) {
+ pub, priv, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatalf("generate key: %v", err)
+ }
+ payloadDoc := []byte(`{"did":"did:plc:alice","didDoc":{"id":"did:plc:alice"},"prev":"sha256:wrong"}`)
+ sig := ed25519.Sign(priv, payloadDoc)
+ opJSON := map[string]any{
+ "did": "did:plc:alice",
+ "didDoc": map[string]any{"id": "did:plc:alice"},
+ "prev": "sha256:wrong",
+ "publicKey": base64.RawURLEncoding.EncodeToString(pub),
+ "sigPayload": base64.RawURLEncoding.EncodeToString(payloadDoc),
+ "sig": base64.RawURLEncoding.EncodeToString(sig),
+ }
+ raw, _ := json.Marshal(opJSON)
+ op, err := types.ParseOperation(types.ExportRecord{Seq: 2, DID: "did:plc:alice", Operation: raw})
+ if err != nil {
+ t.Fatalf("parse operation: %v", err)
+ }
+ v := New(config.VerifyFull)
+ existing := &types.StateV1{DID: "did:plc:alice", ChainTipHash: "sha256:right", LatestOpSeq: 1}
+ if err := v.VerifyOperation(op, existing); err == nil {
+ t.Fatalf("expected prev mismatch error")
+ }
+}
+
+func TestVerifyOperationSecp256k1(t *testing.T) {
+ priv, err := secp256k1.GeneratePrivateKey()
+ if err != nil {
+ t.Fatalf("generate secp256k1 key: %v", err)
+ }
+ pubKey := priv.PubKey()
+ didKeyBytes := append([]byte{0xE7, 0x01}, pubKey.SerializeCompressed()...)
+ didKey := "did:key:z" + base58.Encode(didKeyBytes)
+
+ unsigned := map[string]any{
+ "type": "create",
+ "prev": nil,
+ "handle": "alice.example.com",
+ "service": "https://example.com",
+ "signingKey": didKey,
+ }
+ enc, err := cbor.CanonicalEncOptions().EncMode()
+ if err != nil {
+ t.Fatalf("init cbor encoder: %v", err)
+ }
+ payload, err := enc.Marshal(unsigned)
+ if err != nil {
+ t.Fatalf("marshal cbor: %v", err)
+ }
+ sum := sha256.Sum256(payload)
+ sig := secpECDSA.Sign(priv, sum[:]).Serialize()
+
+ opJSON := map[string]any{
+ "type": "create",
+ "prev": nil,
+ "handle": "alice.example.com",
+ "service": "https://example.com",
+ "signingKey": didKey,
+ "sig": base64.RawURLEncoding.EncodeToString(sig),
+ }
+ raw, _ := json.Marshal(opJSON)
+ op, err := types.ParseOperation(types.ExportRecord{
+ Seq: 1,
+ DID: "did:plc:alice",
+ Operation: raw,
+ })
+ if err != nil {
+ t.Fatalf("parse operation: %v", err)
+ }
+ v := New(config.VerifyFull)
+ if err := v.VerifyOperation(op, nil); err != nil {
+ t.Fatalf("verify operation: %v", err)
+ }
+}
+
+func TestVerifyOperationP256(t *testing.T) {
+ priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatalf("generate p256 key: %v", err)
+ }
+ compressed := elliptic.MarshalCompressed(priv.Curve, priv.PublicKey.X, priv.PublicKey.Y)
+ didKeyBytes := append([]byte{0x80, 0x24}, compressed...)
+ didKey := "did:key:z" + base58.Encode(didKeyBytes)
+
+ unsigned := map[string]any{
+ "type": "create",
+ "prev": nil,
+ "handle": "alice.example.com",
+ "service": "https://example.com",
+ "signingKey": didKey,
+ }
+ enc, err := cbor.CanonicalEncOptions().EncMode()
+ if err != nil {
+ t.Fatalf("init cbor encoder: %v", err)
+ }
+ payload, err := enc.Marshal(unsigned)
+ if err != nil {
+ t.Fatalf("marshal cbor: %v", err)
+ }
+ sum := sha256.Sum256(payload)
+ r, s, err := ecdsa.Sign(rand.Reader, priv, sum[:])
+ if err != nil {
+ t.Fatalf("sign p256: %v", err)
+ }
+ sig := make([]byte, 64)
+ rb := r.Bytes()
+ sb := s.Bytes()
+ copy(sig[32-len(rb):32], rb)
+ copy(sig[64-len(sb):], sb)
+
+ opJSON := map[string]any{
+ "type": "create",
+ "prev": nil,
+ "handle": "alice.example.com",
+ "service": "https://example.com",
+ "signingKey": didKey,
+ "sig": base64.RawURLEncoding.EncodeToString(sig),
+ }
+ raw, _ := json.Marshal(opJSON)
+ op, err := types.ParseOperation(types.ExportRecord{
+ Seq: 1,
+ DID: "did:plc:alice",
+ Operation: raw,
+ })
+ if err != nil {
+ t.Fatalf("parse operation: %v", err)
+ }
+ v := New(config.VerifyFull)
+ if err := v.VerifyOperation(op, nil); err != nil {
+ t.Fatalf("verify operation: %v", err)
+ }
+}