From 6186aa3ba206944b772369a77e35322b715712af Mon Sep 17 00:00:00 2001 From: Ethan Reesor Date: Tue, 9 Jul 2024 18:35:33 -0500 Subject: [PATCH] Pass signature and signable to Verify --- cmd/accumulated/cmd_init.go | 6 +- internal/bsn/msg_anchor.go | 3 +- internal/core/execute/v1/block/signature.go | 6 +- internal/core/execute/v1/block/validate.go | 4 +- .../core/execute/v2/block/msg_block_anchor.go | 4 +- .../core/execute/v2/block/msg_synthetic.go | 2 +- internal/core/execute/v2/block/sig_user.go | 15 +- internal/core/healing/anchors.go | 5 +- internal/core/healing/synthetic.go | 3 +- protocol/signature.go | 191 ++++++++---------- protocol/signature_test.go | 58 ++++-- protocol/signature_utils.go | 62 ++++++ test/sdk/sdk_test.go | 2 +- test/testing/fake_types.go | 26 +-- tools/cmd/debug/verify.go | 2 +- vdk/node/node.go | 6 +- 16 files changed, 215 insertions(+), 180 deletions(-) create mode 100644 protocol/signature_utils.go diff --git a/cmd/accumulated/cmd_init.go b/cmd/accumulated/cmd_init.go index fe4cfda44..6fb1b5585 100644 --- a/cmd/accumulated/cmd_init.go +++ b/cmd/accumulated/cmd_init.go @@ -267,8 +267,7 @@ func initNodeFromSeedProxy(cmd *cobra.Command, args []string) (int, *cfg.Config, return 0, nil, nil, fmt.Errorf("invalid seed list, %v", err) } - txHash := sha256.Sum256(b) - if !resp.Signature.Verify(nil, txHash[:], nil) { + if !resp.Signature.Verify(nil, protocol.SignableHash(sha256.Sum256(b))) { return 0, nil, nil, fmt.Errorf("invalid signature from proxy") } @@ -358,8 +357,7 @@ func initNodeFromSeedProxy(cmd *cobra.Command, args []string) (int, *cfg.Config, return 0, nil, nil, err } - h := sha256.Sum256(d) - if !nc.Signature.Verify(nil, h[:], nil) { + if !nc.Signature.Verify(nil, protocol.SignableHash(sha256.Sum256(d))) { return 0, nil, nil, fmt.Errorf("cannot verify network configuration from proxy") } _, _, found = kp.EntryByKeyHash(nc.Signature.GetPublicKeyHash()) diff --git a/internal/bsn/msg_anchor.go b/internal/bsn/msg_anchor.go index d79c4f12e..b9bab99e0 100644 --- a/internal/bsn/msg_anchor.go +++ b/internal/bsn/msg_anchor.go @@ -56,8 +56,7 @@ func (x BlockAnchor) check(batch *ChangeSet, ctx *MessageContext) (*messaging.Bl return nil, nil, errors.BadRequest.WithFormat("invalid anchor: expected %v, got %v", messaging.MessageTypeBlockSummary, ctx.message.Type()) } - h := msg.Anchor.Hash() - if !msg.Signature.Verify(nil, h[:], nil) { + if !msg.Signature.Verify(nil, msg.Anchor) { return nil, nil, errors.Unauthenticated.WithFormat("invalid signature") } diff --git a/internal/core/execute/v1/block/signature.go b/internal/core/execute/v1/block/signature.go index 7093956e3..332d88929 100644 --- a/internal/core/execute/v1/block/signature.go +++ b/internal/core/execute/v1/block/signature.go @@ -102,7 +102,7 @@ func (x *Executor) processSignature(batch *database.Batch, delivery *chain.Deliv if err != nil { return nil, errors.UnknownError.WithFormat("process delegated signature: %w", err) } - if !md.Nested() && !signature.Verify(signature.Metadata().Hash(), delivery.Transaction.GetHash(), delivery.Transaction) { + if !md.Nested() && !signature.Verify(nil, delivery.Transaction) { return nil, errors.BadRequest.WithFormat("invalid signature") } @@ -137,7 +137,7 @@ func (x *Executor) processSignature(batch *database.Batch, delivery *chain.Deliv } // Basic validation - if !md.Nested() && !signature.Verify(nil, delivery.Transaction.GetHash(), delivery.Transaction) { + if !md.Nested() && !signature.Verify(nil, delivery.Transaction) { return nil, errors.BadRequest.WithFormat("invalid signature") } @@ -153,7 +153,7 @@ func (x *Executor) processSignature(batch *database.Batch, delivery *chain.Deliv } // Basic validation - if !md.Nested() && !signature.Verify(nil, delivery.Transaction.GetHash(), delivery.Transaction) { + if !md.Nested() && !signature.Verify(nil, delivery.Transaction) { return nil, errors.BadRequest.WithFormat("invalid signature") } } diff --git a/internal/core/execute/v1/block/validate.go b/internal/core/execute/v1/block/validate.go index ebdbd9099..844796e55 100644 --- a/internal/core/execute/v1/block/validate.go +++ b/internal/core/execute/v1/block/validate.go @@ -239,7 +239,7 @@ func (x *Executor) validateSignature(batch *database.Batch, delivery *chain.Deli if err != nil { return nil, errors.UnknownError.WithFormat("validate delegated signature: %w", err) } - if !md.Nested() && !signature.Verify(signature.Metadata().Hash(), delivery.Transaction.GetHash(), delivery.Transaction) { + if !md.Nested() && !signature.Verify(nil, delivery.Transaction) { return nil, errors.BadRequest.WithFormat("invalid signature") } if !signature.Delegator.LocalTo(md.Location) { @@ -273,7 +273,7 @@ func (x *Executor) validateSignature(batch *database.Batch, delivery *chain.Deli } // Basic validation - if !md.Nested() && !signature.Verify(nil, delivery.Transaction.GetHash(), delivery.Transaction) { + if !md.Nested() && !signature.Verify(nil, delivery.Transaction) { return nil, errors.BadRequest.With("invalid") } diff --git a/internal/core/execute/v2/block/msg_block_anchor.go b/internal/core/execute/v2/block/msg_block_anchor.go index e5ae53f61..cbe56e76a 100644 --- a/internal/core/execute/v2/block/msg_block_anchor.go +++ b/internal/core/execute/v2/block/msg_block_anchor.go @@ -204,7 +204,7 @@ func (x BlockAnchor) checkSignature(ctx *blockAnchorContext) error { txn := &messaging.TransactionMessage{Transaction: ctx.transaction} seq := *ctx.sequenced seq.Message = txn - if hash := seq.Hash(); ctx.blockAnchor.Signature.Verify(nil, hash[:], nil) { + if ctx.blockAnchor.Signature.Verify(nil, &seq) { return nil } @@ -217,7 +217,7 @@ func (x BlockAnchor) checkSignature(ctx *blockAnchorContext) error { seq.Destination = protocol.DnUrl() txn.Transaction = txn.Transaction.Copy() txn.Transaction.Header.Principal = protocol.DnUrl().JoinPath(ctx.transaction.Header.Principal.Path) - if hash := seq.Hash(); ctx.blockAnchor.Signature.Verify(nil, hash[:], nil) { + if ctx.blockAnchor.Signature.Verify(nil, &seq) { return nil } } diff --git a/internal/core/execute/v2/block/msg_synthetic.go b/internal/core/execute/v2/block/msg_synthetic.go index 2dcb9bd61..72f23e508 100644 --- a/internal/core/execute/v2/block/msg_synthetic.go +++ b/internal/core/execute/v2/block/msg_synthetic.go @@ -87,7 +87,7 @@ func (SyntheticMessage) check(batch *database.Batch, ctx *MessageContext) (*mess // Verify the signature h := syn.Message.Hash() - if !syn.Signature.Verify(nil, h[:], nil) { + if !syn.Signature.Verify(nil, syn.Message) { return nil, errors.BadRequest.With("invalid signature") } diff --git a/internal/core/execute/v2/block/sig_user.go b/internal/core/execute/v2/block/sig_user.go index 69becd56a..9fd3e4d6d 100644 --- a/internal/core/execute/v2/block/sig_user.go +++ b/internal/core/execute/v2/block/sig_user.go @@ -33,22 +33,11 @@ func init() { protocol.SignatureTypeETH, ) - // Vandenberg: RSA and ECDSA signatures + // Vandenberg: RSA, ECDSA, and EIP-712 signatures registerConditionalExec[UserSignature](&signatureExecutors, func(ctx *SignatureContext) bool { return ctx.GetActiveGlobals().ExecutorVersion.V2VandenbergEnabled() }, protocol.SignatureTypeRsaSha256, protocol.SignatureTypeEcdsaSha256, - ) - - // PKI signatures (enabled with Vandenberg) - registerConditionalExec[UserSignature](&signatureExecutors, - func(ctx *SignatureContext) bool { return ctx.GetActiveGlobals().ExecutorVersion.V2VandenbergEnabled() }, - protocol.SignatureTypeEcdsaSha256, - ) - - // Eip712 signatures (enabled with Vandenberg) - registerConditionalExec[UserSignature](&signatureExecutors, - func(ctx *SignatureContext) bool { return ctx.GetActiveGlobals().ExecutorVersion.V2VandenbergEnabled() }, protocol.SignatureTypeEip712TypedData, ) } @@ -123,7 +112,7 @@ func (x UserSignature) check(batch *database.Batch, ctx *userSigContext) error { } // Verify the signature signs the transaction - if !verifySignature(sig, ctx.transaction.GetHash(), ctx.transaction) { + if !verifySignature(sig, ctx.transaction) { return errors.Unauthenticated.WithFormat("invalid signature") } diff --git a/internal/core/healing/anchors.go b/internal/core/healing/anchors.go index 8f9c51a09..9292aaab1 100644 --- a/internal/core/healing/anchors.go +++ b/internal/core/healing/anchors.go @@ -192,8 +192,7 @@ func HealAnchor(ctx context.Context, args HealAnchorArgs, si SequencedInfo) erro } // Filter out bad signatures - h := seq.Hash() - if !sig.Verify(nil, h[:], nil) { + if !sig.Verify(nil, seq) { slog.ErrorContext(ctx, "Node gave us an invalid signature", "id", info) continue } @@ -209,7 +208,7 @@ func HealAnchor(ctx context.Context, args HealAnchorArgs, si SequencedInfo) erro case protocol.UserSignature: // Filter out bad signatures - if !sig.Verify(nil, theAnchorTxn.GetHash(), nil) { + if !sig.Verify(nil, theAnchorTxn) { slog.ErrorContext(ctx, "Node gave us an invalid signature", "id", info) continue } diff --git a/internal/core/healing/synthetic.go b/internal/core/healing/synthetic.go index 602912cc0..45ac3139d 100644 --- a/internal/core/healing/synthetic.go +++ b/internal/core/healing/synthetic.go @@ -99,8 +99,7 @@ func (h *Healer) HealSynthetic(ctx context.Context, args HealSyntheticArgs, si S return fmt.Errorf("synthetic message is not signed") } - hash := msg.Message.Hash() - if !msg.Signature.Verify(nil, hash[:], nil) { + if !msg.Signature.Verify(nil, msg.Message) { return fmt.Errorf("signature is not valid") } diff --git a/protocol/signature.go b/protocol/signature.go index f8623b9ef..daa6f47ab 100644 --- a/protocol/signature.go +++ b/protocol/signature.go @@ -36,26 +36,18 @@ type ethSignatureV1 struct { } // VerifyUserSignatureV1 verifies that the user signature signs the given message using version 1 logic -func VerifyUserSignatureV1(sig UserSignature, message []byte, transaction *Transaction) bool { +func VerifyUserSignatureV1(sig UserSignature, msg Signable) bool { if sig.Type() == SignatureTypeETH { //recast ETHSignature type to use V1 signature format sig = ðSignatureV1{sig.(*ETHSignature)} } - return VerifyUserSignature(sig, message, transaction) + return VerifyUserSignature(sig, msg) } // VerifyUserSignature verifies that the user signature signs the given message. -func VerifyUserSignature(sig UserSignature, message []byte, transaction *Transaction) bool { - if sig.Verify(sig.Metadata().Hash(), message, transaction) { - return true - } - - h, err := sig.Initiator() - if err != nil { - return false - } - return sig.Verify(h.MerkleHash(), message, transaction) +func VerifyUserSignature(sig UserSignature, msg Signable) bool { + return sig.Verify(sig, msg) } type Signature interface { @@ -71,12 +63,14 @@ type Signature interface { Metadata() Signature } +type Signable interface{ Hash() [32]byte } + // UserSignature is a type of signature that can initiate transactions with a // special initiator hash. This type of initiator hash has been deprecated. type UserSignature interface { Signature Initiator() (hash.Hasher, error) - Verify(sigMdHash, hash []byte, transaction *Transaction) bool + Verify(Signature, Signable) bool } type KeySignature interface { @@ -132,28 +126,6 @@ func PublicKeyHash(key []byte, typ SignatureType) ([]byte, error) { } } -func signatureHash(sig Signature) []byte { - // This should never fail unless the signature uses bigints - data, _ := sig.MarshalBinary() - return doSha256(data) -} - -func signingHash(sig Signature, hasher hashFunc, sigMdHash, txnHash []byte) []byte { - if sigMdHash == nil { - sigMdHash = sig.Metadata().Hash() - } - data := sigMdHash - data = append(data, txnHash...) - return hasher(data) -} - -type hashFunc func(data []byte) []byte - -func doSha256(data []byte) []byte { - hash := sha256.Sum256(data) - return hash[:] -} - // generates privatekey and compressed public key func SECP256K1Keypair() (privKey []byte, pubKey []byte) { priv, _ := btc.NewPrivateKey(btc.S256()) @@ -385,18 +357,13 @@ func (s *LegacyED25519Signature) GetVote() VoteType { // Verify returns true if this signature is a valid legacy ED25519 signature of // the hash. -func (e *LegacyED25519Signature) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool { +func (e *LegacyED25519Signature) Verify(sig Signature, msg Signable) bool { if len(e.PublicKey) != 32 || len(e.Signature) != 64 { return false } - if sigMdHash == nil { - sigMdHash = e.Metadata().Hash() - } - data := sigMdHash - data = append(data, common.Uint64Bytes(e.Timestamp)...) - data = append(data, txnHash...) - hash := sha256.Sum256(data) - return ed25519.Verify(e.PublicKey, hash[:], e.Signature) + return verifySig(e, sig, true, msg, func(msg []byte) bool { + return ed25519.Verify(e.PublicKey, msg, e.Signature) + }) } /* @@ -463,11 +430,13 @@ func (s *ED25519Signature) GetVote() VoteType { // Verify returns true if this signature is a valid ED25519 signature of the // hash. -func (e *ED25519Signature) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool { +func (e *ED25519Signature) Verify(sig Signature, msg Signable) bool { if len(e.PublicKey) != 32 || len(e.Signature) != 64 { return false } - return ed25519.Verify(e.PublicKey, signingHash(e, doSha256, sigMdHash, txnHash), e.Signature) + return verifySig(e, sig, true, msg, func(msg []byte) bool { + return ed25519.Verify(e.PublicKey, msg, e.Signature) + }) } /* @@ -497,12 +466,13 @@ func (s *RCD1Signature) GetPublicKeyHash() []byte { return GetRCDHashFromPublicK func (s *RCD1Signature) GetPublicKey() []byte { return s.PublicKey } // Verify returns true if this signature is a valid RCD1 signature of the hash. -func (e *RCD1Signature) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool { +func (e *RCD1Signature) Verify(sig Signature, msg Signable) bool { if len(e.PublicKey) != 32 || len(e.Signature) != 64 { return false } - - return ed25519.Verify(e.PublicKey, signingHash(e, doSha256, sigMdHash, txnHash), e.Signature) + return verifySig(e, sig, true, msg, func(msg []byte) bool { + return ed25519.Verify(e.PublicKey, msg, e.Signature) + }) } // GetSignature returns Signature. @@ -610,8 +580,8 @@ func (s *BTCSignature) GetVote() VoteType { // Verify returns true if this signature is a valid SECP256K1 signature of the // hash. -func (e *BTCSignature) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool { - sig, err := btc.ParseSignature(e.Signature, btc.S256()) +func (e *BTCSignature) Verify(sig Signature, msg Signable) bool { + bsig, err := btc.ParseSignature(e.Signature, btc.S256()) if err != nil { return false } @@ -619,7 +589,9 @@ func (e *BTCSignature) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool { if err != nil { return false } - return sig.Verify(signingHash(e, doSha256, sigMdHash, txnHash), pbkey) + return verifySig(e, sig, true, msg, func(msg []byte) bool { + return bsig.Verify(msg, pbkey) + }) } /* @@ -692,8 +664,8 @@ func (s *BTCLegacySignature) GetVote() VoteType { // Verify returns true if this signature is a valid SECP256K1 signature of the // hash. -func (e *BTCLegacySignature) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool { - sig, err := btc.ParseSignature(e.Signature, btc.S256()) +func (e *BTCLegacySignature) Verify(sig Signature, msg Signable) bool { + bsig, err := btc.ParseSignature(e.Signature, btc.S256()) if err != nil { return false } @@ -701,7 +673,9 @@ func (e *BTCLegacySignature) Verify(sigMdHash, txnHash []byte, _ *Transaction) b if err != nil { return false } - return sig.Verify(signingHash(e, doSha256, sigMdHash, txnHash), pbkey) + return verifySig(e, sig, true, msg, func(msg []byte) bool { + return bsig.Verify(msg, pbkey) + }) } /* @@ -795,9 +769,9 @@ func (s *ETHSignature) GetVote() VoteType { } // Deprecated: Verify returns true if this signature is a valid signature in DER format of the hash. -func (e *ethSignatureV1) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool { +func (e *ethSignatureV1) Verify(sig Signature, msg Signable) bool { //process signature as DER format - sig, err := btc.ParseSignature(e.Signature, btc.S256()) + bsig, err := btc.ParseSignature(e.Signature, btc.S256()) if err != nil { return false } @@ -805,17 +779,21 @@ func (e *ethSignatureV1) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool if err != nil { return false } - return sig.Verify(signingHash(e, doSha256, sigMdHash, txnHash), pbkey) + return verifySig(e, sig, true, msg, func(msg []byte) bool { + return bsig.Verify(msg, pbkey) + }) } // Verify returns true if this signature is a valid RSV signature of the hash, with V2 we drop support for DER format -func (e *ETHSignature) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool { - sig := e.Signature - if len(sig) == 65 { +func (e *ETHSignature) Verify(sig Signature, msg Signable) bool { + s := e.Signature + if len(s) == 65 { //extract RS of the RSV format - sig = sig[:64] + s = s[:64] } - return eth.VerifySignature(e.PublicKey, signingHash(e, doSha256, sigMdHash, txnHash), sig) + return verifySig(e, sig, true, msg, func(msg []byte) bool { + return eth.VerifySignature(e.PublicKey, msg, s) + }) } /* @@ -989,14 +967,15 @@ func (s *DelegatedSignature) Initiator() (hash.Hasher, error) { return hasher, nil } -func (s *DelegatedSignature) Verify(sigMdHash, hash []byte, transaction *Transaction) bool { - switch sig := s.Signature.(type) { - case KeySignature: - return sig.Verify(sigMdHash, hash, transaction) - case *DelegatedSignature: - return sig.Verify(sigMdHash, hash, transaction) +func (s *DelegatedSignature) Verify(sig Signature, msg Signable) bool { + us, ok := s.Signature.(UserSignature) + if !ok { + return false + } + if sig == nil { + sig = s } - return false + return us.Verify(sig, msg) } /* @@ -1114,18 +1093,9 @@ func (s *RsaSha256Signature) Metadata() Signature { return r } -// Initiator returns a Hasher that calculates the Merkle hash of the signature. +// Initiator returns [ErrCannotInitiate]. [RsaSha256Signature] only supports simple hashes. func (s *RsaSha256Signature) Initiator() (hash.Hasher, error) { - if len(s.PublicKey) == 0 || s.Signer == nil || s.SignerVersion == 0 || s.Timestamp == 0 { - return nil, ErrCannotInitiate - } - - hasher := make(hash.Hasher, 0, 4) - hasher.AddBytes(s.PublicKey) - hasher.AddUrl(s.Signer) - hasher.AddUint(s.SignerVersion) - hasher.AddUint(s.Timestamp) - return hasher, nil + return nil, ErrCannotInitiate } // GetVote returns how the signer votes on a particular transaction @@ -1135,7 +1105,7 @@ func (s *RsaSha256Signature) GetVote() VoteType { // Verify returns true if this signature is a valid RSA signature of the // hash. The public key is expected to be in PKCS#1 ASN.1 DER format -func (e *RsaSha256Signature) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool { +func (e *RsaSha256Signature) Verify(sig Signature, msg Signable) bool { //Convert public DER key into and rsa public key struct pubKey, err := x509.ParsePKCS1PublicKey(e.PublicKey) if err != nil { @@ -1148,8 +1118,10 @@ func (e *RsaSha256Signature) Verify(sigMdHash, txnHash []byte, _ *Transaction) b } // Verify signature - err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, signingHash(e, doSha256, sigMdHash, txnHash), e.Signature) - return err == nil + return verifySig(e, sig, false, msg, func(b []byte) bool { + err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, b, e.Signature) + return err == nil + }) } /* @@ -1207,18 +1179,9 @@ func (s *EcdsaSha256Signature) Metadata() Signature { return r } -// Initiator returns a Hasher that calculates the Merkle hash of the signature. +// Initiator returns [ErrCannotInitiate]. [EcdsaSha256Signature] only supports simple hashes. func (s *EcdsaSha256Signature) Initiator() (hash.Hasher, error) { - if len(s.PublicKey) == 0 || s.Signer == nil || s.SignerVersion == 0 || s.Timestamp == 0 { - return nil, ErrCannotInitiate - } - - hasher := make(hash.Hasher, 0, 4) - hasher.AddBytes(s.PublicKey) - hasher.AddUrl(s.Signer) - hasher.AddUint(s.SignerVersion) - hasher.AddUint(s.Timestamp) - return hasher, nil + return nil, ErrCannotInitiate } // GetVote returns how the signer votes on a particular transaction @@ -1228,7 +1191,7 @@ func (s *EcdsaSha256Signature) GetVote() VoteType { // Verify returns true if this signature is a valid ECDSA ANS.1 encoded signature of the // hash. The public key is expected to be in PKCS#1 ASN.1 DER format -func (e *EcdsaSha256Signature) Verify(sigMdHash, txnHash []byte, _ *Transaction) bool { +func (e *EcdsaSha256Signature) Verify(sig Signature, msg Signable) bool { //Convert public ANS.1 encoded key into and associated public key struct pubKey, err := x509.ParsePKIXPublicKey(e.PublicKey) if err != nil { @@ -1239,26 +1202,32 @@ func (e *EcdsaSha256Signature) Verify(sigMdHash, txnHash []byte, _ *Transaction) if !ok { return false } - return ecdsa.VerifyASN1(pub, signingHash(e, doSha256, sigMdHash, txnHash), e.Signature) + + return verifySig(e, sig, false, msg, func(msg []byte) bool { + return ecdsa.VerifyASN1(pub, msg, e.Signature) + }) } /* * EIP-712 Typed Data Signature * privateKey must be ecdsa */ -func SignEip712TypedData(sig *Eip712TypedDataSignature, privateKey []byte, txn *Transaction) error { +func SignEip712TypedData(sig *Eip712TypedDataSignature, privateKey []byte, outer Signature, txn *Transaction) error { priv, err := eth.ToECDSA(privateKey) if err != nil { return err } sig.PublicKey = eth.FromECDSAPub(&priv.PublicKey) - sig.TransactionHash = txn.Hash() - hash, err := Eip712Hasher(txn, sig) + if outer == nil { + outer = sig + } + hash, err := Eip712Hasher(txn, outer) if err != nil { return err } + sig.TransactionHash = txn.Hash() sig.Signature, err = eth.Sign(hash, priv) return nil } @@ -1319,17 +1288,25 @@ func (s *Eip712TypedDataSignature) GetVote() VoteType { // Verify returns true if this signature is a valid EIP-712 signature following // the spec. -func (e *Eip712TypedDataSignature) Verify(_, _ []byte, txn *Transaction) bool { - typedDataTxnHash, err := Eip712Hasher(txn, e) - +func (e *Eip712TypedDataSignature) Verify(sig Signature, msg Signable) bool { + txn, ok := msg.(*Transaction) + if !ok { + // EIP-712 cannot be used to sign something that isn't a transaction + return false + } + + if sig == nil { + sig = e + } + typedDataTxnHash, err := Eip712Hasher(txn, sig) if err != nil { return false } - sig := e.Signature - if len(sig) == 65 { + s := e.Signature + if len(s) == 65 { //extract RS of the RSV format - sig = sig[:64] + s = s[:64] } - return eth.VerifySignature(e.PublicKey, typedDataTxnHash, sig) + return eth.VerifySignature(e.PublicKey, typedDataTxnHash, s) } diff --git a/protocol/signature_test.go b/protocol/signature_test.go index e4199f78d..32a3b0a93 100644 --- a/protocol/signature_test.go +++ b/protocol/signature_test.go @@ -54,7 +54,7 @@ func TestBTCSignature(t *testing.T) { secp.PublicKey = pbkey.SerializeCompressed() require.NoError(t, SignBTC(secp, privkey.Serialize(), nil, hash[:])) - res := secp.Verify(nil, hash[:], nil) + res := secp.Verify(nil, SignableHash(hash)) require.Equal(t, res, true) @@ -74,7 +74,7 @@ func TestBTCLegacySignature(t *testing.T) { secp.PublicKey = pbkey.SerializeUncompressed() require.NoError(t, SignBTCLegacy(secp, privkey.Serialize(), nil, hash[:])) - res := secp.Verify(nil, hash[:], nil) + res := secp.Verify(nil, SignableHash(hash)) require.Equal(t, res, true) @@ -98,9 +98,9 @@ func TestETHSignature(t *testing.T) { t.Logf("Eth ad Der Hash %x", hash[:]) //should fail - require.Equal(t, VerifyUserSignature(secp, hash[:], nil), false) + require.Equal(t, VerifyUserSignature(secp, SignableHash(hash)), false) //should pass - require.Equal(t, VerifyUserSignatureV1(secp, hash[:], nil), true) + require.Equal(t, VerifyUserSignatureV1(secp, SignableHash(hash)), true) //public key should still match keyComp, err := eth.UnmarshalPubkey(secp.PublicKey) @@ -117,9 +117,9 @@ func TestETHSignature(t *testing.T) { t.Logf("Eth as VRS signature %x", secp.Signature) t.Logf("Eth ad VRS Hash %x", hash[:]) //should fail - require.Equal(t, VerifyUserSignatureV1(secp, hash[:], nil), false) + require.Equal(t, VerifyUserSignatureV1(secp, SignableHash(hash)), false) //should pass - require.Equal(t, VerifyUserSignature(secp, hash[:], nil), true) + require.Equal(t, VerifyUserSignature(secp, SignableHash(hash)), true) t.Logf("Signature: %x", secp.Signature) } @@ -320,7 +320,7 @@ func TestRsaSha256Signature(t *testing.T) { require.NoError(t, SignRsaSha256(rsaSha256, x509.MarshalPKCS1PrivateKey(privKey), nil, hash[:])) //should fail - require.Equal(t, VerifyUserSignature(rsaSha256, hash[:], nil), true) + require.Equal(t, VerifyUserSignature(rsaSha256, SignableHash(hash)), true) //public key should still match keyComp, err := x509.ParsePKCS1PublicKey(rsaSha256.PublicKey) require.NoError(t, err) @@ -339,7 +339,7 @@ func TestRsaSha256Signature(t *testing.T) { require.NoError(t, SignRsaSha256(rsaSha256, x509.MarshalPKCS1PrivateKey(privKey), nil, hash[:])) //should fail - require.Equal(t, VerifyUserSignature(rsaSha256, hash[:], nil), true) + require.Equal(t, VerifyUserSignature(rsaSha256, SignableHash(hash)), true) //public key should still match keyComp, err = x509.ParsePKCS1PublicKey(rsaSha256.PublicKey) require.NoError(t, err) @@ -358,7 +358,7 @@ func TestRsaSha256Signature(t *testing.T) { require.NoError(t, SignRsaSha256(rsaSha256, x509.MarshalPKCS1PrivateKey(privKey), nil, hash[:])) //should fail - require.Equal(t, VerifyUserSignature(rsaSha256, hash[:], nil), true) + require.Equal(t, VerifyUserSignature(rsaSha256, SignableHash(hash)), true) //public key should still match keyComp, err = x509.ParsePKCS1PublicKey(rsaSha256.PublicKey) require.NoError(t, err) @@ -487,7 +487,7 @@ func TestTypesFromCerts(t *testing.T) { } //should not fail - require.Equal(t, VerifyUserSignature(sig, hash[:], nil), true) + require.Equal(t, VerifyUserSignature(sig, SignableHash(hash)), true) } } @@ -565,16 +565,18 @@ func TestEip712TypedDataSignature(t *testing.T) { // Sign the transaction priv, _ := SECP256K1Keypair() - require.NoError(t, SignEip712TypedData(eip712sig, priv, txn)) + require.NoError(t, SignEip712TypedData(eip712sig, priv, nil, txn)) // Verify the signature - require.True(t, eip712sig.Verify(nil, nil, txn)) + require.True(t, eip712sig.Verify(nil, txn)) +} - //test edge case: - keyPageUpdate := []byte(`{ +func TestEIP712DelegatedKeyPageUpdate(t *testing.T) { + txn := &Transaction{} + err := txn.UnmarshalJSON([]byte(`{ "header": { - "principal": "acc://adi.acme", - "initiator": "5c90ac449d17c448141def36197ce8d63852b85f91621b1015e553ccbbd0f2f2" + "principal": "acc://adi.acme/ACME", + "initiator": "84e032fba8a5456f631c822a2b2466c18b3fa7804330ab87088ed6e30d690505" }, "body": { "type": "updateKeyPage", @@ -583,12 +585,24 @@ func TestEip712TypedDataSignature(t *testing.T) { "entry": { "keyHash": "e55d973bf691381c94602354d1e1f655f7b1c4bd56760dffeffa2bef4541ec11" } }] } - }`) - - // TODO: Delegated signature - err = txn.UnmarshalJSON(keyPageUpdate) + }`)) require.NoError(t, err) - _, err = Eip712Hasher(txn, eip712sig) - require.NoError(t, err) + inner := &Eip712TypedDataSignature{ + Signer: url.MustParse("acc://adi.acme/book/1"), + SignerVersion: 1, + Timestamp: 1720564975623, + Vote: VoteTypeAccept, + } + outer := &DelegatedSignature{ + Signature: inner, + Delegator: url.MustParse("acc://foo.bar"), + } + + // Sign the transaction + priv, _ := SECP256K1Keypair() + require.NoError(t, SignEip712TypedData(inner, priv, outer, txn)) + + // Verify the signature + require.True(t, outer.Verify(nil, txn)) } diff --git a/protocol/signature_utils.go b/protocol/signature_utils.go new file mode 100644 index 000000000..1316058af --- /dev/null +++ b/protocol/signature_utils.go @@ -0,0 +1,62 @@ +// Copyright 2024 The Accumulate Authors +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +package protocol + +import "crypto/sha256" + +type SignableHash [32]byte + +func (h SignableHash) Hash() [32]byte { return h } + +func verifySig(inner, outer Signature, merkle bool, msg interface{ Hash() [32]byte }, verify func([]byte) bool) bool { + if outer == nil { + outer = inner + } + msgHash := msg.Hash() + if verify(doSha256(outer.Metadata().Hash(), msgHash[:])) { + return true + } + if !merkle { + return false + } + + us, ok := outer.(UserSignature) + if !ok { + return false + } + h, err := us.Initiator() + if err != nil { + return false + } + return verify(doSha256(h.MerkleHash(), msgHash[:])) +} + +func signatureHash(sig Signature) []byte { + // This should never fail unless the signature uses bigints + data, _ := sig.MarshalBinary() + return doSha256(data) +} + +func signingHash(sig Signature, hasher hashFunc, sigMdHash, txnHash []byte) []byte { + if sigMdHash == nil { + sigMdHash = sig.Metadata().Hash() + } + data := sigMdHash + data = append(data, txnHash...) + return hasher(data) +} + +type hashFunc func(data ...[]byte) []byte + +func doSha256(data ...[]byte) []byte { + var all []byte + for _, data := range data { + all = append(all, data...) + } + hash := sha256.Sum256(all) + return hash[:] +} diff --git a/test/sdk/sdk_test.go b/test/sdk/sdk_test.go index 79f8789e9..06a6a9aa8 100644 --- a/test/sdk/sdk_test.go +++ b/test/sdk/sdk_test.go @@ -59,7 +59,7 @@ func TestSDK(t *testing.T) { continue } - require.True(t, sig.Verify(nil, env.Transaction[0].GetHash(), nil), "Signature is valid") + require.True(t, sig.Verify(nil, env.Transaction[0]), "Signature is valid") } }) diff --git a/test/testing/fake_types.go b/test/testing/fake_types.go index 84b6573db..bd835597b 100644 --- a/test/testing/fake_types.go +++ b/test/testing/fake_types.go @@ -29,19 +29,19 @@ func (f *FakeTransactionBody) Type() protocol.TransactionType { return f.TheType var _ protocol.Signature = (*FakeSignature)(nil) var _ protocol.KeySignature = (*FakeSignature)(nil) -func (f *FakeSignature) Type() protocol.SignatureType { return f.TheType } -func (f *FakeSignature) GetVote() protocol.VoteType { return f.Vote } -func (f *FakeSignature) Verify(sigMdHash, hash []byte, txn *protocol.Transaction) bool { return true } -func (f *FakeSignature) Hash() []byte { return make([]byte, 32) } -func (f *FakeSignature) Metadata() protocol.Signature { return f } -func (f *FakeSignature) Initiator() (hash.Hasher, error) { return nil, nil } -func (f *FakeSignature) GetSigner() *url.URL { return f.Signer } -func (f *FakeSignature) RoutingLocation() *url.URL { return f.Signer } -func (f *FakeSignature) GetSignerVersion() uint64 { return f.SignerVersion } -func (f *FakeSignature) GetTimestamp() uint64 { return f.Timestamp } -func (f *FakeSignature) GetPublicKey() []byte { return f.PublicKey } -func (f *FakeSignature) GetSignature() []byte { return make([]byte, 32) } -func (f *FakeSignature) GetTransactionHash() [32]byte { return [32]byte{} } +func (f *FakeSignature) Type() protocol.SignatureType { return f.TheType } +func (f *FakeSignature) GetVote() protocol.VoteType { return f.Vote } +func (f *FakeSignature) Verify(protocol.Signature, protocol.Signable) bool { return true } +func (f *FakeSignature) Hash() []byte { return make([]byte, 32) } +func (f *FakeSignature) Metadata() protocol.Signature { return f } +func (f *FakeSignature) Initiator() (hash.Hasher, error) { return nil, nil } +func (f *FakeSignature) GetSigner() *url.URL { return f.Signer } +func (f *FakeSignature) RoutingLocation() *url.URL { return f.Signer } +func (f *FakeSignature) GetSignerVersion() uint64 { return f.SignerVersion } +func (f *FakeSignature) GetTimestamp() uint64 { return f.Timestamp } +func (f *FakeSignature) GetPublicKey() []byte { return f.PublicKey } +func (f *FakeSignature) GetSignature() []byte { return make([]byte, 32) } +func (f *FakeSignature) GetTransactionHash() [32]byte { return [32]byte{} } func (f *FakeSignature) GetPublicKeyHash() []byte { if f.Type() == protocol.SignatureTypeRCD1 { diff --git a/tools/cmd/debug/verify.go b/tools/cmd/debug/verify.go index deb146856..e82e6f417 100644 --- a/tools/cmd/debug/verify.go +++ b/tools/cmd/debug/verify.go @@ -47,7 +47,7 @@ func verifyEnvelope(_ *cobra.Command, args []string) { slog.Info("Skipping signature: no transaction hash", "type", sigMsg.Signature.Type()) continue } - if !sig.Verify(nil, h[:], nil) { + if !sig.Verify(nil, protocol.SignableHash(h)) { slog.Error("Signature is invalid", "message", i) } } diff --git a/vdk/node/node.go b/vdk/node/node.go index 6aa6f320a..5f681b092 100644 --- a/vdk/node/node.go +++ b/vdk/node/node.go @@ -131,8 +131,7 @@ func initFollowerNodeFromSeedNodeUrl(seedNodeUrl string) (int, *config.Config, * return 0, nil, nil, fmt.Errorf("invalid seed list, %v", err) } - txHash := sha256.Sum256(b) - if !resp.Signature.Verify(nil, txHash[:], nil) { + if !resp.Signature.Verify(nil, protocol.SignableHash(sha256.Sum256(b))) { return 0, nil, nil, fmt.Errorf("invalid signature from proxy") } @@ -222,8 +221,7 @@ func initFollowerNodeFromSeedNodeUrl(seedNodeUrl string) (int, *config.Config, * return 0, nil, nil, err } - h := sha256.Sum256(d) - if !nc.Signature.Verify(nil, h[:], nil) { + if !nc.Signature.Verify(nil, protocol.SignableHash(sha256.Sum256(d))) { return 0, nil, nil, fmt.Errorf("cannot verify network configuration from proxy") } _, _, found = kp.EntryByKeyHash(nc.Signature.GetPublicKeyHash())