Skip to content

Commit 425ebf4

Browse files
authored
Merge pull request #309 from euxaristia/fix/strict-ed25519-verification
fix(core): enforce strict RFC 8032 Ed25519 signature verification
2 parents 225644a + 1fcbbbd commit 425ebf4

2 files changed

Lines changed: 60 additions & 4 deletions

File tree

‎crates/gitlawb-attest/src/attestation.rs‎

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
//! by exact match.
1717
1818
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};
2020
use serde::{de::DeserializeOwned, Deserialize, Serialize};
2121

2222
use crate::error::{Error, Result};
@@ -111,7 +111,7 @@ impl Attestation {
111111
.try_into()
112112
.map_err(|_| Error::Signature("signature must be 64 bytes".to_string()))?;
113113
let sig = Signature::from_bytes(&sig_bytes);
114-
vk.verify(&bytes, &sig)
114+
vk.verify_strict(&bytes, &sig)
115115
.map_err(|e| Error::Signature(format!("ed25519: {e}")))?;
116116

117117
Ok(vk)
@@ -554,4 +554,39 @@ mod tests {
554554
let err = tampered.verify_signature(cert_hash).unwrap_err();
555555
assert!(matches!(err, Error::Signature(_)));
556556
}
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+
}
557592
}

‎crates/gitlawb-core/src/identity.rs‎

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,9 @@ impl Keypair {
7878

7979
/// Verify an Ed25519 signature.
8080
pub fn verify(verifying_key: &VerifyingKey, msg: &[u8], sig_bytes: &[u8; 64]) -> Result<()> {
81-
use ed25519_dalek::Verifier;
8281
let sig = Signature::from_bytes(sig_bytes);
8382
verifying_key
84-
.verify(msg, &sig)
83+
.verify_strict(msg, &sig)
8584
.map_err(|_| Error::SignatureInvalid)
8685
}
8786

@@ -209,6 +208,28 @@ mod tests {
209208
);
210209
}
211210

211+
#[test]
212+
fn verify_rejects_weak_key_signature() {
213+
// Regression guard for strict verification: a signature forged under a
214+
// weak (small-order) public key satisfies the verification equation
215+
// but must be rejected. The identity point is such a key: with R the
216+
// identity and S = 0, [S]B - [k]A is the identity for any message.
217+
let mut weak_key_bytes = [0u8; 32];
218+
weak_key_bytes[0] = 1; // compressed identity point (y = 1, x = 0)
219+
let weak_vk = VerifyingKey::from_bytes(&weak_key_bytes).unwrap();
220+
assert!(weak_vk.is_weak(), "identity point must be a weak key");
221+
222+
let msg = b"forged message";
223+
let mut forged = [0u8; 64];
224+
forged[0] = 1; // R = identity point, S = 0
225+
226+
let result = verify(&weak_vk, msg, &forged);
227+
assert!(
228+
result.is_err(),
229+
"signature under a weak (small-order) public key must be rejected"
230+
);
231+
}
232+
212233
#[test]
213234
fn signed_payload_round_trip() {
214235
let kp = Keypair::generate();

0 commit comments

Comments
 (0)