/* -*- c -*- |
|| This file is part of Pike. For copyright information see COPYRIGHT. |
|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING |
|| for more information. |
*/ |
|
#include "global.h" |
#include "builtin_functions.h" |
#include "interpret.h" |
#include "module.h" |
|
#include "nettle_config.h" |
|
#ifdef HAVE_LIBHOGWEED |
|
DECLARATIONS |
|
#include "nettle.h" |
#include <nettle/dsa.h> |
#include <nettle/rsa.h> |
#include <gmp.h> |
|
#include "bignum.h" |
|
/*! @module Nettle |
*/ |
|
void random_func_wrapper(void *f, unsigned num, uint8_t *out) |
{ |
push_int(num); |
apply_svalue((struct svalue *)f, 1); |
if(TYPEOF(Pike_sp[-1])!=T_STRING) |
Pike_error("Random function did not return string value.\n"); |
if(Pike_sp[-1].u.string->len != (unsigned int)num) |
Pike_error("Random function did not return correct number of bytes.\n"); |
memcpy(out, Pike_sp[-1].u.string->str, num); |
pop_stack(); |
} |
|
/*! @decl array(object(Gmp.mpz)) @ |
*! dsa_generate_keypair(int p_bits, int q_bits, @ |
*! function(int:string(0..255)) rnd) |
*! |
*! Generates a DSA key pair with @[p_bits] number of bits (sometimes |
*! referred to as L) for p, and @[q_bits] number of bits (sometimes |
*! referred to as N) for q, using the random function @[rnd]. |
*! |
*! Valid combinations as per FIPS 186-3 are |
*! @pre{ |
*! p_bits q_bits |
*! 1024 160 |
*! 2048 224 (rejected by some versions of Hogweed) |
*! 2048 256 |
*! 3072 256 |
*! @} |
*! |
*! @returns |
*! @array |
*! @elem Gmp.mpz 0 |
*! The value p, the modulo. |
*! @elem Gmp.mpz 1 |
*! The value q, the group order. |
*! @elem Gmp.mpz 2 |
*! The value g, the generator. |
*! @elem Gmp.mpz 3 |
*! The value y, the public value. |
*! @elem Gmp.mpz 4 |
*! The value x, the private value. |
*! @endarray |
*/ |
PIKEFUN array(object(Gmp.mpz)) |
dsa_generate_keypair(int p_bits, int q_bits, function(int:string(0..255)) rnd) |
{ |
struct dsa_public_key pub; |
struct dsa_private_key key; |
|
dsa_public_key_init(&pub); |
dsa_private_key_init(&key); |
|
if( !nettle_dsa_generate_keypair(&pub, &key, rnd, random_func_wrapper, |
NULL, NULL, p_bits, q_bits) ) |
{ |
dsa_private_key_clear(&key); |
dsa_public_key_clear(&pub); |
|
Pike_error("Illegal parameter value.\n"); |
} |
|
push_bignum((MP_INT *)&pub.p); |
push_bignum((MP_INT *)&pub.q); |
push_bignum((MP_INT *)&pub.g); |
push_bignum((MP_INT *)&pub.y); |
push_bignum((MP_INT *)&key.x); |
|
dsa_private_key_clear(&key); |
dsa_public_key_clear(&pub); |
|
f_aggregate(5); |
stack_pop_n_elems_keep_top(args); /* Remove p_bits, q_bits and rnd. */ |
} |
|
/*! @decl array(object(Gmp.mpz)) @ |
*! rsa_generate_keypair(int bits, int e, function(int:string(0..255)) rnd) |
*! |
*! Generates an RSA key pair with a @[bits] sized modulus (n), using |
*! the provided value for @[e] and random function @[rnd]. |
*! |
*! @returns |
*! @array |
*! @elem Gmp.mpz 0 |
*! The value n, the modulo. |
*! @elem Gmp.mpz 1 |
*! The value d, the private exponent. |
*! @elem Gmp.mpz 2 |
*! The value p, a prime. |
*! @elem Gmp.mpz 3 |
*! The value q, a prime. |
*! @endarray |
*/ |
PIKEFUN array(object(Gmp.mpz)) |
rsa_generate_keypair(int bits, int e, function(int:string(0..255)) rnd) |
{ |
struct rsa_public_key pub; |
struct rsa_private_key key; |
|
rsa_public_key_init(&pub); |
rsa_private_key_init(&key); |
|
mpz_set_ui((MP_INT *)&pub.e, e); |
|
if( !nettle_rsa_generate_keypair(&pub, &key, rnd, random_func_wrapper, |
NULL, NULL, bits, 0) ) |
{ |
rsa_private_key_clear(&key); |
rsa_public_key_clear(&pub); |
|
Pike_error("Illegal parameter value.\n"); |
} |
|
push_bignum((MP_INT *)&pub.n); |
push_bignum((MP_INT *)&key.d); |
push_bignum((MP_INT *)&key.p); |
push_bignum((MP_INT *)&key.q); |
|
rsa_private_key_clear(&key); |
rsa_public_key_clear(&pub); |
|
f_aggregate(4); |
stack_pop_n_elems_keep_top(args); /* Remove bits, e and rnd. */ |
} |
|
#ifdef HAVE_NETTLE_ECDSA_H |
#include <nettle/ecc-curve.h> |
|
#include <nettle/ecc.h> |
|
/*! @class ECC_Curve |
*! |
*! Elliptic Curve Definition |
*/ |
PIKECLASS ECC_Curve |
{ |
CVAR const struct ecc_curve *curve; |
CVAR int field_size; |
|
/*! @decl void create(int(0..) family, int(0..) field_size, int(0..) revision) |
*! |
*! Initialize the curve. |
*/ |
PIKEFUN void create(int(0..) family, int(0..) field_size, int(0..) revision) |
flags ID_STATIC |
{ |
if (THIS->curve) { |
Pike_error("The curve has already been initialized!\n"); |
} |
|
switch(family) { |
case 1: |
if (revision != 1) |
Pike_error("Unsupported revision.\n"); |
switch(field_size) |
{ |
case 192: |
THIS->curve = &nettle_secp_192r1; |
break; |
case 224: |
THIS->curve = &nettle_secp_224r1; |
break; |
case 256: |
THIS->curve = &nettle_secp_256r1; |
break; |
case 384: |
THIS->curve = &nettle_secp_384r1; |
break; |
case 521: |
THIS->curve = &nettle_secp_521r1; |
break; |
default: |
Pike_error("Invalid curve\n"); |
break; |
} |
break; |
default: |
Pike_error("Unknown curve family.\n"); |
break; |
} |
THIS->field_size = field_size; |
} |
|
/*! @decl string(7bit) name() |
*! |
*! Returns the name of the curve. |
*/ |
PIKEFUN string(7bit) name() |
{ |
if (THIS->curve == &nettle_secp_192r1) { |
ref_push_string(MK_STRING("SECP_192R1")); |
} else if (THIS->curve == &nettle_secp_224r1) { |
ref_push_string(MK_STRING("SECP_224R1")); |
} else if (THIS->curve == &nettle_secp_256r1) { |
ref_push_string(MK_STRING("SECP_256R1")); |
} else if (THIS->curve == &nettle_secp_384r1) { |
ref_push_string(MK_STRING("SECP_384R1")); |
} else if (THIS->curve == &nettle_secp_521r1) { |
ref_push_string(MK_STRING("SECP_521R1")); |
} else { |
ref_push_string(MK_STRING("UNKNOWN")); |
} |
} |
|
/*! @decl int size() |
*! |
*! @returns |
*! Returns the size in bits for a single coordinate on the curve. |
*/ |
PIKEFUN int size() |
{ |
push_int(THIS->field_size); |
} |
|
/*! @decl Gmp.mpz new_scalar(function(int:string(8bit)) rnd) |
*! |
*! @param rnd |
*! Randomness function to use as source. |
*! |
*! @returns |
*! Returns a random scalar suitable to use as an @[ECDSA] private key |
*! or as an ECDH exponent. |
*/ |
PIKEFUN object(Gmp.mpz) new_scalar(function(int:string(8bit)) rnd) |
{ |
struct ecc_scalar s; |
struct object *ret; |
|
if (!THIS->curve) Pike_error("No curve defined.\n"); |
|
ecc_scalar_init(&s, THIS->curve); |
|
ecc_scalar_random(&s, rnd, random_func_wrapper); |
|
push_object(ret = fast_clone_object(get_auto_bignum_program())); |
ecc_scalar_get(&s, (mpz_ptr)ret->storage); |
|
ecc_scalar_clear(&s); |
} |
|
/*! @decl array(Gmp.mpz) `*(Gmp.mpz|int scalar) |
*! |
*! Multiply the curve by a scalar. |
*! |
*! This can be used to get the public key from a private key. |
*! |
*! @returns |
*! Returns a new point (x, y) on the curve. |
*/ |
PIKEFUN array(object(Gmp.mpz)) `*(object(Gmp.mpz)|int scalar) |
{ |
struct ecc_scalar s; |
struct ecc_point r; |
struct object *x; |
struct object *y; |
|
if (!THIS->curve) Pike_error("No curve defined.\n"); |
|
convert_svalue_to_bignum(scalar); |
|
ecc_scalar_init(&s, THIS->curve); |
ecc_point_init(&r, THIS->curve); |
|
if (!ecc_scalar_set(&s, (mpz_srcptr)scalar->u.object->storage)) { |
ecc_scalar_clear(&s); |
ecc_point_clear(&r); |
SIMPLE_ARG_ERROR("`*", 1, "Invalid scalar for curve."); |
} |
|
ecc_point_mul_g(&r, &s); |
push_object(x = fast_clone_object(get_auto_bignum_program())); |
push_object(y = fast_clone_object(get_auto_bignum_program())); |
ecc_point_get(&r, (mpz_ptr)x->storage, (mpz_ptr)y->storage); |
|
ecc_scalar_clear(&s); |
ecc_point_clear(&r); |
|
f_aggregate(2); |
} |
|
/*! @decl array(Gmp.mpz) point_mul(Gmp.mpz|int x, Gmp.mpz|int y, @ |
*! Gmp.mpz|int scalar) |
*! |
*! Multiply a point on the curve by a scalar. |
*! |
*! A typical use is for Elliptic Curve Diffie Hellman (ECDH) key exchange. |
*! |
*! @returns |
*! Returns the new point on the curve. |
*/ |
PIKEFUN array(object(Gmp.mpz)) point_mul(object(Gmp.mpz)|int x, |
object(Gmp.mpz)|int y, |
object(Gmp.mpz)|int scalar) |
{ |
struct ecc_point p; |
struct ecc_scalar s; |
struct ecc_point r; |
struct object *rx; |
struct object *ry; |
|
if (!THIS->curve) Pike_error("No curve defined.\n"); |
|
convert_svalue_to_bignum(x); |
convert_svalue_to_bignum(y); |
convert_svalue_to_bignum(scalar); |
|
ecc_point_init(&p, THIS->curve); |
ecc_scalar_init(&s, THIS->curve); |
|
if (!ecc_point_set(&p, |
(mpz_srcptr)x->u.object->storage, |
(mpz_srcptr)y->u.object->storage)) { |
ecc_scalar_clear(&s); |
ecc_point_clear(&p); |
SIMPLE_ARG_ERROR("point_mul", 1, "Invalid point on curve."); |
} |
|
if (!ecc_scalar_set(&s, (mpz_srcptr)scalar->u.object->storage)) { |
ecc_scalar_clear(&s); |
ecc_point_clear(&p); |
SIMPLE_ARG_ERROR("point_mul", 3, "Invalid scalar for curve."); |
} |
|
ecc_point_init(&r, THIS->curve); |
|
ecc_point_mul(&r, &s, &p); |
|
push_object(rx = fast_clone_object(get_auto_bignum_program())); |
push_object(ry = fast_clone_object(get_auto_bignum_program())); |
ecc_point_get(&r, (mpz_ptr)rx->storage, (mpz_ptr)ry->storage); |
|
ecc_point_clear(&r); |
ecc_scalar_clear(&s); |
ecc_point_clear(&p); |
|
f_aggregate(2); |
stack_pop_n_elems_keep_top(args); |
} |
} |
|
/*! @endclass ECC_Curve |
*/ |
|
#endif /* HAVE_NETTLE_ECDSA_H */ |
|
/*! @endmodule Nettle |
*/ |
|
void |
hogweed_init(void) |
{ |
#ifdef HAVE_NETTLE_ECDSA_H |
struct svalue c; |
#endif |
|
INIT; |
} |
|
void |
hogweed_exit(void) |
{ |
EXIT; |
} |
|
#endif |
|