1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//! Module signature verification
//!
//! Provides cryptographic signature verification for module manifests and binaries
//! using direct secp256k1 implementation (no external dependencies on governance infrastructure).
use crate::module::traits::ModuleError;
use secp256k1::{ecdsa::Signature, Message, PublicKey, Secp256k1};
use sha2::{Digest, Sha256};
use std::collections::{HashMap, HashSet};
/// Module signature verifier (independent implementation)
///
/// This implementation uses secp256k1 directly to avoid dependencies on
/// governance infrastructure, maintaining node independence.
pub struct ModuleSigner {
secp: Secp256k1<secp256k1::All>,
}
impl ModuleSigner {
/// Create a new module signer
pub fn new() -> Self {
Self {
secp: Secp256k1::new(),
}
}
/// Verify module manifest signatures
///
/// Verifies that a manifest has been signed by the required number of maintainers
/// according to the threshold (e.g., 2-of-3 means 2 out of 3 maintainers must sign).
///
/// # Arguments
///
/// * `manifest_content` - The raw bytes of the manifest file
/// * `signatures` - Vector of (maintainer_name, signature_hex) pairs
/// * `public_keys` - Map of maintainer_name -> public_key_hex
/// * `threshold` - (required_count, total_count) tuple, e.g., (2, 3) for 2-of-3
///
/// # Returns
///
/// Returns `Ok(true)` if threshold is met, `Ok(false)` if not, or an error if verification fails.
pub fn verify_manifest(
&self,
manifest_content: &[u8],
signatures: &[(String, String)], // (maintainer, signature_hex)
public_keys: &HashMap<String, String>, // (maintainer, pubkey_hex)
threshold: (usize, usize),
) -> Result<bool, ModuleError> {
// Hash manifest content
let message_hash = Sha256::digest(manifest_content);
let message = Message::from_digest_slice(&message_hash)
.map_err(|e| ModuleError::CryptoError(format!("Invalid message hash: {e}")))?;
// Verify each signature
let mut valid_signatures = 0;
let mut verified_signers = HashSet::new();
for (maintainer, sig_hex) in signatures {
// Skip if already verified (prevent duplicate counting)
if verified_signers.contains(maintainer) {
continue;
}
// Parse signature
let sig_bytes = hex::decode(sig_hex)
.map_err(|e| ModuleError::CryptoError(format!("Invalid signature hex: {e}")))?;
// Try compact format first (64 bytes), then DER if that fails
let signature = Signature::from_compact(&sig_bytes)
.or_else(|_| Signature::from_der(&sig_bytes))
.map_err(|e| ModuleError::CryptoError(format!("Invalid signature format: {e}")))?;
// Get public key for this maintainer
if let Some(pubkey_hex) = public_keys.get(maintainer) {
let pubkey_bytes = hex::decode(pubkey_hex)
.map_err(|e| ModuleError::CryptoError(format!("Invalid pubkey hex: {e}")))?;
let public_key = PublicKey::from_slice(&pubkey_bytes)
.map_err(|e| ModuleError::CryptoError(format!("Invalid pubkey: {e}")))?;
// Verify signature
if self
.secp
.verify_ecdsa(&message, &signature, &public_key)
.is_ok()
{
valid_signatures += 1;
verified_signers.insert(maintainer.clone());
}
}
}
// Check threshold
Ok(valid_signatures >= threshold.0)
}
/// Verify binary signatures (same logic, different content)
///
/// Verifies that a module binary has been signed by the required number of maintainers.
/// Uses the same verification logic as manifest verification.
///
/// # Arguments
///
/// * `binary_content` - The raw bytes of the binary file
/// * `signatures` - Vector of (maintainer_name, signature_hex) pairs
/// * `public_keys` - Map of maintainer_name -> public_key_hex
/// * `threshold` - (required_count, total_count) tuple
///
/// # Returns
///
/// Returns `Ok(true)` if threshold is met, `Ok(false)` if not, or an error if verification fails.
pub fn verify_binary(
&self,
binary_content: &[u8],
signatures: &[(String, String)],
public_keys: &HashMap<String, String>,
threshold: (usize, usize),
) -> Result<bool, ModuleError> {
// Same implementation as verify_manifest, just different content
self.verify_manifest(binary_content, signatures, public_keys, threshold)
}
/// Verify a single signature
///
/// Helper method to verify a single signature against a message and public key.
///
/// # Arguments
///
/// * `message` - The message bytes to verify
/// * `signature_hex` - The signature in hex format
/// * `public_key_hex` - The public key in hex format
///
/// # Returns
///
/// Returns `Ok(true)` if signature is valid, `Ok(false)` if invalid, or an error if parsing fails.
pub fn verify_single_signature(
&self,
message: &[u8],
signature_hex: &str,
public_key_hex: &str,
) -> Result<bool, ModuleError> {
// Hash message
let message_hash = Sha256::digest(message);
let secp_message =
Message::from_digest(message_hash.as_slice().try_into().map_err(|_| {
ModuleError::CryptoError("Invalid message hash length".to_string())
})?);
// Parse signature
let sig_bytes = hex::decode(signature_hex)
.map_err(|e| ModuleError::CryptoError(format!("Invalid signature hex: {e}")))?;
let signature = Signature::from_compact(&sig_bytes)
.or_else(|_| Signature::from_der(&sig_bytes))
.map_err(|e| ModuleError::CryptoError(format!("Invalid signature format: {e}")))?;
// Parse public key
let pubkey_bytes = hex::decode(public_key_hex)
.map_err(|e| ModuleError::CryptoError(format!("Invalid pubkey hex: {e}")))?;
let public_key = PublicKey::from_slice(&pubkey_bytes)
.map_err(|e| ModuleError::CryptoError(format!("Invalid pubkey: {e}")))?;
// Verify signature
Ok(self
.secp
.verify_ecdsa(&secp_message, &signature, &public_key)
.is_ok())
}
/// Verify payment address signatures
///
/// Verifies that payment addresses (author and commons) are cryptographically signed
/// by the module maintainers. This prevents node operators from tampering with addresses.
///
/// # Arguments
///
/// * `author_address` - Module author's payment address (75% of payment)
/// * `commons_address` - Commons governance address (15% of payment)
/// * `price_sats` - Module price in satoshis
/// * `signature_hex` - Signature over the payment addresses and price
/// * `public_keys` - Map of maintainer_name -> public_key_hex (from manifest signatures)
/// * `threshold` - (required_count, total_count) tuple for signature threshold
///
/// # Returns
///
/// Returns `Ok(true)` if signature is valid, `Ok(false)` if invalid, or error if verification fails.
pub fn verify_payment_addresses(
&self,
author_address: &str,
commons_address: &str,
price_sats: u64,
signature_hex: &str,
public_keys: &HashMap<String, String>,
threshold: (usize, usize),
) -> Result<bool, ModuleError> {
// Create message: author_address || commons_address || price_sats
let message_data = format!("{author_address}||{commons_address}||{price_sats}");
let message_hash = Sha256::digest(message_data.as_bytes());
let message =
Message::from_digest(message_hash.as_slice().try_into().map_err(|_| {
ModuleError::CryptoError("Invalid message hash length".to_string())
})?);
// Parse signature
let sig_bytes = hex::decode(signature_hex)
.map_err(|e| ModuleError::CryptoError(format!("Invalid signature hex: {e}")))?;
let signature = Signature::from_compact(&sig_bytes)
.or_else(|_| Signature::from_der(&sig_bytes))
.map_err(|e| ModuleError::CryptoError(format!("Invalid signature format: {e}")))?;
// Verify signature against all public keys (need threshold to match)
let mut valid_signatures = 0;
for pubkey_hex in public_keys.values() {
let pubkey_bytes = hex::decode(pubkey_hex)
.map_err(|e| ModuleError::CryptoError(format!("Invalid pubkey hex: {e}")))?;
let public_key = PublicKey::from_slice(&pubkey_bytes)
.map_err(|e| ModuleError::CryptoError(format!("Invalid pubkey: {e}")))?;
if self
.secp
.verify_ecdsa(&message, &signature, &public_key)
.is_ok()
{
valid_signatures += 1;
}
}
// Check threshold (for payment addresses, we require at least one valid signature)
// In practice, this should match the manifest signature threshold
Ok(valid_signatures >= threshold.0)
}
}
impl Default for ModuleSigner {
fn default() -> Self {
Self::new()
}
}