crypto.ecdsa: migrate generate_key and simplify it (part 3) (#23662)

This commit is contained in:
blackshirt 2025-02-09 21:23:11 +07:00 committed by GitHub
parent dd063faf65
commit f3493e126a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 101 additions and 107 deletions

View File

@ -18,10 +18,14 @@ module ecdsa
#include <openssl/objects.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/x509.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
// The following header is available on OpenSSL 3.0, but not in OpenSSL 1.1.1f
//#include <openssl/core.h>
// The new opaque of public key pair high level API
@[typedef]
struct C.EVP_PKEY {}
@ -73,6 +77,8 @@ fn C.BIO_write(b &C.BIO, buf &u8, length int) int
fn C.PEM_read_bio_PrivateKey(bp &C.BIO, x &&C.EVP_PKEY, cb int, u &voidptr) &C.EVP_PKEY
fn C.PEM_read_bio_PUBKEY(bp &C.BIO, x &&C.EVP_PKEY, cb int, u &voidptr) &C.EVP_PKEY
fn C.d2i_PUBKEY(k &&C.EVP_PKEY, pp &&u8, length u32) &C.EVP_PKEY
fn C.i2d_PUBKEY_bio(bo &C.BIO, pkey &C.EVP_PKEY) int
fn C.d2i_PUBKEY_bio(bo &C.BIO, key &&C.EVP_PKEY) &C.EVP_PKEY
// Elliptic curve point related declarations.
@[typedef]

View File

@ -81,58 +81,18 @@ enum KeyFlag {
// generate_key generates a new key pair. If opt was not provided, its default to prime256v1 curve.
// If you want another curve, use in the following manner: `pubkey, pivkey := ecdsa.generate_key(nid: .secp384r1)!`
pub fn generate_key(opt CurveOptions) !(PublicKey, PrivateKey) {
// creates new empty key
ec_key := new_curve(opt)
if ec_key == 0 {
C.EC_KEY_free(ec_key)
return error('Failed to create new EC_KEY')
}
// we duplicate the empty ec_key and shares similiar curve infos
// and used this as public key
pbkey := C.EC_KEY_dup(ec_key)
if pbkey == 0 {
C.EC_KEY_free(ec_key)
C.EC_KEY_free(pbkey)
return error('Failed on EC_KEY_dup')
}
res := C.EC_KEY_generate_key(ec_key)
if res != 1 {
C.EC_KEY_free(ec_key)
C.EC_KEY_free(pbkey)
return error('Failed to generate EC_KEY')
}
// we take public key bits from above generated key
// and stored in duplicated public key object before.
pubkey_point := voidptr(C.EC_KEY_get0_public_key(ec_key))
if pubkey_point == 0 {
C.EC_POINT_free(pubkey_point)
C.EC_KEY_free(ec_key)
C.EC_KEY_free(pbkey)
return error('Failed to get public key BIGNUM')
}
np := C.EC_KEY_set_public_key(pbkey, pubkey_point)
if np != 1 {
C.EC_POINT_free(pubkey_point)
C.EC_KEY_free(ec_key)
C.EC_KEY_free(pbkey)
return error('Failed to set public key')
}
// when using default generate_key, its using underlying curve key size
// and discarded opt.fixed_size flag when its not set.
priv_key := PrivateKey{
key: ec_key
ks_flag: .fixed
}
pub_key := PublicKey{
key: pbkey
}
return pub_key, priv_key
// This can be simplified to just more simpler one
pv := PrivateKey.new(opt)!
pb := pv.public_key()!
return pb, pv
}
// new_key_from_seed creates a new private key from the seed bytes. If opt was not provided,
// its default to prime256v1 curve.
//
// Notes on the seed:
//
// You should make sure, the seed bytes come from a cryptographically secure random generator,
// likes the `crypto.rand` or other trusted sources.
// Internally, the seed size's would be checked to not exceed the key size of underlying curve,
@ -366,16 +326,17 @@ fn (priv_key PrivateKey) sign_message(message []u8) ![]u8 {
}
// bytes represent private key as bytes.
pub fn (priv_key PrivateKey) bytes() ![]u8 {
bn := voidptr(C.EC_KEY_get0_private_key(priv_key.key))
pub fn (pv PrivateKey) bytes() ![]u8 {
// This is the old one
bn := voidptr(C.EC_KEY_get0_private_key(pv.key))
if bn == 0 {
return error('Failed to get private key BIGNUM')
}
num_bytes := (C.BN_num_bits(bn) + 7) / 8
// Get the buffer size to store the seed.
size := if priv_key.ks_flag == .flexible {
size := if pv.ks_flag == .flexible {
// should be non zero
priv_key.ks_size
pv.ks_size
} else {
num_bytes
}
@ -390,19 +351,46 @@ pub fn (priv_key PrivateKey) bytes() ![]u8 {
// seed gets the seed (private key bytes). It will be deprecated.
// Use `PrivateKey.bytes()` instead.
@[deprecated: 'use PrivateKey.bytes() instead']
pub fn (priv_key PrivateKey) seed() ![]u8 {
return priv_key.bytes()
pub fn (pv PrivateKey) seed() ![]u8 {
return pv.bytes()
}
// Get the public key from private key.
pub fn (priv_key PrivateKey) public_key() !PublicKey {
// public_key gets the PublicKey from private key.
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)
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(priv_key.key))
group := voidptr(C.EC_KEY_get0_group(pv.key))
if group == 0 {
return error('Failed to load group from priv_key')
}
@ -411,7 +399,7 @@ pub fn (priv_key PrivateKey) public_key() !PublicKey {
return error('Get unsupported curve nid')
}
// get public key point from private key opaque
pubkey_point := voidptr(C.EC_KEY_get0_public_key(priv_key.key))
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
@ -652,8 +640,3 @@ fn calc_digest(key &C.EC_KEY, message []u8, opt SignerOpts) ![]u8 {
}
return error('Not should be here')
}
// Clear allocated memory for key
fn key_free(ec_key &C.EC_KEY) {
C.EC_KEY_free(ec_key)
}

View File

@ -12,8 +12,9 @@ fn test_ecdsa() {
is_valid := pub_key.verify(message, signature) or { panic(err) }
println('Signature valid: ${is_valid}')
assert is_valid
key_free(priv_key.key)
key_free(pub_key.key)
priv_key.free()
pub_key.free()
}
fn test_ecdsa_signing_with_recommended_hash_options() {
@ -27,9 +28,9 @@ fn test_ecdsa_signing_with_recommended_hash_options() {
// Verify the signature
is_valid := pub_key.verify(message, signature) or { panic(err) }
println('Signature valid: ${is_valid}')
key_free(pub_key.key)
key_free(priv_key.key)
assert is_valid
pub_key.free()
priv_key.free()
}
fn test_generate_key() ! {
@ -37,26 +38,27 @@ fn test_generate_key() ! {
pub_key, priv_key := generate_key() or { panic(err) }
assert pub_key.key != unsafe { nil }
assert priv_key.key != unsafe { nil }
key_free(priv_key.key)
key_free(pub_key.key)
priv_key.free()
pub_key.free()
}
fn test_new_key_from_seed() ! {
// Test generating a key from a seed
seed := [u8(1), 2, 3, 4, 5]
priv_key := new_key_from_seed(seed) or { panic(err) }
retrieved_seed := priv_key.seed() or { panic(err) }
retrieved_seed := priv_key.bytes() or { panic(err) }
assert seed == retrieved_seed
key_free(priv_key.key)
priv_key.free()
}
fn test_new_key_from_seed_with_leading_zeros_bytes() ! {
// Test generating a key from a seed
seed := [u8(0), u8(1), 2, 3, 4, 5]
priv_key := new_key_from_seed(seed) or { panic(err) }
retrieved_seed := priv_key.seed() or { panic(err) }
retrieved_seed := priv_key.bytes() or { panic(err) }
assert seed == retrieved_seed
key_free(priv_key.key)
priv_key.free()
}
fn test_sign_and_verify() ! {
@ -66,17 +68,18 @@ fn test_sign_and_verify() ! {
signature := priv_key.sign(message) or { panic(err) }
is_valid := pub_key.verify(message, signature) or { panic(err) }
assert is_valid
key_free(priv_key.key)
key_free(pub_key.key)
priv_key.free()
pub_key.free()
}
fn test_seed() ! {
// Test retrieving the seed from a private key
pub_key, priv_key := generate_key() or { panic(err) }
seed := priv_key.seed() or { panic(err) }
seed := priv_key.bytes() or { panic(err) }
assert seed.len > 0
key_free(priv_key.key)
key_free(pub_key.key)
priv_key.free()
pub_key.free()
}
fn test_public_key() ! {
@ -85,34 +88,36 @@ fn test_public_key() ! {
pub_key1 := priv_key.public_key() or { panic(err) }
pub_key2, privkk := generate_key() or { panic(err) }
assert !pub_key1.equal(pub_key2)
key_free(pubkk.key)
key_free(privkk.key)
key_free(priv_key.key)
key_free(pub_key1.key)
key_free(pub_key2.key)
pubkk.free()
privkk.free()
priv_key.free()
pub_key1.free()
pub_key2.free()
}
fn test_private_key_equal() ! {
// Test private key equality
pbk, priv_key1 := generate_key() or { panic(err) }
seed := priv_key1.seed() or { panic(err) }
seed := priv_key1.bytes() or { panic(err) }
priv_key2 := new_key_from_seed(seed) or { panic(err) }
assert priv_key1.equal(priv_key2)
key_free(pbk.key)
key_free(priv_key1.key)
key_free(priv_key2.key)
pbk.free()
priv_key1.free()
priv_key2.free()
}
fn test_private_key_equality_on_different_curve() ! {
// default group
pbk, priv_key1 := generate_key() or { panic(err) }
seed := priv_key1.seed() or { panic(err) }
seed := priv_key1.bytes() or { panic(err) }
// using different group
priv_key2 := new_key_from_seed(seed, nid: .secp384r1) or { panic(err) }
assert !priv_key1.equal(priv_key2)
key_free(pbk.key)
key_free(priv_key1.key)
key_free(priv_key2.key)
pbk.free()
priv_key1.free()
priv_key2.free()
}
fn test_public_key_equal() ! {
@ -121,10 +126,10 @@ fn test_public_key_equal() ! {
pub_key1 := priv_key.public_key() or { panic(err) }
pub_key2 := priv_key.public_key() or { panic(err) }
assert pub_key1.equal(pub_key2)
key_free(pbk.key)
key_free(priv_key.key)
key_free(pub_key1.key)
key_free(pub_key2.key)
pbk.free()
priv_key.free()
pub_key1.free()
pub_key2.free()
}
fn test_sign_with_new_key_from_seed() ! {
@ -136,8 +141,8 @@ fn test_sign_with_new_key_from_seed() ! {
pub_key := priv_key.public_key() or { panic(err) }
is_valid := pub_key.verify(message, signature) or { panic(err) }
assert is_valid
key_free(priv_key.key)
key_free(pub_key.key)
priv_key.free()
pub_key.free()
}
fn test_invalid_signature() ! {
@ -148,13 +153,13 @@ fn test_invalid_signature() ! {
result := pub_key.verify(message, invalid_signature) or {
// Expecting verification to fail
assert err.msg() == 'Failed to verify signature'
key_free(pub_key.key)
key_free(pvk.key)
pub_key.free()
pvk.free()
return
}
assert !result
key_free(pub_key.key)
key_free(pvk.key)
pub_key.free()
pvk.free()
}
fn test_different_keys_not_equal() ! {
@ -162,10 +167,10 @@ fn test_different_keys_not_equal() ! {
pbk1, priv_key1 := generate_key() or { panic(err) }
pbk2, priv_key2 := generate_key() or { panic(err) }
assert !priv_key1.equal(priv_key2)
key_free(pbk1.key)
key_free(pbk2.key)
key_free(priv_key1.key)
key_free(priv_key2.key)
pbk1.free()
pbk2.free()
priv_key1.free()
priv_key2.free()
}
fn test_private_key_new() ! {
@ -181,7 +186,7 @@ fn test_private_key_new() ! {
assert is_valid
// new private key
seed := priv_key.seed()!
seed := priv_key.bytes()!
priv_key2 := new_key_from_seed(seed)!
pubkey2 := priv_key2.public_key()!
assert priv_key.equal(priv_key2)

View File

@ -38,7 +38,7 @@ fn test_load_pubkey_from_der_serialized_bytes() ! {
// expected signature was comes from hashed message with sha384
status_with_hashed := pbkey.verify(message_tobe_signed, expected_signature)!
assert status_with_hashed == true
key_free(pbkey.key)
pbkey.free()
}
fn test_for_pubkey_bytes() ! {
@ -50,8 +50,8 @@ fn test_for_pubkey_bytes() ! {
assert pvkey.seed()!.hex() == pv
pbkey := pvkey.public_key()!
assert pbkey.bytes()!.hex() == pb
key_free(pbkey.key)
key_free(pvkey.key)
pbkey.free()
pvkey.free()
}
// above pem-formatted private key read with