test(web): cover constant-time compare path in HMAC verify
Existing rejectsTamperedSignature uses len+1 sig — short-circuits in MessageDigest.isEqual on length mismatch. Same-length tamper test forces the byte-by-byte compare so the constant-time branch is exercised. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -47,4 +47,18 @@ class ArtifactDownloadTokenSignerTest {
|
|||||||
String sig = signer.signRaw(id, pastExp);
|
String sig = signer.signRaw(id, pastExp);
|
||||||
assertThat(signer.verify(id, pastExp, sig)).isFalse();
|
assertThat(signer.verify(id, pastExp, sig)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Forces the constant-time-compare branch by flipping one char at the same length —
|
||||||
|
* exercises {@code MessageDigest.isEqual}'s byte-by-byte path, not the length short-circuit. */
|
||||||
|
@Test
|
||||||
|
void rejectsSameLengthTamperedSignature() {
|
||||||
|
var signer = new ArtifactDownloadTokenSigner(secret, clock);
|
||||||
|
UUID id = UUID.randomUUID();
|
||||||
|
var token = signer.sign(id, Duration.ofMinutes(5));
|
||||||
|
char last = token.sig().charAt(token.sig().length() - 1);
|
||||||
|
char swapped = last == 'A' ? 'B' : 'A';
|
||||||
|
String tampered = token.sig().substring(0, token.sig().length() - 1) + swapped;
|
||||||
|
assertThat(tampered).hasSameSizeAs(token.sig());
|
||||||
|
assertThat(signer.verify(id, token.exp(), tampered)).isFalse();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user