mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42:26 +03:00
324 lines
9.2 KiB
V
324 lines
9.2 KiB
V
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
|
|
|
|
@[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
|
|
|
|
// EVP_PKEY_free(EVP_PKEY *key);
|
|
fn C.EVP_PKEY_free(key &C.EVP_PKEY)
|
|
|
|
// EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey);
|
|
fn C.EVP_PKEY_get1_EC_KEY(pkey &C.EVP_PKEY) &C.EC_KEY
|
|
|
|
// EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp, long length);
|
|
fn C.d2i_PUBKEY(k &&C.EVP_PKEY, pp &&u8, length u32) &C.EVP_PKEY
|
|
|
|
// point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *key);
|
|
fn C.EC_KEY_get_conv_form(k &C.EC_KEY) int
|
|
|
|
// EC_GROUP_get_degree
|
|
fn C.EC_GROUP_get_degree(g &C.EC_GROUP) int
|
|
|
|
// const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key);
|
|
fn C.EC_KEY_get0_public_key(key &C.EC_KEY) &C.EC_POINT
|
|
|
|
// size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point, point_conversion_form_t form, uint8_t *buf, size_t max_out, BN_CTX *ctx);
|
|
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
|
|
|
|
// int EVP_PKEY_get_base_id(const EVP_PKEY *pkey);
|
|
fn C.EVP_PKEY_base_id(key &C.EVP_PKEY) int
|
|
|
|
// int EC_GROUP_get_curve_name(const EC_GROUP *group);
|
|
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.
|
|
// Typically, you can load the bytes from pem formatted of ecdsa public key.
|
|
//
|
|
// Examples:
|
|
// ```codeblock
|
|
// import crypto.pem
|
|
// import crypto.ecdsa
|
|
//
|
|
// const pubkey_sample = '-----BEGIN PUBLIC KEY-----
|
|
// MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+P3rhFkT1fXHYbY3CpcBdh6xTC74MQFx
|
|
// cftNVD3zEPVzo//OalIVatY162ksg8uRWBdvFFuHZ9OMVXkbjwWwhcXP7qmI9rOS
|
|
// LR3AGUldy+bBpV2nT306qCIwgUAMeOJP
|
|
// -----END PUBLIC KEY-----'
|
|
//
|
|
// block, _ := pem.decode(pubkey_sample) or { panic(err) }
|
|
// pubkey := ecdsa.pubkey_from_bytes(block.data)!
|
|
// ```
|
|
pub fn pubkey_from_bytes(bytes []u8) !PublicKey {
|
|
if bytes.len == 0 {
|
|
return error('Invalid bytes')
|
|
}
|
|
mut pub_key := C.EVP_PKEY_new()
|
|
pub_key = C.d2i_PUBKEY(&pub_key, voidptr(&bytes.data), bytes.len)
|
|
if pub_key == 0 {
|
|
C.EVP_PKEY_free(pub_key)
|
|
return error('Error loading public key')
|
|
}
|
|
// Get the NID of this pubkey, and check if the pubkey object was
|
|
// have the correct NID of ec public key type, ie, NID_X9_62_id_ecPublicKey
|
|
nid := C.EVP_PKEY_base_id(pub_key)
|
|
if nid != nid_ec_publickey {
|
|
C.EVP_PKEY_free(pub_key)
|
|
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 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')
|
|
}
|
|
nidgroup := C.EC_GROUP_get_curve_name(group)
|
|
if nidgroup != nid_prime256v1 && nidgroup != nid_secp384r1 && nidgroup != nid_secp521r1
|
|
&& nidgroup != nid_secp256k1 {
|
|
return error('Unsupported group')
|
|
}
|
|
C.EVP_PKEY_free(pub_key)
|
|
// Its OK to return
|
|
return PublicKey{
|
|
key: eckey
|
|
}
|
|
}
|
|
|
|
// 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')
|
|
}
|
|
|
|
group := voidptr(C.EC_KEY_get0_group(pbk.key))
|
|
num_bits := C.EC_GROUP_get_degree(group)
|
|
// 1 byte of conversion format || x || y of EC_POINT
|
|
num_bytes := 1 + 2 * ((num_bits + 7) / 8)
|
|
|
|
ctx := C.BN_CTX_new()
|
|
defer {
|
|
C.BN_CTX_free(ctx)
|
|
}
|
|
|
|
if ctx == 0 {
|
|
C.EC_POINT_free(point)
|
|
C.BN_CTX_free(ctx)
|
|
return error('Failed to create BN_CTX')
|
|
}
|
|
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.
|
|
conv_form := C.EC_KEY_get_conv_form(pbk.key)
|
|
if conv_form !in [2, 3, 4, 6] {
|
|
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
|
|
}
|