1#![forbid(unsafe_code)]
59
60pub mod secp256r1;
61
62use core::fmt;
63
64use jacquard_common::types::crypto::{multikey, KeyCodec, PublicKey};
65use k256::ecdsa::signature::{Signer, Verifier};
66use k256::elliptic_curve::scalar::IsHigh;
67
68const SECP256K1_CODEC: u64 = 0xE7;
70
71const DID_KEY_PREFIX: &str = "did:key:";
73
74const COMPRESSED_LEN: usize = 33;
76
77#[derive(Debug, thiserror::Error)]
79#[non_exhaustive]
80pub enum KeyError {
81 #[error("not a valid secp256k1 private key")]
84 InvalidPrivateKey,
85
86 #[error("not a valid secp256k1 public key")]
88 InvalidPublicKey,
89
90 #[error("not a did:key URI")]
92 NotDidKey,
93
94 #[error("multibase string is not base58btc (expected a leading 'z')")]
97 NotBase58Btc,
98
99 #[error("could not decode multikey: {0}")]
101 Multikey(#[from] jacquard_common::types::crypto::CryptoError),
102
103 #[error("expected a secp256k1 key, got {codec:?}")]
106 WrongCurve {
107 codec: KeyCodec,
109 },
110
111 #[error("expected a 33-byte compressed key, got {got} bytes")]
113 NotCompressed {
114 got: usize,
116 },
117
118 #[error("not a valid compact signature")]
120 InvalidSignature,
121
122 #[error("signature did not verify")]
125 VerificationFailed,
126}
127
128#[derive(Clone, Copy, PartialEq, Eq)]
133pub struct Signature(k256::ecdsa::Signature);
134
135impl Signature {
136 pub fn to_bytes(&self) -> [u8; 64] {
139 self.0.to_bytes().into()
140 }
141
142 pub fn from_bytes(bytes: &[u8; 64]) -> Result<Self, KeyError> {
148 k256::ecdsa::Signature::from_slice(bytes)
149 .map(Self)
150 .map_err(|_| KeyError::InvalidSignature)
151 }
152
153 pub fn is_low_s(&self) -> bool {
156 !bool::from(self.0.s().is_high())
157 }
158
159 pub fn as_k256(&self) -> &k256::ecdsa::Signature {
161 &self.0
162 }
163}
164
165impl From<k256::ecdsa::Signature> for Signature {
166 fn from(inner: k256::ecdsa::Signature) -> Self {
167 Self(inner)
168 }
169}
170
171impl fmt::Debug for Signature {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 f.write_str("Signature(")?;
176 for byte in self.to_bytes() {
177 write!(f, "{byte:02x}")?;
178 }
179 f.write_str(")")
180 }
181}
182
183#[derive(Clone)]
189pub struct SigningKey(k256::ecdsa::SigningKey);
190
191impl SigningKey {
192 pub fn generate() -> Self {
194 Self(k256::ecdsa::SigningKey::random(&mut rand_core::OsRng))
195 }
196
197 pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, KeyError> {
204 k256::ecdsa::SigningKey::from_bytes(bytes.into())
205 .map(Self)
206 .map_err(|_| KeyError::InvalidPrivateKey)
207 }
208
209 pub fn to_bytes(&self) -> [u8; 32] {
213 self.0.to_bytes().into()
214 }
215
216 pub fn sign(&self, message: &[u8]) -> Signature {
221 Signature(self.0.sign(message))
222 }
223
224 pub fn verifying_key(&self) -> VerifyingKey {
226 VerifyingKey(*self.0.verifying_key())
227 }
228}
229
230impl fmt::Debug for SigningKey {
231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232 f.debug_struct("SigningKey")
236 .field("public", &self.verifying_key().to_did_key())
237 .finish_non_exhaustive()
238 }
239}
240
241#[derive(Clone, Copy, PartialEq, Eq)]
243pub struct VerifyingKey(k256::ecdsa::VerifyingKey);
244
245impl VerifyingKey {
246 pub fn to_multibase(&self) -> String {
253 multikey(SECP256K1_CODEC, self.0.to_encoded_point(true).as_bytes())
254 }
255
256 pub fn to_did_key(&self) -> String {
259 format!("{DID_KEY_PREFIX}{}", self.to_multibase())
260 }
261
262 pub fn from_did_key(did: &str) -> Result<Self, KeyError> {
267 let multibase = did
268 .strip_prefix(DID_KEY_PREFIX)
269 .ok_or(KeyError::NotDidKey)?;
270 Self::from_multibase(multibase)
271 }
272
273 pub fn from_multibase(multibase: &str) -> Result<Self, KeyError> {
279 if !multibase.starts_with('z') {
280 return Err(KeyError::NotBase58Btc);
281 }
282 let public = PublicKey::decode_owned(multibase)?;
283 if public.codec != KeyCodec::Secp256k1 {
284 return Err(KeyError::WrongCurve {
285 codec: public.codec,
286 });
287 }
288 if public.bytes.len() != COMPRESSED_LEN {
289 return Err(KeyError::NotCompressed {
290 got: public.bytes.len(),
291 });
292 }
293 let point = public.to_k256().map_err(|_| KeyError::InvalidPublicKey)?;
294 Ok(Self(point.into()))
295 }
296
297 pub fn verify(&self, message: &[u8], sig: &Signature) -> Result<(), KeyError> {
301 self.0
302 .verify(message, &sig.0)
303 .map_err(|_| KeyError::VerificationFailed)
304 }
305
306 pub fn as_k256(&self) -> &k256::ecdsa::VerifyingKey {
308 &self.0
309 }
310}
311
312impl fmt::Debug for VerifyingKey {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 f.debug_tuple("VerifyingKey")
315 .field(&self.to_did_key())
316 .finish()
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 use jacquard_common::types::crypto::decode_uvarint;
325
326 #[test]
327 fn sign_and_verify_round_trip() {
328 let key = SigningKey::generate();
329 let message = b"quernstone attestation payload";
330 let sig = key.sign(message);
331 assert!(key.verifying_key().verify(message, &sig).is_ok());
332 }
333
334 #[test]
335 fn signature_does_not_verify_against_another_message() {
336 let key = SigningKey::generate();
337 let sig = key.sign(b"first message");
338 let err = key.verifying_key().verify(b"second message", &sig);
339 assert!(matches!(err, Err(KeyError::VerificationFailed)));
340 }
341
342 #[test]
343 fn signature_does_not_verify_against_another_key() {
344 let message = b"shared message";
345 let sig = SigningKey::generate().sign(message);
346 let other = SigningKey::generate();
347 assert!(matches!(
348 other.verifying_key().verify(message, &sig),
349 Err(KeyError::VerificationFailed)
350 ));
351 }
352
353 #[test]
354 fn did_key_round_trips() {
355 let key = SigningKey::generate().verifying_key();
356 let did = key.to_did_key();
357 assert_eq!(VerifyingKey::from_did_key(&did).expect("parse"), key);
358 }
359
360 #[test]
361 fn multibase_round_trips() {
362 let key = SigningKey::generate().verifying_key();
363 let mb = key.to_multibase();
364 assert_eq!(VerifyingKey::from_multibase(&mb).expect("parse"), key);
365 }
366
367 #[test]
371 fn did_key_decodes_through_jacquard() {
372 let key = SigningKey::generate().verifying_key();
373 let did = key.to_did_key();
374 let multibase = did.strip_prefix(DID_KEY_PREFIX).expect("prefix");
375
376 let decoded = PublicKey::decode_owned(multibase).expect("jacquard decode");
377 assert_eq!(decoded.codec, KeyCodec::Secp256k1);
378 assert_eq!(
379 decoded.to_k256().expect("to k256"),
380 k256::PublicKey::from(key.as_k256())
381 );
382 }
383
384 #[test]
385 fn multibase_is_base58btc_compressed_secp256k1() {
386 let mb = SigningKey::generate().verifying_key().to_multibase();
387 assert!(mb.starts_with('z'), "expected base58btc multibase: {mb}");
388
389 let (_base, data) = multibase::decode(&mb).expect("multibase decode");
390 let (code, offset) = decode_uvarint(&data).expect("varint");
391 assert_eq!(code, SECP256K1_CODEC);
392 assert_eq!(data.len() - offset, COMPRESSED_LEN);
393 assert!(matches!(data[offset], 0x02 | 0x03));
396 }
397
398 #[test]
402 fn every_signature_is_low_s() {
403 for i in 0..256u32 {
404 let key = SigningKey::generate();
405 let message = format!("message {i}");
406 let sig = key.sign(message.as_bytes());
407 assert!(
408 !bool::from(sig.as_k256().s().is_high()),
409 "high-S signature for message {i}"
410 );
411 assert!(sig.is_low_s());
412 }
413 }
414
415 #[test]
416 fn private_key_bytes_round_trip() {
417 let key = SigningKey::generate();
418 let restored = SigningKey::from_bytes(&key.to_bytes()).expect("reload");
419 assert_eq!(restored.verifying_key(), key.verifying_key());
420 }
421
422 #[test]
423 fn from_bytes_rejects_invalid_scalars() {
424 assert!(matches!(
426 SigningKey::from_bytes(&[0u8; 32]),
427 Err(KeyError::InvalidPrivateKey)
428 ));
429 assert!(matches!(
431 SigningKey::from_bytes(&[0xFFu8; 32]),
432 Err(KeyError::InvalidPrivateKey)
433 ));
434 let order = [
436 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
437 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C,
438 0xD0, 0x36, 0x41, 0x41,
439 ];
440 assert!(matches!(
441 SigningKey::from_bytes(&order),
442 Err(KeyError::InvalidPrivateKey)
443 ));
444 let mut max = order;
446 max[31] = 0x40;
447 assert!(SigningKey::from_bytes(&max).is_ok());
448 }
449
450 #[test]
451 fn debug_never_prints_key_material() {
452 let key = SigningKey::generate();
453 let rendered = format!("{key:?}");
454
455 let public = key.verifying_key().to_did_key();
464 assert_eq!(rendered, format!("SigningKey {{ public: {public:?}, .. }}"));
465
466 let secret = key.to_bytes();
471 let lower: String = secret.iter().map(|b| format!("{b:02x}")).collect();
472 let upper: String = secret.iter().map(|b| format!("{b:02X}")).collect();
473 let derived = format!("{secret:?}");
474 for leak in [&lower, &upper, &derived] {
475 assert!(!rendered.contains(leak), "leaked {leak} in {rendered}");
476 }
477 }
478
479 #[test]
480 fn rejects_did_key_with_wrong_prefix() {
481 let mb = SigningKey::generate().verifying_key().to_multibase();
482 assert!(matches!(
483 VerifyingKey::from_did_key(&mb),
484 Err(KeyError::NotDidKey)
485 ));
486 assert!(matches!(
487 VerifyingKey::from_did_key(&format!("did:plc:{mb}")),
488 Err(KeyError::NotDidKey)
489 ));
490 assert!(matches!(
491 VerifyingKey::from_did_key(""),
492 Err(KeyError::NotDidKey)
493 ));
494 }
495
496 #[test]
497 fn rejects_other_curves() {
498 let ed = format!("{DID_KEY_PREFIX}{}", multikey(0xED, &[7u8; 32]));
500 assert!(matches!(
501 VerifyingKey::from_did_key(&ed),
502 Err(KeyError::WrongCurve {
503 codec: KeyCodec::Ed25519
504 })
505 ));
506
507 let p256 = {
510 let mut bytes = [0u8; COMPRESSED_LEN];
511 bytes[0] = 0x02;
512 format!("{DID_KEY_PREFIX}{}", multikey(0x1200, &bytes))
513 };
514 assert!(matches!(
515 VerifyingKey::from_did_key(&p256),
516 Err(KeyError::WrongCurve {
517 codec: KeyCodec::P256
518 })
519 ));
520 }
521
522 #[test]
523 fn rejects_unknown_multicodec() {
524 let bogus = format!("{DID_KEY_PREFIX}{}", multikey(0x99, &[2u8; COMPRESSED_LEN]));
525 assert!(matches!(
526 VerifyingKey::from_did_key(&bogus),
527 Err(KeyError::Multikey(_))
528 ));
529 }
530
531 #[test]
532 fn rejects_uncompressed_secp256k1() {
533 let uncompressed = SigningKey::generate()
534 .verifying_key()
535 .as_k256()
536 .to_encoded_point(false);
537 assert_eq!(uncompressed.as_bytes().len(), 65);
538 let did = format!(
539 "{DID_KEY_PREFIX}{}",
540 multikey(SECP256K1_CODEC, uncompressed.as_bytes())
541 );
542 assert!(matches!(
543 VerifyingKey::from_did_key(&did),
544 Err(KeyError::NotCompressed { got: 65 })
545 ));
546 }
547
548 #[test]
549 fn rejects_wrong_length() {
550 let short = format!("{DID_KEY_PREFIX}{}", multikey(SECP256K1_CODEC, &[2u8; 20]));
551 assert!(matches!(
552 VerifyingKey::from_did_key(&short),
553 Err(KeyError::Multikey(_)) | Err(KeyError::NotCompressed { .. })
555 ));
556 }
557
558 #[test]
559 fn rejects_bad_base58() {
560 let mut mb = SigningKey::generate().verifying_key().to_multibase();
561 mb.push('0');
563 assert!(matches!(
564 VerifyingKey::from_did_key(&format!("{DID_KEY_PREFIX}{mb}")),
565 Err(KeyError::Multikey(_))
566 ));
567 }
568
569 #[test]
570 fn rejects_non_base58btc_multibase() {
571 let key = SigningKey::generate().verifying_key();
574 let mut payload = jacquard_common::types::crypto::encode_uvarint(SECP256K1_CODEC);
575 payload.extend_from_slice(key.as_k256().to_encoded_point(true).as_bytes());
576 let b64 = multibase::encode(multibase::Base::Base64Url, &payload);
577 assert!(matches!(
578 VerifyingKey::from_multibase(&b64),
579 Err(KeyError::NotBase58Btc)
580 ));
581 }
582
583 #[test]
584 fn signature_bytes_round_trip() {
585 let key = SigningKey::generate();
586 let message = b"wire form";
587 let sig = key.sign(message);
588 let bytes = sig.to_bytes();
589 assert_eq!(bytes.len(), 64);
590 let parsed = Signature::from_bytes(&bytes).expect("parse");
591 assert_eq!(parsed, sig);
592 assert!(key.verifying_key().verify(message, &parsed).is_ok());
593 }
594
595 #[test]
596 fn all_zero_signature_is_rejected() {
597 assert!(matches!(
598 Signature::from_bytes(&[0u8; 64]),
599 Err(KeyError::InvalidSignature)
600 ));
601 }
602
603 #[test]
606 fn high_s_signature_does_not_verify() {
607 let key = SigningKey::generate();
608 let message = b"malleable?";
609 let sig = key.sign(message);
610
611 let (r, s) = sig.as_k256().split_scalars();
612 let flipped = k256::ecdsa::Signature::from_scalars(r.to_bytes(), (-*s).to_bytes())
613 .expect("negated s is a valid scalar");
614 assert!(bool::from(flipped.s().is_high()));
615
616 let flipped = Signature::from(flipped);
617 assert!(!flipped.is_low_s());
618 assert!(matches!(
619 key.verifying_key().verify(message, &flipped),
620 Err(KeyError::VerificationFailed)
621 ));
622 }
623}