mirror of
https://github.com/vlang/v.git
synced 2025-08-03 09:47:15 -04:00
crypto.ecdsa: migrate generate_key and simplify it (part 3) (#23662)
This commit is contained in:
parent
dd063faf65
commit
f3493e126a
@ -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]
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user