mirror of
https://github.com/vlang/v.git
synced 2025-09-08 14:51:53 -04:00
crypto: ecdsa module (on top of openssl)
This commit is contained in:
parent
9c0a7e7955
commit
f7cc9d5daa
129
bench/crypto/ecdsa/ecdsa.c
Normal file
129
bench/crypto/ecdsa/ecdsa.c
Normal file
@ -0,0 +1,129 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define ITERATIONS 1000
|
||||
#define MESSAGE "Benchmark message"
|
||||
|
||||
// Function to calculate the difference in microseconds between two timevals
|
||||
long time_diff_microseconds(struct timeval start, struct timeval end) {
|
||||
long seconds = end.tv_sec - start.tv_sec;
|
||||
long useconds = end.tv_usec - start.tv_usec;
|
||||
return seconds * 1000000 + useconds;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int iterations = ITERATIONS;
|
||||
struct timeval start, end;
|
||||
long total_gen_time = 0;
|
||||
long total_sign_time = 0;
|
||||
long total_verify_time = 0;
|
||||
long avg_gen_time, avg_sign_time, avg_verify_time;
|
||||
|
||||
// Benchmarking key generation
|
||||
printf("Benchmarking key generation...\n");
|
||||
for(int i = 0; i < iterations; i++) {
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
// Create a new EC_KEY object with the P-256 curve
|
||||
EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (key == NULL) {
|
||||
fprintf(stderr, "Error creating EC_KEY object.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Generate the key
|
||||
if (EC_KEY_generate_key(key) != 1) {
|
||||
fprintf(stderr, "Error generating EC key.\n");
|
||||
EC_KEY_free(key);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
gettimeofday(&end, NULL);
|
||||
total_gen_time += time_diff_microseconds(start, end);
|
||||
|
||||
// Free the key
|
||||
EC_KEY_free(key);
|
||||
}
|
||||
avg_gen_time = total_gen_time / iterations;
|
||||
printf("Average key generation time: %ld µs\n", avg_gen_time);
|
||||
|
||||
// Generate a single key for signing and verification
|
||||
EC_KEY *priv_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (priv_key == NULL) {
|
||||
fprintf(stderr, "Error creating EC_KEY object.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (EC_KEY_generate_key(priv_key) != 1) {
|
||||
fprintf(stderr, "Error generating EC key.\n");
|
||||
EC_KEY_free(priv_key);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Prepare the message hash
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256((unsigned char*)MESSAGE, strlen(MESSAGE), hash);
|
||||
|
||||
// Benchmarking signing
|
||||
printf("Benchmarking signing...\n");
|
||||
for(int i = 0; i < iterations; i++) {
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
ECDSA_SIG *signature = ECDSA_do_sign(hash, SHA256_DIGEST_LENGTH, priv_key);
|
||||
if (signature == NULL) {
|
||||
fprintf(stderr, "Error signing message.\n");
|
||||
EC_KEY_free(priv_key);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
gettimeofday(&end, NULL);
|
||||
total_sign_time += time_diff_microseconds(start, end);
|
||||
|
||||
// Free the signature
|
||||
ECDSA_SIG_free(signature);
|
||||
}
|
||||
avg_sign_time = total_sign_time / iterations;
|
||||
printf("Average sign time: %ld µs\n", avg_sign_time);
|
||||
|
||||
// Create a signature for verification benchmarking
|
||||
ECDSA_SIG *signature = ECDSA_do_sign(hash, SHA256_DIGEST_LENGTH, priv_key);
|
||||
if (signature == NULL) {
|
||||
fprintf(stderr, "Error signing message for verification.\n");
|
||||
EC_KEY_free(priv_key);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Benchmarking verification
|
||||
printf("Benchmarking verification...\n");
|
||||
for(int i = 0; i < iterations; i++) {
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
int verify_status = ECDSA_do_verify(hash, SHA256_DIGEST_LENGTH, signature, priv_key);
|
||||
|
||||
gettimeofday(&end, NULL);
|
||||
total_verify_time += time_diff_microseconds(start, end);
|
||||
|
||||
if (verify_status != 1) {
|
||||
fprintf(stderr, "Signature verification failed.\n");
|
||||
ECDSA_SIG_free(signature);
|
||||
EC_KEY_free(priv_key);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
avg_verify_time = total_verify_time / iterations;
|
||||
printf("Average verify time: %ld µs\n", avg_verify_time);
|
||||
|
||||
// Cleanup
|
||||
ECDSA_SIG_free(signature);
|
||||
EC_KEY_free(priv_key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
63
bench/crypto/ecdsa/ecdsa.go
Normal file
63
bench/crypto/ecdsa/ecdsa.go
Normal file
@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
iterations := 1000
|
||||
|
||||
fmt.Println("Benchmarking key generation...")
|
||||
var totalGenTime int64
|
||||
for i := 0; i < iterations; i++ {
|
||||
start := time.Now()
|
||||
_, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
totalGenTime += time.Since(start).Microseconds()
|
||||
}
|
||||
avgGenTime := totalGenTime / int64(iterations)
|
||||
fmt.Printf("Average key generation time: %d µs\n", avgGenTime)
|
||||
|
||||
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
message := []byte("Benchmark message")
|
||||
|
||||
fmt.Println("Benchmarking signing...")
|
||||
var totalSignTime int64
|
||||
for i := 0; i < iterations; i++ {
|
||||
start := time.Now()
|
||||
_, _, err := ecdsa.Sign(rand.Reader, privKey, message)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
totalSignTime += time.Since(start).Microseconds()
|
||||
}
|
||||
avgSignTime := totalSignTime / int64(iterations)
|
||||
fmt.Printf("Average sign time: %d µs\n", avgSignTime)
|
||||
|
||||
r, s, err := ecdsa.Sign(rand.Reader, privKey, message)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pubKey := &privKey.PublicKey
|
||||
|
||||
fmt.Println("Benchmarking verification...")
|
||||
var totalVerifyTime int64
|
||||
for i := 0; i < iterations; i++ {
|
||||
start := time.Now()
|
||||
ecdsa.Verify(pubKey, message, r, s)
|
||||
totalVerifyTime += time.Since(start).Microseconds()
|
||||
}
|
||||
avgVerifyTime := totalVerifyTime / int64(iterations)
|
||||
fmt.Printf("Average verify time: %d µs\n", avgVerifyTime)
|
||||
}
|
||||
|
229
vlib/crypto/ecdsa/ecdsa.v
Normal file
229
vlib/crypto/ecdsa/ecdsa.v
Normal file
@ -0,0 +1,229 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module ecdsa
|
||||
|
||||
#flag darwin -L /opt/homebrew/opt/openssl/lib -I /opt/homebrew/opt/openssl/include
|
||||
|
||||
#flag -I/usr/include/openssl
|
||||
#flag -lcrypto
|
||||
#flag darwin -I/usr/local/opt/openssl/include
|
||||
#flag darwin -L/usr/local/opt/openssl/lib
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
#include <openssl/objects.h>
|
||||
#include <openssl/bn.h>
|
||||
|
||||
// C function declarations
|
||||
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_free(key &C.EC_KEY)
|
||||
fn C.BN_bin2bn(s &u8, len int, ret &C.BIGNUM) &C.BIGNUM
|
||||
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_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_KEY_set_public_key(key &C.EC_KEY, &C.EC_POINT) int
|
||||
fn C.EC_POINT_free(point &C.EC_POINT)
|
||||
fn C.BN_free(a &C.BIGNUM)
|
||||
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
|
||||
fn C.EC_KEY_get0_private_key(key &C.EC_KEY) &C.BIGNUM
|
||||
fn C.BN_num_bits(a &C.BIGNUM) int
|
||||
fn C.BN_bn2bin(a &C.BIGNUM, to &u8) int
|
||||
fn C.EC_KEY_up_ref(key &C.EC_KEY) int
|
||||
fn C.BN_cmp(a &C.BIGNUM, b &C.BIGNUM) int
|
||||
fn C.EC_KEY_get0_public_key(key &C.EC_KEY) &C.EC_POINT
|
||||
fn C.EC_POINT_cmp(group &C.EC_GROUP, a &C.EC_POINT, b &C.EC_POINT, ctx &C.BN_CTX) int
|
||||
fn C.BN_CTX_new() &C.BN_CTX
|
||||
fn C.BN_CTX_free(ctx &C.BN_CTX)
|
||||
|
||||
// NID constants
|
||||
const nid_prime256v1 = C.NID_X9_62_prime256v1
|
||||
|
||||
@[typedef]
|
||||
struct C.EC_KEY {}
|
||||
|
||||
@[typedef]
|
||||
struct C.EC_GROUP {}
|
||||
|
||||
@[typedef]
|
||||
struct C.BIGNUM {}
|
||||
|
||||
@[typedef]
|
||||
struct C.EC_POINT {}
|
||||
|
||||
@[typedef]
|
||||
struct C.ECDSA_SIG {}
|
||||
|
||||
@[typedef]
|
||||
struct C.BN_CTX {}
|
||||
|
||||
pub struct PrivateKey {
|
||||
key &C.EC_KEY
|
||||
}
|
||||
|
||||
pub struct PublicKey {
|
||||
key &C.EC_KEY
|
||||
}
|
||||
|
||||
// Generate a new key pair
|
||||
pub fn generate_key() !(PublicKey, PrivateKey) {
|
||||
nid := nid_prime256v1 // Using NIST P-256 curve
|
||||
ec_key := C.EC_KEY_new_by_curve_name(nid)
|
||||
if ec_key == 0 {
|
||||
return error('Failed to create new EC_KEY')
|
||||
}
|
||||
res := C.EC_KEY_generate_key(ec_key)
|
||||
if res != 1 {
|
||||
C.EC_KEY_free(ec_key)
|
||||
return error('Failed to generate EC_KEY')
|
||||
}
|
||||
priv_key := PrivateKey{
|
||||
key: ec_key
|
||||
}
|
||||
pub_key := PublicKey{
|
||||
key: ec_key
|
||||
}
|
||||
return pub_key, priv_key
|
||||
}
|
||||
|
||||
// Create a new private key from a seed
|
||||
pub fn new_key_from_seed(seed []u8) !PrivateKey {
|
||||
nid := nid_prime256v1
|
||||
// Create a new EC_KEY object with the specified curve
|
||||
ec_key := C.EC_KEY_new_by_curve_name(nid)
|
||||
if ec_key == 0 {
|
||||
return error('Failed to create new EC_KEY')
|
||||
}
|
||||
// Convert the seed bytes into a BIGNUM
|
||||
bn := C.BN_bin2bn(seed.data, seed.len, 0)
|
||||
if bn == 0 {
|
||||
C.EC_KEY_free(ec_key)
|
||||
return error('Failed to create BIGNUM from seed')
|
||||
}
|
||||
// Set the BIGNUM as the private key in the EC_KEY object
|
||||
mut res := C.EC_KEY_set_private_key(ec_key, bn)
|
||||
if res != 1 {
|
||||
C.BN_free(bn)
|
||||
C.EC_KEY_free(ec_key)
|
||||
return error('Failed to set private key')
|
||||
}
|
||||
// Now compute the public key
|
||||
//
|
||||
// Retrieve the EC_GROUP object associated with the EC_KEY
|
||||
group := C.EC_KEY_get0_group(ec_key)
|
||||
// Create a new EC_POINT object for the public key
|
||||
pub_key_point := C.EC_POINT_new(group)
|
||||
// Create a new BN_CTX object for efficient BIGNUM operations
|
||||
ctx := C.BN_CTX_new()
|
||||
if ctx == 0 {
|
||||
C.EC_POINT_free(pub_key_point)
|
||||
C.BN_free(bn)
|
||||
C.EC_KEY_free(ec_key)
|
||||
return error('Failed to create BN_CTX')
|
||||
}
|
||||
defer {
|
||||
C.BN_CTX_free(ctx)
|
||||
}
|
||||
// Perform the point multiplication to compute the public key: pub_key_point = bn * G
|
||||
res = C.EC_POINT_mul(group, pub_key_point, bn, 0, 0, ctx)
|
||||
if res != 1 {
|
||||
C.EC_POINT_free(pub_key_point)
|
||||
C.BN_free(bn)
|
||||
C.EC_KEY_free(ec_key)
|
||||
return error('Failed to compute public key')
|
||||
}
|
||||
// Set the computed public key in the EC_KEY object
|
||||
res = C.EC_KEY_set_public_key(ec_key, pub_key_point)
|
||||
if res != 1 {
|
||||
C.EC_POINT_free(pub_key_point)
|
||||
C.BN_free(bn)
|
||||
C.EC_KEY_free(ec_key)
|
||||
return error('Failed to set public key')
|
||||
}
|
||||
C.EC_POINT_free(pub_key_point)
|
||||
C.BN_free(bn)
|
||||
return PrivateKey{
|
||||
key: ec_key
|
||||
}
|
||||
}
|
||||
|
||||
// Sign a message with private key
|
||||
pub fn (priv_key PrivateKey) sign(message []u8) ![]u8 {
|
||||
if message.len == 0 {
|
||||
return error('Message cannot be null or empty')
|
||||
}
|
||||
mut sig_len := u32(0)
|
||||
sig_size := C.ECDSA_size(priv_key.key)
|
||||
sig := unsafe { malloc(int(sig_size)) }
|
||||
res := C.ECDSA_sign(0, message.data, message.len, sig, &sig_len, priv_key.key)
|
||||
if res != 1 {
|
||||
unsafe { free(sig) }
|
||||
return error('Failed to sign message')
|
||||
}
|
||||
signed_data := unsafe { sig.vbytes(int(sig_len)) }
|
||||
unsafe { free(sig) }
|
||||
return signed_data.clone()
|
||||
}
|
||||
|
||||
// Verify a signature with public key
|
||||
pub fn (pub_key PublicKey) verify(message []u8, sig []u8) !bool {
|
||||
res := C.ECDSA_verify(0, message.data, message.len, sig.data, sig.len, pub_key.key)
|
||||
if res == -1 {
|
||||
return error('Failed to verify signature')
|
||||
}
|
||||
return res == 1
|
||||
}
|
||||
|
||||
// Get the seed (private key bytes)
|
||||
pub fn (priv_key PrivateKey) seed() ![]u8 {
|
||||
bn := C.EC_KEY_get0_private_key(priv_key.key)
|
||||
if bn == 0 {
|
||||
return error('Failed to get private key BIGNUM')
|
||||
}
|
||||
num_bytes := (C.BN_num_bits(bn) + 7) / 8
|
||||
mut buf := []u8{len: int(num_bytes)}
|
||||
res := C.BN_bn2bin(bn, buf.data)
|
||||
if res == 0 {
|
||||
return error('Failed to convert BIGNUM to bytes')
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
// Get the public key from private key
|
||||
pub fn (priv_key PrivateKey) public_key() !PublicKey {
|
||||
// Increase reference count
|
||||
res := C.EC_KEY_up_ref(priv_key.key)
|
||||
if res != 1 {
|
||||
return error('Failed to increment EC_KEY reference count')
|
||||
}
|
||||
return PublicKey{
|
||||
key: priv_key.key
|
||||
}
|
||||
}
|
||||
|
||||
// Compare two private keys
|
||||
pub fn (priv_key PrivateKey) equal(other PrivateKey) bool {
|
||||
bn1 := C.EC_KEY_get0_private_key(priv_key.key)
|
||||
bn2 := C.EC_KEY_get0_private_key(other.key)
|
||||
res := C.BN_cmp(bn1, bn2)
|
||||
return res == 0
|
||||
}
|
||||
|
||||
// Compare two public keys
|
||||
pub fn (pub_key PublicKey) equal(other PublicKey) bool {
|
||||
group := C.EC_KEY_get0_group(pub_key.key)
|
||||
point1 := C.EC_KEY_get0_public_key(pub_key.key)
|
||||
point2 := C.EC_KEY_get0_public_key(other.key)
|
||||
ctx := C.BN_CTX_new()
|
||||
if ctx == 0 {
|
||||
return false
|
||||
}
|
||||
defer {
|
||||
C.BN_CTX_free(ctx)
|
||||
}
|
||||
res := C.EC_POINT_cmp(group, point1, point2, ctx)
|
||||
return res == 0
|
||||
}
|
101
vlib/crypto/ecdsa/ecdsa_test.v
Normal file
101
vlib/crypto/ecdsa/ecdsa_test.v
Normal file
@ -0,0 +1,101 @@
|
||||
module ecdsa
|
||||
|
||||
fn test_ecdsa() {
|
||||
// Generate key pair
|
||||
pub_key, priv_key := generate_key() or { panic(err) }
|
||||
|
||||
// Sign a message
|
||||
message := 'Hello, ECDSA!'.bytes()
|
||||
signature := priv_key.sign(message) or { panic(err) }
|
||||
|
||||
// Verify the signature
|
||||
is_valid := pub_key.verify(message, signature) or { panic(err) }
|
||||
println('Signature valid: ${is_valid}')
|
||||
assert is_valid
|
||||
}
|
||||
|
||||
fn test_generate_key() ! {
|
||||
// Test key generation
|
||||
pub_key, priv_key := generate_key() or { panic(err) }
|
||||
assert pub_key.key != unsafe { nil }
|
||||
assert priv_key.key != unsafe { nil }
|
||||
}
|
||||
|
||||
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) }
|
||||
assert seed == retrieved_seed
|
||||
}
|
||||
|
||||
fn test_sign_and_verify() ! {
|
||||
// Test signing and verifying a message
|
||||
pub_key, priv_key := generate_key() or { panic(err) }
|
||||
message := 'Test message'.bytes()
|
||||
signature := priv_key.sign(message) or { panic(err) }
|
||||
is_valid := pub_key.verify(message, signature) or { panic(err) }
|
||||
assert is_valid
|
||||
}
|
||||
|
||||
fn test_seed() ! {
|
||||
// Test retrieving the seed from a private key
|
||||
_, priv_key := generate_key() or { panic(err) }
|
||||
seed := priv_key.seed() or { panic(err) }
|
||||
assert seed.len > 0
|
||||
}
|
||||
|
||||
fn test_public_key() ! {
|
||||
// Test getting the public key from a private key
|
||||
_, priv_key := generate_key() or { panic(err) }
|
||||
pub_key1 := priv_key.public_key() or { panic(err) }
|
||||
pub_key2, _ := generate_key() or { panic(err) }
|
||||
assert !pub_key1.equal(pub_key2)
|
||||
}
|
||||
|
||||
fn test_private_key_equal() ! {
|
||||
// Test private key equality
|
||||
_, priv_key1 := generate_key() or { panic(err) }
|
||||
seed := priv_key1.seed() or { panic(err) }
|
||||
priv_key2 := new_key_from_seed(seed) or { panic(err) }
|
||||
assert priv_key1.equal(priv_key2)
|
||||
}
|
||||
|
||||
fn test_public_key_equal() ! {
|
||||
// Test public key equality
|
||||
_, priv_key := generate_key() or { panic(err) }
|
||||
pub_key1 := priv_key.public_key() or { panic(err) }
|
||||
pub_key2 := priv_key.public_key() or { panic(err) }
|
||||
assert pub_key1.equal(pub_key2)
|
||||
}
|
||||
|
||||
fn test_sign_with_new_key_from_seed() ! {
|
||||
// Test signing with a key generated from a seed
|
||||
seed := [u8(10), 20, 30, 40, 50]
|
||||
priv_key := new_key_from_seed(seed) or { panic(err) }
|
||||
message := 'Another test message'.bytes()
|
||||
signature := priv_key.sign(message) or { panic(err) }
|
||||
pub_key := priv_key.public_key() or { panic(err) }
|
||||
is_valid := pub_key.verify(message, signature) or { panic(err) }
|
||||
assert is_valid
|
||||
}
|
||||
|
||||
fn test_invalid_signature() ! {
|
||||
// Test verifying an invalid signature
|
||||
pub_key, _ := generate_key() or { panic(err) }
|
||||
message := 'Test message'.bytes()
|
||||
invalid_signature := [u8(1), 2, 3] // Deliberately invalid
|
||||
result := pub_key.verify(message, invalid_signature) or {
|
||||
// Expecting verification to fail
|
||||
assert err.msg() == 'Failed to verify signature'
|
||||
return
|
||||
}
|
||||
assert !result
|
||||
}
|
||||
|
||||
fn test_different_keys_not_equal() ! {
|
||||
// Test that different keys are not equal
|
||||
_, priv_key1 := generate_key() or { panic(err) }
|
||||
_, priv_key2 := generate_key() or { panic(err) }
|
||||
assert !priv_key1.equal(priv_key2)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user