Skip to content

Commit bd8bf8b

Browse files
committed
Wallet examples
1 parent a646561 commit bd8bf8b

File tree

11 files changed

+1336
-0
lines changed

11 files changed

+1336
-0
lines changed

docs/examples/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,12 @@ Here, you will find a number of common usage examples for the go-sdk.
4242
## Cryptography
4343
- [AES](./aes/) - Advanced Encryption Standard (AES) examples.
4444

45+
## Wallet
46+
- [Create Wallet](./create_wallet/) - Create a new wallet instance.
47+
- [Encrypt Data](./encrypt_data/) - Encrypt data using the wallet.
48+
- [Create Signature](./create_signature/) - Create a digital signature.
49+
- [Create HMAC](./create_hmac/) - Create an HMAC.
50+
- [Get Public Key](./get_public_key/) - Retrieve a public key from the wallet.
51+
4552
## Additional Example Documents
4653
- [Converting Transactions from go-bt](./GO_BT.md)
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Create and Verify HMAC Example
2+
3+
This example demonstrates how to use the `wallet` package to create an HMAC (Hash-based Message Authentication Code) for a piece of data and then verify it. This is typically done by a single party to ensure data integrity and authenticity based on a shared secret derived contextually.
4+
5+
## Overview
6+
7+
HMACs provide a way to verify both the data integrity and the authenticity of a message using a shared secret (derived from the wallet's key, `ProtocolID`, `KeyID`, and `Counterparty` context). The process involves:
8+
9+
1. **Creation**: A key is derived based on the wallet's private key and the provided `EncryptionArgs` (including `ProtocolID`, `KeyID`, and `Counterparty`). This key is then used with a hash function (like SHA256, commonly used with HMAC) to produce an authentication code for the data.
10+
2. **Verification**: The same process is followed. If the newly computed HMAC matches the original HMAC, the data is considered authentic and its integrity is verified.
11+
12+
The `wallet.Wallet` provides `CreateHmac` and `VerifyHmac` methods.
13+
14+
## Example Overview
15+
16+
This example demonstrates:
17+
18+
1. Creating a wallet instance (`myWallet`).
19+
2. Defining data for which to create an HMAC.
20+
3. Creating an HMAC for that data using `myWallet`, configured for a "self" operation (i.e., the HMAC is for the wallet's own use or context).
21+
4. Verifying the HMAC using `myWallet` itself.
22+
5. Demonstrating verification failure with tampered data.
23+
6. Demonstrating verification failure with a tampered HMAC.
24+
25+
## Code Walkthrough
26+
27+
### 1. Setup Wallet
28+
29+
```go
30+
privateKey, _ := ec.NewPrivateKey()
31+
myWallet, _ := wallet.NewWallet(privateKey)
32+
33+
// Define ProtocolID and KeyID for HMAC operations
34+
hmacProtocolID := wallet.Protocol{Protocol: "HMAC SelfSign", SecurityLevel: wallet.SecurityLevelSilent}
35+
hmacKeyID := "my hmac key v1"
36+
```
37+
We create a wallet and define a `ProtocolID` and `KeyID`. These, along with the `Counterparty` setting, will determine the derived key used for the HMAC operation.
38+
39+
### 2. Define Data and Create HMAC (for Self)
40+
41+
```go
42+
message := []byte("This is the data to be authenticated with HMAC.")
43+
44+
createHmacArgs := wallet.CreateHmacArgs{
45+
EncryptionArgs: wallet.EncryptionArgs{
46+
ProtocolID: hmacProtocolID,
47+
KeyID: hmacKeyID,
48+
Counterparty: wallet.Counterparty{ // Explicitly set for self-operation
49+
Type: wallet.CounterpartyTypeSelf,
50+
},
51+
},
52+
Data: wallet.JsonByteNoBase64(message),
53+
}
54+
createHmacResult, _ := myWallet.CreateHmac(context.Background(), createHmacArgs, "creator_originator")
55+
hmacBytes := createHmacResult.Hmac
56+
```
57+
To create an HMAC for a "self" context, `EncryptionArgs.Counterparty` is explicitly set to `wallet.Counterparty{Type: wallet.CounterpartyTypeSelf}`.
58+
59+
### 3. Verify HMAC (by Self)
60+
61+
```go
62+
verifyHmacArgs := wallet.VerifyHmacArgs{
63+
EncryptionArgs: wallet.EncryptionArgs{
64+
ProtocolID: hmacProtocolID, // Must match creation
65+
KeyID: hmacKeyID, // Must match creation
66+
Counterparty: wallet.Counterparty{ // Explicitly set for self-operation
67+
Type: wallet.CounterpartyTypeSelf,
68+
},
69+
},
70+
Data: wallet.JsonByteNoBase64(message), // Original data
71+
Hmac: hmacBytes, // HMAC created in the previous step
72+
}
73+
verifyHmacResult, _ := myWallet.VerifyHmac(context.Background(), verifyHmacArgs, "verifier_originator")
74+
// verifyHmacResult.Valid will be true
75+
```
76+
To verify an HMAC made by the same wallet for a "self" context, `EncryptionArgs.Counterparty` is again set to `wallet.Counterparty{Type: wallet.CounterpartyTypeSelf}`. The `ProtocolID` and `KeyID` must also match those used during creation.
77+
78+
### 4. Verification Failure Scenarios
79+
80+
- **Tampered Data**: If the `Data` is changed, `VerifyHmac` will result in `Valid: false`.
81+
```go
82+
tamperedDataArgs := verifyHmacArgs
83+
tamperedDataArgs.Data = wallet.JsonByteNoBase64([]byte("Tampered data!"))
84+
// Result will have Valid: false
85+
```
86+
- **Tampered HMAC**: If the `Hmac` itself is changed, `VerifyHmac` will result in `Valid: false`.
87+
```go
88+
tamperedHmacArgs := verifyHmacArgs
89+
tamperedHmac := append([]byte{0x00}, hmacBytes...) // Alter the HMAC slightly
90+
tamperedHmacArgs.Hmac = wallet.JsonByteNoBase64(tamperedHmac)
91+
// Result will have Valid: false
92+
```
93+
94+
## Running the Example
95+
96+
To run this example:
97+
98+
```bash
99+
cd go-sdk/docs/examples/create_hmac
100+
go mod tidy
101+
go run create_hmac.go
102+
```
103+
104+
## Key Concepts
105+
106+
- **HMAC (Hash-based Message Authentication Code)**: Provides data integrity and authenticity using a shared secret key.
107+
- **`wallet.CreateHmacArgs` / `wallet.VerifyHmacArgs`**: Structs for HMAC creation/verification parameters.
108+
- **`EncryptionArgs`**: Embedded in the above, contains `ProtocolID`, `KeyID`, and `Counterparty` which collectively define the context for deriving the HMAC key.
109+
- **`KeyID`**: Mandatory identifier for keying material. Length must be >= 1 character.
110+
- **`ProtocolID.Protocol`**: String identifying the protocol. Length must be >= 5 chars, containing only letters, numbers, and spaces.
111+
- **`CounterpartyTypeSelf`**: Used in `EncryptionArgs.Counterparty.Type` when a wallet creates or verifies an HMAC for its own context.
112+
113+
## Additional Resources
114+
115+
- [go-sdk `wallet` package documentation](https://pkg.go.dev/github.com/bsv-blockchain/go-sdk/wallet)
116+
- SDK tests, particularly `wallet/wallet_test.go` (`TestHmacCreateVerify`), provide further examples.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
8+
ec "github.com/bsv-blockchain/go-sdk/primitives/ec"
9+
"github.com/bsv-blockchain/go-sdk/wallet"
10+
)
11+
12+
func main() {
13+
ctx := context.Background()
14+
15+
// --- 1. Setup Wallet ---
16+
fmt.Println("--- 1. Setting up Wallet ---")
17+
privateKey, err := ec.NewPrivateKey()
18+
if err != nil {
19+
log.Fatalf("Failed to create private key: %v", err)
20+
}
21+
myWallet, err := wallet.NewWallet(privateKey)
22+
if err != nil {
23+
log.Fatalf("Failed to create wallet: %v", err)
24+
}
25+
fmt.Println("Wallet created.")
26+
27+
// Define ProtocolID and KeyID for HMAC operations
28+
// Protocol names must contain only letters, numbers, and spaces, and be >= 5 chars.
29+
// Key IDs can have spaces, must be >= 1 char.
30+
hmacProtocolID := wallet.Protocol{Protocol: "HMAC SelfSign", SecurityLevel: wallet.SecurityLevelSilent}
31+
hmacKeyID := "my hmac key v1"
32+
33+
message := []byte("This is the data to be authenticated with HMAC.")
34+
fmt.Printf("Original Message: %s\n", string(message))
35+
36+
// --- 2. Create HMAC (for Self) ---
37+
fmt.Println("\n--- 2. Creating HMAC for the message (for self) ---")
38+
createHmacArgs := wallet.CreateHmacArgs{
39+
EncryptionArgs: wallet.EncryptionArgs{
40+
ProtocolID: hmacProtocolID,
41+
KeyID: hmacKeyID,
42+
Counterparty: wallet.Counterparty{ // Explicitly set for self-operation
43+
Type: wallet.CounterpartyTypeSelf,
44+
},
45+
},
46+
Data: wallet.JsonByteNoBase64(message),
47+
}
48+
createHmacResult, err := myWallet.CreateHmac(ctx, createHmacArgs, "creator_originator")
49+
if err != nil {
50+
log.Fatalf("Failed to create HMAC: %v", err)
51+
}
52+
hmacBytes := createHmacResult.Hmac
53+
fmt.Printf("HMAC created: %x\n", hmacBytes)
54+
55+
// --- 3. Verify HMAC (by Self) ---
56+
fmt.Println("\n--- 3. Verifying the HMAC (by self) ---")
57+
verifyHmacArgs := wallet.VerifyHmacArgs{
58+
EncryptionArgs: wallet.EncryptionArgs{
59+
ProtocolID: hmacProtocolID, // Must match creation
60+
KeyID: hmacKeyID, // Must match creation
61+
Counterparty: wallet.Counterparty{ // Explicitly set for self-operation
62+
Type: wallet.CounterpartyTypeSelf,
63+
},
64+
},
65+
Data: wallet.JsonByteNoBase64(message), // Original data
66+
Hmac: hmacBytes, // HMAC from step 2
67+
}
68+
verifyHmacResult, err := myWallet.VerifyHmac(ctx, verifyHmacArgs, "verifier_originator")
69+
if err != nil {
70+
// This path should ideally not be hit if args are correct and HMAC was just created
71+
log.Fatalf("Error during HMAC verification call: %v", err)
72+
}
73+
if verifyHmacResult.Valid {
74+
fmt.Println("HMAC VERIFIED successfully!")
75+
} else {
76+
// This would be an unexpected failure for a valid HMAC
77+
log.Fatalf("HMAC verification FAILED (but should have succeeded).")
78+
}
79+
80+
// --- 4. Verification Failure with Tampered Data ---
81+
fmt.Println("\n--- 4. Verifying with tampered data (expected failure) ---")
82+
tamperedDataArgs := verifyHmacArgs // Copy previous args
83+
tamperedDataArgs.Data = wallet.JsonByteNoBase64([]byte("This is tampered data!"))
84+
tamperedDataVerifyResult, err := myWallet.VerifyHmac(ctx, tamperedDataArgs, "verifier_tampered_data")
85+
if err != nil {
86+
log.Fatalf("Error during tampered data HMAC verification call: %v", err)
87+
}
88+
if tamperedDataVerifyResult.Valid {
89+
fmt.Println("Tampered data HMAC verification unexpectedly SUCCEEDED (Error!).")
90+
} else {
91+
fmt.Println("Tampered data HMAC verification FAILED as expected.")
92+
}
93+
94+
// --- 5. Verification Failure with Tampered HMAC ---
95+
fmt.Println("\n--- 5. Verifying with tampered HMAC (expected failure) ---")
96+
tamperedHmacArgs := verifyHmacArgs // Copy previous args
97+
// Create a slightly altered HMAC. Ensure it's different but same length if possible.
98+
corruptedHmac := make([]byte, len(hmacBytes))
99+
copy(corruptedHmac, hmacBytes)
100+
if len(corruptedHmac) > 0 {
101+
corruptedHmac[0] ^= 0xFF // Flip all bits of the first byte
102+
} else {
103+
corruptedHmac = append(corruptedHmac, 0x00) // Or handle if HMAC was empty (should not be)
104+
}
105+
tamperedHmacArgs.Hmac = wallet.JsonByteNoBase64(corruptedHmac)
106+
tamperedHmacVerifyResult, err := myWallet.VerifyHmac(ctx, tamperedHmacArgs, "verifier_tampered_hmac")
107+
if err != nil {
108+
log.Fatalf("Error during tampered HMAC verification call: %v", err)
109+
}
110+
if tamperedHmacVerifyResult.Valid {
111+
fmt.Println("Tampered HMAC verification unexpectedly SUCCEEDED (Error!).")
112+
} else {
113+
fmt.Println("Tampered HMAC verification FAILED as expected.")
114+
}
115+
116+
fmt.Println("\nCreate and verify HMAC example complete.")
117+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
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

Comments
 (0)