mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-04 08:04:37 +08:00
1781 lines
68 KiB
C
1781 lines
68 KiB
C
/* node:crypto DiffieHellman over the vendored mbedTLS bignum arithmetic:
|
|
* crypto.createDiffieHellman, crypto.getDiffieHellman and
|
|
* crypto.createDiffieHellmanGroup. A DiffieHellman or DiffieHellmanGroup
|
|
* is a native dyn handle. Construction follows Node 24's
|
|
* lib/internal/crypto/diffiehellman.js argument handling and
|
|
* src/crypto/crypto_dh.cc; key generation, verifyError, the public key
|
|
* range check and computeSecret follow the OpenSSL 3 paths of
|
|
* deps/ncrypto and OpenSSL 3.5's DH provider:
|
|
* - a group draws modp5 through modp18 private keys of a fixed width
|
|
* (200 to 400 bits) and modp1 and modp2 keys in [2, p - 2], and its
|
|
* verifyError is 0;
|
|
* - createDiffieHellman parameters that equal an OpenSSL named group
|
|
* draw keys below min(2^N, q) for the group's key length N; other
|
|
* parameters draw keys of bits(p) - 2 bits, and key generation and
|
|
* computeSecret need a 512- to 10000-bit prime;
|
|
* - createDiffieHellman(primeLength, generator) generates a safe prime
|
|
* p ≡ 23 (mod 24) for the generator 2, p ≡ 59 (mod 60) for 5 and
|
|
* p ≡ 11 (mod 12) otherwise, as OpenSSL's dh_builtin_genparams does.
|
|
* Primes longer than mbedTLS's 8192-bit limit throw a coded SC2020
|
|
* error, as do keys and generators past mbedTLS's 80000-byte limit (leading
|
|
* zero bytes aside). All arguments are borrowed. */
|
|
#include "scr_runtime.h"
|
|
|
|
#include <math.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "mbedtls/bignum.h"
|
|
#include "mbedtls/platform_util.h"
|
|
|
|
#include "scr_crypto_args.h"
|
|
#include "scr_dh_groups.h"
|
|
|
|
/* The value the SC2020 error for an unlowered encoding names. */
|
|
#define SD_SUBJECT "A DiffieHellman encoding"
|
|
|
|
/* DH_MIN_MODULUS_BITS and OPENSSL_DH_MAX_MODULUS_BITS. */
|
|
#define SD_MIN_MODULUS_BITS 512
|
|
#define SD_MAX_MODULUS_BITS 10000
|
|
|
|
/* The longest value an mbedtls_mpi holds, in bytes. */
|
|
#define SD_MAX_VALUE_BYTES (MBEDTLS_MPI_MAX_LIMBS * sizeof(mbedtls_mpi_uint))
|
|
|
|
/* DH_check result bits, as verifyError reports them. */
|
|
enum {
|
|
SD_P_NOT_PRIME = 0x01,
|
|
SD_P_NOT_SAFE_PRIME = 0x02,
|
|
SD_NOT_SUITABLE_GENERATOR = 0x08,
|
|
SD_MODULUS_TOO_SMALL = 0x80,
|
|
};
|
|
|
|
/* A crypto.getDiffieHellman group. */
|
|
typedef struct {
|
|
const char *name;
|
|
const unsigned char *p;
|
|
size_t p_len;
|
|
int private_bits; /* ncrypto's GetDhGroupPrivateBits; 0 draws keys in [2, p - 2] */
|
|
} ScDhGroup;
|
|
|
|
static const ScDhGroup sd_groups[] = {
|
|
{"modp1", scr_dh_modp1_p, sizeof scr_dh_modp1_p, 0},
|
|
{"modp2", scr_dh_modp2_p, sizeof scr_dh_modp2_p, 0},
|
|
{"modp5", scr_dh_modp5_p, sizeof scr_dh_modp5_p, 200},
|
|
{"modp14", scr_dh_modp14_p, sizeof scr_dh_modp14_p, 225},
|
|
{"modp15", scr_dh_modp15_p, sizeof scr_dh_modp15_p, 275},
|
|
{"modp16", scr_dh_modp16_p, sizeof scr_dh_modp16_p, 325},
|
|
{"modp17", scr_dh_modp17_p, sizeof scr_dh_modp17_p, 375},
|
|
{"modp18", scr_dh_modp18_p, sizeof scr_dh_modp18_p, 400},
|
|
};
|
|
|
|
/* An OpenSSL DH named group, which the DH provider recognizes in explicit
|
|
* p and g. A NULL q is (p - 1) / 2. */
|
|
typedef struct {
|
|
const unsigned char *p;
|
|
size_t p_len;
|
|
const unsigned char *g;
|
|
size_t g_len;
|
|
const unsigned char *q;
|
|
size_t q_len;
|
|
int keylength;
|
|
} ScDhNamed;
|
|
|
|
static const unsigned char sd_two[] = {2};
|
|
|
|
#define SD_SAFE(p, keylength) {p, sizeof p, sd_two, 1, NULL, 0, keylength}
|
|
#define SD_RFC5114(name, keylength) \
|
|
{scr_dh_##name##_p, sizeof scr_dh_##name##_p, scr_dh_##name##_g, sizeof scr_dh_##name##_g, \
|
|
scr_dh_##name##_q, sizeof scr_dh_##name##_q, keylength}
|
|
|
|
static const ScDhNamed sd_named[] = {
|
|
SD_SAFE(scr_dh_ffdhe2048_p, 225), SD_SAFE(scr_dh_ffdhe3072_p, 275), SD_SAFE(scr_dh_ffdhe4096_p, 325),
|
|
SD_SAFE(scr_dh_ffdhe6144_p, 375), SD_SAFE(scr_dh_ffdhe8192_p, 400), SD_SAFE(scr_dh_modp5_p, 200),
|
|
SD_SAFE(scr_dh_modp14_p, 225), SD_SAFE(scr_dh_modp15_p, 275), SD_SAFE(scr_dh_modp16_p, 325),
|
|
SD_SAFE(scr_dh_modp17_p, 375), SD_SAFE(scr_dh_modp18_p, 400), SD_RFC5114(dh_1024_160, 160),
|
|
SD_RFC5114(dh_2048_224, 224), SD_RFC5114(dh_2048_256, 224),
|
|
};
|
|
|
|
typedef struct ScDh {
|
|
size_t rc;
|
|
const ScDhGroup *group; /* crypto.getDiffieHellman's group, or NULL */
|
|
const ScDhNamed *named; /* the named group createDiffieHellman's parameters equal, or NULL */
|
|
mbedtls_mpi p, g;
|
|
mbedtls_mpi pub; /* when has_pub */
|
|
mbedtls_mpi priv; /* when has_priv */
|
|
bool has_pub;
|
|
bool has_priv;
|
|
int verify_error; /* -1 until computed */
|
|
} ScDh;
|
|
|
|
static ScrDynHandleOps sd_ops;
|
|
static ScrDynHandleOps sd_group_ops;
|
|
|
|
static void *sd_retain(void *h) {
|
|
((ScDh *)h)->rc++;
|
|
return h;
|
|
}
|
|
|
|
static void sd_release(void *h) {
|
|
ScDh *d = h;
|
|
if (--d->rc) return;
|
|
mbedtls_mpi_free(&d->p);
|
|
mbedtls_mpi_free(&d->g);
|
|
mbedtls_mpi_free(&d->pub);
|
|
mbedtls_mpi_free(&d->priv);
|
|
mbedtls_platform_zeroize(d, sizeof *d);
|
|
free(d);
|
|
}
|
|
|
|
static int sd_random(void *state, unsigned char *out, size_t len) {
|
|
(void)state;
|
|
arc4random_buf(out, len);
|
|
return 0;
|
|
}
|
|
|
|
/* ── errors ───────────────────────────────────────────────────────────── */
|
|
|
|
static void sd_throw(int kind, const char *code, const char *message) {
|
|
scr_throw_error_msg_code(kind, message, strlen(message), code);
|
|
}
|
|
|
|
static void sd_operation_failed(const char *message) {
|
|
sd_throw(SCR_ERR_ERROR, "ERR_CRYPTO_OPERATION_FAILED", message);
|
|
}
|
|
|
|
/* An OpenSSL DH error as ThrowCryptoError reports it: the formatted
|
|
* message plus own library, reason and code properties, in Node's key
|
|
* order. */
|
|
static void sd_throw_openssl(const char *packed, const char *reason, const char *code) {
|
|
char message[96];
|
|
snprintf(message, sizeof message, "error:%s:Diffie-Hellman routines::%s", packed, reason);
|
|
ScrStr *text = scr_str_new(message, strlen(message));
|
|
ScrError *e = scr_error_new(SCR_ERR_ERROR, text);
|
|
scr_str_release(text);
|
|
ScrStr *library = scr_str_new("Diffie-Hellman routines", 23);
|
|
scr_error_set_prop(e, "library", 7, scr_dyn_new_str(library));
|
|
scr_str_release(library);
|
|
ScrStr *reason_text = scr_str_new(reason, strlen(reason));
|
|
scr_error_set_prop(e, "reason", 6, scr_dyn_new_str(reason_text));
|
|
scr_str_release(reason_text);
|
|
scr_error_set_code(e, code);
|
|
ScrStr *code_text = scr_str_new(code, strlen(code));
|
|
scr_error_set_prop(e, "code", 4, scr_dyn_new_str(code_text));
|
|
scr_str_release(code_text);
|
|
scr_throw_obj(e, &scr_error_retain_v, &scr_error_release_v, scr_error_trace_arg());
|
|
}
|
|
|
|
static void sd_bad_generator(void) {
|
|
sd_throw_openssl("02800065", "bad generator", "ERR_OSSL_DH_BAD_GENERATOR");
|
|
}
|
|
|
|
static void sd_too_long(void) {
|
|
static const char message[] = "DiffieHellman primes longer than 8192 bits have no native implementation";
|
|
scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020");
|
|
}
|
|
|
|
/* A key or generator past SD_MAX_VALUE_BYTES, which BN_bin2bn reads. */
|
|
static void sd_value_too_long(void) {
|
|
char message[96];
|
|
int n = snprintf(message, sizeof message, "DiffieHellman values longer than %zu bytes have no native implementation",
|
|
(size_t)SD_MAX_VALUE_BYTES);
|
|
scr_throw_error_msg_code(SCR_ERR_ERROR, message, n < 0 ? 0 : (size_t)n, "SC2020");
|
|
}
|
|
|
|
/* validateInt32(value, name, min). */
|
|
static bool sd_validate_int32_min(const char *name, double value, int32_t min) {
|
|
bool integer = isfinite(value) && trunc(value) == value;
|
|
if (integer && value >= min && value <= INT32_MAX) return true;
|
|
char received[48];
|
|
scr_num_received(value, received);
|
|
char text[192];
|
|
int n = integer ? snprintf(text, sizeof text,
|
|
"The value of \"%s\" is out of range. It must be >= %ld && <= 2147483647. "
|
|
"Received %s",
|
|
name, (long)min, received)
|
|
: snprintf(text, sizeof text,
|
|
"The value of \"%s\" is out of range. It must be an integer. Received %s", name,
|
|
received);
|
|
scr_throw_error_msg_code(SCR_ERR_RANGE, text, n < 0 ? 0 : (size_t)n, "ERR_OUT_OF_RANGE");
|
|
return false;
|
|
}
|
|
|
|
/* validateInt32(value, name). */
|
|
static bool sd_validate_int32(const char *name, double value) {
|
|
return sd_validate_int32_min(name, value, INT32_MIN);
|
|
}
|
|
|
|
/* ── arithmetic ───────────────────────────────────────────────────────── */
|
|
|
|
/* The exponent digit width that keeps every mbedtls_mpi_exp_mod exponent
|
|
* within its 8192-bit limit. */
|
|
#define SD_DIGIT_BITS (MBEDTLS_MPI_MAX_BITS - 1)
|
|
|
|
/* base^e mod p, as BN_mod_exp_mont_consttime computes it: false for an
|
|
* even modulus or one below 3. Exponents past 8192 bits are split into
|
|
* SD_DIGIT_BITS-bit digits. */
|
|
static bool sd_exp(mbedtls_mpi *out, const mbedtls_mpi *base, const mbedtls_mpi *e, const mbedtls_mpi *p) {
|
|
if (mbedtls_mpi_cmp_int(p, 1) <= 0 || mbedtls_mpi_get_bit(p, 0) == 0) return false;
|
|
size_t ebits = mbedtls_mpi_bitlen(e);
|
|
if (ebits <= MBEDTLS_MPI_MAX_BITS) return mbedtls_mpi_exp_mod(out, base, e, p, NULL) == 0;
|
|
/* base^e = Π (base^(2^(k·w)))^digit_k for the w-bit digits of e. */
|
|
mbedtls_mpi acc, power, digit, term, step;
|
|
mbedtls_mpi_init(&acc);
|
|
mbedtls_mpi_init(&power);
|
|
mbedtls_mpi_init(&digit);
|
|
mbedtls_mpi_init(&term);
|
|
mbedtls_mpi_init(&step);
|
|
int ret = 0;
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&acc, 1));
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&power, base));
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&step, 1));
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&step, SD_DIGIT_BITS));
|
|
for (size_t offset = 0; offset < ebits; offset += SD_DIGIT_BITS) {
|
|
/* Bit by bit, so each digit costs its own width, not the exponent's. */
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&digit, 0));
|
|
for (size_t bit = 0; bit < SD_DIGIT_BITS && offset + bit < ebits; bit++)
|
|
if (mbedtls_mpi_get_bit(e, offset + bit)) MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&digit, bit, 1));
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&term, &power, &digit, p, NULL));
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&acc, &acc, &term));
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&acc, &acc, p));
|
|
if (offset + SD_DIGIT_BITS < ebits) MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&power, &power, &step, p, NULL));
|
|
}
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_copy(out, &acc));
|
|
cleanup:
|
|
mbedtls_mpi_free(&acc);
|
|
mbedtls_mpi_free(&power);
|
|
mbedtls_mpi_free(&digit);
|
|
mbedtls_mpi_free(&term);
|
|
mbedtls_mpi_free(&step);
|
|
return ret == 0;
|
|
}
|
|
|
|
/* Drops a big-endian value's leading zero bytes, which BN_bin2bn ignores
|
|
* but mbedtls_mpi_read_binary counts against SD_MAX_VALUE_BYTES. */
|
|
static void sd_strip(const unsigned char **data, size_t *len) {
|
|
while (*len > 0 && **data == 0) {
|
|
(*data)++;
|
|
(*len)--;
|
|
}
|
|
}
|
|
|
|
/* BN_bin2bn: false when the value, without its leading zeros, is longer
|
|
* than SD_MAX_VALUE_BYTES. */
|
|
static bool sd_read(mbedtls_mpi *x, const ScrBytes *bytes) {
|
|
const unsigned char *data = bytes->data;
|
|
size_t len = bytes->len;
|
|
sd_strip(&data, &len);
|
|
if (len > SD_MAX_VALUE_BYTES) return false;
|
|
if (mbedtls_mpi_read_binary(x, data, len) != 0) scr_trap("scriptc: out of memory\n");
|
|
return true;
|
|
}
|
|
|
|
/* BN_cmp over two big-endian values, for ones sd_read cannot hold. */
|
|
static int sd_cmp_bytes(const ScrBytes *a, const ScrBytes *b) {
|
|
const unsigned char *a_data = a->data, *b_data = b->data;
|
|
size_t a_len = a->len, b_len = b->len;
|
|
sd_strip(&a_data, &a_len);
|
|
sd_strip(&b_data, &b_len);
|
|
if (a_len != b_len) return a_len < b_len ? -1 : 1;
|
|
return a_len ? memcmp(a_data, b_data, a_len) : 0;
|
|
}
|
|
|
|
/* A uniformly random number of exactly `bits` bits (the top bit set), and
|
|
* odd when `odd`, as BN_priv_rand writes it. */
|
|
static bool sd_random_bits(mbedtls_mpi *x, size_t bits, bool odd) {
|
|
size_t len = (bits + 7) / 8;
|
|
unsigned char *buf = malloc(len);
|
|
if (!buf) scr_trap("scriptc: out of memory\n");
|
|
arc4random_buf(buf, len);
|
|
unsigned extra = (unsigned)(len * 8 - bits);
|
|
buf[0] &= (unsigned char)(0xff >> extra);
|
|
buf[0] |= (unsigned char)(0x80 >> extra);
|
|
if (odd) buf[len - 1] |= 1;
|
|
bool ok = mbedtls_mpi_read_binary(x, buf, len) == 0;
|
|
mbedtls_platform_zeroize(buf, len);
|
|
free(buf);
|
|
return ok;
|
|
}
|
|
|
|
/* ── primes ───────────────────────────────────────────────────────────── */
|
|
|
|
/* The odd primes below 17864 (OpenSSL's first 2048 primes but 2), which
|
|
* trial division divides by. Consecutive primes are grouped into products
|
|
* below the half-limb bound mbedtls_mpi_mod_int divides correctly by, so
|
|
* one call reduces by several. */
|
|
#define SD_SMALL_PRIMES 2047
|
|
#define SD_SMALL_LIMIT 17864
|
|
#define SD_PRODUCT_LIMIT ((uint64_t)1 << (sizeof(mbedtls_mpi_uint) * 4))
|
|
|
|
typedef struct {
|
|
uint16_t primes[SD_SMALL_PRIMES];
|
|
uint64_t products[SD_SMALL_PRIMES];
|
|
uint16_t starts[SD_SMALL_PRIMES + 1]; /* products[g] covers primes[starts[g]] to primes[starts[g + 1] - 1] */
|
|
size_t groups;
|
|
uint32_t mods[SD_SMALL_PRIMES];
|
|
} ScDhSmallPrimes;
|
|
|
|
static ScDhSmallPrimes *sd_small_primes(void) {
|
|
ScDhSmallPrimes *t = calloc(1, sizeof *t);
|
|
unsigned char *composite = calloc(SD_SMALL_LIMIT, 1);
|
|
if (!t || !composite) scr_trap("scriptc: out of memory\n");
|
|
size_t count = 0;
|
|
for (uint32_t n = 3; n < SD_SMALL_LIMIT && count < SD_SMALL_PRIMES; n += 2) {
|
|
if (composite[n]) continue;
|
|
t->primes[count++] = (uint16_t)n;
|
|
for (uint32_t m = n * n; m < SD_SMALL_LIMIT; m += 2 * n) composite[m] = 1;
|
|
}
|
|
free(composite);
|
|
uint64_t product = 1;
|
|
for (size_t i = 0; i < SD_SMALL_PRIMES; i++) {
|
|
if (product > SD_PRODUCT_LIMIT / t->primes[i]) {
|
|
t->products[t->groups++] = product;
|
|
t->starts[t->groups] = (uint16_t)i;
|
|
product = 1;
|
|
}
|
|
product *= t->primes[i];
|
|
}
|
|
t->products[t->groups++] = product;
|
|
t->starts[t->groups] = SD_SMALL_PRIMES;
|
|
return t;
|
|
}
|
|
|
|
/* x mod each small prime, into t->mods. */
|
|
static bool sd_small_residues(ScDhSmallPrimes *t, const mbedtls_mpi *x) {
|
|
for (size_t g = 0; g < t->groups; g++) {
|
|
mbedtls_mpi_uint r;
|
|
if (mbedtls_mpi_mod_int(&r, x, (mbedtls_mpi_sint)t->products[g]) != 0) return false;
|
|
for (size_t i = t->starts[g]; i < t->starts[g + 1]; i++) t->mods[i] = (uint32_t)(r % t->primes[i]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if defined(__SIZEOF_INT128__)
|
|
#define SD_MONT 1
|
|
typedef unsigned __int128 sd_u128;
|
|
|
|
/* Montgomery arithmetic modulo an odd n of k 64-bit limbs, little-endian,
|
|
* with R = 2^(64k), for the Miller-Rabin rounds. It runs in variable time
|
|
* and reads only public values. */
|
|
typedef struct {
|
|
size_t k;
|
|
uint64_t mm; /* -n^-1 mod 2^64 */
|
|
uint64_t *n; /* k limbs */
|
|
uint64_t *rr; /* R^2 mod n, k limbs */
|
|
uint64_t *acc; /* k limbs */
|
|
uint64_t *table; /* 16·k limbs: base^i·R mod n */
|
|
uint64_t *t; /* 2k + 1 limbs of products */
|
|
} ScDhMont;
|
|
|
|
static void sd_limbs_read(uint64_t *out, size_t k, const mbedtls_mpi *x, unsigned char *buf) {
|
|
if (mbedtls_mpi_write_binary_le(x, buf, 8 * k) != 0) scr_trap("scriptc: internal error: limb overflow\n");
|
|
for (size_t i = 0; i < k; i++) {
|
|
uint64_t w = 0;
|
|
for (size_t b = 8; b-- > 0;) w = (w << 8) | buf[8 * i + b];
|
|
out[i] = w;
|
|
}
|
|
}
|
|
|
|
static bool sd_limbs_write(mbedtls_mpi *x, const uint64_t *in, size_t k, unsigned char *buf) {
|
|
for (size_t i = 0; i < k; i++)
|
|
for (size_t b = 0; b < 8; b++) buf[8 * i + b] = (unsigned char)(in[i] >> (8 * b));
|
|
return mbedtls_mpi_read_binary_le(x, buf, 8 * k) == 0;
|
|
}
|
|
|
|
static bool sd_mont_init(ScDhMont *m, const mbedtls_mpi *n) {
|
|
size_t k = (mbedtls_mpi_bitlen(n) + 63) / 64;
|
|
m->k = k;
|
|
m->n = calloc(21 * k + 1, sizeof(uint64_t));
|
|
if (!m->n) scr_trap("scriptc: out of memory\n");
|
|
m->rr = m->n + k;
|
|
m->acc = m->rr + k;
|
|
m->table = m->acc + k;
|
|
m->t = m->table + 16 * k;
|
|
unsigned char *buf = malloc(8 * k);
|
|
if (!buf) scr_trap("scriptc: out of memory\n");
|
|
sd_limbs_read(m->n, k, n, buf);
|
|
/* Newton's iteration doubles the correct low bits of n^-1 from 3. */
|
|
uint64_t inverse = m->n[0];
|
|
for (int i = 0; i < 6; i++) inverse *= 2 - m->n[0] * inverse;
|
|
m->mm = (uint64_t)0 - inverse;
|
|
mbedtls_mpi rr;
|
|
mbedtls_mpi_init(&rr);
|
|
bool ok = mbedtls_mpi_lset(&rr, 1) == 0 && mbedtls_mpi_shift_l(&rr, 128 * k) == 0 &&
|
|
mbedtls_mpi_mod_mpi(&rr, &rr, n) == 0;
|
|
if (ok) sd_limbs_read(m->rr, k, &rr, buf);
|
|
mbedtls_mpi_free(&rr);
|
|
free(buf);
|
|
return ok;
|
|
}
|
|
|
|
static void sd_mont_free(ScDhMont *m) {
|
|
free(m->n);
|
|
}
|
|
|
|
/* r = v mod n for v < 2n, given as k limbs and a top limb. */
|
|
static void sd_mont_final(const ScDhMont *m, uint64_t *r, const uint64_t *v, uint64_t top) {
|
|
size_t k = m->k;
|
|
bool ge = top != 0;
|
|
if (!ge) {
|
|
ge = true;
|
|
for (size_t j = k; j-- > 0;)
|
|
if (v[j] != m->n[j]) {
|
|
ge = v[j] > m->n[j];
|
|
break;
|
|
}
|
|
}
|
|
if (!ge) {
|
|
if (r != v) memmove(r, v, k * sizeof *r);
|
|
return;
|
|
}
|
|
uint64_t borrow = 0;
|
|
for (size_t j = 0; j < k; j++) {
|
|
sd_u128 d = (sd_u128)v[j] - m->n[j] - borrow;
|
|
r[j] = (uint64_t)d;
|
|
borrow = (uint64_t)(d >> 64) & 1;
|
|
}
|
|
}
|
|
|
|
/* r = t·R^-1 mod n for the 2k-limb product in t. */
|
|
static void sd_mont_redc(ScDhMont *m, uint64_t *r) {
|
|
size_t k = m->k;
|
|
uint64_t *t = m->t;
|
|
t[2 * k] = 0;
|
|
for (size_t i = 0; i < k; i++) {
|
|
uint64_t q = t[i] * m->mm;
|
|
uint64_t carry = 0;
|
|
for (size_t j = 0; j < k; j++) {
|
|
sd_u128 v = (sd_u128)q * m->n[j] + t[i + j] + carry;
|
|
t[i + j] = (uint64_t)v;
|
|
carry = (uint64_t)(v >> 64);
|
|
}
|
|
for (size_t j = i + k; carry != 0 && j <= 2 * k; j++) {
|
|
sd_u128 v = (sd_u128)t[j] + carry;
|
|
t[j] = (uint64_t)v;
|
|
carry = (uint64_t)(v >> 64);
|
|
}
|
|
}
|
|
sd_mont_final(m, r, t + k, t[2 * k]);
|
|
}
|
|
|
|
static void sd_mont_mul(ScDhMont *m, uint64_t *r, const uint64_t *a, const uint64_t *b) {
|
|
size_t k = m->k;
|
|
uint64_t *t = m->t;
|
|
memset(t, 0, 2 * k * sizeof *t);
|
|
for (size_t i = 0; i < k; i++) {
|
|
uint64_t carry = 0;
|
|
for (size_t j = 0; j < k; j++) {
|
|
sd_u128 v = (sd_u128)a[i] * b[j] + t[i + j] + carry;
|
|
t[i + j] = (uint64_t)v;
|
|
carry = (uint64_t)(v >> 64);
|
|
}
|
|
t[i + k] = carry;
|
|
}
|
|
sd_mont_redc(m, r);
|
|
}
|
|
|
|
/* r = a^2·R^-1 mod n: the cross products once, doubled, plus the squares. */
|
|
static void sd_mont_sqr(ScDhMont *m, uint64_t *r, const uint64_t *a) {
|
|
size_t k = m->k;
|
|
uint64_t *t = m->t;
|
|
memset(t, 0, 2 * k * sizeof *t);
|
|
for (size_t i = 0; i < k; i++) {
|
|
uint64_t carry = 0;
|
|
for (size_t j = i + 1; j < k; j++) {
|
|
sd_u128 v = (sd_u128)a[i] * a[j] + t[i + j] + carry;
|
|
t[i + j] = (uint64_t)v;
|
|
carry = (uint64_t)(v >> 64);
|
|
}
|
|
t[i + k] = carry;
|
|
}
|
|
uint64_t shifted = 0;
|
|
for (size_t i = 0; i < 2 * k; i++) {
|
|
uint64_t v = t[i];
|
|
t[i] = (v << 1) | shifted;
|
|
shifted = v >> 63;
|
|
}
|
|
uint64_t carry = 0;
|
|
for (size_t i = 0; i < k; i++) {
|
|
sd_u128 square = (sd_u128)a[i] * a[i];
|
|
sd_u128 low = (sd_u128)t[2 * i] + (uint64_t)square + carry;
|
|
t[2 * i] = (uint64_t)low;
|
|
sd_u128 high = (sd_u128)t[2 * i + 1] + (uint64_t)(square >> 64) + (uint64_t)(low >> 64);
|
|
t[2 * i + 1] = (uint64_t)high;
|
|
carry = (uint64_t)(high >> 64);
|
|
}
|
|
sd_mont_redc(m, r);
|
|
}
|
|
|
|
/* x = 2x mod n. */
|
|
static void sd_mont_double(const ScDhMont *m, uint64_t *x) {
|
|
size_t k = m->k;
|
|
uint64_t top = x[k - 1] >> 63;
|
|
for (size_t j = k; j-- > 1;) x[j] = (x[j] << 1) | (x[j - 1] >> 63);
|
|
x[0] <<= 1;
|
|
sd_mont_final(m, x, x, top);
|
|
}
|
|
|
|
/* out = base^e mod n, or 2^e mod n for a NULL base. A base of 2 doubles
|
|
* instead of multiplying; others use a 4-bit fixed window. */
|
|
static bool sd_mont_pow(ScDhMont *m, mbedtls_mpi *out, const mbedtls_mpi *base, const mbedtls_mpi *e) {
|
|
size_t k = m->k;
|
|
unsigned char *buf = malloc(8 * k);
|
|
if (!buf) scr_trap("scriptc: out of memory\n");
|
|
/* acc = R mod n, the Montgomery form of 1. */
|
|
memset(m->t, 0, 2 * k * sizeof *m->t);
|
|
memcpy(m->t, m->rr, k * sizeof *m->t);
|
|
sd_mont_redc(m, m->acc);
|
|
size_t bits = mbedtls_mpi_bitlen(e);
|
|
if (!base) {
|
|
for (size_t i = bits; i-- > 0;) {
|
|
sd_mont_sqr(m, m->acc, m->acc);
|
|
if (mbedtls_mpi_get_bit(e, i)) sd_mont_double(m, m->acc);
|
|
}
|
|
} else {
|
|
memcpy(m->table, m->acc, k * sizeof *m->table);
|
|
sd_limbs_read(m->table + k, k, base, buf);
|
|
sd_mont_mul(m, m->table + k, m->table + k, m->rr);
|
|
for (size_t i = 2; i < 16; i++) sd_mont_mul(m, m->table + i * k, m->table + (i - 1) * k, m->table + k);
|
|
size_t windows = (bits + 3) / 4;
|
|
for (size_t w = windows; w-- > 0;) {
|
|
if (w + 1 < windows)
|
|
for (int s = 0; s < 4; s++) sd_mont_sqr(m, m->acc, m->acc);
|
|
unsigned digit = 0;
|
|
for (size_t b = 4; b-- > 0;) digit = (digit << 1) | (unsigned)mbedtls_mpi_get_bit(e, 4 * w + b);
|
|
if (digit) sd_mont_mul(m, m->acc, m->acc, m->table + digit * k);
|
|
}
|
|
}
|
|
/* Leave the Montgomery form. */
|
|
memset(m->t, 0, 2 * k * sizeof *m->t);
|
|
memcpy(m->t, m->acc, k * sizeof *m->t);
|
|
sd_mont_redc(m, m->acc);
|
|
bool ok = sd_limbs_write(out, m->acc, k, buf);
|
|
free(buf);
|
|
return ok;
|
|
}
|
|
#else
|
|
#define SD_MONT 0
|
|
#endif
|
|
|
|
/* Miller-Rabin state for an odd n > 3: n - 1 = d·2^s. */
|
|
typedef struct {
|
|
mbedtls_mpi n1, d, a, x;
|
|
size_t s;
|
|
#if SD_MONT
|
|
ScDhMont mont;
|
|
#else
|
|
mbedtls_mpi rr; /* the Montgomery constant mbedtls_mpi_exp_mod caches */
|
|
#endif
|
|
} ScDhMillerRabin;
|
|
|
|
static bool sd_mr_init(ScDhMillerRabin *m, const mbedtls_mpi *n) {
|
|
mbedtls_mpi_init(&m->n1);
|
|
mbedtls_mpi_init(&m->d);
|
|
mbedtls_mpi_init(&m->a);
|
|
mbedtls_mpi_init(&m->x);
|
|
#if SD_MONT
|
|
if (!sd_mont_init(&m->mont, n)) return false;
|
|
#else
|
|
mbedtls_mpi_init(&m->rr);
|
|
#endif
|
|
if (mbedtls_mpi_sub_int(&m->n1, n, 1) != 0 || mbedtls_mpi_copy(&m->d, &m->n1) != 0) return false;
|
|
m->s = mbedtls_mpi_lsb(&m->d);
|
|
return mbedtls_mpi_shift_r(&m->d, m->s) == 0;
|
|
}
|
|
|
|
static void sd_mr_free(ScDhMillerRabin *m) {
|
|
mbedtls_mpi_free(&m->n1);
|
|
mbedtls_mpi_free(&m->d);
|
|
mbedtls_mpi_free(&m->a);
|
|
mbedtls_mpi_free(&m->x);
|
|
#if SD_MONT
|
|
sd_mont_free(&m->mont);
|
|
#else
|
|
mbedtls_mpi_free(&m->rr);
|
|
#endif
|
|
}
|
|
|
|
/* x = a^e mod n, or 2^e mod n when base2. */
|
|
static bool sd_mr_pow(ScDhMillerRabin *m, const mbedtls_mpi *n, const mbedtls_mpi *e, bool base2) {
|
|
#if SD_MONT
|
|
(void)n;
|
|
return sd_mont_pow(&m->mont, &m->x, base2 ? NULL : &m->a, e);
|
|
#else
|
|
if (base2 && mbedtls_mpi_lset(&m->a, 2) != 0) return false;
|
|
return mbedtls_mpi_exp_mod(&m->x, &m->a, e, n, &m->rr) == 0;
|
|
#endif
|
|
}
|
|
|
|
/* One round with the base 2, or with a uniformly random base in
|
|
* [2, n - 2]: 1 when n is a strong probable prime to the base, 0 when it
|
|
* is composite, -1 when an operation fails. */
|
|
static int sd_mr_round(ScDhMillerRabin *m, const mbedtls_mpi *n, bool base2) {
|
|
if (!base2 && mbedtls_mpi_random(&m->a, 2, &m->n1, sd_random, NULL) != 0) return -1;
|
|
if (!sd_mr_pow(m, n, &m->d, base2)) return -1;
|
|
if (mbedtls_mpi_cmp_int(&m->x, 1) == 0 || mbedtls_mpi_cmp_mpi(&m->x, &m->n1) == 0) return 1;
|
|
for (size_t i = 1; i < m->s; i++) {
|
|
if (mbedtls_mpi_mul_mpi(&m->x, &m->x, &m->x) != 0 || mbedtls_mpi_mod_mpi(&m->x, &m->x, n) != 0) return -1;
|
|
if (mbedtls_mpi_cmp_mpi(&m->x, &m->n1) == 0) return 1;
|
|
if (mbedtls_mpi_cmp_int(&m->x, 1) == 0) return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* 2^(n-1) ≡ 1 (mod n) for an odd n > 3. */
|
|
static int sd_fermat2(const mbedtls_mpi *n) {
|
|
ScDhMillerRabin m;
|
|
int ret = sd_mr_init(&m, n) && sd_mr_pow(&m, n, &m.n1, true) ? mbedtls_mpi_cmp_int(&m.x, 1) == 0 : -1;
|
|
sd_mr_free(&m);
|
|
return ret;
|
|
}
|
|
|
|
/* OpenSSL's Miller-Rabin round count: 64, or 128 past 2048 bits. */
|
|
static int sd_mr_rounds(size_t bits) {
|
|
return bits > 2048 ? 128 : 64;
|
|
}
|
|
|
|
/* BN_is_prime_ex(x, checks): trial division by the small primes, then
|
|
* Miller-Rabin rounds with random bases, at least OpenSSL's round count. */
|
|
static bool sd_is_prime_checks(ScDhSmallPrimes *t, const mbedtls_mpi *x, int checks) {
|
|
if (mbedtls_mpi_cmp_int(x, 2) < 0) return false;
|
|
if (mbedtls_mpi_get_bit(x, 0) == 0) return mbedtls_mpi_cmp_int(x, 2) == 0;
|
|
if (!sd_small_residues(t, x)) return false;
|
|
for (size_t i = 0; i < SD_SMALL_PRIMES; i++)
|
|
if (t->mods[i] == 0) return mbedtls_mpi_cmp_int(x, t->primes[i]) == 0;
|
|
/* No prime factor up to sqrt(x). */
|
|
if (mbedtls_mpi_cmp_int(x, (mbedtls_mpi_sint)SD_SMALL_LIMIT * SD_SMALL_LIMIT) < 0) return true;
|
|
ScDhMillerRabin m;
|
|
bool prime = sd_mr_init(&m, x);
|
|
int rounds = sd_mr_rounds(mbedtls_mpi_bitlen(x));
|
|
if (checks > rounds) rounds = checks;
|
|
for (int i = 0; i < rounds && prime; i++) prime = sd_mr_round(&m, x, false) == 1;
|
|
sd_mr_free(&m);
|
|
return prime;
|
|
}
|
|
|
|
/* BN_check_prime. */
|
|
static bool sd_is_prime(ScDhSmallPrimes *t, const mbedtls_mpi *x) {
|
|
return sd_is_prime_checks(t, x, 0);
|
|
}
|
|
|
|
/* DH_check for parameters without q, as ncrypto's CheckDhParams reports
|
|
* it. */
|
|
static int sd_check_params(const mbedtls_mpi *p, const mbedtls_mpi *g) {
|
|
int codes = 0;
|
|
if (mbedtls_mpi_get_bit(p, 0) == 0) codes |= SD_P_NOT_PRIME;
|
|
if (mbedtls_mpi_cmp_int(g, 1) <= 0) codes |= SD_NOT_SUITABLE_GENERATOR;
|
|
if (mbedtls_mpi_bitlen(p) < SD_MIN_MODULUS_BITS) codes |= SD_MODULUS_TOO_SMALL;
|
|
mbedtls_mpi t;
|
|
mbedtls_mpi_init(&t);
|
|
if (mbedtls_mpi_sub_int(&t, p, 1) != 0) scr_trap("scriptc: out of memory\n");
|
|
if (mbedtls_mpi_cmp_mpi(g, &t) >= 0) codes |= SD_NOT_SUITABLE_GENERATOR;
|
|
/* BN_check_prime(p), then BN_check_prime((p - 1) / 2) for a prime p.
|
|
* For an odd p > 3 whose (p - 1) / 2 is prime, p is prime exactly when
|
|
* 3 does not divide it and 2^(p-1) ≡ 1 (mod p) (Pocklington's
|
|
* criterion), so (p - 1) / 2 is tested first. */
|
|
ScDhSmallPrimes *small = sd_small_primes();
|
|
if (mbedtls_mpi_copy(&t, p) != 0 || mbedtls_mpi_shift_r(&t, 1) != 0) scr_trap("scriptc: out of memory\n");
|
|
bool p_prime, q_prime;
|
|
if (mbedtls_mpi_get_bit(p, 0) == 0 || mbedtls_mpi_cmp_int(p, 3) <= 0) {
|
|
p_prime = sd_is_prime(small, p);
|
|
q_prime = p_prime && sd_is_prime(small, &t);
|
|
} else {
|
|
q_prime = sd_is_prime(small, &t);
|
|
mbedtls_mpi_uint r = 0;
|
|
if (!q_prime) p_prime = sd_is_prime(small, p);
|
|
else p_prime = mbedtls_mpi_mod_int(&r, p, 3) == 0 && r != 0 && sd_fermat2(p) == 1;
|
|
}
|
|
if (!p_prime) codes |= SD_P_NOT_PRIME;
|
|
else if (!q_prime) codes |= SD_P_NOT_SAFE_PRIME;
|
|
free(small);
|
|
mbedtls_mpi_free(&t);
|
|
return codes;
|
|
}
|
|
|
|
/* The named group whose p and g equal these, as ossl_dh_cache_named_group
|
|
* finds it, or NULL. */
|
|
static const ScDhNamed *sd_find_named(const mbedtls_mpi *p, const mbedtls_mpi *g) {
|
|
size_t p_len = mbedtls_mpi_size(p);
|
|
const ScDhNamed *found = NULL;
|
|
mbedtls_mpi x;
|
|
mbedtls_mpi_init(&x);
|
|
for (size_t i = 0; i < sizeof sd_named / sizeof sd_named[0] && !found; i++) {
|
|
const ScDhNamed *n = &sd_named[i];
|
|
if (n->p_len != p_len) continue;
|
|
if (mbedtls_mpi_read_binary(&x, n->p, n->p_len) != 0 || mbedtls_mpi_cmp_mpi(&x, p) != 0) continue;
|
|
if (mbedtls_mpi_read_binary(&x, n->g, n->g_len) == 0 && mbedtls_mpi_cmp_mpi(&x, g) == 0) found = n;
|
|
}
|
|
mbedtls_mpi_free(&x);
|
|
return found;
|
|
}
|
|
|
|
/* The safe-prime sieve: the odd primes below a bound that grows with the
|
|
* prime's length and do not divide the step `add`, each with add^-1 and a
|
|
* start's residue modulo it, and the marks of a window of candidates
|
|
* start + k·add. */
|
|
#define SD_SIEVE_WINDOW 65536
|
|
|
|
static uint32_t sd_sieve_bound(size_t bits) {
|
|
/* mbedtls_mpi_mod_int divides correctly by values below 2^(limb bits / 2). */
|
|
if (sizeof(mbedtls_mpi_uint) < 8) return 1u << 16;
|
|
return bits < 1024 ? 1u << 18 : bits < 2048 ? 1u << 22 : 1u << 24;
|
|
}
|
|
|
|
typedef struct {
|
|
uint32_t *primes;
|
|
uint32_t *inverse;
|
|
uint32_t *residue;
|
|
size_t count;
|
|
unsigned char marks[SD_SIEVE_WINDOW];
|
|
} ScDhSieve;
|
|
|
|
/* a^-1 mod m for gcd(a, m) = 1. */
|
|
static uint32_t sd_inverse(uint32_t a, uint32_t m) {
|
|
int64_t t = 0, next = 1;
|
|
int64_t r = m, rest = a;
|
|
while (rest != 0) {
|
|
int64_t quotient = r / rest;
|
|
int64_t t_next = t - quotient * next;
|
|
t = next;
|
|
next = t_next;
|
|
int64_t r_next = r - quotient * rest;
|
|
r = rest;
|
|
rest = r_next;
|
|
}
|
|
return (uint32_t)(t < 0 ? t + m : t);
|
|
}
|
|
|
|
static ScDhSieve *sd_sieve_new(size_t bits, unsigned add) {
|
|
uint32_t bound = sd_sieve_bound(bits);
|
|
ScDhSieve *sv = calloc(1, sizeof *sv);
|
|
unsigned char *composite = calloc(bound / 2, 1); /* composite[i] for 2i + 1 */
|
|
if (!sv || !composite) scr_trap("scriptc: out of memory\n");
|
|
size_t count = 0;
|
|
for (uint32_t i = 1; i < bound / 2; i++) {
|
|
if (composite[i]) continue;
|
|
uint32_t n = 2 * i + 1;
|
|
for (uint64_t m = (uint64_t)n * n; m < bound; m += 2 * n) composite[m / 2] = 1;
|
|
if (add % n != 0) count++;
|
|
}
|
|
sv->primes = malloc(count * sizeof(uint32_t));
|
|
sv->inverse = malloc(count * sizeof(uint32_t));
|
|
sv->residue = malloc(count * sizeof(uint32_t));
|
|
if (!sv->primes || !sv->inverse || !sv->residue) scr_trap("scriptc: out of memory\n");
|
|
for (uint32_t i = 1; i < bound / 2; i++) {
|
|
uint32_t n = 2 * i + 1;
|
|
if (composite[i] || add % n == 0) continue;
|
|
sv->inverse[sv->count] = sd_inverse(add % n, n);
|
|
sv->primes[sv->count++] = n;
|
|
}
|
|
free(composite);
|
|
return sv;
|
|
}
|
|
|
|
static void sd_sieve_free(ScDhSieve *sv) {
|
|
free(sv->primes);
|
|
free(sv->inverse);
|
|
free(sv->residue);
|
|
free(sv);
|
|
}
|
|
|
|
/* Marks the candidates start + k·add of the window that a sieve prime
|
|
* divides, or whose (p - 1) / 2 it divides, then moves the residues to
|
|
* the next window. */
|
|
static void sd_sieve_window(ScDhSieve *sv, unsigned add) {
|
|
memset(sv->marks, 0, sizeof sv->marks);
|
|
for (size_t i = 0; i < sv->count; i++) {
|
|
uint64_t r = sv->primes[i];
|
|
uint64_t s = sv->residue[i];
|
|
/* start + k·add ≡ t (mod r) for t = 0 and t = 1. */
|
|
for (uint64_t t = 0; t < 2; t++) {
|
|
uint64_t k = (r + t - s) % r * sv->inverse[i] % r;
|
|
for (; k < SD_SIEVE_WINDOW; k += r) sv->marks[k] = 1;
|
|
}
|
|
sv->residue[i] = (uint32_t)((s + (uint64_t)SD_SIEVE_WINDOW * add) % r);
|
|
}
|
|
}
|
|
|
|
/* A random safe prime of exactly `bits` bits with p ≡ rem (mod add),
|
|
* rem ≡ 2 (mod 3), as BN_generate_prime_ex2(p, bits, safe = 1, add, rem)
|
|
* draws one: a random start of `bits` bits moved to p ≡ rem (mod add),
|
|
* then candidates stepping by `add` past values that a sieve prime
|
|
* divides or that make (p - 1) / 2 divisible by one. A candidate is kept
|
|
* when p is a strong probable prime to the base 2 and q = (p - 1) / 2
|
|
* passes OpenSSL's Miller-Rabin round count; a prime q then proves p prime
|
|
* (Pocklington's criterion with 2^(p-1) ≡ 1 and gcd(2^2 - 1, p) = 1).
|
|
* Candidates past `bits` bits take a new start. With `top_two`, starts
|
|
* also set the second-highest bit, as BN_RAND_TOP_TWO does. */
|
|
static bool sd_generate_safe_prime(mbedtls_mpi *p, size_t bits, unsigned add, unsigned rem, bool top_two) {
|
|
ScDhSieve *sv = sd_sieve_new(bits, add);
|
|
int rounds = sd_mr_rounds(bits);
|
|
mbedtls_mpi start, q;
|
|
mbedtls_mpi_init(&start);
|
|
mbedtls_mpi_init(&q);
|
|
int ret = 0;
|
|
while (ret == 0) {
|
|
mbedtls_mpi_uint r;
|
|
if (!sd_random_bits(&start, bits, true) || (top_two && mbedtls_mpi_set_bit(&start, bits - 2, 1) != 0) ||
|
|
mbedtls_mpi_mod_int(&r, &start, add) != 0 ||
|
|
mbedtls_mpi_sub_int(&start, &start, (mbedtls_mpi_sint)r) != 0 ||
|
|
mbedtls_mpi_add_int(&start, &start, rem) != 0 ||
|
|
(mbedtls_mpi_bitlen(&start) < bits && mbedtls_mpi_add_int(&start, &start, add) != 0)) {
|
|
ret = -1;
|
|
break;
|
|
}
|
|
for (size_t i = 0; i < sv->count && ret == 0; i++) {
|
|
if (mbedtls_mpi_mod_int(&r, &start, sv->primes[i]) != 0) ret = -1;
|
|
sv->residue[i] = (uint32_t)r;
|
|
}
|
|
bool fits = true;
|
|
while (ret == 0 && fits) {
|
|
sd_sieve_window(sv, add);
|
|
for (uint32_t k = 0; k < SD_SIEVE_WINDOW && ret == 0 && fits; k++) {
|
|
if (sv->marks[k]) continue;
|
|
if (mbedtls_mpi_add_int(p, &start, (mbedtls_mpi_sint)k * add) != 0 || mbedtls_mpi_copy(&q, p) != 0 ||
|
|
mbedtls_mpi_shift_r(&q, 1) != 0) {
|
|
ret = -1;
|
|
break;
|
|
}
|
|
fits = mbedtls_mpi_bitlen(p) == bits;
|
|
if (!fits) break;
|
|
ScDhMillerRabin mr;
|
|
ret = sd_mr_init(&mr, p) ? sd_mr_round(&mr, p, true) : -1;
|
|
sd_mr_free(&mr);
|
|
if (ret != 1) continue;
|
|
ret = sd_mr_init(&mr, &q) ? sd_mr_round(&mr, &q, true) : -1;
|
|
for (int i = 1; i < rounds && ret == 1; i++) ret = sd_mr_round(&mr, &q, false);
|
|
sd_mr_free(&mr);
|
|
}
|
|
if (ret == 0 && fits && mbedtls_mpi_add_int(&start, &start, (mbedtls_mpi_sint)SD_SIEVE_WINDOW * add) != 0)
|
|
ret = -1;
|
|
}
|
|
}
|
|
sd_sieve_free(sv);
|
|
mbedtls_mpi_free(&start);
|
|
mbedtls_mpi_free(&q);
|
|
return ret == 1;
|
|
}
|
|
|
|
/* ── construction ─────────────────────────────────────────────────────── */
|
|
|
|
static ScDh *sd_alloc(void) {
|
|
ScDh *d = calloc(1, sizeof *d);
|
|
if (!d) scr_trap("scriptc: out of memory\n");
|
|
d->rc = 1;
|
|
mbedtls_mpi_init(&d->p);
|
|
mbedtls_mpi_init(&d->g);
|
|
mbedtls_mpi_init(&d->pub);
|
|
mbedtls_mpi_init(&d->priv);
|
|
return d;
|
|
}
|
|
|
|
static ScrDyn *sd_box(ScDh *d) {
|
|
ScrDynHandleTag tag = d->group ? SCR_DYNH_DIFFIE_HELLMAN_GROUP : SCR_DYNH_DIFFIE_HELLMAN;
|
|
scr_dyn_handle_install(tag, d->group ? &sd_group_ops : &sd_ops);
|
|
ScrDyn *result = scr_dyn_new_handle(d, tag); /* retains */
|
|
sd_release(d);
|
|
return result;
|
|
}
|
|
|
|
static bool sd_is_view(const ScrDyn *value) {
|
|
return value->kind == SCR_DYN_BYTES || scr_buffer_storage_is(value);
|
|
}
|
|
|
|
/* Buffer.isEncoding(value). */
|
|
static bool sd_is_encoding(const ScrDyn *value) {
|
|
return value->kind == SCR_DYN_STR && value->v.str->len > 0 && ca_normalize_encoding(value->v.str) != NULL;
|
|
}
|
|
|
|
/* new DiffieHellman(primeLength, generator): generated parameters. */
|
|
static ScrDyn *sd_generate(double bits, const ScrBytes *generator_bytes, double generator) {
|
|
if (bits < 2) {
|
|
sd_throw_openssl("0280007E", "modulus too small", "ERR_OSSL_DH_MODULUS_TOO_SMALL");
|
|
return NULL;
|
|
}
|
|
if (generator_bytes) {
|
|
sd_throw(SCR_ERR_TYPE, "ERR_INVALID_ARG_TYPE", "Second argument must be an int32");
|
|
return NULL;
|
|
}
|
|
if (generator < 2) {
|
|
sd_bad_generator();
|
|
return NULL;
|
|
}
|
|
if (bits < SD_MIN_MODULUS_BITS || bits > SD_MAX_MODULUS_BITS) {
|
|
sd_throw(SCR_ERR_TYPE, "ERR_INVALID_ARG_VALUE", "Invalid DH parameters");
|
|
return NULL;
|
|
}
|
|
if (bits > MBEDTLS_MPI_MAX_BITS) {
|
|
sd_too_long();
|
|
return NULL;
|
|
}
|
|
unsigned add = 12;
|
|
if (generator == 2) add = 24;
|
|
else if (generator == 5) add = 60;
|
|
ScDh *d = sd_alloc();
|
|
if (!sd_generate_safe_prime(&d->p, (size_t)bits, add, add - 1, false) || mbedtls_mpi_lset(&d->g, (mbedtls_mpi_sint)generator) != 0) {
|
|
sd_release(d);
|
|
sd_throw(SCR_ERR_TYPE, "ERR_INVALID_ARG_VALUE", "Invalid DH parameters");
|
|
return NULL;
|
|
}
|
|
/* A safe prime of at least 512 bits with a generator below p - 1 has no
|
|
* DH_check findings. */
|
|
d->verify_error = 0;
|
|
return sd_box(d);
|
|
}
|
|
|
|
/* new DiffieHellman(prime, generator) over explicit parameters. */
|
|
static ScrDyn *sd_from_prime(const ScrBytes *prime, const ScrBytes *generator_bytes, double generator) {
|
|
ScDh *d = sd_alloc();
|
|
bool p_held = sd_read(&d->p, prime);
|
|
bool g_held = true;
|
|
if (generator_bytes) g_held = sd_read(&d->g, generator_bytes);
|
|
else if (mbedtls_mpi_lset(&d->g, (mbedtls_mpi_sint)generator) != 0) scr_trap("scriptc: out of memory\n");
|
|
if (!p_held || !g_held) {
|
|
/* A prime this long is past MBEDTLS_MPI_MAX_BITS, and a generator
|
|
* this long is at least a prime of SD_MIN_MODULUS_BITS. */
|
|
bool bad = g_held ? mbedtls_mpi_cmp_int(&d->g, 2) < 0
|
|
: !p_held ? sd_cmp_bytes(generator_bytes, prime) >= 0
|
|
: mbedtls_mpi_bitlen(&d->p) >= SD_MIN_MODULUS_BITS;
|
|
sd_release(d);
|
|
if (bad) sd_bad_generator();
|
|
else if (!p_held) sd_too_long();
|
|
else sd_value_too_long();
|
|
return NULL;
|
|
}
|
|
if (mbedtls_mpi_cmp_int(&d->g, 2) < 0 ||
|
|
(mbedtls_mpi_bitlen(&d->p) >= SD_MIN_MODULUS_BITS && mbedtls_mpi_cmp_mpi(&d->g, &d->p) >= 0)) {
|
|
sd_release(d);
|
|
sd_bad_generator();
|
|
return NULL;
|
|
}
|
|
if (mbedtls_mpi_bitlen(&d->p) > MBEDTLS_MPI_MAX_BITS) {
|
|
sd_release(d);
|
|
sd_too_long();
|
|
return NULL;
|
|
}
|
|
d->named = sd_find_named(&d->p, &d->g);
|
|
/* A named group's parameters have no DH_check findings. Other
|
|
* parameters are checked when verifyError is first read. */
|
|
d->verify_error = d->named ? 0 : -1;
|
|
return sd_box(d);
|
|
}
|
|
|
|
/* new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding). */
|
|
static ScrDyn *sd_new(const ScrDyn *size_or_key, const ScrDyn *key_encoding, const ScrDyn *generator,
|
|
const ScrDyn *generator_encoding) {
|
|
static const char *const types =
|
|
"one of type number or string or an instance of ArrayBuffer, Buffer, TypedArray, or DataView";
|
|
bool size = size_or_key->kind == SCR_DYN_NUM;
|
|
if (!size && size_or_key->kind != SCR_DYN_STR && !sd_is_view(size_or_key)) {
|
|
scr_dyn_arg_type_fail("sizeOrKey", types, size_or_key);
|
|
return NULL;
|
|
}
|
|
if (size && !sd_validate_int32("sizeOrKey", size_or_key->v.num)) return NULL;
|
|
/* A truthy keyEncoding that names no encoding is the generator. */
|
|
const ScrDyn *gen = generator;
|
|
const ScrDyn *gen_encoding = generator_encoding;
|
|
const ScrDyn *encoding = key_encoding;
|
|
if (scr_dyn_truthy(key_encoding) && !sd_is_encoding(key_encoding) &&
|
|
!(key_encoding->kind == SCR_DYN_STR && ca_str_is(key_encoding->v.str, "buffer"))) {
|
|
gen_encoding = generator;
|
|
gen = key_encoding;
|
|
encoding = scr_dyn_undefined();
|
|
}
|
|
ScrBytes *prime = NULL;
|
|
if (size_or_key->kind == SCR_DYN_STR) prime = ca_string_bytes(SD_SUBJECT, size_or_key->v.str, encoding);
|
|
else if (!size) prime = scr_bytes_buffer_source(size_or_key);
|
|
if (!size && !prime) return NULL;
|
|
ScrBytes *gen_bytes = NULL;
|
|
double gen_number = 2;
|
|
ScrDyn *result = NULL;
|
|
if (!scr_dyn_truthy(gen)) {
|
|
gen_number = 2;
|
|
} else if (gen->kind == SCR_DYN_NUM) {
|
|
if (!sd_validate_int32("generator", gen->v.num)) goto done;
|
|
gen_number = gen->v.num;
|
|
} else if (gen->kind == SCR_DYN_STR) {
|
|
if (!(gen_bytes = ca_string_bytes(SD_SUBJECT, gen->v.str, gen_encoding))) goto done;
|
|
} else if (sd_is_view(gen)) {
|
|
gen_bytes = scr_bytes_buffer_source(gen);
|
|
} else {
|
|
scr_dyn_arg_type_fail("generator", types, gen);
|
|
goto done;
|
|
}
|
|
result = size ? sd_generate(size_or_key->v.num + 0, gen_bytes, gen_number)
|
|
: sd_from_prime(prime, gen_bytes, gen_number);
|
|
done:
|
|
if (prime) scr_bytes_release(prime);
|
|
if (gen_bytes) scr_bytes_release(gen_bytes);
|
|
return result;
|
|
}
|
|
|
|
ScrDyn *scr_crypto_dh_new(ScrDyn *size_or_key, ScrDyn *key_encoding, ScrDyn *generator, ScrDyn *generator_encoding) {
|
|
ScrDyn *args[4] = {size_or_key, key_encoding, generator, generator_encoding};
|
|
ScrDyn *unboxed[4] = {NULL, NULL, NULL, NULL};
|
|
ScrDyn *result = NULL;
|
|
for (size_t i = 0; i < 4; i++)
|
|
if (!(unboxed[i] = ca_unbox(args[i]))) goto done;
|
|
result = sd_new(unboxed[0], unboxed[1], unboxed[2], unboxed[3]);
|
|
done:
|
|
for (size_t i = 0; i < 4; i++) scr_dyn_release(unboxed[i]);
|
|
return result;
|
|
}
|
|
|
|
/* new DiffieHellmanGroup(name): names match case-insensitively. */
|
|
ScrDyn *scr_crypto_dh_group_new(ScrDyn *name) {
|
|
ScrDyn *unboxed = ca_unbox(name);
|
|
if (!unboxed) return NULL;
|
|
if (unboxed->kind != SCR_DYN_STR) {
|
|
scr_dyn_release(unboxed);
|
|
sd_throw(SCR_ERR_TYPE, "ERR_INVALID_ARG_TYPE", "Group name must be a string");
|
|
return NULL;
|
|
}
|
|
const ScDhGroup *group = NULL;
|
|
for (size_t i = 0; i < sizeof sd_groups / sizeof sd_groups[0] && !group; i++)
|
|
if (ca_ascii_eq_ci(unboxed->v.str->data, unboxed->v.str->len, sd_groups[i].name)) group = &sd_groups[i];
|
|
scr_dyn_release(unboxed);
|
|
if (!group) {
|
|
sd_throw(SCR_ERR_ERROR, "ERR_CRYPTO_UNKNOWN_DH_GROUP", "Unknown DH group");
|
|
return NULL;
|
|
}
|
|
ScDh *d = sd_alloc();
|
|
d->group = group;
|
|
if (mbedtls_mpi_read_binary(&d->p, group->p, group->p_len) != 0 || mbedtls_mpi_lset(&d->g, 2) != 0)
|
|
scr_trap("scriptc: out of memory\n");
|
|
d->verify_error = 0;
|
|
return sd_box(d);
|
|
}
|
|
|
|
/* ── checkPrime and generatePrime ─────────────────────────────────────────
|
|
* crypto.checkPrime(Sync) and generatePrime(Sync) over the primality test
|
|
* and prime search above, with random.js's argument checks and ncrypto's
|
|
* RandomPrimeConfig and BN_generate_prime_ex2 errors. The callback forms
|
|
* compute at once and call back in a later macrotask. */
|
|
|
|
/* The longest candidate the Miller-Rabin rounds take: sd_mont_init's R^2
|
|
* has twice the candidate's limbs, within MBEDTLS_MPI_MAX_LIMBS, and
|
|
* mbedtls_mpi_exp_mod stops at MBEDTLS_MPI_MAX_BITS. */
|
|
#if SD_MONT
|
|
#define SD_MAX_PRIME_BITS 262144
|
|
#else
|
|
#define SD_MAX_PRIME_BITS MBEDTLS_MPI_MAX_BITS
|
|
#endif
|
|
|
|
static void sd_prime_too_long(const char *what) {
|
|
char message[128];
|
|
int n = snprintf(message, sizeof message, "%s longer than %d bits have no native implementation", what,
|
|
SD_MAX_PRIME_BITS);
|
|
scr_throw_error_msg_code(SCR_ERR_ERROR, message, n < 0 ? 0 : (size_t)n, "SC2020");
|
|
}
|
|
|
|
/* An OpenSSL bignum error as a crypto job reports it: an Error with the
|
|
* formatted message and no other properties. */
|
|
static void sd_throw_bignum(const char *packed, const char *reason) {
|
|
char message[96];
|
|
int n = snprintf(message, sizeof message, "error:%s:bignum routines::%s", packed, reason);
|
|
ScrStr *text = scr_str_new(message, n < 0 ? 0 : (size_t)n);
|
|
ScrError *e = scr_error_new(SCR_ERR_ERROR, text);
|
|
scr_str_release(text);
|
|
scr_throw_obj(e, &scr_error_retain_v, &scr_error_release_v, scr_error_trace_arg());
|
|
}
|
|
|
|
/* validateObject(value, name): an object that is neither null nor an
|
|
* array. */
|
|
static bool sd_validate_object(const char *name, const ScrDyn *value) {
|
|
ScrStr *type = scr_dyn_typeof(value);
|
|
bool object = type->len == 6 && memcmp(type->data, "object", 6) == 0;
|
|
scr_str_release(type);
|
|
if (object && value->kind != SCR_DYN_NULL && value->kind != SCR_DYN_ARR) return true;
|
|
scr_dyn_arg_type_fail(name, "of type object", value);
|
|
return false;
|
|
}
|
|
|
|
static bool sd_validate_function(const char *name, const ScrDyn *value) {
|
|
if (scr_dyn_is_callable(value)) return true;
|
|
scr_dyn_arg_type_fail(name, "of type function", value);
|
|
return false;
|
|
}
|
|
|
|
/* options[key] (borrowed), or undefined. */
|
|
static const ScrDyn *sd_option(const ScrDyn *options, const char *key) {
|
|
const ScrDyn *value = options->kind == SCR_DYN_OBJ ? scr_dyn_obj_get(options, key, strlen(key)) : NULL;
|
|
return value ? value : scr_dyn_undefined();
|
|
}
|
|
|
|
static const char sd_large_number_types[] =
|
|
"of type bigint or an instance of ArrayBuffer, TypedArray, Buffer, or DataView";
|
|
|
|
/* A LargeNumberLike argument as a bignum: a bigint (unsignedBigIntToBuffer,
|
|
* which rejects negative values), or the bytes of an ArrayBuffer or view.
|
|
* False with the error pending. */
|
|
static bool sd_large_number(mbedtls_mpi *x, const char *name, bool property, const ScrDyn *value) {
|
|
if (value->kind == SCR_DYN_BIGINT) {
|
|
ScrStr *text = scr_bigint_to_string(value->v.bigint, 16);
|
|
if (!text) return false;
|
|
bool negative = text->len > 0 && text->data[0] == '-';
|
|
if (negative) {
|
|
scr_str_release(text);
|
|
ScrStr *decimal = scr_bigint_to_string(value->v.bigint, 10);
|
|
if (!decimal) return false;
|
|
/* ERR_OUT_OF_RANGE's received value groups the digits of a bigint
|
|
* past 2^32 in threes. */
|
|
ScrBigInt *limit = scr_bigint_from_i64(-((int64_t)1 << 32));
|
|
bool group = scr_bigint_cmp_f64(value->v.bigint, limit) < 0;
|
|
scr_bigint_release(limit);
|
|
char digits[160];
|
|
size_t len = decimal->len < 120 ? decimal->len : 120, out = 0;
|
|
for (size_t i = 0; i < len; i++) {
|
|
size_t rest = len - i;
|
|
if (group && i > 1 && rest % 3 == 0) digits[out++] = '_';
|
|
digits[out++] = decimal->data[i];
|
|
}
|
|
digits[out] = '\0';
|
|
scr_str_release(decimal);
|
|
char message[256];
|
|
int n = snprintf(message, sizeof message, "The value of \"%s\" is out of range. It must be >= 0. Received %sn",
|
|
name, digits);
|
|
scr_throw_error_msg_code(SCR_ERR_RANGE, message, n < 0 ? 0 : (size_t)n, "ERR_OUT_OF_RANGE");
|
|
return false;
|
|
}
|
|
char *hex = malloc(text->len + 1);
|
|
if (!hex) scr_trap("scriptc: out of memory\n");
|
|
memcpy(hex, text->data, text->len);
|
|
hex[text->len] = '\0';
|
|
scr_str_release(text);
|
|
bool ok = strlen(hex) <= SD_MAX_VALUE_BYTES * 2;
|
|
if (ok && mbedtls_mpi_read_string(x, 16, hex) != 0) scr_trap("scriptc: out of memory\n");
|
|
free(hex);
|
|
if (!ok) sd_value_too_long();
|
|
return ok;
|
|
}
|
|
if (!sd_is_view(value)) {
|
|
if (property) scr_dyn_prop_type_fail(name, sd_large_number_types, value);
|
|
else scr_dyn_arg_type_fail(name, sd_large_number_types, value);
|
|
return false;
|
|
}
|
|
ScrBytes *bytes = scr_bytes_buffer_source(value);
|
|
if (!bytes) return false;
|
|
bool held = sd_read(x, bytes);
|
|
scr_bytes_release(bytes);
|
|
if (!held) sd_value_too_long();
|
|
return held;
|
|
}
|
|
|
|
/* calc_trial_divisions: how many of the first primes (2 included) the
|
|
* search divides a candidate of `bits` bits by. */
|
|
static size_t sd_trial_divisions(size_t bits) {
|
|
return bits <= 512 ? 64 : bits <= 1024 ? 128 : bits <= 2048 ? 384 : bits <= 4096 ? 1024 : 2048;
|
|
}
|
|
|
|
static uint64_t sd_word(const mbedtls_mpi *x) {
|
|
if (mbedtls_mpi_bitlen(x) > 64) return UINT64_MAX;
|
|
unsigned char buf[8];
|
|
if (mbedtls_mpi_write_binary(x, buf, 8) != 0) return UINT64_MAX;
|
|
uint64_t w = 0;
|
|
for (int i = 0; i < 8; i++) w = w << 8 | buf[i];
|
|
return w;
|
|
}
|
|
|
|
static bool sd_add_word(mbedtls_mpi *x, uint64_t w) {
|
|
unsigned char buf[8];
|
|
for (int i = 7; i >= 0; i--, w >>= 8) buf[i] = (unsigned char)w;
|
|
mbedtls_mpi t;
|
|
mbedtls_mpi_init(&t);
|
|
bool ok = mbedtls_mpi_read_binary(&t, buf, 8) == 0 && mbedtls_mpi_add_mpi(x, x, &t) == 0;
|
|
mbedtls_mpi_free(&t);
|
|
return ok;
|
|
}
|
|
|
|
/* probable_prime and bn_probable_prime_dh: a random odd candidate of
|
|
* `bits` bits (the top two bits set, or with `add` the top bit, moved to
|
|
* rem modulo add), then the first one stepping by 2, by 4 for a safe
|
|
* prime, or by add, that no trial-division prime divides, nor for a safe
|
|
* prime divides (p - 1) / 2. */
|
|
static bool sd_probable_prime(ScDhSmallPrimes *t, mbedtls_mpi *rnd, size_t bits, bool safe, const mbedtls_mpi *add,
|
|
const mbedtls_mpi *rem) {
|
|
size_t trials = sd_trial_divisions(bits) - 1; /* odd primes */
|
|
uint64_t step = safe ? 4 : 2;
|
|
uint64_t maxdelta = UINT64_MAX - t->primes[trials - 1];
|
|
if (add) {
|
|
step = sd_word(add);
|
|
if (maxdelta > UINT64_MAX - step) maxdelta = UINT64_MAX - step;
|
|
}
|
|
mbedtls_mpi r;
|
|
mbedtls_mpi_init(&r);
|
|
bool ok = true;
|
|
for (;;) {
|
|
if (!sd_random_bits(rnd, bits, true)) ok = false;
|
|
if (ok && !add) {
|
|
if ((bits >= 2 && mbedtls_mpi_set_bit(rnd, bits - 2, 1) != 0) || (safe && mbedtls_mpi_set_bit(rnd, 1, 1) != 0))
|
|
ok = false;
|
|
} else if (ok) {
|
|
ok = mbedtls_mpi_mod_mpi(&r, rnd, add) == 0 && mbedtls_mpi_sub_mpi(rnd, rnd, &r) == 0 &&
|
|
(rem ? mbedtls_mpi_add_mpi(rnd, rnd, rem) == 0 : mbedtls_mpi_add_int(rnd, rnd, safe ? 3 : 1) == 0);
|
|
if (ok && (mbedtls_mpi_bitlen(rnd) < bits || sd_word(rnd) < (safe ? 5u : 3u)))
|
|
ok = mbedtls_mpi_add_mpi(rnd, rnd, add) == 0;
|
|
}
|
|
if (!ok || !sd_small_residues(t, rnd)) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
uint64_t delta = 0, word = sd_word(rnd);
|
|
bool again = false;
|
|
for (size_t i = 0; i < trials && !again;) {
|
|
uint64_t prime = t->primes[i];
|
|
/* A one-word candidate needs only the primes up to its square root. */
|
|
if (bits <= 31 && delta <= 0x7fffffff && prime * prime > word + delta) break;
|
|
uint64_t m = (t->mods[i] + delta) % prime;
|
|
if (safe ? m <= 1 : m == 0) {
|
|
delta += step;
|
|
if (delta > maxdelta) again = true;
|
|
i = 0;
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
if (again) continue;
|
|
if (!sd_add_word(rnd, delta)) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
if (add || mbedtls_mpi_bitlen(rnd) == bits) break;
|
|
}
|
|
mbedtls_mpi_free(&r);
|
|
return ok;
|
|
}
|
|
|
|
/* BN_generate_prime_ex2(p, bits, safe, add, rem): false with OpenSSL's
|
|
* error pending. Long safe primes without add take the sieve over
|
|
* p ≡ 11 (mod 12), which every safe prime above 7 satisfies. */
|
|
static bool sd_generate_prime(mbedtls_mpi *p, size_t bits, bool safe, const mbedtls_mpi *add, const mbedtls_mpi *rem) {
|
|
if (bits < 2 || (!add && safe && bits < 6 && bits != 3)) {
|
|
sd_throw_bignum("01800076", "bits too small");
|
|
return false;
|
|
}
|
|
if (add && mbedtls_mpi_cmp_int(add, 0) == 0) {
|
|
sd_throw_bignum("01800067", "div by zero");
|
|
return false;
|
|
}
|
|
if (!add && safe && bits >= 64) {
|
|
if (!sd_generate_safe_prime(p, bits, 12, 11, true)) scr_trap("scriptc: out of memory\n");
|
|
return true;
|
|
}
|
|
ScDhSmallPrimes *t = sd_small_primes();
|
|
mbedtls_mpi q;
|
|
mbedtls_mpi_init(&q);
|
|
bool found = false;
|
|
while (!found) {
|
|
if (!sd_probable_prime(t, p, bits, safe, add, rem)) scr_trap("scriptc: out of memory\n");
|
|
if (!safe) {
|
|
found = sd_is_prime(t, p);
|
|
} else {
|
|
if (mbedtls_mpi_copy(&q, p) != 0 || mbedtls_mpi_shift_r(&q, 1) != 0) scr_trap("scriptc: out of memory\n");
|
|
found = sd_is_prime(t, p) && sd_is_prime(t, &q);
|
|
}
|
|
}
|
|
mbedtls_mpi_free(&q);
|
|
free(t);
|
|
return true;
|
|
}
|
|
|
|
/* checkPrime's arguments after the candidate: Node's options and checks
|
|
* rules. -1 with the error pending. */
|
|
static int sd_prime_checks(const ScrDyn *options) {
|
|
if (!sd_validate_object("options", options)) return -1;
|
|
const ScrDyn *checks = sd_option(options, "checks");
|
|
if (checks->kind == SCR_DYN_UNDEF) return 0;
|
|
if (checks->kind != SCR_DYN_NUM) {
|
|
scr_dyn_prop_type_fail("options.checks", "of type number", checks);
|
|
return -1;
|
|
}
|
|
if (!sd_validate_int32_min("options.checks", checks->v.num, 0)) return -1;
|
|
return (int)checks->v.num;
|
|
}
|
|
|
|
/* The candidate's verdict as a boolean dyn (+1), or NULL with the error
|
|
* pending. */
|
|
static ScrDyn *sd_check_prime(const mbedtls_mpi *candidate, int checks) {
|
|
if (mbedtls_mpi_bitlen(candidate) > SD_MAX_PRIME_BITS) {
|
|
sd_prime_too_long("checkPrime candidates");
|
|
return NULL;
|
|
}
|
|
ScDhSmallPrimes *t = sd_small_primes();
|
|
bool prime = sd_is_prime_checks(t, candidate, checks);
|
|
free(t);
|
|
return scr_dyn_new_bool(prime);
|
|
}
|
|
|
|
/* A borrowed, unboxed view of each argument (+1 each); false after a
|
|
* throw. */
|
|
static bool sd_unbox_all(ScrDyn **out, ScrDyn *const *args, size_t count) {
|
|
for (size_t i = 0; i < count; i++) out[i] = NULL;
|
|
for (size_t i = 0; i < count; i++) {
|
|
ScrDyn *value = args[i];
|
|
out[i] = value->kind == SCR_DYN_TYPED_REF ? scr_dyn_typed_ref_materialize(value) : scr_dyn_retain(value);
|
|
if (!out[i]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void sd_release_all(ScrDyn **values, size_t count) {
|
|
for (size_t i = 0; i < count; i++)
|
|
if (values[i]) scr_dyn_release(values[i]);
|
|
}
|
|
|
|
/* checkPrimeSync(candidate[, options]). */
|
|
ScrDyn *scr_crypto_check_prime_sync(ScrDyn *candidate, ScrDyn *options) {
|
|
ScrDyn *args[2] = {candidate, options};
|
|
ScrDyn *v[2];
|
|
ScrDyn *result = NULL;
|
|
mbedtls_mpi x;
|
|
mbedtls_mpi_init(&x);
|
|
if (sd_unbox_all(v, args, 2) && sd_large_number(&x, "candidate", false, v[0])) {
|
|
ScrDyn *empty = v[1]->kind == SCR_DYN_UNDEF ? scr_dyn_new_obj() : NULL;
|
|
int checks = sd_prime_checks(empty ? empty : v[1]);
|
|
if (empty) scr_dyn_release(empty);
|
|
if (checks >= 0) result = sd_check_prime(&x, checks);
|
|
}
|
|
mbedtls_mpi_free(&x);
|
|
sd_release_all(v, 2);
|
|
return result;
|
|
}
|
|
|
|
/* A deferred callback(...args): the callback and its one or two
|
|
* arguments, each in a box. */
|
|
static void sd_deliver(ScrClosure *self) {
|
|
ScrDyn *callback = (ScrDyn *)scr_box_get_ref(self->caps[0]);
|
|
size_t argc = self->ncaps - 1;
|
|
ScrDyn *args[2] = {NULL, NULL};
|
|
for (size_t i = 0; i < argc; i++) args[i] = (ScrDyn *)scr_box_get_ref(self->caps[i + 1]);
|
|
ScrDyn *result = scr_dyn_call(callback, args, argc, "callback");
|
|
if (result) scr_dyn_release(result);
|
|
for (size_t i = 0; i < argc; i++) scr_dyn_release(args[i]);
|
|
scr_dyn_release(callback);
|
|
}
|
|
|
|
/* Calls callback(first[, second]) in a later macrotask (moves first and
|
|
* second). */
|
|
static void sd_defer(const ScrDyn *callback, ScrDyn *first, ScrDyn *second) {
|
|
ScrDyn *values[3] = {scr_dyn_retain((ScrDyn *)callback), first, second};
|
|
size_t count = second ? 3 : 2;
|
|
ScrClosure *c = scr_closure_new((void *)sd_deliver, count);
|
|
for (size_t i = 0; i < count; i++) {
|
|
c->caps[i] = scr_box_new_obj(scr_dyn_retain_v, scr_dyn_release_v, NULL);
|
|
scr_box_set_ref(c->caps[i], values[i]);
|
|
}
|
|
scr_loop_defer(c);
|
|
}
|
|
|
|
/* The callback's error argument on success (+1): Node's undefined, or
|
|
* null for a callback typed to take Error | null. */
|
|
static ScrDyn *sd_no_error(bool null_error) {
|
|
return null_error ? scr_dyn_new_null() : scr_dyn_retain((ScrDyn *)scr_dyn_undefined());
|
|
}
|
|
|
|
/* The pending error, taken as a dyn value (+1). */
|
|
static ScrDyn *sd_take_error(void) {
|
|
ScrCaught *caught = scr_exc_take();
|
|
ScrDyn *error = scr_caught_to_dyn(caught);
|
|
scr_caught_release(caught);
|
|
return error;
|
|
}
|
|
|
|
/* checkPrime(candidate[, options], callback): argument errors throw; the
|
|
* callback receives (undefined, verdict) in a later macrotask (null for
|
|
* undefined when `null_error`). */
|
|
void scr_crypto_check_prime_async(ScrDyn *candidate, ScrDyn *options, ScrDyn *callback, bool null_error) {
|
|
ScrDyn *args[3] = {candidate, options, callback};
|
|
ScrDyn *v[3];
|
|
mbedtls_mpi x;
|
|
mbedtls_mpi_init(&x);
|
|
if (sd_unbox_all(v, args, 3) && sd_large_number(&x, "candidate", false, v[0])) {
|
|
ScrDyn *bag = v[1], *fn = v[2];
|
|
ScrDyn *empty = NULL;
|
|
if (scr_dyn_is_callable(bag)) {
|
|
fn = bag;
|
|
bag = empty = scr_dyn_new_obj();
|
|
} else if (bag->kind == SCR_DYN_UNDEF) {
|
|
bag = empty = scr_dyn_new_obj();
|
|
}
|
|
int checks = sd_validate_function("callback", fn) ? sd_prime_checks(bag) : -1;
|
|
ScrDyn *verdict = checks >= 0 ? sd_check_prime(&x, checks) : NULL;
|
|
if (verdict) sd_defer(fn, sd_no_error(null_error), verdict);
|
|
if (empty) scr_dyn_release(empty);
|
|
}
|
|
mbedtls_mpi_free(&x);
|
|
sd_release_all(v, 3);
|
|
}
|
|
|
|
/* RandomPrimeConfig: the size and options checks, then the add and rem
|
|
* bounds. False with the error pending. */
|
|
typedef struct {
|
|
size_t bits;
|
|
bool safe, bigint, has_add, has_rem;
|
|
mbedtls_mpi add, rem;
|
|
} ScDhPrimeConfig;
|
|
|
|
static void sd_prime_config_free(ScDhPrimeConfig *c) {
|
|
mbedtls_mpi_free(&c->add);
|
|
mbedtls_mpi_free(&c->rem);
|
|
}
|
|
|
|
static bool sd_prime_size(const ScrDyn *size) {
|
|
if (size->kind != SCR_DYN_NUM) {
|
|
scr_dyn_arg_type_fail("size", "of type number", size);
|
|
return false;
|
|
}
|
|
return sd_validate_int32_min("size", size->v.num, 1);
|
|
}
|
|
|
|
static bool sd_prime_config(ScDhPrimeConfig *c, double size, const ScrDyn *options) {
|
|
if (!sd_validate_object("options", options)) return false;
|
|
const ScrDyn *safe = sd_option(options, "safe");
|
|
const ScrDyn *bigint = sd_option(options, "bigint");
|
|
const ScrDyn *add = sd_option(options, "add");
|
|
const ScrDyn *rem = sd_option(options, "rem");
|
|
if (safe->kind != SCR_DYN_UNDEF && safe->kind != SCR_DYN_BOOL) {
|
|
scr_dyn_prop_type_fail("options.safe", "of type boolean", safe);
|
|
return false;
|
|
}
|
|
if (bigint->kind != SCR_DYN_UNDEF && bigint->kind != SCR_DYN_BOOL) {
|
|
scr_dyn_prop_type_fail("options.bigint", "of type boolean", bigint);
|
|
return false;
|
|
}
|
|
c->safe = safe->kind == SCR_DYN_BOOL && safe->v.b;
|
|
c->bigint = bigint->kind == SCR_DYN_BOOL && bigint->v.b;
|
|
c->has_add = add->kind != SCR_DYN_UNDEF;
|
|
c->has_rem = rem->kind != SCR_DYN_UNDEF;
|
|
if (c->has_add && !sd_large_number(&c->add, "options.add", true, add)) return false;
|
|
if (c->has_rem && !sd_large_number(&c->rem, "options.rem", true, rem)) return false;
|
|
c->bits = (size_t)size;
|
|
if (c->has_add) {
|
|
if (mbedtls_mpi_bitlen(&c->add) > c->bits) {
|
|
sd_throw(SCR_ERR_RANGE, "ERR_OUT_OF_RANGE", "invalid options.add");
|
|
return false;
|
|
}
|
|
if (c->has_rem && mbedtls_mpi_cmp_mpi(&c->add, &c->rem) <= 0) {
|
|
sd_throw(SCR_ERR_RANGE, "ERR_OUT_OF_RANGE", "invalid options.rem");
|
|
return false;
|
|
}
|
|
}
|
|
if (c->bits > SD_MAX_PRIME_BITS) {
|
|
sd_prime_too_long("generatePrime sizes");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* The generated prime as an ArrayBuffer of its bytes, or a bigint (+1),
|
|
* or NULL with OpenSSL's error pending. */
|
|
static ScrDyn *sd_prime_result(ScDhPrimeConfig *c) {
|
|
mbedtls_mpi p;
|
|
mbedtls_mpi_init(&p);
|
|
ScrDyn *result = NULL;
|
|
if (sd_generate_prime(&p, c->bits, c->safe, c->has_add ? &c->add : NULL, c->has_add && c->has_rem ? &c->rem : NULL)) {
|
|
if (c->bigint) {
|
|
size_t len = 0;
|
|
mbedtls_mpi_write_string(&p, 16, NULL, 0, &len);
|
|
char *hex = malloc(len + 2);
|
|
if (!hex) scr_trap("scriptc: out of memory\n");
|
|
hex[0] = '0';
|
|
hex[1] = 'x';
|
|
if (mbedtls_mpi_write_string(&p, 16, hex + 2, len, &len) != 0) scr_trap("scriptc: out of memory\n");
|
|
ScrStr *text = scr_str_new(hex, strlen(hex));
|
|
free(hex);
|
|
ScrBigInt *value = scr_bigint_parse(text);
|
|
scr_str_release(text);
|
|
if (value) {
|
|
result = scr_dyn_new_bigint(value);
|
|
scr_bigint_release(value);
|
|
}
|
|
} else {
|
|
size_t len = mbedtls_mpi_size(&p);
|
|
ScrBytes *bytes = scr_bytes_new(SCR_BYTES_U8, (double)len);
|
|
if (mbedtls_mpi_write_binary(&p, bytes->data, len) != 0) scr_trap("scriptc: out of memory\n");
|
|
result = scr_array_buffer_from_bytes(bytes);
|
|
scr_bytes_release(bytes);
|
|
}
|
|
}
|
|
mbedtls_mpi_free(&p);
|
|
return result;
|
|
}
|
|
|
|
/* generatePrimeSync(size[, options]). */
|
|
ScrDyn *scr_crypto_generate_prime_sync(ScrDyn *size, ScrDyn *options) {
|
|
ScrDyn *args[2] = {size, options};
|
|
ScrDyn *v[2];
|
|
ScrDyn *result = NULL;
|
|
ScDhPrimeConfig c = {0};
|
|
mbedtls_mpi_init(&c.add);
|
|
mbedtls_mpi_init(&c.rem);
|
|
if (sd_unbox_all(v, args, 2) && sd_prime_size(v[0])) {
|
|
ScrDyn *empty = v[1]->kind == SCR_DYN_UNDEF ? scr_dyn_new_obj() : NULL;
|
|
if (sd_prime_config(&c, v[0]->v.num, empty ? empty : v[1])) result = sd_prime_result(&c);
|
|
if (empty) scr_dyn_release(empty);
|
|
}
|
|
sd_prime_config_free(&c);
|
|
sd_release_all(v, 2);
|
|
return result;
|
|
}
|
|
|
|
/* generatePrime(size[, options], callback): argument errors throw; the
|
|
* callback receives (undefined, prime), or OpenSSL's error alone, in a
|
|
* later macrotask (null for undefined when `null_error`). */
|
|
void scr_crypto_generate_prime_async(ScrDyn *size, ScrDyn *options, ScrDyn *callback, bool null_error) {
|
|
ScrDyn *args[3] = {size, options, callback};
|
|
ScrDyn *v[3];
|
|
ScDhPrimeConfig c = {0};
|
|
mbedtls_mpi_init(&c.add);
|
|
mbedtls_mpi_init(&c.rem);
|
|
if (sd_unbox_all(v, args, 3) && sd_prime_size(v[0])) {
|
|
ScrDyn *bag = v[1], *fn = v[2];
|
|
ScrDyn *empty = NULL;
|
|
if (scr_dyn_is_callable(bag)) {
|
|
fn = bag;
|
|
bag = empty = scr_dyn_new_obj();
|
|
}
|
|
if (sd_validate_function("callback", fn) && sd_prime_config(&c, v[0]->v.num, bag)) {
|
|
ScrDyn *prime = sd_prime_result(&c);
|
|
if (prime) sd_defer(fn, sd_no_error(null_error), prime);
|
|
else sd_defer(fn, sd_take_error(), NULL);
|
|
}
|
|
if (empty) scr_dyn_release(empty);
|
|
}
|
|
sd_prime_config_free(&c);
|
|
sd_release_all(v, 3);
|
|
}
|
|
|
|
/* ── methods ──────────────────────────────────────────────────────────── */
|
|
|
|
static ScrDyn *sd_arg(ScrDyn *const *args, size_t argc, size_t index) {
|
|
return index < argc ? args[index] : scr_dyn_undefined();
|
|
}
|
|
|
|
/* The minimal big-endian bytes of a value, as BignumPointer::Encode
|
|
* writes them (none for 0), passed through encode(). */
|
|
static ScrDyn *sd_encode_value(const mbedtls_mpi *value, const ScrDyn *encoding) {
|
|
size_t len = mbedtls_mpi_size(value);
|
|
ScrBytes *out = scr_bytes_new(SCR_BYTES_U8, (double)len);
|
|
if (mbedtls_mpi_write_binary(value, out->data, len) != 0) scr_trap("scriptc: out of memory\n");
|
|
return ca_encode(SD_SUBJECT, out, encoding);
|
|
}
|
|
|
|
/* A new private key for generateKeys(). */
|
|
static bool sd_new_private_key(ScDh *d, mbedtls_mpi *priv) {
|
|
if (d->group) {
|
|
if (d->group->private_bits) return sd_random_bits(priv, (size_t)d->group->private_bits, false);
|
|
/* BN_priv_rand_range(p - 3) + 2. */
|
|
mbedtls_mpi bound;
|
|
mbedtls_mpi_init(&bound);
|
|
bool ok = mbedtls_mpi_sub_int(&bound, &d->p, 1) == 0 && mbedtls_mpi_random(priv, 2, &bound, sd_random, NULL) == 0;
|
|
mbedtls_mpi_free(&bound);
|
|
return ok;
|
|
}
|
|
size_t bits = mbedtls_mpi_bitlen(&d->p);
|
|
if (bits < SD_MIN_MODULUS_BITS || bits > SD_MAX_MODULUS_BITS) return false;
|
|
if (!d->named) return sd_random_bits(priv, bits - 2, false);
|
|
/* ossl_ffc_generate_private_key: 1 <= priv < min(2^N, q). */
|
|
mbedtls_mpi bound, q;
|
|
mbedtls_mpi_init(&bound);
|
|
mbedtls_mpi_init(&q);
|
|
bool ok = mbedtls_mpi_lset(&bound, 1) == 0 && mbedtls_mpi_shift_l(&bound, (size_t)d->named->keylength) == 0;
|
|
if (ok && d->named->q) ok = mbedtls_mpi_read_binary(&q, d->named->q, d->named->q_len) == 0;
|
|
else if (ok) ok = mbedtls_mpi_copy(&q, &d->p) == 0 && mbedtls_mpi_shift_r(&q, 1) == 0;
|
|
if (ok && mbedtls_mpi_cmp_mpi(&q, &bound) < 0) ok = mbedtls_mpi_copy(&bound, &q) == 0;
|
|
ok = ok && mbedtls_mpi_random(priv, 1, &bound, sd_random, NULL) == 0;
|
|
mbedtls_mpi_free(&bound);
|
|
mbedtls_mpi_free(&q);
|
|
return ok;
|
|
}
|
|
|
|
/* generateKeys(encoding): a new key pair, or the public key of the
|
|
* private key the object has. */
|
|
static ScrDyn *sd_generate_keys(ScDh *d, const ScrDyn *encoding) {
|
|
mbedtls_mpi priv, pub;
|
|
mbedtls_mpi_init(&priv);
|
|
mbedtls_mpi_init(&pub);
|
|
bool fresh = !d->has_priv;
|
|
bool ok = fresh ? sd_new_private_key(d, &priv) : mbedtls_mpi_copy(&priv, &d->priv) == 0;
|
|
ok = ok && sd_exp(&pub, &d->g, &priv, &d->p);
|
|
if (ok) {
|
|
mbedtls_mpi_swap(&d->priv, &priv);
|
|
mbedtls_mpi_swap(&d->pub, &pub);
|
|
d->has_priv = true;
|
|
d->has_pub = true;
|
|
}
|
|
mbedtls_mpi_free(&priv);
|
|
mbedtls_mpi_free(&pub);
|
|
if (!ok || mbedtls_mpi_cmp_int(&d->pub, 0) == 0) {
|
|
sd_operation_failed("Key generation failed");
|
|
return NULL;
|
|
}
|
|
return sd_encode_value(&d->pub, encoding);
|
|
}
|
|
|
|
/* Whether OpenSSL's DH derivation accepts explicit parameters' private
|
|
* key: a nonzero key and a 512- to 10000-bit prime. */
|
|
static bool sd_derives(const ScDh *d) {
|
|
size_t bits = mbedtls_mpi_bitlen(&d->p);
|
|
return mbedtls_mpi_cmp_int(&d->priv, 0) != 0 && bits >= SD_MIN_MODULUS_BITS && bits <= SD_MAX_MODULUS_BITS;
|
|
}
|
|
|
|
/* DH_check_pub_key's subgroup test: under a named group with its own q
|
|
* (the RFC 5114 groups), a peer key must have key^q mod p = 1. The safe
|
|
* prime groups' q is (p - 1) / 2, which OpenSSL does not test. */
|
|
static bool sd_in_subgroup(const ScDh *d, const mbedtls_mpi *peer) {
|
|
if (!d->named || !d->named->q) return true;
|
|
mbedtls_mpi q, r;
|
|
mbedtls_mpi_init(&q);
|
|
mbedtls_mpi_init(&r);
|
|
if (mbedtls_mpi_read_binary(&q, d->named->q, d->named->q_len) != 0 || !sd_exp(&r, peer, &q, &d->p))
|
|
scr_trap("scriptc: out of memory\n");
|
|
bool in = mbedtls_mpi_cmp_int(&r, 1) == 0;
|
|
mbedtls_mpi_free(&q);
|
|
mbedtls_mpi_free(&r);
|
|
return in;
|
|
}
|
|
|
|
/* computeSecret(key, inputEncoding, outputEncoding): key^priv mod p,
|
|
* padded to the prime's length. A key outside [2, p - 2], or outside a
|
|
* named group's order-q subgroup, is rejected as DH_check_pub_key does.
|
|
* A secret of 0, 1 or p - 1 is predictable (a peer key of small order,
|
|
* or a composite modulus), so it fails like OpenSSL's derivation does. */
|
|
static ScrDyn *sd_compute_secret(ScDh *d, const ScrDyn *key, const ScrDyn *in_enc, const ScrDyn *out_enc) {
|
|
ScrBytes *bytes = ca_key_bytes(SD_SUBJECT, key, in_enc);
|
|
if (!bytes) return NULL;
|
|
ScrDyn *result = NULL;
|
|
mbedtls_mpi peer, bound, secret;
|
|
mbedtls_mpi_init(&peer);
|
|
mbedtls_mpi_init(&bound);
|
|
mbedtls_mpi_init(&secret);
|
|
/* A key longer than p, past its leading zeros, is above p - 1. */
|
|
const unsigned char *data = bytes->data;
|
|
size_t len = bytes->len;
|
|
sd_strip(&data, &len);
|
|
bool longer = len > mbedtls_mpi_size(&d->p);
|
|
if ((!longer && mbedtls_mpi_read_binary(&peer, data, len) != 0) || mbedtls_mpi_sub_int(&bound, &d->p, 1) != 0)
|
|
scr_trap("scriptc: out of memory\n");
|
|
if (!longer && mbedtls_mpi_cmp_int(&peer, 1) <= 0) {
|
|
sd_throw(SCR_ERR_RANGE, "ERR_CRYPTO_INVALID_KEYLEN", "Supplied key is too small");
|
|
} else if (longer || mbedtls_mpi_cmp_mpi(&peer, &bound) >= 0) {
|
|
sd_throw(SCR_ERR_RANGE, "ERR_CRYPTO_INVALID_KEYLEN", "Supplied key is too large");
|
|
} else if (!sd_in_subgroup(d, &peer)) {
|
|
sd_throw(SCR_ERR_RANGE, "ERR_CRYPTO_INVALID_KEYTYPE", "Supplied key is invalid");
|
|
} else if (!d->has_priv || (!d->group && !sd_derives(d)) || !sd_exp(&secret, &peer, &d->priv, &d->p) ||
|
|
mbedtls_mpi_cmp_int(&secret, 1) <= 0 || mbedtls_mpi_cmp_mpi(&secret, &bound) == 0) {
|
|
sd_operation_failed("Failed to compute shared secret");
|
|
} else {
|
|
size_t len = mbedtls_mpi_size(&d->p);
|
|
ScrBytes *out = scr_bytes_new(SCR_BYTES_U8, (double)len);
|
|
if (mbedtls_mpi_write_binary(&secret, out->data, len) != 0) scr_trap("scriptc: out of memory\n");
|
|
result = ca_encode(SD_SUBJECT, out, out_enc);
|
|
}
|
|
mbedtls_mpi_free(&peer);
|
|
mbedtls_mpi_free(&bound);
|
|
mbedtls_mpi_free(&secret);
|
|
scr_bytes_release(bytes);
|
|
return result;
|
|
}
|
|
|
|
static ScrDyn *sd_get_value(const mbedtls_mpi *value, bool present, const char *missing, const ScrDyn *encoding) {
|
|
if (!present || mbedtls_mpi_cmp_int(value, 0) == 0) {
|
|
sd_throw(SCR_ERR_ERROR, "ERR_CRYPTO_INVALID_STATE", missing);
|
|
return NULL;
|
|
}
|
|
return sd_encode_value(value, encoding);
|
|
}
|
|
|
|
/* setPublicKey(key, encoding) and setPrivateKey(key, encoding). */
|
|
static ScrDyn *sd_set_value(mbedtls_mpi *value, bool *present, ScrDyn *self, const ScrDyn *key, const ScrDyn *encoding) {
|
|
ScrBytes *bytes = ca_key_bytes(SD_SUBJECT, key, encoding);
|
|
if (!bytes) return NULL;
|
|
bool held = sd_read(value, bytes);
|
|
scr_bytes_release(bytes);
|
|
if (!held) {
|
|
sd_value_too_long();
|
|
return NULL;
|
|
}
|
|
*present = true;
|
|
return scr_dyn_retain(self);
|
|
}
|
|
|
|
static bool sd_name_is(const char *method, const char *name) {
|
|
return strcmp(method, name) == 0;
|
|
}
|
|
|
|
static ScrDyn *sd_dispatch(ScDh *d, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
|
const char *what) {
|
|
const ScrDyn *a0 = sd_arg(args, argc, 0);
|
|
if (sd_name_is(method, "generateKeys")) return sd_generate_keys(d, a0);
|
|
if (sd_name_is(method, "computeSecret"))
|
|
return sd_compute_secret(d, a0, sd_arg(args, argc, 1), sd_arg(args, argc, 2));
|
|
if (sd_name_is(method, "getPrime")) return sd_get_value(&d->p, true, "p is null", a0);
|
|
if (sd_name_is(method, "getGenerator")) return sd_get_value(&d->g, true, "g is null", a0);
|
|
if (sd_name_is(method, "getPublicKey"))
|
|
return sd_get_value(&d->pub, d->has_pub, "No public key - did you forget to generate one?", a0);
|
|
if (sd_name_is(method, "getPrivateKey"))
|
|
return sd_get_value(&d->priv, d->has_priv, "No private key - did you forget to generate one?", a0);
|
|
/* DiffieHellmanGroup has no key setters. */
|
|
if (!d->group && sd_name_is(method, "setPublicKey"))
|
|
return sd_set_value(&d->pub, &d->has_pub, self, a0, sd_arg(args, argc, 1));
|
|
if (!d->group && sd_name_is(method, "setPrivateKey"))
|
|
return sd_set_value(&d->priv, &d->has_priv, self, a0, sd_arg(args, argc, 1));
|
|
/* The Object.prototype methods a DiffieHellman inherits. */
|
|
static const char *const inherited[] = {"constructor", "hasOwnProperty", "isPrototypeOf",
|
|
"propertyIsEnumerable", "toLocaleString", "toString",
|
|
"valueOf"};
|
|
for (size_t i = 0; i < sizeof inherited / sizeof inherited[0]; i++)
|
|
if (sd_name_is(method, inherited[i])) {
|
|
static const char message[] = "DiffieHellman's Object.prototype methods have no lowering";
|
|
scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020");
|
|
return NULL;
|
|
}
|
|
ScrJsonBuf b;
|
|
scr_jb_init(&b);
|
|
scr_jb_puts(&b, what);
|
|
scr_jb_puts(&b, " is not a function");
|
|
scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b));
|
|
return NULL;
|
|
}
|
|
|
|
/* Every method reads at most three arguments. */
|
|
static ScrDyn *sd_invoke(void *h, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
|
const char *what) {
|
|
ScrDyn *unboxed[3] = {NULL, NULL, NULL};
|
|
size_t count = argc < 3 ? argc : 3;
|
|
ScrDyn *result = NULL;
|
|
for (size_t i = 0; i < count; i++)
|
|
if (!(unboxed[i] = ca_unbox(args[i]))) goto done;
|
|
result = sd_dispatch(h, self, method, unboxed, count, what);
|
|
done:
|
|
for (size_t i = 0; i < count; i++) scr_dyn_release(unboxed[i]);
|
|
return result;
|
|
}
|
|
|
|
/* verifyError, the one own data property. */
|
|
static ScrDyn *sd_get(void *h, const char *key, size_t key_len) {
|
|
ScDh *d = h;
|
|
if (key_len == 11 && memcmp(key, "verifyError", 11) == 0) {
|
|
if (d->verify_error < 0) d->verify_error = sd_check_params(&d->p, &d->g);
|
|
return scr_dyn_new_num(d->verify_error);
|
|
}
|
|
static const char message[] =
|
|
"DiffieHellman property reads other than verifyError have no lowering; call generateKeys(), "
|
|
"computeSecret(), getPrime(), getGenerator(), getPublicKey(), getPrivateKey(), setPublicKey() or "
|
|
"setPrivateKey()";
|
|
scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020");
|
|
return NULL;
|
|
}
|
|
|
|
static bool sd_set(void *h, const char *key, size_t key_len, const ScrDyn *value) {
|
|
(void)h; (void)key; (void)key_len; (void)value;
|
|
static const char message[] = "Writing DiffieHellman properties has no lowering";
|
|
scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020");
|
|
return true;
|
|
}
|
|
|
|
static ScrDynHandleOps sd_ops = {"DiffieHellman", sd_retain, sd_release, sd_invoke, sd_get, sd_set, NULL, NULL, NULL};
|
|
static ScrDynHandleOps sd_group_ops = {"DiffieHellmanGroup", sd_retain, sd_release, sd_invoke, sd_get, sd_set,
|
|
NULL, NULL, NULL};
|