|
16 | 16 | //! by exact match. |
17 | 17 |
|
18 | 18 | use base64::{engine::general_purpose::URL_SAFE_NO_PAD as B64U, Engine}; |
19 | | -use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; |
| 19 | +use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey}; |
20 | 20 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; |
21 | 21 |
|
22 | 22 | use crate::error::{Error, Result}; |
@@ -111,7 +111,7 @@ impl Attestation { |
111 | 111 | .try_into() |
112 | 112 | .map_err(|_| Error::Signature("signature must be 64 bytes".to_string()))?; |
113 | 113 | let sig = Signature::from_bytes(&sig_bytes); |
114 | | - vk.verify(&bytes, &sig) |
| 114 | + vk.verify_strict(&bytes, &sig) |
115 | 115 | .map_err(|e| Error::Signature(format!("ed25519: {e}")))?; |
116 | 116 |
|
117 | 117 | Ok(vk) |
@@ -554,4 +554,39 @@ mod tests { |
554 | 554 | let err = tampered.verify_signature(cert_hash).unwrap_err(); |
555 | 555 | assert!(matches!(err, Error::Signature(_))); |
556 | 556 | } |
| 557 | + |
| 558 | + /// Regression guard for strict verification: a signature forged under a |
| 559 | + /// weak (small-order) public key satisfies the verification equation |
| 560 | + /// but must be rejected. The identity point is such a key: with R the |
| 561 | + /// identity and S = 0, [S]B - [k]A is the identity for any message. |
| 562 | + #[test] |
| 563 | + fn verify_rejects_weak_key_signature() { |
| 564 | + let mut weak_key_bytes = [0u8; 32]; |
| 565 | + weak_key_bytes[0] = 1; // compressed identity point (y = 1, x = 0) |
| 566 | + let weak_vk = VerifyingKey::from_bytes(&weak_key_bytes).unwrap(); |
| 567 | + assert!(weak_vk.is_weak(), "identity point must be a weak key"); |
| 568 | + |
| 569 | + let cert_hash = sample_cert_hash(); |
| 570 | + let mut forged = [0u8; 64]; |
| 571 | + forged[0] = 1; // R = identity point, S = 0 |
| 572 | + let forged_sig = B64U.encode(forged); |
| 573 | + |
| 574 | + // Build an attestation with the weak key as signer and the forged |
| 575 | + // signature. The weak-key check happens at verify time. |
| 576 | + let mut att = dummy_attestation(&SigningKey::generate(&mut OsRng), cert_hash); |
| 577 | + let mut buf = Vec::with_capacity(ED25519_MULTICODEC.len() + 32); |
| 578 | + buf.extend_from_slice(&ED25519_MULTICODEC); |
| 579 | + buf.extend_from_slice(&weak_key_bytes); |
| 580 | + att.signer = format!( |
| 581 | + "did:key:{}", |
| 582 | + multibase::encode(multibase::Base::Base58Btc, &buf) |
| 583 | + ); |
| 584 | + att.sig = forged_sig; |
| 585 | + |
| 586 | + let err = att.verify_signature(cert_hash).unwrap_err(); |
| 587 | + assert!( |
| 588 | + matches!(err, Error::Signature(_)), |
| 589 | + "signature under a weak (small-order) public key must be rejected" |
| 590 | + ); |
| 591 | + } |
557 | 592 | } |
0 commit comments