1use std::fmt;
17use std::str::FromStr;
18
19use k256::ecdsa::{
20 RecoveryId, Signature as K256Signature, SigningKey, VerifyingKey,
21 signature::hazmat::PrehashSigner,
22};
23use sha3::{Digest, Keccak256};
24use subtle::ConstantTimeEq;
25use zeroize::{Zeroize, ZeroizeOnDrop};
26
27use crate::swarm::bytes::{decode_hex, encode_hex};
28use crate::swarm::errors::Error;
29use crate::swarm::typed_bytes::{
30 ETH_ADDRESS_LENGTH, EthAddress, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
31 Signature,
32};
33
34#[derive(Clone, Zeroize, ZeroizeOnDrop)]
42pub struct PrivateKey([u8; PRIVATE_KEY_LENGTH]);
43
44impl PartialEq for PrivateKey {
45 fn eq(&self, other: &Self) -> bool {
46 self.0.ct_eq(&other.0).into()
47 }
48}
49
50impl Eq for PrivateKey {}
51
52impl PrivateKey {
53 pub const LENGTH: usize = PRIVATE_KEY_LENGTH;
55
56 pub fn new(b: &[u8]) -> Result<Self, Error> {
58 if b.len() != PRIVATE_KEY_LENGTH {
59 return Err(Error::LengthMismatch {
60 kind: "PrivateKey",
61 expected: &[PRIVATE_KEY_LENGTH],
62 got: b.len(),
63 });
64 }
65 let mut a = [0u8; PRIVATE_KEY_LENGTH];
66 a.copy_from_slice(b);
67 Ok(Self(a))
68 }
69
70 pub fn from_hex(s: &str) -> Result<Self, Error> {
72 Self::new(&decode_hex(s)?)
73 }
74
75 pub fn as_bytes(&self) -> &[u8] {
77 &self.0
78 }
79
80 pub fn to_hex(&self) -> String {
82 encode_hex(&self.0)
83 }
84
85 fn signing_key(&self) -> Result<SigningKey, Error> {
86 SigningKey::from_slice(&self.0).map_err(Error::crypto)
87 }
88
89 pub fn public_key(&self) -> Result<PublicKey, Error> {
91 let sk = self.signing_key()?;
92 let vk = VerifyingKey::from(&sk);
93 let point = vk.to_encoded_point(false);
94 let bytes = point.as_bytes();
96 debug_assert_eq!(bytes.len(), 65);
97 let mut a = [0u8; PUBLIC_KEY_LENGTH];
98 a.copy_from_slice(&bytes[1..]);
99 Ok(PublicKey(a))
100 }
101
102 pub fn sign(&self, data: &[u8]) -> Result<Signature, Error> {
105 let digest = eth_signed_message_digest(data);
106 let sk = self.signing_key()?;
107 let (sig, recovery_id): (K256Signature, RecoveryId) =
108 sk.sign_prehash(&digest).map_err(Error::crypto)?;
109 let normalized = sig.normalize_s().unwrap_or(sig);
110 let recovery_id = if normalized != sig {
113 RecoveryId::from_byte(recovery_id.to_byte() ^ 1).expect("flipped recovery id valid")
114 } else {
115 recovery_id
116 };
117
118 let mut out = [0u8; SIGNATURE_LENGTH];
119 let bytes = normalized.to_bytes();
120 out[..64].copy_from_slice(&bytes);
121 out[64] = recovery_id.to_byte() + 27;
122 Signature::new(&out)
123 }
124}
125
126impl fmt::Debug for PrivateKey {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 f.write_str("PrivateKey(<redacted>)")
130 }
131}
132
133impl FromStr for PrivateKey {
134 type Err = Error;
135 fn from_str(s: &str) -> Result<Self, Self::Err> {
136 Self::from_hex(s)
137 }
138}
139
140#[derive(Clone, Copy, PartialEq, Eq, Hash)]
146pub struct PublicKey([u8; PUBLIC_KEY_LENGTH]);
147
148impl PublicKey {
149 pub const LENGTH: usize = PUBLIC_KEY_LENGTH;
151
152 pub fn new(b: &[u8]) -> Result<Self, Error> {
155 match b.len() {
156 PUBLIC_KEY_LENGTH => {
157 let mut a = [0u8; PUBLIC_KEY_LENGTH];
158 a.copy_from_slice(b);
159 Ok(Self(a))
160 }
161 33 => {
162 let vk = VerifyingKey::from_sec1_bytes(b).map_err(Error::crypto)?;
163 let point = vk.to_encoded_point(false);
164 let bytes = point.as_bytes();
165 let mut a = [0u8; PUBLIC_KEY_LENGTH];
166 a.copy_from_slice(&bytes[1..]);
167 Ok(Self(a))
168 }
169 n => Err(Error::LengthMismatch {
170 kind: "PublicKey",
171 expected: &[33, PUBLIC_KEY_LENGTH],
172 got: n,
173 }),
174 }
175 }
176
177 pub fn from_hex(s: &str) -> Result<Self, Error> {
180 Self::new(&decode_hex(s)?)
181 }
182
183 pub fn as_bytes(&self) -> &[u8] {
185 &self.0
186 }
187
188 pub fn to_hex(&self) -> String {
190 encode_hex(&self.0)
191 }
192
193 pub fn address(&self) -> EthAddress {
195 let mut h = Keccak256::new();
196 h.update(self.0);
197 let out = h.finalize();
198 let mut a = [0u8; ETH_ADDRESS_LENGTH];
199 a.copy_from_slice(&out[12..]);
200 EthAddress::new(&a).expect("hash slice has fixed length")
201 }
202
203 pub fn compressed_bytes(&self) -> Result<[u8; 33], Error> {
205 let mut full = [0u8; 65];
206 full[0] = 0x04;
207 full[1..].copy_from_slice(&self.0);
208 let vk = VerifyingKey::from_sec1_bytes(&full).map_err(Error::crypto)?;
209 let point = vk.to_encoded_point(true);
210 let bytes = point.as_bytes();
211 if bytes.len() != 33 {
212 return Err(Error::crypto("compressed point not 33 bytes"));
213 }
214 let mut a = [0u8; 33];
215 a.copy_from_slice(bytes);
216 Ok(a)
217 }
218
219 pub fn compressed_hex(&self) -> Result<String, Error> {
221 Ok(encode_hex(&self.compressed_bytes()?))
222 }
223}
224
225impl fmt::Debug for PublicKey {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write!(f, "PublicKey({})", self.to_hex())
228 }
229}
230
231impl fmt::Display for PublicKey {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 f.write_str(&self.to_hex())
234 }
235}
236
237impl FromStr for PublicKey {
238 type Err = Error;
239 fn from_str(s: &str) -> Result<Self, Self::Err> {
240 Self::from_hex(s)
241 }
242}
243
244impl Signature {
247 pub fn recover_public_key(&self, data: &[u8]) -> Result<PublicKey, Error> {
250 let digest = eth_signed_message_digest(data);
251 let bytes = self.as_bytes();
252 let v = bytes[64];
253 let recovery_byte = if v >= 27 { v - 27 } else { v };
254 let recovery_id =
255 RecoveryId::from_byte(recovery_byte).ok_or_else(|| Error::crypto("invalid V byte"))?;
256 let sig = K256Signature::from_slice(&bytes[..64]).map_err(Error::crypto)?;
257 let vk = VerifyingKey::recover_from_prehash(&digest, &sig, recovery_id)
258 .map_err(Error::crypto)?;
259 let point = vk.to_encoded_point(false);
260 let raw = point.as_bytes();
261 let mut a = [0u8; PUBLIC_KEY_LENGTH];
262 a.copy_from_slice(&raw[1..]);
263 Ok(PublicKey(a))
264 }
265
266 pub fn is_valid(&self, data: &[u8], expected: EthAddress) -> bool {
269 match self.recover_public_key(data) {
270 Ok(pk) => pk.address() == expected,
271 Err(_) => false,
272 }
273 }
274}
275
276pub fn eth_signed_message_digest(data: &[u8]) -> [u8; 32] {
281 let mut h = Keccak256::new();
282 h.update(data);
283 let inner = h.finalize();
284
285 let mut h = Keccak256::new();
286 h.update(b"\x19Ethereum Signed Message:\n32");
287 h.update(inner);
288 let out = h.finalize();
289 let mut a = [0u8; 32];
290 a.copy_from_slice(&out);
291 a
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297
298 fn priv_repeat(byte: u8) -> PrivateKey {
299 PrivateKey::new(&[byte; PRIVATE_KEY_LENGTH]).unwrap()
300 }
301
302 #[test]
303 fn private_to_public_to_address_is_consistent() {
304 let pk = priv_repeat(0x11);
305 let pub_a = pk.public_key().unwrap();
306 let pub_b = pk.public_key().unwrap();
307 assert_eq!(pub_a.as_bytes(), pub_b.as_bytes());
308 assert_eq!(pub_a.address(), pub_b.address());
309 }
310
311 #[test]
312 fn public_key_compressed_round_trip() {
313 let pk = priv_repeat(0x22);
314 let pub_a = pk.public_key().unwrap();
315 let compressed = pub_a.compressed_bytes().unwrap();
316 assert_eq!(compressed.len(), 33);
317 let pub_b = PublicKey::new(&compressed).unwrap();
318 assert_eq!(pub_a.as_bytes(), pub_b.as_bytes());
319 }
320
321 #[test]
322 fn sign_recover_round_trip() {
323 let pk = priv_repeat(0x33);
324 let data = b"hello swarm";
325 let sig = pk.sign(data).unwrap();
326 let v = sig.as_bytes()[64];
328 assert!(v == 27 || v == 28, "V was {v}");
329 let recovered = sig.recover_public_key(data).unwrap();
330 assert_eq!(recovered.as_bytes(), pk.public_key().unwrap().as_bytes());
331 assert!(sig.is_valid(data, pk.public_key().unwrap().address()));
332 assert!(!sig.is_valid(b"tampered", pk.public_key().unwrap().address()));
333 }
334
335 #[test]
336 fn debug_does_not_leak_private_bytes() {
337 let pk = priv_repeat(0x44);
338 let s = format!("{pk:?}");
339 assert!(!s.contains("44"));
340 assert!(s.contains("redacted"));
341 }
342
343 #[test]
344 fn zeroize_clears_private_bytes() {
345 let mut pk = priv_repeat(0x55);
346 assert_eq!(pk.as_bytes(), &[0x55; PRIVATE_KEY_LENGTH]);
347 pk.zeroize();
348 assert_eq!(pk.as_bytes(), &[0u8; PRIVATE_KEY_LENGTH]);
349 }
350
351 #[test]
352 fn private_key_is_zeroize_on_drop() {
353 fn assert_zod<T: zeroize::ZeroizeOnDrop>() {}
357 assert_zod::<PrivateKey>();
358 }
359
360 #[test]
361 fn private_key_eq_is_correct() {
362 let a = priv_repeat(0x66);
363 let b = priv_repeat(0x66);
364 let c = priv_repeat(0x77);
365 assert_eq!(a, b);
366 assert_ne!(a, c);
367 }
368}