|
| 1 | +# Create and Verify Signature Example |
| 2 | + |
| 3 | +This example demonstrates how to use the `wallet` package to create a digital signature for a piece of data (or its hash) and then verify that signature, focusing on operations performed by a single wallet (self-signing and self-verification). |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +Digital signatures provide a way to verify the authenticity and integrity of data. The process involves: |
| 8 | +1. **Signing**: The sender uses their private key to create a signature for the data. |
| 9 | +2. **Verification**: The recipient (or the sender themselves) uses the corresponding public key to verify the signature. |
| 10 | + |
| 11 | +The `wallet.Wallet` provides functionality for both creating and verifying ECDSA signatures. |
| 12 | + |
| 13 | +## Example Overview |
| 14 | + |
| 15 | +This example demonstrates: |
| 16 | + |
| 17 | +1. Creating a wallet instance (`signerWallet`). |
| 18 | +2. Defining data to be signed. |
| 19 | +3. Creating a signature for that data using `signerWallet`, configured for a "self" operation. |
| 20 | +4. Verifying the signature using `signerWallet` itself. |
| 21 | +5. Demonstrating verification failure with tampered data. |
| 22 | +6. Demonstrating signing a pre-computed hash of data (for a "self" operation) and verifying it with `signerWallet`. |
| 23 | + |
| 24 | +## Code Walkthrough |
| 25 | + |
| 26 | +### 1. Setup Wallet |
| 27 | + |
| 28 | +```go |
| 29 | +privateKey, _ := ec.NewPrivateKey() |
| 30 | +signerWallet, _ := wallet.NewWallet(privateKey) |
| 31 | + |
| 32 | +selfProtocolID := wallet.Protocol{Protocol: "ECDSA SelfSign", SecurityLevel: wallet.SecurityLevelSilent} |
| 33 | +selfKeyID := "my signing key v1" |
| 34 | +``` |
| 35 | +We create a wallet and define a `ProtocolID` and `KeyID` for our self-signing operations. `KeyID` is mandatory for these wallet operations. |
| 36 | + |
| 37 | +### 2. Define Data and Create Signature (for Self) |
| 38 | + |
| 39 | +```go |
| 40 | +message := []byte("This is the message to be signed by myself, for myself.") |
| 41 | + |
| 42 | +createSigArgs := wallet.CreateSignatureArgs{ |
| 43 | + EncryptionArgs: wallet.EncryptionArgs{ |
| 44 | + ProtocolID: selfProtocolID, |
| 45 | + KeyID: selfKeyID, |
| 46 | + Counterparty: wallet.Counterparty{ // Explicitly set for self-signing |
| 47 | + Type: wallet.CounterpartyTypeSelf, |
| 48 | + }, |
| 49 | + }, |
| 50 | + Data: wallet.JsonByteNoBase64(message), |
| 51 | +} |
| 52 | +sigResult, _ := signerWallet.CreateSignature(context.Background(), createSigArgs, "signer_originator") |
| 53 | +``` |
| 54 | +To sign for "self", `EncryptionArgs.Counterparty` is explicitly set to `wallet.Counterparty{Type: wallet.CounterpartyTypeSelf}`. |
| 55 | + |
| 56 | +### 3. Verify Signature (by Self) |
| 57 | + |
| 58 | +```go |
| 59 | +verifyArgs := wallet.VerifySignatureArgs{ |
| 60 | + EncryptionArgs: wallet.EncryptionArgs{ |
| 61 | + ProtocolID: selfProtocolID, // Must match signing |
| 62 | + KeyID: selfKeyID, // Must match signing |
| 63 | + Counterparty: wallet.Counterparty{ // Explicitly set for self-verification |
| 64 | + Type: wallet.CounterpartyTypeSelf, |
| 65 | + }, |
| 66 | + }, |
| 67 | + Data: wallet.JsonByteNoBase64(message), |
| 68 | + Signature: sigResult.Signature, |
| 69 | + // ForSelf field is NOT used here; Counterparty in EncryptionArgs dictates self-verification |
| 70 | +} |
| 71 | +verifyResult, _ := signerWallet.VerifySignature(context.Background(), verifyArgs, "verifier_originator") |
| 72 | +// verifyResult.Valid will be true |
| 73 | +``` |
| 74 | +To verify a signature made by the same wallet (self-verification), `EncryptionArgs.Counterparty` is again set to `wallet.Counterparty{Type: wallet.CounterpartyTypeSelf}`. The top-level `ForSelf` field in `VerifySignatureArgs` is not used in this scenario. |
| 75 | + |
| 76 | +### 4. Verification with Tampered Data (Failure Case) |
| 77 | + |
| 78 | +```go |
| 79 | +tamperedMessage := []byte("This is NOT the message that was signed.") |
| 80 | +verifyArgsTampered := verifyArgs |
| 81 | +verifyArgsTampered.Data = wallet.JsonByteNoBase64(tamperedMessage) |
| 82 | + |
| 83 | +// This call is expected to return an error, or result.Valid == false |
| 84 | +tamperedVerifyResult, err := signerWallet.VerifySignature(context.Background(), verifyArgsTampered, "verifier_tampered_originator") |
| 85 | +// if err != nil, verification failed as expected. |
| 86 | +// if err == nil and !tamperedVerifyResult.Valid, verification failed as expected. |
| 87 | +``` |
| 88 | +If the data is altered, `VerifySignature` will indicate failure, either by returning an error (e.g., "signature is not valid") or by returning a result with `Valid` as `false`. |
| 89 | + |
| 90 | +### 5. Signing and Verifying a Pre-computed Hash (for Self) |
| 91 | + |
| 92 | +Similar to signing raw data, when signing a pre-computed hash for a "self" operation: |
| 93 | + |
| 94 | +```go |
| 95 | +messageHash := sha256.Sum256(message) // Pre-compute SHA256 hash |
| 96 | + |
| 97 | +createSigForHashArgs := wallet.CreateSignatureArgs{ |
| 98 | + EncryptionArgs: wallet.EncryptionArgs{ |
| 99 | + ProtocolID: /* define appropriate ProtocolID */, |
| 100 | + KeyID: /* define appropriate KeyID */, |
| 101 | + Counterparty: wallet.Counterparty{ |
| 102 | + Type: wallet.CounterpartyTypeSelf, |
| 103 | + }, |
| 104 | + }, |
| 105 | + HashToDirectlySign: wallet.JsonByteNoBase64(messageHash[:]), |
| 106 | +} |
| 107 | +sigFromHashResult, _ := signerWallet.CreateSignature(context.Background(), createSigForHashArgs, "signer_originator_hash") |
| 108 | + |
| 109 | +// Verification of signature made from hash (by self) |
| 110 | +verifyHashArgs := wallet.VerifySignatureArgs{ |
| 111 | + EncryptionArgs: createSigForHashArgs.EncryptionArgs, // Match args from hash signing |
| 112 | + HashToDirectlyVerify: wallet.JsonByteNoBase64(messageHash[:]), |
| 113 | + Signature: sigFromHashResult.Signature, |
| 114 | +} |
| 115 | +verifyHashResult, _ := signerWallet.VerifySignature(context.Background(), verifyHashArgs, "verifier_originator_hash") |
| 116 | +// verifyHashResult.Valid will be true |
| 117 | +``` |
| 118 | +Again, `CounterpartyTypeSelf` is used in `EncryptionArgs` for both creation and verification. |
| 119 | + |
| 120 | +## Running the Example |
| 121 | + |
| 122 | +To run this example: |
| 123 | + |
| 124 | +```bash |
| 125 | +cd go-sdk/docs/examples/create_signature |
| 126 | +go mod tidy |
| 127 | +go run create_signature.go |
| 128 | +``` |
| 129 | + |
| 130 | +## Key Concepts |
| 131 | + |
| 132 | +- **Digital Signature**: Ensures data authenticity and integrity. |
| 133 | +- **ECDSA**: Elliptic Curve Digital Signature Algorithm. |
| 134 | +- **`wallet.CreateSignatureArgs` / `wallet.VerifySignatureArgs`**: Structs for signing/verification parameters. |
| 135 | +- **`EncryptionArgs`**: Embedded in the above, contains `ProtocolID`, `KeyID`, and `Counterparty`. |
| 136 | +- **`KeyID`**: Mandatory identifier for keying material. Length must be >= 1 character. |
| 137 | +- **`ProtocolID.Protocol`**: String identifying the protocol. Length must be >= 5 chars, containing only letters, numbers, and spaces. |
| 138 | +- **`CounterpartyTypeSelf`**: Used in `EncryptionArgs.Counterparty.Type` when a wallet signs data for itself or verifies its own signatures. |
| 139 | +- When using `wallet.VerifySignature` for self-verification with `CounterpartyTypeSelf` in `EncryptionArgs`, the top-level `VerifySignatureArgs.ForSelf` field is not set. |
| 140 | + |
| 141 | +## Additional Resources |
| 142 | + |
| 143 | +- [go-sdk `wallet` package documentation](https://pkg.go.dev/github.com/bsv-blockchain/go-sdk/wallet) |
| 144 | +- [go-sdk `primitives/ec` package documentation](https://pkg.go.dev/github.com/bsv-blockchain/go-sdk/primitives/ec) |
| 145 | +- SDK tests, particularly `wallet/wallet_test.go` (`TestDefaultSignatureOperations`), can provide further examples. |
0 commit comments