mirror of
https://github.com/vlang/v.git
synced 2025-09-14 15:02:33 +03:00
crypto.ecdsa: improve safety checking, unify signing (and verifying) api to accept options (#23463)
This commit is contained in:
parent
3c4878063e
commit
c2b7dbf9b4
8 changed files with 752 additions and 109 deletions
|
@ -3,6 +3,8 @@ module ecdsa
|
|||
#include <openssl/evp.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
// #define NID_X9_62_id_ecPublicKey 408
|
||||
const nid_ec_publickey = C.NID_X9_62_id_ecPublicKey
|
||||
|
@ -10,6 +12,12 @@ const nid_ec_publickey = C.NID_X9_62_id_ecPublicKey
|
|||
@[typedef]
|
||||
struct C.EVP_PKEY {}
|
||||
|
||||
@[typedef]
|
||||
struct C.BIO_METHOD {}
|
||||
|
||||
@[typedef]
|
||||
pub struct C.BIO {}
|
||||
|
||||
// EVP_PKEY *EVP_PKEY_new(void);
|
||||
fn C.EVP_PKEY_new() &C.EVP_PKEY
|
||||
|
||||
|
@ -41,6 +49,24 @@ fn C.EVP_PKEY_base_id(key &C.EVP_PKEY) int
|
|||
fn C.EC_GROUP_get_curve_name(g &C.EC_GROUP) int
|
||||
fn C.EC_GROUP_free(group &C.EC_GROUP)
|
||||
|
||||
// BIO * BIO_new(BIO_METHOD *type);
|
||||
fn C.BIO_new(t &C.BIO_METHOD) &C.BIO
|
||||
|
||||
// void BIO_free_all(BIO *a);
|
||||
fn C.BIO_free_all(a &C.BIO)
|
||||
|
||||
// BIO_METHOD * BIO_s_mem(void);
|
||||
fn C.BIO_s_mem() &C.BIO_METHOD
|
||||
|
||||
// int BIO_write(BIO *b, const void *buf, int len);
|
||||
fn C.BIO_write(b &C.BIO, buf &u8, length int) int
|
||||
|
||||
// EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u);
|
||||
fn C.PEM_read_bio_PrivateKey(bp &C.BIO, x &&C.EVP_PKEY, cb int, u &voidptr) &C.EVP_PKEY
|
||||
|
||||
// EVP_PKEY *PEM_read_bio_PUBKEY(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u);
|
||||
fn C.PEM_read_bio_PUBKEY(bp &C.BIO, x &&C.EVP_PKEY, cb int, u &voidptr) &C.EVP_PKEY
|
||||
|
||||
// pubkey_from_bytes loads ECDSA Public Key from bytes array.
|
||||
// The bytes of data should be a valid of ASN.1 DER serialized SubjectPublicKeyInfo structrue of RFC 5480.
|
||||
// Otherwise, its should an error.
|
||||
|
@ -49,6 +75,7 @@ fn C.EC_GROUP_free(group &C.EC_GROUP)
|
|||
// Examples:
|
||||
// ```codeblock
|
||||
// import crypto.pem
|
||||
// import crypto.ecdsa
|
||||
//
|
||||
// const pubkey_sample = '-----BEGIN PUBLIC KEY-----
|
||||
// MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+P3rhFkT1fXHYbY3CpcBdh6xTC74MQFx
|
||||
|
@ -57,7 +84,7 @@ fn C.EC_GROUP_free(group &C.EC_GROUP)
|
|||
// -----END PUBLIC KEY-----'
|
||||
//
|
||||
// block, _ := pem.decode(pubkey_sample) or { panic(err) }
|
||||
// pubkey := pubkey_from_bytes(block.data)!
|
||||
// pubkey := ecdsa.pubkey_from_bytes(block.data)!
|
||||
// ```
|
||||
pub fn pubkey_from_bytes(bytes []u8) !PublicKey {
|
||||
if bytes.len == 0 {
|
||||
|
@ -79,7 +106,7 @@ pub fn pubkey_from_bytes(bytes []u8) !PublicKey {
|
|||
|
||||
eckey := C.EVP_PKEY_get1_EC_KEY(pub_key)
|
||||
if eckey == 0 {
|
||||
key_free(eckey)
|
||||
C.EC_KEY_free(eckey)
|
||||
return error('Failed to get ec key')
|
||||
}
|
||||
// check the group for the supported curve(s)
|
||||
|
@ -100,9 +127,10 @@ pub fn pubkey_from_bytes(bytes []u8) !PublicKey {
|
|||
}
|
||||
}
|
||||
|
||||
// bytes gets the bytes of public key parts of this keypair.
|
||||
// bytes gets the bytes of public key.
|
||||
pub fn (pbk PublicKey) bytes() ![]u8 {
|
||||
point := voidptr(C.EC_KEY_get0_public_key(pbk.key))
|
||||
// defer { C.EC_POINT_free(point)}
|
||||
if point == 0 {
|
||||
C.EC_POINT_free(point)
|
||||
return error('Failed to get public key BIGNUM')
|
||||
|
@ -126,6 +154,7 @@ pub fn (pbk PublicKey) bytes() ![]u8 {
|
|||
mut buf := []u8{len: num_bytes}
|
||||
|
||||
// Get conversion format.
|
||||
//
|
||||
// The uncompressed form is indicated by 0x04 and the compressed form is indicated
|
||||
// by either 0x02 or 0x03, hybrid 0x06
|
||||
// The public key MUST be rejected if any other value is included in the first octet.
|
||||
|
@ -134,7 +163,162 @@ pub fn (pbk PublicKey) bytes() ![]u8 {
|
|||
return error('bad conversion format')
|
||||
}
|
||||
n := C.EC_POINT_point2oct(group, point, conv_form, buf.data, buf.len, ctx)
|
||||
|
||||
if n == 0 {
|
||||
return error('EC_POINT_point2oct failed')
|
||||
}
|
||||
// returns the clone of the buffer[..n]
|
||||
return buf[..n].clone()
|
||||
}
|
||||
|
||||
// pubkey_from_string loads a PublicKey from valid PEM-formatted string in s.
|
||||
pub fn pubkey_from_string(s string) !PublicKey {
|
||||
if s.len == 0 {
|
||||
return error('Null length string was not allowed')
|
||||
}
|
||||
mut evpkey := C.EVP_PKEY_new()
|
||||
bo := C.BIO_new(C.BIO_s_mem())
|
||||
if bo == 0 {
|
||||
return error('Failed to create BIO_new')
|
||||
}
|
||||
n := C.BIO_write(bo, s.str, s.len)
|
||||
if n <= 0 {
|
||||
C.BIO_free_all(bo)
|
||||
return error('BIO_write failed')
|
||||
}
|
||||
evpkey = C.PEM_read_bio_PUBKEY(bo, &evpkey, 0, 0)
|
||||
if evpkey == 0 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Error loading key')
|
||||
}
|
||||
// Get the NID of this key, and check if the key object was
|
||||
// have the correct NID of ec public key type, ie, NID_X9_62_id_ecPublicKey
|
||||
nid := C.EVP_PKEY_base_id(evpkey)
|
||||
if nid != nid_ec_publickey {
|
||||
C.BIO_free_all(bo)
|
||||
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 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Failed to get ec key')
|
||||
}
|
||||
// check the group for the supported curve(s)
|
||||
if !is_valid_supported_group(eckey) {
|
||||
C.BIO_free_all(bo)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Unsupported group')
|
||||
}
|
||||
chk := C.EC_KEY_check_key(eckey)
|
||||
if chk == 0 {
|
||||
C.EC_KEY_free(eckey)
|
||||
return error('EC_KEY_check_key failed')
|
||||
}
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
C.BIO_free_all(bo)
|
||||
// Its OK to return
|
||||
return PublicKey{
|
||||
key: eckey
|
||||
}
|
||||
}
|
||||
|
||||
// privkey_from_string loads a PrivateKey from valid PEM-formatted string in s.
|
||||
// Underlying wrapper support for old secg and pkcs8 private key format, but this was not heavily tested.
|
||||
// This routine does not support for the pkcs8 EncryptedPrivateKeyInfo format.
|
||||
// See [usage_test.v](https://github.com/vlang/v/blob/master/vlib/crypto/ecdsa/example/ecdsa_seed_test.v) file
|
||||
// for example of usage.
|
||||
pub fn privkey_from_string(s string) !PrivateKey {
|
||||
if s.len == 0 {
|
||||
return error('null string was not allowed')
|
||||
}
|
||||
mut evpkey := C.EVP_PKEY_new()
|
||||
bo := C.BIO_new(C.BIO_s_mem())
|
||||
if bo == 0 {
|
||||
return error('Failed to create BIO_new')
|
||||
}
|
||||
n := C.BIO_write(bo, s.str, s.len)
|
||||
if n <= 0 {
|
||||
C.BIO_free_all(bo)
|
||||
return error('BIO_write failed')
|
||||
}
|
||||
evpkey = C.PEM_read_bio_PrivateKey(bo, &evpkey, 0, 0)
|
||||
if evpkey == 0 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Error loading key')
|
||||
}
|
||||
|
||||
// Get the NID of this key, and check if the key object was
|
||||
// have the correct NID of ec public key type, ie, NID_X9_62_id_ecPublicKey
|
||||
nid := C.EVP_PKEY_base_id(evpkey)
|
||||
if nid != nid_ec_publickey {
|
||||
C.BIO_free_all(bo)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Get an nid of non ecPublicKey')
|
||||
}
|
||||
|
||||
eckey := C.EVP_PKEY_get1_EC_KEY(evpkey)
|
||||
if eckey == 0 {
|
||||
C.BIO_free_all(bo)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Failed to get ec key')
|
||||
}
|
||||
// check the group for the supported curve(s)
|
||||
if !is_valid_supported_group(eckey) {
|
||||
C.BIO_free_all(bo)
|
||||
C.EC_KEY_free(eckey)
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
return error('Unsupported group')
|
||||
}
|
||||
|
||||
chk := C.EC_KEY_check_key(eckey)
|
||||
if chk == 0 {
|
||||
C.EC_KEY_free(eckey)
|
||||
return error('EC_KEY_check_key failed')
|
||||
}
|
||||
ksize := ec_key_size(eckey)!
|
||||
|
||||
C.EVP_PKEY_free(evpkey)
|
||||
C.BIO_free_all(bo)
|
||||
|
||||
// Its OK to return
|
||||
return PrivateKey{
|
||||
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)
|
||||
key_size := (num_bits + 7) / 8
|
||||
return key_size
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue