mirror of
https://github.com/vlang/v.git
synced 2025-08-03 09:47:15 -04:00
crypto.ecdsa: clean out old deprecated keypair opaque from the module, make -cstrict pass for all the tests, and with both gcc and clang (#23887)
This commit is contained in:
parent
03d033fa4b
commit
309aebfaf1
@ -38,12 +38,14 @@ struct C.EVP_PKEY {}
|
||||
|
||||
fn C.EVP_PKEY_new() &C.EVP_PKEY
|
||||
fn C.EVP_PKEY_free(key &C.EVP_PKEY)
|
||||
fn C.EVP_PKEY_get1_EC_KEY(pkey &C.EVP_PKEY) &C.EC_KEY
|
||||
fn C.EVP_PKEY_base_id(key &C.EVP_PKEY) int
|
||||
fn C.EVP_PKEY_bits(pkey &C.EVP_PKEY) int
|
||||
fn C.EVP_PKEY_size(key &C.EVP_PKEY) int
|
||||
fn C.EVP_PKEY_eq(a &C.EVP_PKEY, b &C.EVP_PKEY) int
|
||||
fn C.EVP_PKEY_check(ctx &C.EVP_PKEY_CTX) int
|
||||
fn C.EVP_PKEY_public_check(ctx &C.EVP_PKEY_CTX) int
|
||||
|
||||
fn C.EVP_PKEY_get_group_name(pkey &C.EVP_PKEY, gname &u8, gname_sz u32, gname_len &usize) int
|
||||
fn C.EVP_PKEY_get1_encoded_public_key(pkey &C.EVP_PKEY, ppub &&u8) int
|
||||
fn C.EVP_PKEY_get_bn_param(pkey &C.EVP_PKEY, key_name &u8, bn &&C.BIGNUM) int
|
||||
fn C.EVP_PKEY_fromdata_init(ctx &C.EVP_PKEY_CTX) int
|
||||
@ -86,23 +88,6 @@ fn C.EVP_PKEY_CTX_free(ctx &C.EVP_PKEY_CTX)
|
||||
|
||||
fn C.EVP_PKEY_get_bits(pkey &C.EVP_PKEY) int
|
||||
|
||||
// Elliptic curve keypair declarations
|
||||
@[typedef]
|
||||
struct C.EC_KEY {}
|
||||
|
||||
fn C.EC_KEY_new_by_curve_name(nid int) &C.EC_KEY
|
||||
fn C.EC_KEY_generate_key(key &C.EC_KEY) int
|
||||
fn C.EC_KEY_dup(src &C.EC_KEY) &C.EC_KEY
|
||||
fn C.EC_KEY_free(key &C.EC_KEY)
|
||||
fn C.EC_KEY_set_public_key(key &C.EC_KEY, &C.EC_POINT) int
|
||||
fn C.EC_KEY_set_private_key(key &C.EC_KEY, prv &C.BIGNUM) int
|
||||
fn C.EC_KEY_get0_group(key &C.EC_KEY) &C.EC_GROUP
|
||||
fn C.EC_KEY_get0_private_key(key &C.EC_KEY) &C.BIGNUM
|
||||
fn C.EC_KEY_get0_public_key(key &C.EC_KEY) &C.EC_POINT
|
||||
fn C.EC_KEY_get_conv_form(k &C.EC_KEY) int
|
||||
fn C.EC_KEY_check_key(key &C.EC_KEY) int
|
||||
fn C.EC_KEY_up_ref(key &C.EC_KEY) int
|
||||
|
||||
// BIO input output declarations.
|
||||
@[typedef]
|
||||
struct C.BIO_METHOD {}
|
||||
@ -126,9 +111,7 @@ struct C.EC_POINT {}
|
||||
|
||||
fn C.EC_POINT_new(group &C.EC_GROUP) &C.EC_POINT
|
||||
fn C.EC_POINT_mul(group &C.EC_GROUP, r &C.EC_POINT, n &C.BIGNUM, q &C.EC_POINT, m &C.BIGNUM, ctx &C.BN_CTX) int
|
||||
fn C.EC_POINT_point2oct(g &C.EC_GROUP, p &C.EC_POINT, form int, buf &u8, max_out int, ctx &C.BN_CTX) int
|
||||
fn C.EC_POINT_point2buf(group &C.EC_GROUP, point &C.EC_POINT, form int, pbuf &&u8, ctx &C.BN_CTX) int
|
||||
fn C.EC_POINT_cmp(group &C.EC_GROUP, a &C.EC_POINT, b &C.EC_POINT, ctx &C.BN_CTX) int
|
||||
fn C.EC_POINT_free(point &C.EC_POINT)
|
||||
|
||||
// Elliptic group (curve) related declarations.
|
||||
@ -136,9 +119,6 @@ fn C.EC_POINT_free(point &C.EC_POINT)
|
||||
struct C.EC_GROUP {}
|
||||
|
||||
fn C.EC_GROUP_free(group &C.EC_GROUP)
|
||||
fn C.EC_GROUP_get_degree(g &C.EC_GROUP) int
|
||||
fn C.EC_GROUP_get_curve_name(g &C.EC_GROUP) int
|
||||
fn C.EC_GROUP_cmp(a &C.EC_GROUP, b &C.EC_GROUP, ctx &C.BN_CTX) int
|
||||
fn C.EC_GROUP_new_by_curve_name(nid int) &C.EC_GROUP
|
||||
|
||||
// Elliptic BIGNUM related declarations.
|
||||
@ -160,14 +140,6 @@ struct C.BN_CTX {}
|
||||
fn C.BN_CTX_new() &C.BN_CTX
|
||||
fn C.BN_CTX_free(ctx &C.BN_CTX)
|
||||
|
||||
// ELliptic ECDSA signing and verifying related declarations.
|
||||
@[typedef]
|
||||
struct C.ECDSA_SIG {}
|
||||
|
||||
fn C.ECDSA_size(key &C.EC_KEY) u32
|
||||
fn C.ECDSA_sign(type_ int, dgst &u8, dgstlen int, sig &u8, siglen &u32, eckey &C.EC_KEY) int
|
||||
fn C.ECDSA_verify(type_ int, dgst &u8, dgstlen int, sig &u8, siglen int, eckey &C.EC_KEY) int
|
||||
|
||||
@[typedef]
|
||||
struct C.EVP_MD_CTX {}
|
||||
|
||||
|
@ -4,9 +4,6 @@
|
||||
module ecdsa
|
||||
|
||||
import hash
|
||||
import crypto
|
||||
import crypto.sha256
|
||||
import crypto.sha512
|
||||
|
||||
// NID constants
|
||||
//
|
||||
@ -121,7 +118,7 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey {
|
||||
if seed.len == 0 {
|
||||
return error('Seed with null-length was not allowed')
|
||||
}
|
||||
evpkey := evpkey_from_seed(seed, opt)!
|
||||
evpkey := evpkey_from_seed(seed, opt) or { return err }
|
||||
num_bits := C.EVP_PKEY_get_bits(evpkey)
|
||||
key_size := (num_bits + 7) / 8
|
||||
if seed.len > key_size {
|
||||
@ -135,15 +132,7 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey {
|
||||
return error('seed size doesnt match with curve key size')
|
||||
}
|
||||
}
|
||||
// TODO: remove this when its ready to go out
|
||||
eckey := C.EVP_PKEY_get1_EC_KEY(evpkey)
|
||||
if eckey == 0 {
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('EVP_PKEY_get1_EC_KEY failed')
|
||||
}
|
||||
mut pvkey := PrivateKey{
|
||||
key: eckey
|
||||
evpkey: evpkey
|
||||
}
|
||||
|
||||
@ -161,11 +150,8 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey {
|
||||
// PrivateKey represents ECDSA private key. Actually its a key pair,
|
||||
// contains private key and public key parts.
|
||||
pub struct PrivateKey {
|
||||
// The new high level of keypair opaque, set to nil now.
|
||||
evpkey &C.EVP_PKEY = unsafe { nil }
|
||||
// TODO: when all has been migrated to the new one,
|
||||
// removes this low level declarations.
|
||||
key &C.EC_KEY
|
||||
// The new high level of keypair opaque
|
||||
evpkey &C.EVP_PKEY
|
||||
mut:
|
||||
// ks_flag with .flexible value allowing
|
||||
// flexible-size seed bytes as key.
|
||||
@ -229,23 +215,12 @@ pub fn PrivateKey.new(opt CurveOptions) !PrivateKey {
|
||||
return error('EVP_PKEY_keygen failed')
|
||||
}
|
||||
|
||||
// EVP_PKEY_get1_EC_KEY was deprecated in 3.0. Its used here for compatibility purposes
|
||||
// to support the old key function.
|
||||
// TODO: removes this when its ready to obsolete.
|
||||
eckey := C.EVP_PKEY_get1_EC_KEY(evpkey)
|
||||
if eckey == 0 {
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('EVP_PKEY_get1_EC_KEY failed')
|
||||
}
|
||||
// Cleans up the context
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
// when using default this function, its using underlying curve key size
|
||||
// and discarded opt.fixed_size flag when its not set.
|
||||
priv_key := PrivateKey{
|
||||
evpkey: evpkey
|
||||
key: eckey
|
||||
ks_flag: .fixed
|
||||
}
|
||||
return priv_key
|
||||
@ -254,12 +229,8 @@ pub fn PrivateKey.new(opt CurveOptions) !PrivateKey {
|
||||
// sign performs signing the message with the options. By default options,
|
||||
// it will perform hashing before signing the message.
|
||||
pub fn (pv PrivateKey) sign(message []u8, opt SignerOpts) ![]u8 {
|
||||
if pv.evpkey != unsafe { nil } {
|
||||
digest := calc_digest_with_evpkey(pv.evpkey, message, opt)!
|
||||
return sign_digest(pv.evpkey, digest)!
|
||||
}
|
||||
digest := calc_digest_with_eckey(pv.key, message, opt)!
|
||||
return pv.sign_digest(digest)!
|
||||
digest := calc_digest_with_evpkey(pv.evpkey, message, opt)!
|
||||
return sign_digest(pv.evpkey, digest)!
|
||||
}
|
||||
|
||||
// sign_with_options signs message with the options. It will be deprecated,
|
||||
@ -269,24 +240,6 @@ pub fn (pv PrivateKey) sign_with_options(message []u8, opt SignerOpts) ![]u8 {
|
||||
return pv.sign(message, opt)
|
||||
}
|
||||
|
||||
// sign_digest sign a digest with private key.
|
||||
fn (pv PrivateKey) sign_digest(digest []u8) ![]u8 {
|
||||
if digest.len == 0 {
|
||||
return error('Digest cannot be null or empty')
|
||||
}
|
||||
mut sig_len := u32(0)
|
||||
sig_size := C.ECDSA_size(pv.key)
|
||||
sig := unsafe { malloc(int(sig_size)) }
|
||||
res := C.ECDSA_sign(0, digest.data, digest.len, sig, &sig_len, pv.key)
|
||||
if res != 1 {
|
||||
unsafe { free(sig) }
|
||||
return error('Failed to sign digest')
|
||||
}
|
||||
signed_data := unsafe { sig.vbytes(int(sig_len)) }
|
||||
unsafe { free(sig) }
|
||||
return signed_data.clone()
|
||||
}
|
||||
|
||||
// bytes represent private key as bytes.
|
||||
pub fn (pv PrivateKey) bytes() ![]u8 {
|
||||
bn := C.BN_new()
|
||||
@ -325,70 +278,17 @@ pub fn (pv PrivateKey) seed() ![]u8 {
|
||||
pub fn (pv PrivateKey) public_key() !PublicKey {
|
||||
// Check if EVP_PKEY opaque was availables or not.
|
||||
// TODO: removes this check when its ready
|
||||
if pv.evpkey != unsafe { nil } {
|
||||
bo := C.BIO_new(C.BIO_s_mem())
|
||||
n := C.i2d_PUBKEY_bio(bo, pv.evpkey)
|
||||
assert n != 0
|
||||
// stores this bio as another key
|
||||
pbkey := C.d2i_PUBKEY_bio(bo, 0)
|
||||
|
||||
// TODO: removes this when its ready to obsolete.
|
||||
eckey := C.EVP_PKEY_get1_EC_KEY(pbkey)
|
||||
if eckey == 0 {
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_free(pbkey)
|
||||
C.BIO_free_all(bo)
|
||||
return error('EVP_PKEY_get1_EC_KEY failed')
|
||||
}
|
||||
C.BIO_free_all(bo)
|
||||
bo := C.BIO_new(C.BIO_s_mem())
|
||||
n := C.i2d_PUBKEY_bio(bo, pv.evpkey)
|
||||
assert n != 0
|
||||
// stores this bio as another key
|
||||
pbkey := C.d2i_PUBKEY_bio(bo, 0)
|
||||
|
||||
C.BIO_free_all(bo)
|
||||
|
||||
return PublicKey{
|
||||
evpkey: pbkey
|
||||
key: eckey
|
||||
}
|
||||
}
|
||||
// Otherwise, use the old EC_KEY opaque.
|
||||
// TODO: removes this when its ready to obsolete
|
||||
//
|
||||
// There are some issues concerned when returning PublicKey directly using underlying
|
||||
// `PrivateKey.key`. This private key containing sensitive information inside it, so return
|
||||
// this without care maybe can lead to some serious security impact.
|
||||
// See https://discord.com/channels/592103645835821068/592320321995014154/1329261267965448253
|
||||
// So, we instead return a new EC_KEY opaque based information availables on private key object
|
||||
// without private key bits has been set on this new opaque.
|
||||
group := voidptr(C.EC_KEY_get0_group(pv.key))
|
||||
if group == 0 {
|
||||
return error('Failed to load group from priv_key')
|
||||
}
|
||||
nid := C.EC_GROUP_get_curve_name(group)
|
||||
if nid != nid_prime256v1 && nid != nid_secp384r1 && nid != nid_secp521r1 && nid != nid_secp256k1 {
|
||||
return error('Get unsupported curve nid')
|
||||
}
|
||||
// get public key point from private key opaque
|
||||
pubkey_point := voidptr(C.EC_KEY_get0_public_key(pv.key))
|
||||
if pubkey_point == 0 {
|
||||
// C.EC_POINT_free(pubkey_point)
|
||||
// todo: maybe its not set, just calculates new one
|
||||
return error('Failed to get public key BIGNUM')
|
||||
}
|
||||
// creates a new EC_KEY opaque based on the same NID with private key and
|
||||
// sets public key on it.
|
||||
pub_key := C.EC_KEY_new_by_curve_name(nid)
|
||||
np := C.EC_KEY_set_public_key(pub_key, pubkey_point)
|
||||
if np != 1 {
|
||||
// C.EC_POINT_free(pubkey_point)
|
||||
C.EC_KEY_free(pub_key)
|
||||
return error('Failed to set public key')
|
||||
}
|
||||
// performs explicit check
|
||||
chk := C.EC_KEY_check_key(pub_key)
|
||||
if chk == 0 {
|
||||
C.EC_KEY_free(pub_key)
|
||||
return error('EC_KEY_check_key failed')
|
||||
}
|
||||
// OK ?
|
||||
return PublicKey{
|
||||
key: pub_key
|
||||
evpkey: pbkey
|
||||
}
|
||||
}
|
||||
|
||||
@ -397,229 +297,44 @@ pub fn (pv PrivateKey) public_key() !PublicKey {
|
||||
// - whether both of private keys lives under the same group (curve),
|
||||
// - compares if two private key bytes was equal.
|
||||
pub fn (priv_key PrivateKey) equal(other PrivateKey) bool {
|
||||
if priv_key.evpkey != unsafe { nil } && other.evpkey != unsafe { nil } {
|
||||
eq := C.EVP_PKEY_eq(voidptr(priv_key.evpkey), voidptr(other.evpkey))
|
||||
return eq == 1
|
||||
}
|
||||
// TODO: remove this when its ready
|
||||
group1 := voidptr(C.EC_KEY_get0_group(priv_key.key))
|
||||
group2 := voidptr(C.EC_KEY_get0_group(other.key))
|
||||
ctx := C.BN_CTX_new()
|
||||
if ctx == 0 {
|
||||
return false
|
||||
}
|
||||
defer {
|
||||
C.BN_CTX_free(ctx)
|
||||
}
|
||||
gres := C.EC_GROUP_cmp(group1, group2, ctx)
|
||||
// Its lives on the same group
|
||||
if gres == 0 {
|
||||
bn1 := voidptr(C.EC_KEY_get0_private_key(priv_key.key))
|
||||
bn2 := voidptr(C.EC_KEY_get0_private_key(other.key))
|
||||
res := C.BN_cmp(bn1, bn2)
|
||||
return res == 0
|
||||
}
|
||||
return false
|
||||
eq := C.EVP_PKEY_eq(voidptr(priv_key.evpkey), voidptr(other.evpkey))
|
||||
return eq == 1
|
||||
}
|
||||
|
||||
// free clears out allocated memory for PrivateKey
|
||||
// Dont use PrivateKey after calling `.free()`
|
||||
pub fn (pv &PrivateKey) free() {
|
||||
C.EC_KEY_free(pv.key)
|
||||
C.EVP_PKEY_free(pv.evpkey)
|
||||
}
|
||||
|
||||
// PublicKey represents ECDSA public key for verifying message.
|
||||
pub struct PublicKey {
|
||||
// The new high level of keypair opaque, set to nil now.
|
||||
evpkey &C.EVP_PKEY = unsafe { nil }
|
||||
// Remove this when its fully obsoleted by the new one.
|
||||
key &C.EC_KEY
|
||||
// The new high level of keypair opaque
|
||||
evpkey &C.EVP_PKEY
|
||||
}
|
||||
|
||||
// verify verifies a message with the signature are valid with public key provided .
|
||||
// You should provide it with the same SignerOpts used with the `.sign()` call.
|
||||
// or verify would fail (false).
|
||||
pub fn (pb PublicKey) verify(message []u8, sig []u8, opt SignerOpts) !bool {
|
||||
if pb.evpkey != unsafe { nil } {
|
||||
digest := calc_digest_with_evpkey(pb.evpkey, message, opt)!
|
||||
return verify_signature(pb.evpkey, sig, digest)
|
||||
}
|
||||
digest := calc_digest_with_eckey(pb.key, message, opt)!
|
||||
res := C.ECDSA_verify(0, digest.data, digest.len, sig.data, sig.len, pb.key)
|
||||
if res == -1 {
|
||||
return error('Failed to verify signature')
|
||||
}
|
||||
return res == 1
|
||||
digest := calc_digest_with_evpkey(pb.evpkey, message, opt)!
|
||||
return verify_signature(pb.evpkey, sig, digest)
|
||||
}
|
||||
|
||||
// Compare two public keys
|
||||
pub fn (pub_key PublicKey) equal(other PublicKey) bool {
|
||||
if pub_key.evpkey != unsafe { nil } && other.evpkey != unsafe { nil } {
|
||||
eq := C.EVP_PKEY_eq(voidptr(pub_key.evpkey), voidptr(other.evpkey))
|
||||
return eq == 1
|
||||
}
|
||||
// TODO: check validity of the group
|
||||
group1 := voidptr(C.EC_KEY_get0_group(pub_key.key))
|
||||
group2 := voidptr(C.EC_KEY_get0_group(other.key))
|
||||
if group1 == 0 || group2 == 0 {
|
||||
return false
|
||||
}
|
||||
ctx := C.BN_CTX_new()
|
||||
if ctx == 0 {
|
||||
return false
|
||||
}
|
||||
defer {
|
||||
C.BN_CTX_free(ctx)
|
||||
}
|
||||
gres := C.EC_GROUP_cmp(group1, group2, ctx)
|
||||
// Its lives on the same group
|
||||
if gres == 0 {
|
||||
point1 := voidptr(C.EC_KEY_get0_public_key(pub_key.key))
|
||||
point2 := voidptr(C.EC_KEY_get0_public_key(other.key))
|
||||
if point1 == 0 || point2 == 0 {
|
||||
return false
|
||||
}
|
||||
res := C.EC_POINT_cmp(group1, point1, point2, ctx)
|
||||
return res == 0
|
||||
}
|
||||
|
||||
return false
|
||||
eq := C.EVP_PKEY_eq(voidptr(pub_key.evpkey), voidptr(other.evpkey))
|
||||
return eq == 1
|
||||
}
|
||||
|
||||
// free clears out allocated memory for PublicKey.
|
||||
// Dont use PublicKey after calling `.free()`
|
||||
pub fn (pb &PublicKey) free() {
|
||||
C.EC_KEY_free(pb.key)
|
||||
C.EVP_PKEY_free(pb.evpkey)
|
||||
}
|
||||
|
||||
// Helpers
|
||||
//
|
||||
// new_curve creates a new empty curve based on curve NID, default to prime256v1 (or secp256r1).
|
||||
fn new_curve(opt CurveOptions) &C.EC_KEY {
|
||||
mut nid := nid_prime256v1
|
||||
match opt.nid {
|
||||
.prime256v1 {
|
||||
// do nothing
|
||||
}
|
||||
.secp384r1 {
|
||||
nid = nid_secp384r1
|
||||
}
|
||||
.secp521r1 {
|
||||
nid = nid_secp521r1
|
||||
}
|
||||
.secp256k1 {
|
||||
nid = nid_secp256k1
|
||||
}
|
||||
}
|
||||
return C.EC_KEY_new_by_curve_name(nid)
|
||||
}
|
||||
|
||||
// Gets recommended hash function of the key.
|
||||
// Its purposes for hashing message to be signed.
|
||||
fn recommended_hash(key &C.EC_KEY) !crypto.Hash {
|
||||
group := voidptr(C.EC_KEY_get0_group(key))
|
||||
if group == 0 {
|
||||
return error('Unable to load group')
|
||||
}
|
||||
// gets the bits size of private key group
|
||||
num_bits := C.EC_GROUP_get_degree(group)
|
||||
match true {
|
||||
// use sha256
|
||||
num_bits <= 256 {
|
||||
return .sha256
|
||||
}
|
||||
num_bits > 256 && num_bits <= 384 {
|
||||
return .sha384
|
||||
}
|
||||
// TODO: what hash should be used if the size is over > 512 bits
|
||||
num_bits > 384 {
|
||||
return .sha512
|
||||
}
|
||||
else {
|
||||
return error('Unsupported bits size')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_digest_with_recommended_hash(key &C.EC_KEY, msg []u8) ![]u8 {
|
||||
h := recommended_hash(key)!
|
||||
match h {
|
||||
.sha256 {
|
||||
return sha256.sum256(msg)
|
||||
}
|
||||
.sha384 {
|
||||
return sha512.sum384(msg)
|
||||
}
|
||||
.sha512 {
|
||||
return sha512.sum512(msg)
|
||||
}
|
||||
else {
|
||||
return error('Unsupported hash')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calc_digest_with_eckey tries to calculates digest (hash) of the message based on options provided.
|
||||
// If the options was .with_no_hash, its has the same behaviour with .with_recommended_hash.
|
||||
fn calc_digest_with_eckey(key &C.EC_KEY, message []u8, opt SignerOpts) ![]u8 {
|
||||
if message.len == 0 {
|
||||
return error('null-length messages')
|
||||
}
|
||||
// check key size bits
|
||||
group := voidptr(C.EC_KEY_get0_group(key))
|
||||
if group == 0 {
|
||||
return error('fail to load group')
|
||||
}
|
||||
num_bits := C.EC_GROUP_get_degree(group)
|
||||
key_size := (num_bits + 7) / 8
|
||||
// we're working on mutable copy of SignerOpts, with some issues when make it as a mutable.
|
||||
// ie, declaring a mutable parameter that accepts a struct with the `@[params]` attribute is not allowed.
|
||||
mut cfg := opt
|
||||
match cfg.hash_config {
|
||||
// There are issues when your message size was exceeds the current key size with .with_no_hash options.
|
||||
// See https://discord.com/channels/592103645835821068/592114487759470596/1334319744098107423
|
||||
// So, we aliased it into .with_recommended_hash
|
||||
.with_no_hash, .with_recommended_hash {
|
||||
return calc_digest_with_recommended_hash(key, message)!
|
||||
}
|
||||
.with_custom_hash {
|
||||
if !cfg.allow_custom_hash {
|
||||
return error('custom hash was not allowed, set it into true')
|
||||
}
|
||||
if cfg.custom_hash == unsafe { nil } {
|
||||
return error('Custom hasher was not defined')
|
||||
}
|
||||
// If current Private Key size is bigger then current hash output size,
|
||||
// by default its not allowed, until set the allow_smaller_size into true
|
||||
if key_size > cfg.custom_hash.size() {
|
||||
if !cfg.allow_smaller_size {
|
||||
return error('Hash into smaller size than current key size was not allowed')
|
||||
}
|
||||
}
|
||||
// we need to reset the custom hash before writes message
|
||||
cfg.custom_hash.reset()
|
||||
_ := cfg.custom_hash.write(message)!
|
||||
digest := cfg.custom_hash.sum([]u8{})
|
||||
// NOTES:
|
||||
// NIST FIPS 186-4 at the end of section 6.4 states that:
|
||||
// When the length of the output of the hash function is greater than the bit length of n,
|
||||
// then the leftmost n bits of the hash function output block shall be used in any calculation
|
||||
// using the hash function output during the generation or verification of a digital signature
|
||||
// with output of custom_hash was bigger than bit length (key size)
|
||||
// TODO:
|
||||
// Maybe better to pick up only required bytes from digest, ie,
|
||||
// out := digest[..key_size].clone()
|
||||
// unsafe { digest.free() }
|
||||
// return out
|
||||
// Currently, just boildown to the caller
|
||||
return digest
|
||||
}
|
||||
}
|
||||
return error('Not should be here')
|
||||
}
|
||||
|
||||
// calc_digest_with_evpkey get the digest of the messages under the EVP_PKEY and options
|
||||
fn calc_digest_with_evpkey(key &C.EVP_PKEY, message []u8, opt SignerOpts) ![]u8 {
|
||||
if message.len == 0 {
|
||||
@ -789,7 +504,7 @@ fn evpkey_from_seed(seed []u8, opt CurveOptions) !&C.EVP_PKEY {
|
||||
}
|
||||
// Build EC_POINT from this BIGNUM and gets bytes represantion of this point
|
||||
// in uncompressed format.
|
||||
point := ec_point_mult(group, bn)!
|
||||
point := ec_point_mult(group, bn) or { return err }
|
||||
pub_bytes := point_2_buf(group, point, point_conversion_uncompressed)!
|
||||
|
||||
// Lets build params builder
|
||||
@ -797,7 +512,8 @@ fn evpkey_from_seed(seed []u8, opt CurveOptions) !&C.EVP_PKEY {
|
||||
assert param_bld != 0
|
||||
|
||||
// push the group, private and public key bytes infos into the builder
|
||||
n := C.OSSL_PARAM_BLD_push_utf8_string(param_bld, c'group', opt.nid.str().str, 0)
|
||||
n := C.OSSL_PARAM_BLD_push_utf8_string(param_bld, c'group', voidptr(opt.nid.str().str),
|
||||
0)
|
||||
m := C.OSSL_PARAM_BLD_push_BN(param_bld, c'priv', bn)
|
||||
o := C.OSSL_PARAM_BLD_push_octet_string(param_bld, c'pub', pub_bytes.data, pub_bytes.len)
|
||||
if n <= 0 || m <= 0 || o <= 0 {
|
||||
|
@ -34,10 +34,10 @@ fn test_ecdsa_signing_with_recommended_hash_options() {
|
||||
}
|
||||
|
||||
fn test_generate_key() ! {
|
||||
// Test key generation
|
||||
// Test key generation with high level opaque
|
||||
pub_key, priv_key := generate_key() or { panic(err) }
|
||||
assert pub_key.key != unsafe { nil }
|
||||
assert priv_key.key != unsafe { nil }
|
||||
assert pub_key.evpkey != unsafe { nil }
|
||||
assert priv_key.evpkey != unsafe { nil }
|
||||
|
||||
priv_key.free()
|
||||
pub_key.free()
|
||||
@ -176,7 +176,7 @@ fn test_different_keys_not_equal() ! {
|
||||
fn test_private_key_new() ! {
|
||||
priv_key := PrivateKey.new()!
|
||||
assert priv_key.ks_flag == .fixed
|
||||
size := ec_key_size(priv_key.key)!
|
||||
size := evp_key_size(priv_key.evpkey)!
|
||||
assert size == 32
|
||||
pubkey := priv_key.public_key()!
|
||||
|
||||
|
@ -37,26 +37,34 @@ pub fn pubkey_from_bytes(bytes []u8) !PublicKey {
|
||||
return error('Get an nid of non ecPublicKey')
|
||||
}
|
||||
|
||||
eckey := C.EVP_PKEY_get1_EC_KEY(pub_key)
|
||||
if eckey == 0 {
|
||||
C.EC_KEY_free(eckey)
|
||||
return error('Failed to get ec key')
|
||||
// check the key
|
||||
pctx := C.EVP_PKEY_CTX_new(pub_key, 0)
|
||||
if pctx == 0 {
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
C.EVP_PKEY_free(pub_key)
|
||||
return error('EVP_PKEY_CTX_new failed')
|
||||
}
|
||||
// check the group for the supported curve(s)
|
||||
group := voidptr(C.EC_KEY_get0_group(eckey))
|
||||
if group == 0 {
|
||||
C.EC_GROUP_free(group)
|
||||
return error('Failed to load group from key')
|
||||
// performs public-only evpkey check
|
||||
nck := C.EVP_PKEY_public_check(pctx)
|
||||
if nck != 1 {
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
C.EVP_PKEY_free(pub_key)
|
||||
return error('EVP_PKEY_check failed')
|
||||
}
|
||||
nidgroup := C.EC_GROUP_get_curve_name(group)
|
||||
if nidgroup != nid_prime256v1 && nidgroup != nid_secp384r1 && nidgroup != nid_secp521r1
|
||||
&& nidgroup != nid_secp256k1 {
|
||||
// Matching the supported group
|
||||
gn := key_group_name(pub_key)!
|
||||
// TODO: using shortname constant
|
||||
if gn != 'secp256k1' && gn != 'secp384r1' && gn != 'secp521r1' && gn != 'prime256v1' {
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
C.EVP_PKEY_free(pub_key)
|
||||
return error('Unsupported group')
|
||||
}
|
||||
C.EVP_PKEY_free(pub_key)
|
||||
// Cleans up
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
|
||||
// Its OK to return
|
||||
return PublicKey{
|
||||
key: eckey
|
||||
evpkey: pub_key
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,10 +91,13 @@ pub fn pubkey_from_string(s string) !PublicKey {
|
||||
mut evpkey := C.EVP_PKEY_new()
|
||||
bo := C.BIO_new(C.BIO_s_mem())
|
||||
if bo == 0 {
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
C.BIO_free_all(bo)
|
||||
return error('Failed to create BIO_new')
|
||||
}
|
||||
n := C.BIO_write(bo, s.str, s.len)
|
||||
if n <= 0 {
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
C.BIO_free_all(bo)
|
||||
return error('BIO_write failed')
|
||||
}
|
||||
@ -104,33 +115,38 @@ pub fn pubkey_from_string(s string) !PublicKey {
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Get an nid of non ecPublicKey')
|
||||
}
|
||||
// Gets the ec key
|
||||
eckey := C.EVP_PKEY_get1_EC_KEY(evpkey)
|
||||
if eckey == 0 {
|
||||
// check the key
|
||||
pctx := C.EVP_PKEY_CTX_new(evpkey, 0)
|
||||
if pctx == 0 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Failed to get ec key')
|
||||
return error('EVP_PKEY_CTX_new failed')
|
||||
}
|
||||
// check the group for the supported curve(s)
|
||||
if !is_valid_supported_group(eckey) {
|
||||
// performs public-only evpkey check
|
||||
nck := C.EVP_PKEY_public_check(pctx)
|
||||
if nck != 1 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('EVP_PKEY_check failed')
|
||||
}
|
||||
// Matching the supported group
|
||||
gn := key_group_name(evpkey)!
|
||||
// TODO: using shortname constant
|
||||
if gn != 'secp256k1' && gn != 'secp384r1' && gn != 'secp521r1' && gn != 'prime256v1' {
|
||||
C.BIO_free_all(bo)
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Unsupported group')
|
||||
}
|
||||
chk := C.EC_KEY_check_key(eckey)
|
||||
if chk == 0 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('EC_KEY_check_key failed')
|
||||
}
|
||||
// Cleans up
|
||||
C.BIO_free_all(bo)
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
|
||||
// Its OK to return
|
||||
return PublicKey{
|
||||
evpkey: evpkey
|
||||
key: eckey
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,11 +162,14 @@ pub fn privkey_from_string(s string) !PrivateKey {
|
||||
mut evpkey := C.EVP_PKEY_new()
|
||||
bo := C.BIO_new(C.BIO_s_mem())
|
||||
if bo == 0 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Failed to create BIO_new')
|
||||
}
|
||||
n := C.BIO_write(bo, s.str, s.len)
|
||||
if n <= 0 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('BIO_write failed')
|
||||
}
|
||||
evpkey = C.PEM_read_bio_PrivateKey(bo, &evpkey, 0, 0)
|
||||
@ -168,66 +187,61 @@ pub fn privkey_from_string(s string) !PrivateKey {
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Get an nid of non ecPublicKey')
|
||||
}
|
||||
|
||||
eckey := C.EVP_PKEY_get1_EC_KEY(evpkey)
|
||||
if eckey == 0 {
|
||||
pctx := C.EVP_PKEY_CTX_new(evpkey, 0)
|
||||
if pctx == 0 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Failed to get ec key')
|
||||
return error('EVP_PKEY_CTX_new failed')
|
||||
}
|
||||
// check the group for the supported curve(s)
|
||||
if !is_valid_supported_group(eckey) {
|
||||
// performs evpkey check
|
||||
nck := C.EVP_PKEY_check(pctx)
|
||||
if nck != 1 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('EVP_PKEY_check failed')
|
||||
}
|
||||
// Matching the supported group
|
||||
gn := key_group_name(evpkey)!
|
||||
// TODO: using shortname constant
|
||||
if gn != 'secp256k1' && gn != 'secp384r1' && gn != 'secp521r1' && gn != 'prime256v1' {
|
||||
C.BIO_free_all(bo)
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Unsupported group')
|
||||
}
|
||||
|
||||
chk := C.EC_KEY_check_key(eckey)
|
||||
if chk == 0 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('EC_KEY_check_key failed')
|
||||
}
|
||||
ksize := ec_key_size(eckey)!
|
||||
|
||||
// Cleans up
|
||||
C.BIO_free_all(bo)
|
||||
C.EVP_PKEY_CTX_free(pctx)
|
||||
|
||||
// Its OK to return
|
||||
return PrivateKey{
|
||||
evpkey: evpkey
|
||||
key: eckey
|
||||
ks_flag: .fixed
|
||||
ks_size: ksize
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
//
|
||||
// is_valid_supported_group checks whether this eckey has valid group of supported curve.
|
||||
@[inline]
|
||||
fn is_valid_supported_group(eckey &C.EC_KEY) bool {
|
||||
group := voidptr(C.EC_KEY_get0_group(eckey))
|
||||
if group == 0 {
|
||||
return false
|
||||
}
|
||||
nidgroup := C.EC_GROUP_get_curve_name(group)
|
||||
if nidgroup == nid_prime256v1 || nidgroup == nid_secp384r1 || nidgroup == nid_secp521r1
|
||||
|| nidgroup == nid_secp256k1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// key_size get the key size of this ec key
|
||||
fn ec_key_size(ec_key &C.EC_KEY) !int {
|
||||
group := voidptr(C.EC_KEY_get0_group(ec_key))
|
||||
if group == 0 {
|
||||
return error('Unable to load group')
|
||||
}
|
||||
num_bits := C.EC_GROUP_get_degree(group)
|
||||
// evp_key_size get the key size of this ec key
|
||||
fn evp_key_size(key &C.EVP_PKEY) !int {
|
||||
num_bits := C.EVP_PKEY_get_bits(key)
|
||||
key_size := (num_bits + 7) / 8
|
||||
|
||||
return key_size
|
||||
}
|
||||
|
||||
const default_groupname_size = 25 // short name commonly only take 10-15 length
|
||||
|
||||
// key_group_name returns underlying group name of the key as a string.
|
||||
fn key_group_name(key &C.EVP_PKEY) !string {
|
||||
gname := []u8{len: default_groupname_size}
|
||||
gname_len := usize(0)
|
||||
mut s := C.EVP_PKEY_get_group_name(key, gname.data, u32(gname.len), &gname_len)
|
||||
if s == 0 {
|
||||
unsafe { gname.free() }
|
||||
return error('fail to get group name')
|
||||
}
|
||||
group := gname[..gname_len].bytestr()
|
||||
unsafe { gname.free() }
|
||||
return group
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user