Skip to content

Commit e4d1011

Browse files
authored
crypto.ecdsa: fix handling of sign() with custom_hash (#23619)
1 parent fc1cae5 commit e4d1011

2 files changed

Lines changed: 83 additions & 3 deletions

File tree

‎vlib/crypto/ecdsa/ecdsa.v‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,6 @@ fn calc_digest(key &C.EC_KEY, message []u8, opt SignerOpts) ![]u8 {
540540
return error('fail to load group')
541541
}
542542
num_bits := C.EC_GROUP_get_degree(group)
543-
// check for key size matching
544543
key_size := (num_bits + 7) / 8
545544
// we're working on mutable copy of SignerOpts, with some issues when make it as a mutable.
546545
// ie, declaring a mutable parameter that accepts a struct with the `@[params]` attribute is not allowed.
@@ -566,8 +565,22 @@ fn calc_digest(key &C.EC_KEY, message []u8, opt SignerOpts) ![]u8 {
566565
return error('Hash into smaller size than current key size was not allowed')
567566
}
568567
}
569-
digest := cfg.custom_hash.sum(message)
570-
defer { unsafe { cfg.custom_hash.free() } }
568+
// we need to reset the custom hash before writes message
569+
cfg.custom_hash.reset()
570+
_ := cfg.custom_hash.write(message)!
571+
digest := cfg.custom_hash.sum([]u8{})
572+
// NOTES:
573+
// NIST FIPS 186-4 at the end of section 6.4 states that:
574+
// When the length of the output of the hash function is greater than the bit length of n,
575+
// then the leftmost n bits of the hash function output block shall be used in any calculation
576+
// using the hash function output during the generation or verification of a digital signature
577+
// with output of custom_hash was bigger than bit length (key size)
578+
// TODO:
579+
// Maybe better to pick up only required bytes from digest, ie,
580+
// out := digest[..key_size].clone()
581+
// unsafe { digest.free() }
582+
// return out
583+
// Currently, just boildown to the caller
571584
return digest
572585
}
573586
}

‎vlib/crypto/ecdsa/util_test.v‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ module ecdsa
22

33
import encoding.hex
44
import crypto.pem
5+
import crypto.sha1
6+
import crypto.sha512
57

68
// This material wss generated with https://emn178.github.io/online-tools/ecdsa/key-generator
79
// with curve SECG secp384r1 aka NIST P-384
@@ -126,3 +128,68 @@ UHhnmmVRraSwrVkPdYIeXhH/Ob4+8OLcwrQBMv4RXsD1GVFsgkvEYDTEb/vnMA==
126128
return
127129
}
128130
}
131+
132+
fn test_key_signing_verifying_with_custom_hash() ! {
133+
// privatekey_sample was P-384 key
134+
pvkey := privkey_from_string(privatekey_sample)!
135+
// public key part
136+
pbkey := pvkey.public_key()!
137+
pbk := pubkey_from_string(public_key_sample)!
138+
139+
// lets sign the message with default hash, ie, sha384
140+
signature := pvkey.sign(message_tobe_signed)!
141+
verified := pbkey.verify(message_tobe_signed, signature)!
142+
assert verified == true
143+
144+
// Use the bigger custom hash
145+
opt0 := SignerOpts{
146+
hash_config: .with_custom_hash
147+
allow_custom_hash: true
148+
custom_hash: sha512.new()
149+
}
150+
// online-generated signature with sha512 digest with the same params from https://emn178.github.io/online-tools/ecdsa/sign/
151+
online_sign0 := hex.decode('3066023100b54b479b64961481074c4200a9dec83fb8a42bb7db53cf97f1da131504a058ead85d0a9e4e32be14098bc9b4d1a5a8dd023100f9c7de178a286329103f684d1eab1ccfe359c65a41a1459d7f535b703c57048f25931b1670ab4ec7a812d94c69063522')!
152+
// library generated signature
153+
sign0 := pvkey.sign(message_tobe_signed, opt0)!
154+
v00 := pbkey.verify(message_tobe_signed, sign0, opt0)!
155+
// this own signature should assert into true
156+
assert v00 == true
157+
158+
// verify online-generated signature
159+
v01 := pbkey.verify(message_tobe_signed, online_sign0, opt0)!
160+
assert v01 == true
161+
162+
// with public_key_sample key
163+
v02 := pbk.verify(message_tobe_signed, sign0, opt0)!
164+
assert v02 == true
165+
v03 := pbk.verify(message_tobe_signed, online_sign0, opt0)!
166+
assert v03 == true
167+
168+
// Use smaller custom hash
169+
opt1 := SignerOpts{
170+
hash_config: .with_custom_hash
171+
allow_custom_hash: true
172+
allow_smaller_size: true
173+
custom_hash: sha1.new()
174+
}
175+
// online-generated signature with SHA1 digest
176+
online_sign1 := hex.decode('306602310084299d8a70bf512c25cd2b79ae36509572f2bd6f198baeee074683578a70b4af8008e1cf451a2df1a887cf43daff4eea023100dceb267fe5037025c2af9f37911e05a36cbe666dd90fd6904020b5db056e86f25f9439a0ccb443d113b174cab6e2ad61')!
177+
// library generated signature
178+
sign1 := pvkey.sign(message_tobe_signed, opt1)!
179+
verified1 := pbkey.verify(message_tobe_signed, sign1, opt1)!
180+
// this own signature should assert into true
181+
assert verified1 == true
182+
// verify online-generated signature
183+
verified11 := pbkey.verify(message_tobe_signed, online_sign1, opt1)!
184+
assert verified11 == true
185+
186+
// verify with public_key_sample key
187+
v11 := pbk.verify(message_tobe_signed, sign1, opt1)!
188+
assert v11 == true
189+
v12 := pbk.verify(message_tobe_signed, online_sign1, opt1)!
190+
assert v12 == true
191+
192+
pvkey.free()
193+
pbkey.free()
194+
pbk.free()
195+
}

0 commit comments

Comments
 (0)