Skip to content

Commit 956d9bc

Browse files
authored
Merge pull request algorand#16 from nullun/chore/deterministic-length-validation
Harden length validation for compressed det1024 signatures
2 parents 1f71e3a + b58119d commit 956d9bc

4 files changed

Lines changed: 62 additions & 5 deletions

File tree

deterministic.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ int falcon_det1024_keygen(shake256_context *rng, void *privkey, void *pubkey) {
2222
}
2323

2424
// Domain separator used to construct the fixed versioned salt string.
25-
uint8_t falcon_det1024_salt_rest[38] = {"FALCON_DET"};
25+
static const uint8_t falcon_det1024_salt_rest[38] = {"FALCON_DET"};
2626

2727
// Construct the fixed salt for a given version.
2828
void falcon_det1024_write_salt(uint8_t dst[40], uint8_t salt_version) {
@@ -85,6 +85,10 @@ int falcon_det1024_convert_compressed_to_ct(void *sig_ct,
8585
int16_t coeffs[1 << FALCON_DET1024_LOGN];
8686
size_t v;
8787

88+
if (sig_compressed_len < 2) {
89+
return FALCON_ERR_BADSIG;
90+
}
91+
8892
if (((uint8_t*)sig_compressed)[0] != FALCON_DET1024_SIG_COMPRESSED_HEADER) {
8993
return FALCON_ERR_BADSIG;
9094
}
@@ -95,6 +99,12 @@ int falcon_det1024_convert_compressed_to_ct(void *sig_ct,
9599
return FALCON_ERR_SIZE;
96100
}
97101

102+
// Reject trailing bytes, matching the exact-consumption check
103+
// that falcon_verify applies to compressed signatures.
104+
if (v != sig_compressed_len-2) {
105+
return FALCON_ERR_BADSIG;
106+
}
107+
98108
uint8_t *sig = sig_ct;
99109
sig[0] = FALCON_DET1024_SIG_CT_HEADER;
100110
sig[1] = ((uint8_t*)sig_compressed)[1]; // Copy the salt_version byte.
@@ -133,12 +143,11 @@ int falcon_det1024_verify_compressed(const void *sig, size_t sig_len,
133143
}
134144

135145
// Add back the salt; drop the version byte.
136-
size_t salted_sig_len = sig_len + 40 - 1;
137-
138-
if (salted_sig_len > FALCON_DET1024_SALTED_SIG_COMPRESSED_MAXSIZE){
146+
if (sig_len - 1 > FALCON_DET1024_SALTED_SIG_COMPRESSED_MAXSIZE - 40) {
139147
return FALCON_ERR_BADSIG;
140148
}
141149

150+
size_t salted_sig_len = sig_len + 40 - 1;
142151

143152
falcon_det1024_resalt(salted_sig, sig, sig_len);
144153

falcon.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ func (sk *PrivateKey) SignCompressed(msg []byte) (CompressedSignature, error) {
119119
func (sig *CompressedSignature) ConvertToCT() (CTSignature, error) {
120120
sigCT := CTSignature{}
121121

122+
if len(*sig) < 2 {
123+
return CTSignature{}, fmt.Errorf("signature too short: %w", ErrConvertFail)
124+
}
125+
122126
r := C.falcon_det1024_convert_compressed_to_ct(unsafe.Pointer(&sigCT[0]), unsafe.Pointer(&(*sig)[0]), C.size_t(len(*sig)))
123127
if r != 0 {
124128
return CTSignature{}, fmt.Errorf("error code %d: %w", int(r), ErrConvertFail)

falcon_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,47 @@ func BenchmarkFalconVerify(b *testing.B) {
421421
pk.Verify(sigs[i], strs[i][:])
422422
}
423423
}
424+
425+
func TestFalconMalformedSignatures(t *testing.T) {
426+
seed := make([]byte, 64)
427+
rand.Read(seed)
428+
429+
pub, priv, err := GenerateKey(seed)
430+
if err != nil {
431+
t.Fatalf("failed to generate keys. err message: %s", err)
432+
}
433+
434+
msg := make([]byte, 64)
435+
rand.Read(msg)
436+
437+
sig, err := priv.SignCompressed(msg)
438+
if err != nil {
439+
t.Fatalf("failed to sign message. err message: %s", err)
440+
}
441+
442+
// A signature shorter than the 2-byte header and salt-version prefix must be rejected.
443+
for _, short := range []CompressedSignature{nil, {}, sig[:0], sig[:1]} {
444+
err = pub.Verify(short, msg)
445+
if err == nil {
446+
t.Fatalf("expected verify to fail on %d-byte signature", len(short))
447+
}
448+
449+
_, err = short.ConvertToCT()
450+
if err == nil {
451+
t.Fatalf("expected ConvertToCT to fail on %d-byte signature", len(short))
452+
}
453+
}
454+
455+
// A valid signature with trailing bytes appended must be rejected.
456+
trailing := append(append(CompressedSignature{}, sig...), 0)
457+
458+
err = pub.Verify(trailing, msg)
459+
if err == nil {
460+
t.Fatalf("expected verify to fail on signature with trailing bytes")
461+
}
462+
463+
_, err = trailing.ConvertToCT()
464+
if err == nil {
465+
t.Fatalf("expected ConvertToCT to fail on signature with trailing bytes")
466+
}
467+
}

tests/test_deterministic.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ hextobin(uint8_t *buf, size_t max_len, const char *src)
6262
}
6363
}
6464

65-
uint8_t sigs_ct[NUM_KATS][FALCON_DET1024_SIG_CT_SIZE];
65+
static uint8_t sigs_ct[NUM_KATS][FALCON_DET1024_SIG_CT_SIZE];
6666

6767
void test_inner(size_t data_len) {
6868
uint8_t pubkey[FALCON_DET1024_PUBKEY_SIZE];

0 commit comments

Comments
 (0)