From: Thomas Pornin Date: Sun, 5 Aug 2018 22:02:36 +0000 (+0200) Subject: Added support code for RSA and EC key encoding (including reconstruction of all publi... X-Git-Tag: v0.6~4 X-Git-Url: https://bearssl.org/gitweb//home/git/?a=commitdiff_plain;h=d8fa415fcabecb7d7c8461d8edbe2f265911e72c;p=BearSSL Added support code for RSA and EC key encoding (including reconstruction of all public and private key elements from the private key structure), with raw and PKCS#8 formats, both in DER and PEM. --- diff --git a/inc/bearssl_ec.h b/inc/bearssl_ec.h index 533296d..db22692 100644 --- a/inc/bearssl_ec.h +++ b/inc/bearssl_ec.h @@ -28,6 +28,8 @@ #include #include +#include "bearssl_rand.h" + #ifdef __cplusplus extern "C" { #endif @@ -797,6 +799,83 @@ br_ecdsa_vrfy br_ecdsa_vrfy_asn1_get_default(void); */ br_ecdsa_vrfy br_ecdsa_vrfy_raw_get_default(void); +/** + * \brief Maximum size for EC private key element buffer. + * + * This is the largest number of bytes that `br_ec_keygen()` may need or + * ever return. + */ +#define BR_EC_KBUF_PRIV_MAX_SIZE 72 + +/** + * \brief Maximum size for EC public key element buffer. + * + * This is the largest number of bytes that `br_ec_compute_public()` may + * need or ever return. + */ +#define BR_EC_KBUF_PUB_MAX_SIZE 145 + +/** + * \brief Generate a new EC private key. + * + * If the specified `curve` is not supported by the elliptic curve + * implementation (`impl`), then this function returns zero. + * + * The `sk` structure fields are set to the new private key data. In + * particular, `sk.x` is made to point to the provided key buffer (`kbuf`), + * in which the actual private key data is written. That buffer is assumed + * to be large enough. The `BR_EC_KBUF_PRIV_MAX_SIZE` defines the maximum + * size for all supported curves. + * + * The number of bytes used in `kbuf` is returned. If `kbuf` is `NULL`, then + * the private key is not actually generated, and `sk` may also be `NULL`; + * the minimum length for `kbuf` is still computed and returned. + * + * If `sk` is `NULL` but `kbuf` is not `NULL`, then the private key is + * still generated and stored in `kbuf`. + * + * \param rng_ctx source PRNG context (already initialized). + * \param impl the elliptic curve implementation. + * \param sk the private key structure to fill, or `NULL`. + * \param kbuf the key element buffer, or `NULL`. + * \param curve the curve identifier. + * \return the key data length (in bytes), or zero. + */ +size_t br_ec_keygen(const br_prng_class **rng_ctx, + const br_ec_impl *impl, br_ec_private_key *sk, + void *kbuf, int curve); + +/** + * \brief Compute EC public key from EC private key. + * + * This function uses the provided elliptic curve implementation (`impl`) + * to compute the public key corresponding to the private key held in `sk`. + * The public key point is written into `kbuf`, which is then linked from + * the `*pk` structure. The size of the public key point, i.e. the number + * of bytes used in `kbuf`, is returned. + * + * If `kbuf` is `NULL`, then the public key point is NOT computed, and + * the public key structure `*pk` is unmodified (`pk` may be `NULL` in + * that case). The size of the public key point is still returned. + * + * If `pk` is `NULL` but `kbuf` is not `NULL`, then the public key + * point is computed and stored in `kbuf`, and its size is returned. + * + * If the curve used by the private key is not supported by the curve + * implementation, then this function returns zero. + * + * The private key MUST be valid. An off-range private key value is not + * necessarily detected, and leads to unpredictable results. + * + * \param impl the elliptic curve implementation. + * \param pk the public key structure to fill (or `NULL`). + * \param kbuf the public key point buffer (or `NULL`). + * \param sk the source private key. + * \return the public key point length (in bytes), or zero. + */ +size_t br_ec_compute_pub(const br_ec_impl *impl, br_ec_public_key *pk, + void *kbuf, const br_ec_private_key *sk); + #ifdef __cplusplus } #endif diff --git a/inc/bearssl_pem.h b/inc/bearssl_pem.h index 5e466bc..8dba582 100644 --- a/inc/bearssl_pem.h +++ b/inc/bearssl_pem.h @@ -236,6 +236,57 @@ br_pem_decoder_name(br_pem_decoder_context *ctx) return ctx->name; } +/** + * \brief Encode an object in PEM. + * + * This function encodes the provided binary object (`data`, of length `len` + * bytes) into PEM. The `banner` text will be included in the header and + * footer (e.g. use `"CERTIFICATE"` to get a `"BEGIN CERTIFICATE"` header). + * + * The length (in characters) of the PEM output is returned; that length + * does NOT include the terminating zero, that this function nevertheless + * adds. If using the returned value for allocation purposes, the allocated + * buffer size MUST be at least one byte larger than the returned size. + * + * If `dest` is `NULL`, then the encoding does not happen; however, the + * length of the encoded object is still computed and returned. + * + * The `data` pointer may be `NULL` only if `len` is zero (when encoding + * an object of length zero, which is not very useful), or when `dest` + * is `NULL` (in that case, source data bytes are ignored). + * + * Some `flags` can be specified to alter the encoding behaviour: + * + * - If `BR_PEM_LINE64` is set, then line-breaking will occur after + * every 64 characters of output, instead of the default of 76. + * + * - If `BR_PEM_CRLF` is set, then end-of-line sequence will use + * CR+LF instead of a single LF. + * + * The `data` and `dest` buffers may overlap, in which case the source + * binary data is destroyed in the process. Note that the PEM-encoded output + * is always larger than the source binary. + * + * \param dest the destination buffer (or `NULL`). + * \param data the source buffer (can be `NULL` in some cases). + * \param len the source length (in bytes). + * \param banner the PEM banner expression. + * \param flags the behavioural flags. + * \return the PEM object length (in characters), EXCLUDING the final zero. + */ +size_t br_pem_encode(void *dest, const void *data, size_t len, + const char *banner, unsigned flags); + +/** + * \brief PEM encoding flag: split lines at 64 characters. + */ +#define BR_PEM_LINE64 0x0001 + +/** + * \brief PEM encoding flag: use CR+LF line endings. + */ +#define BR_PEM_CRLF 0x0002 + #ifdef __cplusplus } #endif diff --git a/inc/bearssl_rsa.h b/inc/bearssl_rsa.h index 7c79d33..f3825d1 100644 --- a/inc/bearssl_rsa.h +++ b/inc/bearssl_rsa.h @@ -28,6 +28,8 @@ #include #include +#include "bearssl_rand.h" + #ifdef __cplusplus extern "C" { #endif @@ -1068,7 +1070,7 @@ uint32_t br_rsa_i62_oaep_decrypt( * Similarly, the public key elements are written in `kbuf_pub`, with * pointers and lengths set in `pk`. * - * If `sk` is `NULL`, then `kbuf_pub` may be `NULL`, and only the + * If `pk` is `NULL`, then `kbuf_pub` may be `NULL`, and only the * private key is set. * * If `pubexp` is not zero, then its value will be used as public @@ -1098,8 +1100,8 @@ uint32_t br_rsa_i62_oaep_decrypt( */ typedef uint32_t (*br_rsa_keygen)( const br_prng_class **rng_ctx, - br_rsa_private_key *sk, unsigned char *kbuf_priv, - br_rsa_public_key *pk, unsigned char *kbuf_pub, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, unsigned size, uint32_t pubexp); /** @@ -1118,8 +1120,8 @@ typedef uint32_t (*br_rsa_keygen)( */ uint32_t br_rsa_i15_keygen( const br_prng_class **rng_ctx, - br_rsa_private_key *sk, unsigned char *kbuf_priv, - br_rsa_public_key *pk, unsigned char *kbuf_pub, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, unsigned size, uint32_t pubexp); /** @@ -1138,8 +1140,8 @@ uint32_t br_rsa_i15_keygen( */ uint32_t br_rsa_i31_keygen( const br_prng_class **rng_ctx, - br_rsa_private_key *sk, unsigned char *kbuf_priv, - br_rsa_public_key *pk, unsigned char *kbuf_pub, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, unsigned size, uint32_t pubexp); /** @@ -1162,8 +1164,8 @@ uint32_t br_rsa_i31_keygen( */ uint32_t br_rsa_i62_keygen( const br_prng_class **rng_ctx, - br_rsa_private_key *sk, unsigned char *kbuf_priv, - br_rsa_public_key *pk, unsigned char *kbuf_pub, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, unsigned size, uint32_t pubexp); /** @@ -1184,6 +1186,176 @@ br_rsa_keygen br_rsa_i62_keygen_get(void); */ br_rsa_keygen br_rsa_keygen_get_default(void); +/** + * \brief Type for a modulus computing function. + * + * Such a function computes the public modulus from the private key. The + * encoded modulus (unsigned big-endian) is written on `n`, and the size + * (in bytes) is returned. If `n` is `NULL`, then the size is returned but + * the modulus itself is not computed. + * + * If the key size exceeds an internal limit, 0 is returned. + * + * \param n destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the modulus length (in bytes), or 0. + */ +typedef size_t (*br_rsa_compute_modulus)(void *n, const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA modulus ("i15" engine). + * + * \see br_rsa_compute_modulus + * + * \param n destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the modulus length (in bytes), or 0. + */ +size_t br_rsa_i15_compute_modulus(void *n, const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA modulus ("i31" engine). + * + * \see br_rsa_compute_modulus + * + * \param n destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the modulus length (in bytes), or 0. + */ +size_t br_rsa_i31_compute_modulus(void *n, const br_rsa_private_key *sk); + +/** + * \brief Get "default" RSA implementation (recompute modulus). + * + * This returns the preferred implementation of RSA (recompute modulus) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_compute_modulus br_rsa_compute_modulus_get_default(void); + +/** + * \brief Type for a public exponent computing function. + * + * Such a function recomputes the public exponent from the private key. + * 0 is returned if any of the following occurs: + * + * - Either `p` or `q` is not equal to 3 modulo 4. + * + * - The public exponent does not fit on 32 bits. + * + * - An internal limit is exceeded. + * + * - The private key is invalid in some way. + * + * For all private keys produced by the key generator functions + * (`br_rsa_keygen` type), this function succeeds and returns the true + * public exponent. The public exponent is always an odd integer greater + * than 1. + * + * \return the public exponent, or 0. + */ +typedef uint32_t (*br_rsa_compute_pubexp)(const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA public exponent ("i15" engine). + * + * \see br_rsa_compute_pubexp + * + * \return the public exponent, or 0. + */ +uint32_t br_rsa_i15_compute_pubexp(const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA public exponent ("i31" engine). + * + * \see br_rsa_compute_pubexp + * + * \return the public exponent, or 0. + */ +uint32_t br_rsa_i31_compute_pubexp(const br_rsa_private_key *sk); + +/** + * \brief Get "default" RSA implementation (recompute public exponent). + * + * This returns the preferred implementation of RSA (recompute public + * exponent) on the current system. + * + * \return the default implementation. + */ +br_rsa_compute_pubexp br_rsa_compute_pubexp_get_default(void); + +/** + * \brief Type for a private exponent computing function. + * + * An RSA private key (`br_rsa_private_key`) contains two reduced + * private exponents, which are sufficient to perform private key + * operations. However, standard encoding formats for RSA private keys + * require also a copy of the complete private exponent (non-reduced), + * which this function recomputes. + * + * This function suceeds if all the following conditions hold: + * + * - Both private factors `p` and `q` are equal to 3 modulo 4. + * + * - The provided public exponent `pubexp` is correct, and, in particular, + * is odd, relatively prime to `p-1` and `q-1`, and greater than 1. + * + * - No internal storage limit is exceeded. + * + * For all private keys produced by the key generator functions + * (`br_rsa_keygen` type), this function succeeds. Note that the API + * restricts the public exponent to a maximum size of 32 bits. + * + * The encoded private exponent is written in `d` (unsigned big-endian + * convention), and the length (in bytes) is returned. If `d` is `NULL`, + * then the exponent is not written anywhere, but the length is still + * returned. On error, 0 is returned. + * + * Not all error conditions are detected when `d` is `NULL`; therefore, the + * returned value shall be checked also when actually producing the value. + * + * \param d destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the private exponent length (in bytes), or 0. + */ +typedef size_t (*br_rsa_compute_privexp)(void *d, + const br_rsa_private_key *sk, uint32_t pubexp); + +/** + * \brief Recompute RSA private exponent ("i15" engine). + * + * \see br_rsa_compute_privexp + * + * \param d destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the private exponent length (in bytes), or 0. + */ +size_t br_rsa_i15_compute_privexp(void *d, + const br_rsa_private_key *sk, uint32_t pubexp); + +/** + * \brief Recompute RSA private exponent ("i31" engine). + * + * \see br_rsa_compute_privexp + * + * \param d destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the private exponent length (in bytes), or 0. + */ +size_t br_rsa_i31_compute_privexp(void *d, + const br_rsa_private_key *sk, uint32_t pubexp); + +/** + * \brief Get "default" RSA implementation (recompute private exponent). + * + * This returns the preferred implementation of RSA (recompute private + * exponent) on the current system. + * + * \return the default implementation. + */ +br_rsa_compute_privexp br_rsa_compute_privexp_get_default(void); + #ifdef __cplusplus } #endif diff --git a/inc/bearssl_x509.h b/inc/bearssl_x509.h index 10e3510..49d2fba 100644 --- a/inc/bearssl_x509.h +++ b/inc/bearssl_x509.h @@ -1249,6 +1249,147 @@ br_skey_decoder_get_ec(const br_skey_decoder_context *ctx) } } +/** + * \brief Encode an RSA private key (raw DER format). + * + * This function encodes the provided key into the "raw" format specified + * in PKCS#1 (RFC 8017, Appendix C, type `RSAPrivateKey`), with DER + * encoding rules. + * + * The key elements are: + * + * - `sk`: the private key (`p`, `q`, `dp`, `dq` and `iq`) + * + * - `pk`: the public key (`n` and `e`) + * + * - `d` (size: `dlen` bytes): the private exponent + * + * The public key elements, and the private exponent `d`, can be + * recomputed from the private key (see `br_rsa_compute_modulus()`, + * `br_rsa_compute_pubexp()` and `br_rsa_compute_privexp()`). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the RSA private key. + * \param pk the RSA public key. + * \param d the RSA private exponent. + * \param dlen the RSA private exponent length (in bytes). + * \return the encoded key length (in bytes). + */ +size_t br_encode_rsa_raw_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen); + +/** + * \brief Encode an RSA private key (PKCS#8 DER format). + * + * This function encodes the provided key into the PKCS#8 format + * (RFC 5958, type `OneAsymmetricKey`). It wraps around the "raw DER" + * format for the RSA key, as implemented by `br_encode_rsa_raw_der()`. + * + * The key elements are: + * + * - `sk`: the private key (`p`, `q`, `dp`, `dq` and `iq`) + * + * - `pk`: the public key (`n` and `e`) + * + * - `d` (size: `dlen` bytes): the private exponent + * + * The public key elements, and the private exponent `d`, can be + * recomputed from the private key (see `br_rsa_compute_modulus()`, + * `br_rsa_compute_pubexp()` and `br_rsa_compute_privexp()`). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the RSA private key. + * \param pk the RSA public key. + * \param d the RSA private exponent. + * \param dlen the RSA private exponent length (in bytes). + * \return the encoded key length (in bytes). + */ +size_t br_encode_rsa_pkcs8_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen); + +/** + * \brief Encode an EC private key (raw DER format). + * + * This function encodes the provided key into the "raw" format specified + * in RFC 5915 (type `ECPrivateKey`), with DER encoding rules. + * + * The private key is provided in `sk`, the public key being `pk`. If + * `pk` is `NULL`, then the encoded key will not include the public key + * in its `publicKey` field (which is nominally optional). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * If the key cannot be encoded (e.g. because there is no known OBJECT + * IDENTIFIER for the used curve), then 0 is returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the EC private key. + * \param pk the EC public key (or `NULL`). + * \return the encoded key length (in bytes), or 0. + */ +size_t br_encode_ec_raw_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk); + +/** + * \brief Encode an EC private key (PKCS#8 DER format). + * + * This function encodes the provided key into the PKCS#8 format + * (RFC 5958, type `OneAsymmetricKey`). The curve is identified + * by an OID provided as parameters to the `privateKeyAlgorithm` + * field. The private key value (contents of the `privateKey` field) + * contains the DER encoding of the `ECPrivateKey` type defined in + * RFC 5915, without the `parameters` field (since they would be + * redundant with the information in `privateKeyAlgorithm`). + * + * The private key is provided in `sk`, the public key being `pk`. If + * `pk` is not `NULL`, then the encoded public key is included in the + * `publicKey` field of the private key value (but not in the `publicKey` + * field of the PKCS#8 `OneAsymmetricKey` wrapper). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * If the key cannot be encoded (e.g. because there is no known OBJECT + * IDENTIFIER for the used curve), then 0 is returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the EC private key. + * \param pk the EC public key (or `NULL`). + * \return the encoded key length (in bytes), or 0. + */ +size_t br_encode_ec_pkcs8_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk); + +/** + * \brief PEM banner for RSA private key (raw). + */ +#define BR_ENCODE_PEM_RSA_RAW "RSA PRIVATE KEY" + +/** + * \brief PEM banner for EC private key (raw). + */ +#define BR_ENCODE_PEM_EC_RAW "EC PRIVATE KEY" + +/** + * \brief PEM banner for an RSA or EC private key in PKCS#8 format. + */ +#define BR_ENCODE_PEM_PKCS8 "PRIVATE KEY" + #ifdef __cplusplus } #endif diff --git a/mk/Rules.mk b/mk/Rules.mk index 88195fd..fd55dfd 100644 --- a/mk/Rules.mk +++ b/mk/Rules.mk @@ -19,6 +19,7 @@ OBJ = \ $(OBJDIR)$Penc64be$O \ $(OBJDIR)$Penc64le$O \ $(OBJDIR)$Ppemdec$O \ + $(OBJDIR)$Ppemenc$O \ $(OBJDIR)$Pec_all_m15$O \ $(OBJDIR)$Pec_all_m31$O \ $(OBJDIR)$Pec_c25519_i15$O \ @@ -27,10 +28,12 @@ OBJ = \ $(OBJDIR)$Pec_c25519_m31$O \ $(OBJDIR)$Pec_curve25519$O \ $(OBJDIR)$Pec_default$O \ + $(OBJDIR)$Pec_keygen$O \ $(OBJDIR)$Pec_p256_m15$O \ $(OBJDIR)$Pec_p256_m31$O \ $(OBJDIR)$Pec_prime_i15$O \ $(OBJDIR)$Pec_prime_i31$O \ + $(OBJDIR)$Pec_pubkey$O \ $(OBJDIR)$Pec_secp256r1$O \ $(OBJDIR)$Pec_secp384r1$O \ $(OBJDIR)$Pec_secp521r1$O \ @@ -126,27 +129,36 @@ OBJ = \ $(OBJDIR)$Phmac_drbg$O \ $(OBJDIR)$Psysrng$O \ $(OBJDIR)$Prsa_default_keygen$O \ + $(OBJDIR)$Prsa_default_modulus$O \ $(OBJDIR)$Prsa_default_oaep_decrypt$O \ $(OBJDIR)$Prsa_default_oaep_encrypt$O \ $(OBJDIR)$Prsa_default_pkcs1_sign$O \ $(OBJDIR)$Prsa_default_pkcs1_vrfy$O \ $(OBJDIR)$Prsa_default_priv$O \ + $(OBJDIR)$Prsa_default_privexp$O \ $(OBJDIR)$Prsa_default_pub$O \ + $(OBJDIR)$Prsa_default_pubexp$O \ $(OBJDIR)$Prsa_i15_keygen$O \ + $(OBJDIR)$Prsa_i15_modulus$O \ $(OBJDIR)$Prsa_i15_oaep_decrypt$O \ $(OBJDIR)$Prsa_i15_oaep_encrypt$O \ $(OBJDIR)$Prsa_i15_pkcs1_sign$O \ $(OBJDIR)$Prsa_i15_pkcs1_vrfy$O \ $(OBJDIR)$Prsa_i15_priv$O \ + $(OBJDIR)$Prsa_i15_privexp$O \ $(OBJDIR)$Prsa_i15_pub$O \ + $(OBJDIR)$Prsa_i15_pubexp$O \ $(OBJDIR)$Prsa_i31_keygen$O \ $(OBJDIR)$Prsa_i31_keygen_inner$O \ + $(OBJDIR)$Prsa_i31_modulus$O \ $(OBJDIR)$Prsa_i31_oaep_decrypt$O \ $(OBJDIR)$Prsa_i31_oaep_encrypt$O \ $(OBJDIR)$Prsa_i31_pkcs1_sign$O \ $(OBJDIR)$Prsa_i31_pkcs1_vrfy$O \ $(OBJDIR)$Prsa_i31_priv$O \ + $(OBJDIR)$Prsa_i31_privexp$O \ $(OBJDIR)$Prsa_i31_pub$O \ + $(OBJDIR)$Prsa_i31_pubexp$O \ $(OBJDIR)$Prsa_i32_oaep_decrypt$O \ $(OBJDIR)$Prsa_i32_oaep_encrypt$O \ $(OBJDIR)$Prsa_i32_pkcs1_sign$O \ @@ -254,6 +266,11 @@ OBJ = \ $(OBJDIR)$Ppoly1305_ctmul32$O \ $(OBJDIR)$Ppoly1305_ctmulq$O \ $(OBJDIR)$Ppoly1305_i15$O \ + $(OBJDIR)$Pasn1enc$O \ + $(OBJDIR)$Pencode_ec_pk8der$O \ + $(OBJDIR)$Pencode_ec_rawder$O \ + $(OBJDIR)$Pencode_rsa_pk8der$O \ + $(OBJDIR)$Pencode_rsa_rawder$O \ $(OBJDIR)$Pskey_decoder$O \ $(OBJDIR)$Px509_decoder$O \ $(OBJDIR)$Px509_knownkey$O \ @@ -393,6 +410,9 @@ $(OBJDIR)$Penc64le$O: src$Pcodec$Penc64le.c $(HEADERSPRIV) $(OBJDIR)$Ppemdec$O: src$Pcodec$Ppemdec.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Ppemdec$O src$Pcodec$Ppemdec.c +$(OBJDIR)$Ppemenc$O: src$Pcodec$Ppemenc.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Ppemenc$O src$Pcodec$Ppemenc.c + $(OBJDIR)$Pec_all_m15$O: src$Pec$Pec_all_m15.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pec_all_m15$O src$Pec$Pec_all_m15.c @@ -417,6 +437,9 @@ $(OBJDIR)$Pec_curve25519$O: src$Pec$Pec_curve25519.c $(HEADERSPRIV) $(OBJDIR)$Pec_default$O: src$Pec$Pec_default.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pec_default$O src$Pec$Pec_default.c +$(OBJDIR)$Pec_keygen$O: src$Pec$Pec_keygen.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pec_keygen$O src$Pec$Pec_keygen.c + $(OBJDIR)$Pec_p256_m15$O: src$Pec$Pec_p256_m15.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pec_p256_m15$O src$Pec$Pec_p256_m15.c @@ -429,6 +452,9 @@ $(OBJDIR)$Pec_prime_i15$O: src$Pec$Pec_prime_i15.c $(HEADERSPRIV) $(OBJDIR)$Pec_prime_i31$O: src$Pec$Pec_prime_i31.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pec_prime_i31$O src$Pec$Pec_prime_i31.c +$(OBJDIR)$Pec_pubkey$O: src$Pec$Pec_pubkey.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pec_pubkey$O src$Pec$Pec_pubkey.c + $(OBJDIR)$Pec_secp256r1$O: src$Pec$Pec_secp256r1.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pec_secp256r1$O src$Pec$Pec_secp256r1.c @@ -714,6 +740,9 @@ $(OBJDIR)$Psysrng$O: src$Prand$Psysrng.c $(HEADERSPRIV) $(OBJDIR)$Prsa_default_keygen$O: src$Prsa$Prsa_default_keygen.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_default_keygen$O src$Prsa$Prsa_default_keygen.c +$(OBJDIR)$Prsa_default_modulus$O: src$Prsa$Prsa_default_modulus.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_default_modulus$O src$Prsa$Prsa_default_modulus.c + $(OBJDIR)$Prsa_default_oaep_decrypt$O: src$Prsa$Prsa_default_oaep_decrypt.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_default_oaep_decrypt$O src$Prsa$Prsa_default_oaep_decrypt.c @@ -729,12 +758,21 @@ $(OBJDIR)$Prsa_default_pkcs1_vrfy$O: src$Prsa$Prsa_default_pkcs1_vrfy.c $(HEADER $(OBJDIR)$Prsa_default_priv$O: src$Prsa$Prsa_default_priv.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_default_priv$O src$Prsa$Prsa_default_priv.c +$(OBJDIR)$Prsa_default_privexp$O: src$Prsa$Prsa_default_privexp.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_default_privexp$O src$Prsa$Prsa_default_privexp.c + $(OBJDIR)$Prsa_default_pub$O: src$Prsa$Prsa_default_pub.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_default_pub$O src$Prsa$Prsa_default_pub.c +$(OBJDIR)$Prsa_default_pubexp$O: src$Prsa$Prsa_default_pubexp.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_default_pubexp$O src$Prsa$Prsa_default_pubexp.c + $(OBJDIR)$Prsa_i15_keygen$O: src$Prsa$Prsa_i15_keygen.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i15_keygen$O src$Prsa$Prsa_i15_keygen.c +$(OBJDIR)$Prsa_i15_modulus$O: src$Prsa$Prsa_i15_modulus.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i15_modulus$O src$Prsa$Prsa_i15_modulus.c + $(OBJDIR)$Prsa_i15_oaep_decrypt$O: src$Prsa$Prsa_i15_oaep_decrypt.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i15_oaep_decrypt$O src$Prsa$Prsa_i15_oaep_decrypt.c @@ -750,15 +788,24 @@ $(OBJDIR)$Prsa_i15_pkcs1_vrfy$O: src$Prsa$Prsa_i15_pkcs1_vrfy.c $(HEADERSPRIV) $(OBJDIR)$Prsa_i15_priv$O: src$Prsa$Prsa_i15_priv.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i15_priv$O src$Prsa$Prsa_i15_priv.c +$(OBJDIR)$Prsa_i15_privexp$O: src$Prsa$Prsa_i15_privexp.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i15_privexp$O src$Prsa$Prsa_i15_privexp.c + $(OBJDIR)$Prsa_i15_pub$O: src$Prsa$Prsa_i15_pub.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i15_pub$O src$Prsa$Prsa_i15_pub.c +$(OBJDIR)$Prsa_i15_pubexp$O: src$Prsa$Prsa_i15_pubexp.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i15_pubexp$O src$Prsa$Prsa_i15_pubexp.c + $(OBJDIR)$Prsa_i31_keygen$O: src$Prsa$Prsa_i31_keygen.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i31_keygen$O src$Prsa$Prsa_i31_keygen.c $(OBJDIR)$Prsa_i31_keygen_inner$O: src$Prsa$Prsa_i31_keygen_inner.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i31_keygen_inner$O src$Prsa$Prsa_i31_keygen_inner.c +$(OBJDIR)$Prsa_i31_modulus$O: src$Prsa$Prsa_i31_modulus.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i31_modulus$O src$Prsa$Prsa_i31_modulus.c + $(OBJDIR)$Prsa_i31_oaep_decrypt$O: src$Prsa$Prsa_i31_oaep_decrypt.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i31_oaep_decrypt$O src$Prsa$Prsa_i31_oaep_decrypt.c @@ -774,9 +821,15 @@ $(OBJDIR)$Prsa_i31_pkcs1_vrfy$O: src$Prsa$Prsa_i31_pkcs1_vrfy.c $(HEADERSPRIV) $(OBJDIR)$Prsa_i31_priv$O: src$Prsa$Prsa_i31_priv.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i31_priv$O src$Prsa$Prsa_i31_priv.c +$(OBJDIR)$Prsa_i31_privexp$O: src$Prsa$Prsa_i31_privexp.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i31_privexp$O src$Prsa$Prsa_i31_privexp.c + $(OBJDIR)$Prsa_i31_pub$O: src$Prsa$Prsa_i31_pub.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i31_pub$O src$Prsa$Prsa_i31_pub.c +$(OBJDIR)$Prsa_i31_pubexp$O: src$Prsa$Prsa_i31_pubexp.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i31_pubexp$O src$Prsa$Prsa_i31_pubexp.c + $(OBJDIR)$Prsa_i32_oaep_decrypt$O: src$Prsa$Prsa_i32_oaep_decrypt.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Prsa_i32_oaep_decrypt$O src$Prsa$Prsa_i32_oaep_decrypt.c @@ -1098,6 +1151,21 @@ $(OBJDIR)$Ppoly1305_ctmulq$O: src$Psymcipher$Ppoly1305_ctmulq.c $(HEADERSPRIV) $(OBJDIR)$Ppoly1305_i15$O: src$Psymcipher$Ppoly1305_i15.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Ppoly1305_i15$O src$Psymcipher$Ppoly1305_i15.c +$(OBJDIR)$Pasn1enc$O: src$Px509$Pasn1enc.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pasn1enc$O src$Px509$Pasn1enc.c + +$(OBJDIR)$Pencode_ec_pk8der$O: src$Px509$Pencode_ec_pk8der.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pencode_ec_pk8der$O src$Px509$Pencode_ec_pk8der.c + +$(OBJDIR)$Pencode_ec_rawder$O: src$Px509$Pencode_ec_rawder.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pencode_ec_rawder$O src$Px509$Pencode_ec_rawder.c + +$(OBJDIR)$Pencode_rsa_pk8der$O: src$Px509$Pencode_rsa_pk8der.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pencode_rsa_pk8der$O src$Px509$Pencode_rsa_pk8der.c + +$(OBJDIR)$Pencode_rsa_rawder$O: src$Px509$Pencode_rsa_rawder.c $(HEADERSPRIV) + $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pencode_rsa_rawder$O src$Px509$Pencode_rsa_rawder.c + $(OBJDIR)$Pskey_decoder$O: src$Px509$Pskey_decoder.c $(HEADERSPRIV) $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pskey_decoder$O src$Px509$Pskey_decoder.c diff --git a/mk/mkrules.sh b/mk/mkrules.sh index 0e3895e..16f16ab 100755 --- a/mk/mkrules.sh +++ b/mk/mkrules.sh @@ -67,6 +67,7 @@ coresrc=" \ src/codec/enc64be.c \ src/codec/enc64le.c \ src/codec/pemdec.c \ + src/codec/pemenc.c \ src/ec/ec_all_m15.c \ src/ec/ec_all_m31.c \ src/ec/ec_c25519_i15.c \ @@ -75,10 +76,12 @@ coresrc=" \ src/ec/ec_c25519_m31.c \ src/ec/ec_curve25519.c \ src/ec/ec_default.c \ + src/ec/ec_keygen.c \ src/ec/ec_p256_m15.c \ src/ec/ec_p256_m31.c \ src/ec/ec_prime_i15.c \ src/ec/ec_prime_i31.c \ + src/ec/ec_pubkey.c \ src/ec/ec_secp256r1.c \ src/ec/ec_secp384r1.c \ src/ec/ec_secp521r1.c \ @@ -174,27 +177,36 @@ coresrc=" \ src/rand/hmac_drbg.c \ src/rand/sysrng.c \ src/rsa/rsa_default_keygen.c \ + src/rsa/rsa_default_modulus.c \ src/rsa/rsa_default_oaep_decrypt.c \ src/rsa/rsa_default_oaep_encrypt.c \ src/rsa/rsa_default_pkcs1_sign.c \ src/rsa/rsa_default_pkcs1_vrfy.c \ src/rsa/rsa_default_priv.c \ + src/rsa/rsa_default_privexp.c \ src/rsa/rsa_default_pub.c \ + src/rsa/rsa_default_pubexp.c \ src/rsa/rsa_i15_keygen.c \ + src/rsa/rsa_i15_modulus.c \ src/rsa/rsa_i15_oaep_decrypt.c \ src/rsa/rsa_i15_oaep_encrypt.c \ src/rsa/rsa_i15_pkcs1_sign.c \ src/rsa/rsa_i15_pkcs1_vrfy.c \ src/rsa/rsa_i15_priv.c \ + src/rsa/rsa_i15_privexp.c \ src/rsa/rsa_i15_pub.c \ + src/rsa/rsa_i15_pubexp.c \ src/rsa/rsa_i31_keygen.c \ src/rsa/rsa_i31_keygen_inner.c \ + src/rsa/rsa_i31_modulus.c \ src/rsa/rsa_i31_oaep_decrypt.c \ src/rsa/rsa_i31_oaep_encrypt.c \ src/rsa/rsa_i31_pkcs1_sign.c \ src/rsa/rsa_i31_pkcs1_vrfy.c \ src/rsa/rsa_i31_priv.c \ + src/rsa/rsa_i31_privexp.c \ src/rsa/rsa_i31_pub.c \ + src/rsa/rsa_i31_pubexp.c \ src/rsa/rsa_i32_oaep_decrypt.c \ src/rsa/rsa_i32_oaep_encrypt.c \ src/rsa/rsa_i32_pkcs1_sign.c \ @@ -302,6 +314,11 @@ coresrc=" \ src/symcipher/poly1305_ctmul32.c \ src/symcipher/poly1305_ctmulq.c \ src/symcipher/poly1305_i15.c \ + src/x509/asn1enc.c \ + src/x509/encode_ec_pk8der.c \ + src/x509/encode_ec_rawder.c \ + src/x509/encode_rsa_pk8der.c \ + src/x509/encode_rsa_rawder.c \ src/x509/skey_decoder.c \ src/x509/x509_decoder.c \ src/x509/x509_knownkey.c \ diff --git a/src/codec/pemenc.c b/src/codec/pemenc.c new file mode 100644 index 0000000..236601e --- /dev/null +++ b/src/codec/pemenc.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* + * Get the appropriate Base64 character for a numeric value in the + * 0..63 range. This is constant-time. + */ +static char +b64char(uint32_t x) +{ + /* + * Values 0 to 25 map to 0x41..0x5A ('A' to 'Z') + * Values 26 to 51 map to 0x61..0x7A ('a' to 'z') + * Values 52 to 61 map to 0x30..0x39 ('0' to '9') + * Value 62 maps to 0x2B ('+') + * Value 63 maps to 0x2F ('/') + */ + uint32_t a, b, c; + + a = x - 26; + b = x - 52; + c = x - 62; + + /* + * Looking at bits 8..15 of values a, b and c: + * + * x a b c + * --------------------- + * 0..25 FF FF FF + * 26..51 00 FF FF + * 52..61 00 00 FF + * 62..63 00 00 00 + */ + return (char)(((x + 0x41) & ((a & b & c) >> 8)) + | ((x + (0x61 - 26)) & ((~a & b & c) >> 8)) + | ((x - (52 - 0x30)) & ((~a & ~b & c) >> 8)) + | ((0x2B + ((x & 1) << 2)) & (~(a | b | c) >> 8))); +} + +/* see bearssl_pem.h */ +size_t +br_pem_encode(void *dest, const void *data, size_t len, + const char *banner, unsigned flags) +{ + size_t dlen, banner_len, lines; + char *d; + unsigned char *buf; + size_t u; + int off, lim; + + banner_len = strlen(banner); + /* FIXME: try to avoid divisions here, as they may pull + an extra libc function. */ + if ((flags & BR_PEM_LINE64) != 0) { + lines = (len + 47) / 48; + } else { + lines = (len + 56) / 57; + } + dlen = (banner_len << 1) + 30 + (((len + 2) / 3) << 2) + + lines + 2; + if ((flags & BR_PEM_CRLF) != 0) { + dlen += lines + 2; + } + + if (dest == NULL) { + return dlen; + } + + d = dest; + + /* + * We always move the source data to the end of output buffer; + * the encoding process never "catches up" except at the very + * end. This also handles all conditions of partial or total + * overlap. + */ + buf = (unsigned char *)d + dlen - len; + memmove(buf, data, len); + + memcpy(d, "-----BEGIN ", 11); + d += 11; + memcpy(d, banner, banner_len); + d += banner_len; + memcpy(d, "-----", 5); + d += 5; + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + + off = 0; + lim = (flags & BR_PEM_LINE64) != 0 ? 16 : 19; + for (u = 0; (u + 2) < len; u += 3) { + uint32_t w; + + w = ((uint32_t)buf[u] << 16) + | ((uint32_t)buf[u + 1] << 8) + | (uint32_t)buf[u + 2]; + *d ++ = b64char(w >> 18); + *d ++ = b64char((w >> 12) & 0x3F); + *d ++ = b64char((w >> 6) & 0x3F); + *d ++ = b64char(w & 0x3F); + if (++ off == lim) { + off = 0; + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + } + } + if (u < len) { + uint32_t w; + + w = (uint32_t)buf[u] << 16; + if (u + 1 < len) { + w |= (uint32_t)buf[u + 1] << 8; + } + *d ++ = b64char(w >> 18); + *d ++ = b64char((w >> 12) & 0x3F); + if (u + 1 < len) { + *d ++ = b64char((w >> 6) & 0x3F); + } else { + *d ++ = 0x3D; + } + *d ++ = 0x3D; + off ++; + } + if (off != 0) { + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + } + + memcpy(d, "-----END ", 9); + d += 9; + memcpy(d, banner, banner_len); + d += banner_len; + memcpy(d, "-----", 5); + d += 5; + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + + /* Final zero, not counted in returned length. */ + *d ++ = 0x00; + + return dlen; +} diff --git a/src/ec/ec_keygen.c b/src/ec/ec_keygen.c new file mode 100644 index 0000000..02a3096 --- /dev/null +++ b/src/ec/ec_keygen.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_ec.h */ +size_t +br_ec_keygen(const br_prng_class **rng_ctx, + const br_ec_impl *impl, br_ec_private_key *sk, + void *kbuf, int curve) +{ + const unsigned char *order; + unsigned char *buf; + size_t len; + unsigned mask; + + if (curve < 0 || curve >= 32 + || ((impl->supported_curves >> curve) & 1) == 0) + { + return 0; + } + order = impl->order(curve, &len); + while (len > 0 && *order == 0) { + order ++; + len --; + } + if (kbuf == NULL || len == 0) { + return len; + } + mask = order[0]; + mask |= (mask >> 1); + mask |= (mask >> 2); + mask |= (mask >> 4); + + /* + * We generate sequences of random bits of the right size, until + * the value is strictly lower than the curve order (we also + * check for all-zero values, which are invalid). + */ + buf = kbuf; + for (;;) { + size_t u; + unsigned cc, zz; + + (*rng_ctx)->generate(rng_ctx, buf, len); + buf[0] &= mask; + cc = 0; + u = len; + zz = 0; + while (u -- > 0) { + cc = ((unsigned)(buf[u] - order[u] - cc) >> 8) & 1; + zz |= buf[u]; + } + if (cc != 0 && zz != 0) { + break; + } + } + + if (sk != NULL) { + sk->curve = curve; + sk->x = buf; + sk->xlen = len; + } + return len; +} diff --git a/src/ec/ec_pubkey.c b/src/ec/ec_pubkey.c new file mode 100644 index 0000000..383ff28 --- /dev/null +++ b/src/ec/ec_pubkey.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +static const unsigned char POINT_LEN[] = { + 0, /* 0: not a valid curve ID */ + 43, /* sect163k1 */ + 43, /* sect163r1 */ + 43, /* sect163r2 */ + 51, /* sect193r1 */ + 51, /* sect193r2 */ + 61, /* sect233k1 */ + 61, /* sect233r1 */ + 61, /* sect239k1 */ + 73, /* sect283k1 */ + 73, /* sect283r1 */ + 105, /* sect409k1 */ + 105, /* sect409r1 */ + 145, /* sect571k1 */ + 145, /* sect571r1 */ + 41, /* secp160k1 */ + 41, /* secp160r1 */ + 41, /* secp160r2 */ + 49, /* secp192k1 */ + 49, /* secp192r1 */ + 57, /* secp224k1 */ + 57, /* secp224r1 */ + 65, /* secp256k1 */ + 65, /* secp256r1 */ + 97, /* secp384r1 */ + 133, /* secp521r1 */ + 65, /* brainpoolP256r1 */ + 97, /* brainpoolP384r1 */ + 129, /* brainpoolP512r1 */ + 32, /* curve25519 */ + 56, /* curve448 */ +}; + +/* see bearssl_ec.h */ +size_t +br_ec_compute_pub(const br_ec_impl *impl, br_ec_public_key *pk, + void *kbuf, const br_ec_private_key *sk) +{ + int curve; + size_t len; + + curve = sk->curve; + if (curve < 0 || curve >= 32 || curve >= (int)(sizeof POINT_LEN) + || ((impl->supported_curves >> curve) & 1) == 0) + { + return 0; + } + if (kbuf == NULL) { + return POINT_LEN[curve]; + } + len = impl->mulgen(kbuf, sk->x, sk->xlen, curve); + if (pk != NULL) { + pk->curve = curve; + pk->q = kbuf; + pk->qlen = len; + } + return len; +} diff --git a/src/inner.h b/src/inner.h index c507102..d7e4091 100644 --- a/src/inner.h +++ b/src/inner.h @@ -1973,8 +1973,8 @@ void br_mgf1_xor(void *data, size_t len, * implementations. */ uint32_t br_rsa_i31_keygen_inner(const br_prng_class **rng, - br_rsa_private_key *sk, unsigned char *kbuf_priv, - br_rsa_public_key *pk, unsigned char *kbuf_pub, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, unsigned size, uint32_t pubexp, br_i31_modpow_opt_type mp31); /* ==================================================================== */ @@ -2027,6 +2027,72 @@ void br_ecdsa_i31_bits2int(uint32_t *x, void br_ecdsa_i15_bits2int(uint16_t *x, const void *src, size_t len, uint32_t ebitlen); +/* ==================================================================== */ +/* + * ASN.1 support functions. + */ + +/* + * A br_asn1_uint structure contains encoding information about an + * INTEGER nonnegative value: pointer to the integer contents (unsigned + * big-endian representation), length of the integer contents, + * and length of the encoded value. The data shall have minimal length: + * - If the integer value is zero, then 'len' must be zero. + * - If the integer value is not zero, then data[0] must be non-zero. + * + * Under these conditions, 'asn1len' is necessarily equal to either len + * or len+1. + */ +typedef struct { + const unsigned char *data; + size_t len; + size_t asn1len; +} br_asn1_uint; + +/* + * Given an encoded integer (unsigned big-endian, with possible leading + * bytes of value 0), returned the "prepared INTEGER" structure. + */ +br_asn1_uint br_asn1_uint_prepare(const void *xdata, size_t xlen); + +/* + * Encode an ASN.1 length. The length of the encoded length is returned. + * If 'dest' is NULL, then no encoding is performed, but the length of + * the encoded length is still computed and returned. + */ +size_t br_asn1_encode_length(void *dest, size_t len); + +/* + * Convenient macro for computing lengths of lengths. + */ +#define len_of_len(len) br_asn1_encode_length(NULL, len) + +/* + * Encode a (prepared) ASN.1 INTEGER. The encoded length is returned. + * If 'dest' is NULL, then no encoding is performed, but the length of + * the encoded integer is still computed and returned. + */ +size_t br_asn1_encode_uint(void *dest, br_asn1_uint pp); + +/* + * Get the OID that identifies an elliptic curve. Returned value is + * the DER-encoded OID, with the length (always one byte) but without + * the tag. Thus, the first byte of the returned buffer contains the + * number of subsequent bytes in the value. If the curve is not + * recognised, NULL is returned. + */ +const unsigned char *br_get_curve_OID(int curve); + +/* + * Inner function for EC private key encoding. This is equivalent to + * the API function br_encode_ec_raw_der(), except for an extra + * parameter: if 'include_curve_oid' is zero, then the curve OID is + * _not_ included in the output blob (this is for PKCS#8 support). + */ +size_t br_encode_ec_raw_der_inner(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk, + int include_curve_oid); + /* ==================================================================== */ /* * SSL/TLS support functions. diff --git a/src/rsa/rsa_default_modulus.c b/src/rsa/rsa_default_modulus.c new file mode 100644 index 0000000..57d4be5 --- /dev/null +++ b/src/rsa/rsa_default_modulus.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_rsa.h */ +br_rsa_compute_modulus +br_rsa_compute_modulus_get_default(void) +{ +#if BR_LOMUL + return &br_rsa_i15_compute_modulus; +#else + return &br_rsa_i31_compute_modulus; +#endif +} diff --git a/src/rsa/rsa_default_privexp.c b/src/rsa/rsa_default_privexp.c new file mode 100644 index 0000000..cda4555 --- /dev/null +++ b/src/rsa/rsa_default_privexp.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_rsa.h */ +br_rsa_compute_privexp +br_rsa_compute_privexp_get_default(void) +{ +#if BR_LOMUL + return &br_rsa_i15_compute_privexp; +#else + return &br_rsa_i31_compute_privexp; +#endif +} diff --git a/src/rsa/rsa_default_pubexp.c b/src/rsa/rsa_default_pubexp.c new file mode 100644 index 0000000..47bc000 --- /dev/null +++ b/src/rsa/rsa_default_pubexp.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_rsa.h */ +br_rsa_compute_pubexp +br_rsa_compute_pubexp_get_default(void) +{ +#if BR_LOMUL + return &br_rsa_i15_compute_pubexp; +#else + return &br_rsa_i31_compute_pubexp; +#endif +} diff --git a/src/rsa/rsa_i15_keygen.c b/src/rsa/rsa_i15_keygen.c index 0f4435f..1c011fe 100644 --- a/src/rsa/rsa_i15_keygen.c +++ b/src/rsa/rsa_i15_keygen.c @@ -435,8 +435,8 @@ bufswap(void *b1, void *b2, size_t len) /* see bearssl_rsa.h */ uint32_t br_rsa_i15_keygen(const br_prng_class **rng, - br_rsa_private_key *sk, unsigned char *kbuf_priv, - br_rsa_public_key *pk, unsigned char *kbuf_pub, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, unsigned size, uint32_t pubexp) { uint32_t esize_p, esize_q; diff --git a/src/rsa/rsa_i15_modulus.c b/src/rsa/rsa_i15_modulus.c new file mode 100644 index 0000000..d61c794 --- /dev/null +++ b/src/rsa/rsa_i15_modulus.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_rsa.h */ +size_t +br_rsa_i15_compute_modulus(void *n, const br_rsa_private_key *sk) +{ + uint16_t tmp[2 * ((BR_MAX_RSA_SIZE + 14) / 15) + 5]; + uint16_t *t, *p, *q; + const unsigned char *pbuf, *qbuf; + size_t nlen, plen, qlen, tlen; + + /* + * Compute actual byte and lengths for p and q. + */ + pbuf = sk->p; + plen = sk->plen; + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + qbuf = sk->q; + qlen = sk->qlen; + while (qlen > 0 && *qbuf == 0) { + qbuf ++; + qlen --; + } + + t = tmp; + tlen = (sizeof tmp) / (sizeof tmp[0]); + + /* + * Decode p. + */ + if ((15 * tlen) < (plen << 3) + 15) { + return 0; + } + br_i15_decode(t, pbuf, plen); + p = t; + plen = (p[0] + 31) >> 4; + t += plen; + tlen -= plen; + + /* + * Decode q. + */ + if ((15 * tlen) < (qlen << 3) + 15) { + return 0; + } + br_i15_decode(t, qbuf, qlen); + q = t; + qlen = (q[0] + 31) >> 4; + t += qlen; + tlen -= qlen; + + /* + * Computation can proceed only if we have enough room for the + * modulus. + */ + if (tlen < (plen + qlen + 1)) { + return 0; + } + + /* + * Private key already contains the modulus bit length, from which + * we can infer the output length. Even if n is NULL, we still had + * to decode p and q to make sure that the product can be computed. + */ + nlen = (sk->n_bitlen + 7) >> 3; + if (n != NULL) { + br_i15_zero(t, p[0]); + br_i15_mulacc(t, p, q); + br_i15_encode(n, nlen, t); + } + return nlen; +} diff --git a/src/rsa/rsa_i15_privexp.c b/src/rsa/rsa_i15_privexp.c new file mode 100644 index 0000000..57d6918 --- /dev/null +++ b/src/rsa/rsa_i15_privexp.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_rsa.h */ +size_t +br_rsa_i15_compute_privexp(void *d, + const br_rsa_private_key *sk, uint32_t e) +{ + /* + * We want to invert e modulo phi = (p-1)(q-1). This first + * requires computing phi, which is easy since we have the factors + * p and q in the private key structure. + * + * Since p = 3 mod 4 and q = 3 mod 4, phi/4 is an odd integer. + * We could invert e modulo phi/4 then patch the result to + * modulo phi, but this would involve assembling three modulus-wide + * values (phi/4, 1 and e) and calling moddiv, that requires + * three more temporaries, for a total of six big integers, or + * slightly more than 3 kB of stack space for RSA-4096. This + * exceeds our stack requirements. + * + * Instead, we first use one step of the extended GCD: + * + * - We compute phi = k*e + r (Euclidean division of phi by e). + * If public exponent e is correct, then r != 0 (e must be + * invertible modulo phi). We also have k != 0 since we + * enforce non-ridiculously-small factors. + * + * - We find small u, v such that u*e - v*r = 1 (using a + * binary GCD; we can arrange for u < r and v < e, i.e. all + * values fit on 32 bits). + * + * - Solution is: d = u + v*k + * This last computation is exact: since u < r and v < e, + * the above implies d < r + e*((phi-r)/e) = phi + */ + + uint16_t tmp[4 * ((BR_MAX_RSA_FACTOR + 14) / 15) + 12]; + uint16_t *p, *q, *k, *m, *z, *phi; + const unsigned char *pbuf, *qbuf; + size_t plen, qlen, u, len, dlen; + uint32_t r, a, b, u0, v0, u1, v1, he, hr; + int i; + + /* + * Check that e is correct. + */ + if (e < 3 || (e & 1) == 0) { + return 0; + } + + /* + * Check lengths of p and q, and that they are both odd. + */ + pbuf = sk->p; + plen = sk->plen; + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + if (plen < 5 || plen > (BR_MAX_RSA_FACTOR / 8) + || (pbuf[plen - 1] & 1) != 1) + { + return 0; + } + qbuf = sk->q; + qlen = sk->qlen; + while (qlen > 0 && *qbuf == 0) { + qbuf ++; + qlen --; + } + if (qlen < 5 || qlen > (BR_MAX_RSA_FACTOR / 8) + || (qbuf[qlen - 1] & 1) != 1) + { + return 0; + } + + /* + * Output length is that of the modulus. + */ + dlen = (sk->n_bitlen + 7) >> 3; + if (d == NULL) { + return dlen; + } + + p = tmp; + br_i15_decode(p, pbuf, plen); + plen = (p[0] + 15) >> 4; + q = p + 1 + plen; + br_i15_decode(q, qbuf, qlen); + qlen = (q[0] + 15) >> 4; + + /* + * Compute phi = (p-1)*(q-1), then move it over p-1 and q-1 (that + * we do not need anymore). The mulacc function sets the announced + * bit length of t to be the sum of the announced bit lengths of + * p-1 and q-1, which is usually exact but may overshoot by one 1 + * bit in some cases; we readjust it to its true length. + */ + p[1] --; + q[1] --; + phi = q + 1 + qlen; + br_i15_zero(phi, p[0]); + br_i15_mulacc(phi, p, q); + len = (phi[0] + 15) >> 4; + memmove(tmp, phi, (1 + len) * sizeof *phi); + phi = tmp; + phi[0] = br_i15_bit_length(phi + 1, len); + len = (phi[0] + 15) >> 4; + + /* + * Divide phi by public exponent e. The final remainder r must be + * non-zero (otherwise, the key is invalid). The quotient is k, + * which we write over phi, since we don't need phi after that. + */ + r = 0; + for (u = len; u >= 1; u --) { + /* + * Upon entry, r < e, and phi[u] < 2^15; hence, + * hi:lo < e*2^15. Thus, the produced word k[u] + * must be lower than 2^15, and the new remainder r + * is lower than e. + */ + uint32_t hi, lo; + + hi = r >> 17; + lo = (r << 15) + phi[u]; + phi[u] = br_divrem(hi, lo, e, &r); + } + if (r == 0) { + return 0; + } + k = phi; + + /* + * Compute u and v such that u*e - v*r = GCD(e,r). We use + * a binary GCD algorithm, with 6 extra integers a, b, + * u0, u1, v0 and v1. Initial values are: + * a = e u0 = 1 v0 = 0 + * b = r u1 = r v1 = e-1 + * The following invariants are maintained: + * a = u0*e - v0*r + * b = u1*e - v1*r + * 0 < a <= e + * 0 < b <= r + * 0 <= u0 <= r + * 0 <= v0 <= e + * 0 <= u1 <= r + * 0 <= v1 <= e + * + * At each iteration, we reduce either a or b by one bit, and + * adjust u0, u1, v0 and v1 to maintain the invariants: + * - if a is even, then a <- a/2 + * - otherwise, if b is even, then b <- b/2 + * - otherwise, if a > b, then a <- (a-b)/2 + * - otherwise, if b > a, then b <- (b-a)/2 + * Algorithm stops when a = b. At that point, the common value + * is the GCD of e and r; it must be 1 (otherwise, the private + * key or public exponent is not valid). The (u0,v0) or (u1,v1) + * pairs are the solution we are looking for. + * + * Since either a or b is reduced by at least 1 bit at each + * iteration, 62 iterations are enough to reach the end + * condition. + * + * To maintain the invariants, we must compute the same operations + * on the u* and v* values that we do on a and b: + * - When a is divided by 2, u0 and v0 must be divided by 2. + * - When b is divided by 2, u1 and v1 must be divided by 2. + * - When b is subtracted from a, u1 and v1 are subtracted from + * u0 and v0, respectively. + * - When a is subtracted from b, u0 and v0 are subtracted from + * u1 and v1, respectively. + * + * However, we want to keep the u* and v* values in their proper + * ranges. The following remarks apply: + * + * - When a is divided by 2, then a is even. Therefore: + * + * * If r is odd, then u0 and v0 must have the same parity; + * if they are both odd, then adding r to u0 and e to v0 + * makes them both even, and the division by 2 brings them + * back to the proper range. + * + * * If r is even, then u0 must be even; if v0 is odd, then + * adding r to u0 and e to v0 makes them both even, and the + * division by 2 brings them back to the proper range. + * + * Thus, all we need to do is to look at the parity of v0, + * and add (r,e) to (u0,v0) when v0 is odd. In order to avoid + * a 32-bit overflow, we can add ((r+1)/2,(e/2)+1) after the + * division (r+1 does not overflow since r < e; and (e/2)+1 + * is equal to (e+1)/2 since e is odd). + * + * - When we subtract b from a, three cases may occur: + * + * * u1 <= u0 and v1 <= v0: just do the subtractions + * + * * u1 > u0 and v1 > v0: compute: + * (u0, v0) <- (u0 + r - u1, v0 + e - v1) + * + * * u1 <= u0 and v1 > v0: compute: + * (u0, v0) <- (u0 + r - u1, v0 + e - v1) + * + * The fourth case (u1 > u0 and v1 <= v0) is not possible + * because it would contradict "b < a" (which is the reason + * why we subtract b from a). + * + * The tricky case is the third one: from the equations, it + * seems that u0 may go out of range. However, the invariants + * and ranges of other values imply that, in that case, the + * new u0 does not actually exceed the range. + * + * We can thus handle the subtraction by adding (r,e) based + * solely on the comparison between v0 and v1. + */ + a = e; + b = r; + u0 = 1; + v0 = 0; + u1 = r; + v1 = e - 1; + hr = (r + 1) >> 1; + he = (e >> 1) + 1; + for (i = 0; i < 62; i ++) { + uint32_t oa, ob, agtb, bgta; + uint32_t sab, sba, da, db; + uint32_t ctl; + + oa = a & 1; /* 1 if a is odd */ + ob = b & 1; /* 1 if b is odd */ + agtb = GT(a, b); /* 1 if a > b */ + bgta = GT(b, a); /* 1 if b > a */ + + sab = oa & ob & agtb; /* 1 if a <- a-b */ + sba = oa & ob & bgta; /* 1 if b <- b-a */ + + /* a <- a-b, u0 <- u0-u1, v0 <- v0-v1 */ + ctl = GT(v1, v0); + a -= b & -sab; + u0 -= (u1 - (r & -ctl)) & -sab; + v0 -= (v1 - (e & -ctl)) & -sab; + + /* b <- b-a, u1 <- u1-u0 mod r, v1 <- v1-v0 mod e */ + ctl = GT(v0, v1); + b -= a & -sba; + u1 -= (u0 - (r & -ctl)) & -sba; + v1 -= (v0 - (e & -ctl)) & -sba; + + da = NOT(oa) | sab; /* 1 if a <- a/2 */ + db = (oa & NOT(ob)) | sba; /* 1 if b <- b/2 */ + + /* a <- a/2, u0 <- u0/2, v0 <- v0/2 */ + ctl = v0 & 1; + a ^= (a ^ (a >> 1)) & -da; + u0 ^= (u0 ^ ((u0 >> 1) + (hr & -ctl))) & -da; + v0 ^= (v0 ^ ((v0 >> 1) + (he & -ctl))) & -da; + + /* b <- b/2, u1 <- u1/2 mod r, v1 <- v1/2 mod e */ + ctl = v1 & 1; + b ^= (b ^ (b >> 1)) & -db; + u1 ^= (u1 ^ ((u1 >> 1) + (hr & -ctl))) & -db; + v1 ^= (v1 ^ ((v1 >> 1) + (he & -ctl))) & -db; + } + + /* + * Check that the GCD is indeed 1. If not, then the key is invalid + * (and there's no harm in leaking that piece of information). + */ + if (a != 1) { + return 0; + } + + /* + * Now we have u0*e - v0*r = 1. Let's compute the result as: + * d = u0 + v0*k + * We still have k in the tmp[] array, and its announced bit + * length is that of phi. + */ + m = k + 1 + len; + m[0] = (2 << 4) + 2; /* bit length is 32 bits, encoded */ + m[1] = v0 & 0x7FFF; + m[2] = (v0 >> 15) & 0x7FFF; + m[3] = v0 >> 30; + z = m + 4; + br_i15_zero(z, k[0]); + z[1] = u0 & 0x7FFF; + z[2] = (u0 >> 15) & 0x7FFF; + z[3] = u0 >> 30; + br_i15_mulacc(z, k, m); + + /* + * Encode the result. + */ + br_i15_encode(d, dlen, z); + return dlen; +} diff --git a/src/rsa/rsa_i15_pubexp.c b/src/rsa/rsa_i15_pubexp.c new file mode 100644 index 0000000..803bff7 --- /dev/null +++ b/src/rsa/rsa_i15_pubexp.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* + * Recompute public exponent, based on factor p and reduced private + * exponent dp. + */ +static uint32_t +get_pubexp(const unsigned char *pbuf, size_t plen, + const unsigned char *dpbuf, size_t dplen) +{ + /* + * dp is the inverse of e modulo p-1. If p = 3 mod 4, then + * p-1 = 2*((p-1)/2). Taken modulo 2, e is odd and has inverse 1; + * thus, dp must be odd. + * + * We compute the inverse of dp modulo (p-1)/2. This requires + * first reducing dp modulo (p-1)/2 (this can be done with a + * conditional subtract, no need to use the generic modular + * reduction function); then, we use moddiv. + */ + + uint16_t tmp[6 * ((BR_MAX_RSA_FACTOR + 29) / 15)]; + uint16_t *p, *dp, *x; + size_t len; + uint32_t e; + + /* + * Compute actual factor length (in bytes) and check that it fits + * under our size constraints. + */ + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + if (plen == 0 || plen < 5 || plen > (BR_MAX_RSA_FACTOR / 8)) { + return 0; + } + + /* + * Compute actual reduced exponent length (in bytes) and check that + * it is not longer than p. + */ + while (dplen > 0 && *dpbuf == 0) { + dpbuf ++; + dplen --; + } + if (dplen > plen || dplen == 0 + || (dplen == plen && dpbuf[0] > pbuf[0])) + { + return 0; + } + + /* + * Verify that p = 3 mod 4 and that dp is odd. + */ + if ((pbuf[plen - 1] & 3) != 3 || (dpbuf[dplen - 1] & 1) != 1) { + return 0; + } + + /* + * Decode p and compute (p-1)/2. + */ + p = tmp; + br_i15_decode(p, pbuf, plen); + len = (p[0] + 31) >> 4; + br_i15_rshift(p, 1); + + /* + * Decode dp and make sure its announced bit length matches that of + * p (we already know that the size of dp, in bits, does not exceed + * the size of p, so we just have to copy the header word). + */ + dp = p + len; + memset(dp, 0, len * sizeof *dp); + br_i15_decode(dp, dpbuf, dplen); + dp[0] = p[0]; + + /* + * Subtract (p-1)/2 from dp if necessary. + */ + br_i15_sub(dp, p, NOT(br_i15_sub(dp, p, 0))); + + /* + * If another subtraction is needed, then this means that the + * value was invalid. We don't care to leak information about + * invalid keys. + */ + if (br_i15_sub(dp, p, 0) == 0) { + return 0; + } + + /* + * Invert dp modulo (p-1)/2. If the inversion fails, then the + * key value was invalid. + */ + x = dp + len; + br_i15_zero(x, p[0]); + x[1] = 1; + if (br_i15_moddiv(x, dp, p, br_i15_ninv15(p[1]), x + len) == 0) { + return 0; + } + + /* + * We now have an inverse. We must set it to zero (error) if its + * length is greater than 32 bits and/or if it is an even integer. + * Take care that the bit_length function returns an encoded + * bit length. + */ + e = (uint32_t)x[1] | ((uint32_t)x[2] << 15) | ((uint32_t)x[3] << 30); + e &= -LT(br_i15_bit_length(x + 1, len - 1), 35); + e &= -(e & 1); + return e; +} + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_compute_pubexp(const br_rsa_private_key *sk) +{ + /* + * Get the public exponent from both p and q. This is the right + * exponent if we get twice the same value. + */ + uint32_t ep, eq; + + ep = get_pubexp(sk->p, sk->plen, sk->dp, sk->dplen); + eq = get_pubexp(sk->q, sk->qlen, sk->dq, sk->dqlen); + return ep & -EQ(ep, eq); +} diff --git a/src/rsa/rsa_i31_keygen.c b/src/rsa/rsa_i31_keygen.c index 0e26246..77708f8 100644 --- a/src/rsa/rsa_i31_keygen.c +++ b/src/rsa/rsa_i31_keygen.c @@ -27,8 +27,8 @@ /* see bearssl_rsa.h */ uint32_t br_rsa_i31_keygen(const br_prng_class **rng, - br_rsa_private_key *sk, unsigned char *kbuf_priv, - br_rsa_public_key *pk, unsigned char *kbuf_pub, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, unsigned size, uint32_t pubexp) { return br_rsa_i31_keygen_inner(rng, diff --git a/src/rsa/rsa_i31_keygen_inner.c b/src/rsa/rsa_i31_keygen_inner.c index 69120e7..9ec881b 100644 --- a/src/rsa/rsa_i31_keygen_inner.c +++ b/src/rsa/rsa_i31_keygen_inner.c @@ -456,8 +456,8 @@ bufswap(void *b1, void *b2, size_t len) /* see inner.h */ uint32_t br_rsa_i31_keygen_inner(const br_prng_class **rng, - br_rsa_private_key *sk, unsigned char *kbuf_priv, - br_rsa_public_key *pk, unsigned char *kbuf_pub, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, unsigned size, uint32_t pubexp, br_i31_modpow_opt_type mp31) { uint32_t esize_p, esize_q; diff --git a/src/rsa/rsa_i31_modulus.c b/src/rsa/rsa_i31_modulus.c new file mode 100644 index 0000000..c469cf3 --- /dev/null +++ b/src/rsa/rsa_i31_modulus.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_rsa.h */ +size_t +br_rsa_i31_compute_modulus(void *n, const br_rsa_private_key *sk) +{ + uint32_t tmp[2 * ((BR_MAX_RSA_SIZE + 30) / 31) + 5]; + uint32_t *t, *p, *q; + const unsigned char *pbuf, *qbuf; + size_t nlen, plen, qlen, tlen; + + /* + * Compute actual byte and lengths for p and q. + */ + pbuf = sk->p; + plen = sk->plen; + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + qbuf = sk->q; + qlen = sk->qlen; + while (qlen > 0 && *qbuf == 0) { + qbuf ++; + qlen --; + } + + t = tmp; + tlen = (sizeof tmp) / (sizeof tmp[0]); + + /* + * Decode p. + */ + if ((31 * tlen) < (plen << 3) + 31) { + return 0; + } + br_i31_decode(t, pbuf, plen); + p = t; + plen = (p[0] + 63) >> 5; + t += plen; + tlen -= plen; + + /* + * Decode q. + */ + if ((31 * tlen) < (qlen << 3) + 31) { + return 0; + } + br_i31_decode(t, qbuf, qlen); + q = t; + qlen = (q[0] + 63) >> 5; + t += qlen; + tlen -= qlen; + + /* + * Computation can proceed only if we have enough room for the + * modulus. + */ + if (tlen < (plen + qlen + 1)) { + return 0; + } + + /* + * Private key already contains the modulus bit length, from which + * we can infer the output length. Even if n is NULL, we still had + * to decode p and q to make sure that the product can be computed. + */ + nlen = (sk->n_bitlen + 7) >> 3; + if (n != NULL) { + br_i31_zero(t, p[0]); + br_i31_mulacc(t, p, q); + br_i31_encode(n, nlen, t); + } + return nlen; +} diff --git a/src/rsa/rsa_i31_privexp.c b/src/rsa/rsa_i31_privexp.c new file mode 100644 index 0000000..eee62a0 --- /dev/null +++ b/src/rsa/rsa_i31_privexp.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_rsa.h */ +size_t +br_rsa_i31_compute_privexp(void *d, + const br_rsa_private_key *sk, uint32_t e) +{ + /* + * We want to invert e modulo phi = (p-1)(q-1). This first + * requires computing phi, which is easy since we have the factors + * p and q in the private key structure. + * + * Since p = 3 mod 4 and q = 3 mod 4, phi/4 is an odd integer. + * We could invert e modulo phi/4 then patch the result to + * modulo phi, but this would involve assembling three modulus-wide + * values (phi/4, 1 and e) and calling moddiv, that requires + * three more temporaries, for a total of six big integers, or + * slightly more than 3 kB of stack space for RSA-4096. This + * exceeds our stack requirements. + * + * Instead, we first use one step of the extended GCD: + * + * - We compute phi = k*e + r (Euclidean division of phi by e). + * If public exponent e is correct, then r != 0 (e must be + * invertible modulo phi). We also have k != 0 since we + * enforce non-ridiculously-small factors. + * + * - We find small u, v such that u*e - v*r = 1 (using a + * binary GCD; we can arrange for u < r and v < e, i.e. all + * values fit on 32 bits). + * + * - Solution is: d = u + v*k + * This last computation is exact: since u < r and v < e, + * the above implies d < r + e*((phi-r)/e) = phi + */ + + uint32_t tmp[4 * ((BR_MAX_RSA_FACTOR + 30) / 31) + 12]; + uint32_t *p, *q, *k, *m, *z, *phi; + const unsigned char *pbuf, *qbuf; + size_t plen, qlen, u, len, dlen; + uint32_t r, a, b, u0, v0, u1, v1, he, hr; + int i; + + /* + * Check that e is correct. + */ + if (e < 3 || (e & 1) == 0) { + return 0; + } + + /* + * Check lengths of p and q, and that they are both odd. + */ + pbuf = sk->p; + plen = sk->plen; + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + if (plen < 5 || plen > (BR_MAX_RSA_FACTOR / 8) + || (pbuf[plen - 1] & 1) != 1) + { + return 0; + } + qbuf = sk->q; + qlen = sk->qlen; + while (qlen > 0 && *qbuf == 0) { + qbuf ++; + qlen --; + } + if (qlen < 5 || qlen > (BR_MAX_RSA_FACTOR / 8) + || (qbuf[qlen - 1] & 1) != 1) + { + return 0; + } + + /* + * Output length is that of the modulus. + */ + dlen = (sk->n_bitlen + 7) >> 3; + if (d == NULL) { + return dlen; + } + + p = tmp; + br_i31_decode(p, pbuf, plen); + plen = (p[0] + 31) >> 5; + q = p + 1 + plen; + br_i31_decode(q, qbuf, qlen); + qlen = (q[0] + 31) >> 5; + + /* + * Compute phi = (p-1)*(q-1), then move it over p-1 and q-1 (that + * we do not need anymore). The mulacc function sets the announced + * bit length of t to be the sum of the announced bit lengths of + * p-1 and q-1, which is usually exact but may overshoot by one 1 + * bit in some cases; we readjust it to its true length. + */ + p[1] --; + q[1] --; + phi = q + 1 + qlen; + br_i31_zero(phi, p[0]); + br_i31_mulacc(phi, p, q); + len = (phi[0] + 31) >> 5; + memmove(tmp, phi, (1 + len) * sizeof *phi); + phi = tmp; + phi[0] = br_i31_bit_length(phi + 1, len); + len = (phi[0] + 31) >> 5; + + /* + * Divide phi by public exponent e. The final remainder r must be + * non-zero (otherwise, the key is invalid). The quotient is k, + * which we write over phi, since we don't need phi after that. + */ + r = 0; + for (u = len; u >= 1; u --) { + /* + * Upon entry, r < e, and phi[u] < 2^31; hence, + * hi:lo < e*2^31. Thus, the produced word k[u] + * must be lower than 2^31, and the new remainder r + * is lower than e. + */ + uint32_t hi, lo; + + hi = r >> 1; + lo = (r << 31) + phi[u]; + phi[u] = br_divrem(hi, lo, e, &r); + } + if (r == 0) { + return 0; + } + k = phi; + + /* + * Compute u and v such that u*e - v*r = GCD(e,r). We use + * a binary GCD algorithm, with 6 extra integers a, b, + * u0, u1, v0 and v1. Initial values are: + * a = e u0 = 1 v0 = 0 + * b = r u1 = r v1 = e-1 + * The following invariants are maintained: + * a = u0*e - v0*r + * b = u1*e - v1*r + * 0 < a <= e + * 0 < b <= r + * 0 <= u0 <= r + * 0 <= v0 <= e + * 0 <= u1 <= r + * 0 <= v1 <= e + * + * At each iteration, we reduce either a or b by one bit, and + * adjust u0, u1, v0 and v1 to maintain the invariants: + * - if a is even, then a <- a/2 + * - otherwise, if b is even, then b <- b/2 + * - otherwise, if a > b, then a <- (a-b)/2 + * - otherwise, if b > a, then b <- (b-a)/2 + * Algorithm stops when a = b. At that point, the common value + * is the GCD of e and r; it must be 1 (otherwise, the private + * key or public exponent is not valid). The (u0,v0) or (u1,v1) + * pairs are the solution we are looking for. + * + * Since either a or b is reduced by at least 1 bit at each + * iteration, 62 iterations are enough to reach the end + * condition. + * + * To maintain the invariants, we must compute the same operations + * on the u* and v* values that we do on a and b: + * - When a is divided by 2, u0 and v0 must be divided by 2. + * - When b is divided by 2, u1 and v1 must be divided by 2. + * - When b is subtracted from a, u1 and v1 are subtracted from + * u0 and v0, respectively. + * - When a is subtracted from b, u0 and v0 are subtracted from + * u1 and v1, respectively. + * + * However, we want to keep the u* and v* values in their proper + * ranges. The following remarks apply: + * + * - When a is divided by 2, then a is even. Therefore: + * + * * If r is odd, then u0 and v0 must have the same parity; + * if they are both odd, then adding r to u0 and e to v0 + * makes them both even, and the division by 2 brings them + * back to the proper range. + * + * * If r is even, then u0 must be even; if v0 is odd, then + * adding r to u0 and e to v0 makes them both even, and the + * division by 2 brings them back to the proper range. + * + * Thus, all we need to do is to look at the parity of v0, + * and add (r,e) to (u0,v0) when v0 is odd. In order to avoid + * a 32-bit overflow, we can add ((r+1)/2,(e/2)+1) after the + * division (r+1 does not overflow since r < e; and (e/2)+1 + * is equal to (e+1)/2 since e is odd). + * + * - When we subtract b from a, three cases may occur: + * + * * u1 <= u0 and v1 <= v0: just do the subtractions + * + * * u1 > u0 and v1 > v0: compute: + * (u0, v0) <- (u0 + r - u1, v0 + e - v1) + * + * * u1 <= u0 and v1 > v0: compute: + * (u0, v0) <- (u0 + r - u1, v0 + e - v1) + * + * The fourth case (u1 > u0 and v1 <= v0) is not possible + * because it would contradict "b < a" (which is the reason + * why we subtract b from a). + * + * The tricky case is the third one: from the equations, it + * seems that u0 may go out of range. However, the invariants + * and ranges of other values imply that, in that case, the + * new u0 does not actually exceed the range. + * + * We can thus handle the subtraction by adding (r,e) based + * solely on the comparison between v0 and v1. + */ + a = e; + b = r; + u0 = 1; + v0 = 0; + u1 = r; + v1 = e - 1; + hr = (r + 1) >> 1; + he = (e >> 1) + 1; + for (i = 0; i < 62; i ++) { + uint32_t oa, ob, agtb, bgta; + uint32_t sab, sba, da, db; + uint32_t ctl; + + oa = a & 1; /* 1 if a is odd */ + ob = b & 1; /* 1 if b is odd */ + agtb = GT(a, b); /* 1 if a > b */ + bgta = GT(b, a); /* 1 if b > a */ + + sab = oa & ob & agtb; /* 1 if a <- a-b */ + sba = oa & ob & bgta; /* 1 if b <- b-a */ + + /* a <- a-b, u0 <- u0-u1, v0 <- v0-v1 */ + ctl = GT(v1, v0); + a -= b & -sab; + u0 -= (u1 - (r & -ctl)) & -sab; + v0 -= (v1 - (e & -ctl)) & -sab; + + /* b <- b-a, u1 <- u1-u0 mod r, v1 <- v1-v0 mod e */ + ctl = GT(v0, v1); + b -= a & -sba; + u1 -= (u0 - (r & -ctl)) & -sba; + v1 -= (v0 - (e & -ctl)) & -sba; + + da = NOT(oa) | sab; /* 1 if a <- a/2 */ + db = (oa & NOT(ob)) | sba; /* 1 if b <- b/2 */ + + /* a <- a/2, u0 <- u0/2, v0 <- v0/2 */ + ctl = v0 & 1; + a ^= (a ^ (a >> 1)) & -da; + u0 ^= (u0 ^ ((u0 >> 1) + (hr & -ctl))) & -da; + v0 ^= (v0 ^ ((v0 >> 1) + (he & -ctl))) & -da; + + /* b <- b/2, u1 <- u1/2 mod r, v1 <- v1/2 mod e */ + ctl = v1 & 1; + b ^= (b ^ (b >> 1)) & -db; + u1 ^= (u1 ^ ((u1 >> 1) + (hr & -ctl))) & -db; + v1 ^= (v1 ^ ((v1 >> 1) + (he & -ctl))) & -db; + } + + /* + * Check that the GCD is indeed 1. If not, then the key is invalid + * (and there's no harm in leaking that piece of information). + */ + if (a != 1) { + return 0; + } + + /* + * Now we have u0*e - v0*r = 1. Let's compute the result as: + * d = u0 + v0*k + * We still have k in the tmp[] array, and its announced bit + * length is that of phi. + */ + m = k + 1 + len; + m[0] = (1 << 5) + 1; /* bit length is 32 bits, encoded */ + m[1] = v0 & 0x7FFFFFFF; + m[2] = v0 >> 31; + z = m + 3; + br_i31_zero(z, k[0]); + z[1] = u0 & 0x7FFFFFFF; + z[2] = u0 >> 31; + br_i31_mulacc(z, k, m); + + /* + * Encode the result. + */ + br_i31_encode(d, dlen, z); + return dlen; +} diff --git a/src/rsa/rsa_i31_pubexp.c b/src/rsa/rsa_i31_pubexp.c new file mode 100644 index 0000000..f26537d --- /dev/null +++ b/src/rsa/rsa_i31_pubexp.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* + * Recompute public exponent, based on factor p and reduced private + * exponent dp. + */ +static uint32_t +get_pubexp(const unsigned char *pbuf, size_t plen, + const unsigned char *dpbuf, size_t dplen) +{ + /* + * dp is the inverse of e modulo p-1. If p = 3 mod 4, then + * p-1 = 2*((p-1)/2). Taken modulo 2, e is odd and has inverse 1; + * thus, dp must be odd. + * + * We compute the inverse of dp modulo (p-1)/2. This requires + * first reducing dp modulo (p-1)/2 (this can be done with a + * conditional subtract, no need to use the generic modular + * reduction function); then, we use moddiv. + */ + + uint32_t tmp[6 * ((BR_MAX_RSA_FACTOR + 61) / 31)]; + uint32_t *p, *dp, *x; + size_t len; + uint32_t e; + + /* + * Compute actual factor length (in bytes) and check that it fits + * under our size constraints. + */ + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + if (plen == 0 || plen < 5 || plen > (BR_MAX_RSA_FACTOR / 8)) { + return 0; + } + + /* + * Compute actual reduced exponent length (in bytes) and check that + * it is not longer than p. + */ + while (dplen > 0 && *dpbuf == 0) { + dpbuf ++; + dplen --; + } + if (dplen > plen || dplen == 0 + || (dplen == plen && dpbuf[0] > pbuf[0])) + { + return 0; + } + + /* + * Verify that p = 3 mod 4 and that dp is odd. + */ + if ((pbuf[plen - 1] & 3) != 3 || (dpbuf[dplen - 1] & 1) != 1) { + return 0; + } + + /* + * Decode p and compute (p-1)/2. + */ + p = tmp; + br_i31_decode(p, pbuf, plen); + len = (p[0] + 63) >> 5; + br_i31_rshift(p, 1); + + /* + * Decode dp and make sure its announced bit length matches that of + * p (we already know that the size of dp, in bits, does not exceed + * the size of p, so we just have to copy the header word). + */ + dp = p + len; + memset(dp, 0, len * sizeof *dp); + br_i31_decode(dp, dpbuf, dplen); + dp[0] = p[0]; + + /* + * Subtract (p-1)/2 from dp if necessary. + */ + br_i31_sub(dp, p, NOT(br_i31_sub(dp, p, 0))); + + /* + * If another subtraction is needed, then this means that the + * value was invalid. We don't care to leak information about + * invalid keys. + */ + if (br_i31_sub(dp, p, 0) == 0) { + return 0; + } + + /* + * Invert dp modulo (p-1)/2. If the inversion fails, then the + * key value was invalid. + */ + x = dp + len; + br_i31_zero(x, p[0]); + x[1] = 1; + if (br_i31_moddiv(x, dp, p, br_i31_ninv31(p[1]), x + len) == 0) { + return 0; + } + + /* + * We now have an inverse. We must set it to zero (error) if its + * length is greater than 32 bits and/or if it is an even integer. + * Take care that the bit_length function returns an encoded + * bit length. + */ + e = (uint32_t)x[1] | ((uint32_t)x[2] << 31); + e &= -LT(br_i31_bit_length(x + 1, len - 1), 34); + e &= -(e & 1); + return e; +} + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i31_compute_pubexp(const br_rsa_private_key *sk) +{ + /* + * Get the public exponent from both p and q. This is the right + * exponent if we get twice the same value. + */ + uint32_t ep, eq; + + ep = get_pubexp(sk->p, sk->plen, sk->dp, sk->dplen); + eq = get_pubexp(sk->q, sk->qlen, sk->dq, sk->dqlen); + return ep & -EQ(ep, eq); +} diff --git a/src/rsa/rsa_i62_keygen.c b/src/rsa/rsa_i62_keygen.c index 7d60af9..8f55c37 100644 --- a/src/rsa/rsa_i62_keygen.c +++ b/src/rsa/rsa_i62_keygen.c @@ -29,8 +29,8 @@ /* see bearssl_rsa.h */ uint32_t br_rsa_i62_keygen(const br_prng_class **rng, - br_rsa_private_key *sk, unsigned char *kbuf_priv, - br_rsa_public_key *pk, unsigned char *kbuf_pub, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, unsigned size, uint32_t pubexp) { return br_rsa_i31_keygen_inner(rng, diff --git a/src/x509/asn1enc.c b/src/x509/asn1enc.c new file mode 100644 index 0000000..7a74963 --- /dev/null +++ b/src/x509/asn1enc.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see inner.h */ +br_asn1_uint +br_asn1_uint_prepare(const void *xdata, size_t xlen) +{ + const unsigned char *x; + br_asn1_uint t; + + x = xdata; + while (xlen > 0 && *x == 0) { + x ++; + xlen --; + } + t.data = x; + t.len = xlen; + t.asn1len = xlen; + if (xlen == 0 || x[0] >= 0x80) { + t.asn1len ++; + } + return t; +} + +/* see inner.h */ +size_t +br_asn1_encode_length(void *dest, size_t len) +{ + unsigned char *buf; + size_t z; + int i, j; + + buf = dest; + if (len < 0x80) { + if (buf != NULL) { + *buf = len; + } + return 1; + } + i = 0; + for (z = len; z != 0; z >>= 8) { + i ++; + } + if (buf != NULL) { + *buf ++ = 0x80 + i; + for (j = i - 1; j >= 0; j --) { + *buf ++ = len >> (j << 3); + } + } + return i + 1; +} + +/* see inner.h */ +size_t +br_asn1_encode_uint(void *dest, br_asn1_uint pp) +{ + unsigned char *buf; + size_t lenlen; + + if (dest == NULL) { + return 1 + br_asn1_encode_length(NULL, pp.asn1len) + pp.asn1len; + } + buf = dest; + *buf ++ = 0x02; + lenlen = br_asn1_encode_length(buf, pp.asn1len); + buf += lenlen; + *buf = 0x00; + memcpy(buf + pp.asn1len - pp.len, pp.data, pp.len); + return 1 + lenlen + pp.asn1len; +} diff --git a/src/x509/encode_ec_pk8der.c b/src/x509/encode_ec_pk8der.c new file mode 100644 index 0000000..53717ce --- /dev/null +++ b/src/x509/encode_ec_pk8der.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_x509.h */ +size_t +br_encode_ec_pkcs8_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk) +{ + /* + * ASN.1 format: + * + * OneAsymmetricKey ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] Attributes OPTIONAL, + * ..., + * [[2: publicKey [1] PublicKey OPTIONAL ]], + * ... + * } + * + * We don't include attributes or public key (the public key + * is included in the private key value instead). The + * 'version' field is an INTEGER that we will set to 0 + * (meaning 'v1', compatible with previous versions of PKCS#8). + * The 'privateKeyAlgorithm' structure is an AlgorithmIdentifier + * whose OID should be id-ecPublicKey, with, as parameters, the + * curve OID. The 'privateKey' is an OCTET STRING, whose value + * is the "raw DER" encoding of the key pair. + */ + + /* + * OID id-ecPublicKey (1.2.840.10045.2.1), DER-encoded (with + * the tag). + */ + static const unsigned char OID_ECPUBKEY[] = { + 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 + }; + + size_t len_version, len_privateKeyAlgorithm, len_privateKeyValue; + size_t len_privateKey, len_seq; + const unsigned char *oid; + + oid = br_get_curve_OID(sk->curve); + if (oid == NULL) { + return 0; + } + len_version = 3; + len_privateKeyAlgorithm = 2 + sizeof OID_ECPUBKEY + 2 + oid[0]; + len_privateKeyValue = br_encode_ec_raw_der_inner(NULL, sk, pk, 0); + len_privateKey = 1 + len_of_len(len_privateKeyValue) + + len_privateKeyValue; + len_seq = len_version + len_privateKeyAlgorithm + len_privateKey; + + if (dest == NULL) { + return 1 + len_of_len(len_seq) + len_seq; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, len_seq); + buf += lenlen; + + /* version */ + *buf ++ = 0x02; + *buf ++ = 0x01; + *buf ++ = 0x00; + + /* privateKeyAlgorithm */ + *buf ++ = 0x30; + *buf ++ = (sizeof OID_ECPUBKEY) + 2 + oid[0]; + memcpy(buf, OID_ECPUBKEY, sizeof OID_ECPUBKEY); + buf += sizeof OID_ECPUBKEY; + *buf ++ = 0x06; + memcpy(buf, oid, 1 + oid[0]); + buf += 1 + oid[0]; + + /* privateKey */ + *buf ++ = 0x04; + buf += br_asn1_encode_length(buf, len_privateKeyValue); + br_encode_ec_raw_der_inner(buf, sk, pk, 0); + + return 1 + lenlen + len_seq; + } +} diff --git a/src/x509/encode_ec_rawder.c b/src/x509/encode_ec_rawder.c new file mode 100644 index 0000000..5985909 --- /dev/null +++ b/src/x509/encode_ec_rawder.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see inner.h */ +const unsigned char * +br_get_curve_OID(int curve) +{ + static const unsigned char OID_secp256r1[] = { + 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 + }; + static const unsigned char OID_secp384r1[] = { + 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 + }; + static const unsigned char OID_secp521r1[] = { + 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 + }; + + switch (curve) { + case BR_EC_secp256r1: return OID_secp256r1; + case BR_EC_secp384r1: return OID_secp384r1; + case BR_EC_secp521r1: return OID_secp521r1; + default: + return NULL; + } +} + +/* see inner.h */ +size_t +br_encode_ec_raw_der_inner(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk, + int include_curve_oid) +{ + /* + * ASN.1 format: + * + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL + * } + * + * The tages '[0]' and '[1]' are explicit. The 'ECParameters' + * is a CHOICE; in our case, it will always be an OBJECT IDENTIFIER + * that identifies the curve. + * + * The value of the 'privateKey' field is the raw unsigned big-endian + * encoding of the private key (integer modulo the curve subgroup + * order); there is no INTEGER tag, and the leading bit may be 1. + * Also, leading bytes of value 0x00 are _not_ removed. + * + * The 'publicKey' contents are the raw encoded public key point, + * normally uncompressed (leading byte of value 0x04, followed + * by the unsigned big-endian encodings of the X and Y coordinates, + * padded to the full field length if necessary). + */ + + size_t len_version, len_privateKey, len_parameters, len_publicKey; + size_t len_publicKey_bits, len_seq; + const unsigned char *oid; + + if (include_curve_oid) { + oid = br_get_curve_OID(sk->curve); + if (oid == NULL) { + return 0; + } + } else { + oid = NULL; + } + len_version = 3; + len_privateKey = 1 + len_of_len(sk->xlen) + sk->xlen; + if (include_curve_oid) { + len_parameters = 4 + oid[0]; + } else { + len_parameters = 0; + } + if (pk == NULL) { + len_publicKey = 0; + len_publicKey_bits = 0; + } else { + len_publicKey_bits = 2 + len_of_len(pk->qlen) + pk->qlen; + len_publicKey = 1 + len_of_len(len_publicKey_bits) + + len_publicKey_bits; + } + len_seq = len_version + len_privateKey + len_parameters + len_publicKey; + if (dest == NULL) { + return 1 + len_of_len(len_seq) + len_seq; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, len_seq); + buf += lenlen; + + /* version */ + *buf ++ = 0x02; + *buf ++ = 0x01; + *buf ++ = 0x01; + + /* privateKey */ + *buf ++ = 0x04; + buf += br_asn1_encode_length(buf, sk->xlen); + memcpy(buf, sk->x, sk->xlen); + buf += sk->xlen; + + /* parameters */ + if (include_curve_oid) { + *buf ++ = 0xA0; + *buf ++ = oid[0] + 2; + *buf ++ = 0x06; + memcpy(buf, oid, oid[0] + 1); + buf += oid[0] + 1; + } + + /* publicKey */ + if (pk != NULL) { + *buf ++ = 0xA1; + buf += br_asn1_encode_length(buf, len_publicKey_bits); + *buf ++ = 0x03; + buf += br_asn1_encode_length(buf, pk->qlen + 1); + *buf ++ = 0x00; + memcpy(buf, pk->q, pk->qlen); + /* buf += pk->qlen; */ + } + + return 1 + lenlen + len_seq; + } +} + +/* see bearssl_x509.h */ +size_t +br_encode_ec_raw_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk) +{ + return br_encode_ec_raw_der_inner(dest, sk, pk, 1); +} diff --git a/src/x509/encode_rsa_pk8der.c b/src/x509/encode_rsa_pk8der.c new file mode 100644 index 0000000..c053503 --- /dev/null +++ b/src/x509/encode_rsa_pk8der.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_x509.h */ +size_t +br_encode_rsa_pkcs8_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen) +{ + /* + * ASN.1 format: + * + * OneAsymmetricKey ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] Attributes OPTIONAL, + * ..., + * [[2: publicKey [1] PublicKey OPTIONAL ]], + * ... + * } + * + * We don't include attributes or public key. The 'version' field + * is an INTEGER that we will set to 0 (meaning 'v1', compatible + * with previous versions of PKCS#8). The 'privateKeyAlgorithm' + * structure is an AlgorithmIdentifier whose OID should be + * rsaEncryption, with NULL parameters. The 'privateKey' is an + * OCTET STRING, whose value is the "raw DER" encoding of the + * key pair. + * + * Since the private key value comes last, this function really + * adds a header, which is mostly fixed (only some lengths have + * to be modified. + */ + + /* + * Concatenation of: + * - DER encoding of an INTEGER of value 0 (the 'version' field) + * - DER encoding of a PrivateKeyAlgorithmIdentifier that uses + * the rsaEncryption OID, and NULL parameters + * - An OCTET STRING tag + */ + static const unsigned char PK8_HEAD[] = { + 0x02, 0x01, 0x00, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x04 + }; + + size_t len_raw, len_seq; + + len_raw = br_encode_rsa_raw_der(NULL, sk, pk, d, dlen); + len_seq = (sizeof PK8_HEAD) + len_of_len(len_raw) + len_raw; + if (dest == NULL) { + return 1 + len_of_len(len_seq) + len_seq; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, len_seq); + buf += lenlen; + + /* version, privateKeyAlgorithm, privateKey tag */ + memcpy(buf, PK8_HEAD, sizeof PK8_HEAD); + buf += sizeof PK8_HEAD; + + /* privateKey */ + buf += br_asn1_encode_length(buf, len_raw); + br_encode_rsa_raw_der(buf, sk, pk, d, dlen); + + return 1 + lenlen + len_seq; + } +} diff --git a/src/x509/encode_rsa_rawder.c b/src/x509/encode_rsa_rawder.c new file mode 100644 index 0000000..1a8052b --- /dev/null +++ b/src/x509/encode_rsa_rawder.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "inner.h" + +/* see bearssl_x509.h */ +size_t +br_encode_rsa_raw_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen) +{ + /* + * ASN.1 format: + * + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + * + * The 'version' field is an INTEGER of value 0 (meaning: there + * are exactly two prime factors), and 'otherPrimeInfos' will + * be absent (because there are exactly two prime factors). + */ + + br_asn1_uint num[9]; + size_t u, slen; + + /* + * For all INTEGER values, get the pointer and length for the + * data bytes. + */ + num[0] = br_asn1_uint_prepare(NULL, 0); + num[1] = br_asn1_uint_prepare(pk->n, pk->nlen); + num[2] = br_asn1_uint_prepare(pk->e, pk->elen); + num[3] = br_asn1_uint_prepare(d, dlen); + num[4] = br_asn1_uint_prepare(sk->p, sk->plen); + num[5] = br_asn1_uint_prepare(sk->q, sk->qlen); + num[6] = br_asn1_uint_prepare(sk->dp, sk->dplen); + num[7] = br_asn1_uint_prepare(sk->dq, sk->dqlen); + num[8] = br_asn1_uint_prepare(sk->iq, sk->iqlen); + + /* + * Get the length of the SEQUENCE contents. + */ + slen = 0; + for (u = 0; u < 9; u ++) { + uint32_t ilen; + + ilen = num[u].asn1len; + slen += 1 + len_of_len(ilen) + ilen; + } + + if (dest == NULL) { + return 1 + len_of_len(slen) + slen; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, slen); + buf += lenlen; + for (u = 0; u < 9; u ++) { + buf += br_asn1_encode_uint(buf, num[u]); + } + return 1 + lenlen + slen; + } +} diff --git a/test/test_crypto.c b/test/test_crypto.c index 60a431c..70029b3 100644 --- a/test/test_crypto.c +++ b/test/test_crypto.c @@ -5721,8 +5721,9 @@ test_RSA_OAEP(const char *name, } static void -test_RSA_keygen(const char *name, br_rsa_keygen kg, - br_rsa_pkcs1_sign sign, br_rsa_pkcs1_vrfy vrfy) +test_RSA_keygen(const char *name, br_rsa_keygen kg, br_rsa_compute_modulus cm, + br_rsa_compute_pubexp ce, br_rsa_compute_privexp cd, + br_rsa_public pub, br_rsa_pkcs1_sign sign, br_rsa_pkcs1_vrfy vrfy) { br_hmac_drbg_context rng; int i; @@ -5732,25 +5733,30 @@ test_RSA_keygen(const char *name, br_rsa_keygen kg, br_hmac_drbg_init(&rng, &br_sha256_vtable, "seed for RSA keygen", 19); - for (i = 0; i < 40; i ++) { + for (i = 0; i <= 42; i ++) { unsigned size; - uint32_t pubexp; + uint32_t pubexp, z; br_rsa_private_key sk; - br_rsa_public_key pk; + br_rsa_public_key pk, pk2; unsigned char kbuf_priv[BR_RSA_KBUF_PRIV_SIZE(2048)]; unsigned char kbuf_pub[BR_RSA_KBUF_PUB_SIZE(2048)]; + unsigned char n2[256], d[256], msg1[256], msg2[256]; uint32_t mod[256]; uint32_t cc; size_t u, v; unsigned char sig[257], hv[32], hv2[sizeof hv]; unsigned mask1, mask2; + int j; if (i <= 35) { size = 1024 + i; pubexp = 17; - } else { + } else if (i <= 40) { size = 2048; pubexp = (i << 1) - 69; + } else { + size = 2048; + pubexp = 0xFFFFFFFF; } if (!kg(&rng.vtable, @@ -5760,14 +5766,15 @@ test_RSA_keygen(const char *name, br_rsa_keygen kg, exit(EXIT_FAILURE); } + z = pubexp; for (u = pk.elen; u > 0; u --) { - if (pk.e[u - 1] != (pubexp & 0xFF)) { + if (pk.e[u - 1] != (z & 0xFF)) { fprintf(stderr, "wrong public exponent\n"); exit(EXIT_FAILURE); } - pubexp >>= 8; + z >>= 8; } - if (pubexp != 0) { + if (z != 0) { fprintf(stderr, "truncated public exponent\n"); exit(EXIT_FAILURE); } @@ -5806,28 +5813,86 @@ test_RSA_keygen(const char *name, br_rsa_keygen kg, exit(EXIT_FAILURE); } - rng.vtable->generate(&rng.vtable, hv, sizeof hv); - memset(sig, 0, sizeof sig); - sig[pk.nlen] = 0x00; - if (!sign(BR_HASH_OID_SHA256, hv, sizeof hv, &sk, sig)) { - fprintf(stderr, "signature error\n"); + if (cm(NULL, &sk) != pk.nlen) { + fprintf(stderr, "wrong recomputed modulus length\n"); exit(EXIT_FAILURE); } - if (sig[pk.nlen] != 0x00) { - fprintf(stderr, "signature length error\n"); + if (cm(n2, &sk) != pk.nlen || memcmp(pk.n, n2, pk.nlen) != 0) { + fprintf(stderr, "wrong recomputed modulus value\n"); exit(EXIT_FAILURE); } - if (!vrfy(sig, pk.nlen, BR_HASH_OID_SHA256, sizeof hv, - &pk, hv2)) - { - fprintf(stderr, "signature verification error (1)\n"); + + z = ce(&sk); + if (z != pubexp) { + fprintf(stderr, + "wrong recomputed pubexp: %lu (exp: %lu)\n", + (unsigned long)z, (unsigned long)pubexp); exit(EXIT_FAILURE); } - if (memcmp(hv, hv2, sizeof hv) != 0) { - fprintf(stderr, "signature verification error (2)\n"); + + if (cd(NULL, &sk, pubexp) != pk.nlen) { + fprintf(stderr, + "wrong recomputed privexp length (1)\n"); + exit(EXIT_FAILURE); + } + if (cd(d, &sk, pubexp) != pk.nlen) { + fprintf(stderr, + "wrong recomputed privexp length (2)\n"); + exit(EXIT_FAILURE); + } + /* + * To check that the private exponent is correct, we make + * it into a _public_ key, and use the public-key operation + * to perform the modular exponentiation. + */ + pk2 = pk; + pk2.e = d; + pk2.elen = pk.nlen; + rng.vtable->generate(&rng.vtable, msg1, pk.nlen); + msg1[0] = 0x00; + memcpy(msg2, msg1, pk.nlen); + if (!pub(msg2, pk.nlen, &pk2) || !pub(msg2, pk.nlen, &pk)) { + fprintf(stderr, "public-key operation error\n"); + exit(EXIT_FAILURE); + } + if (memcmp(msg1, msg2, pk.nlen) != 0) { + fprintf(stderr, "wrong recomputed privexp\n"); exit(EXIT_FAILURE); } + /* + * We test the RSA operation over a some random messages. + */ + for (j = 0; j < 20; j ++) { + rng.vtable->generate(&rng.vtable, hv, sizeof hv); + memset(sig, 0, sizeof sig); + sig[pk.nlen] = 0x00; + if (!sign(BR_HASH_OID_SHA256, + hv, sizeof hv, &sk, sig)) + { + fprintf(stderr, + "signature error (%d)\n", j); + exit(EXIT_FAILURE); + } + if (sig[pk.nlen] != 0x00) { + fprintf(stderr, + "signature length error (%d)\n", j); + exit(EXIT_FAILURE); + } + if (!vrfy(sig, pk.nlen, BR_HASH_OID_SHA256, sizeof hv, + &pk, hv2)) + { + fprintf(stderr, + "signature verif error (%d)\n", j); + exit(EXIT_FAILURE); + } + if (memcmp(hv, hv2, sizeof hv) != 0) { + fprintf(stderr, + "signature extract error (%d)\n", j); + exit(EXIT_FAILURE); + } + } + printf("."); fflush(stdout); } @@ -5845,6 +5910,8 @@ test_RSA_i15(void) test_RSA_OAEP("RSA i15 OAEP", &br_rsa_i15_oaep_encrypt, &br_rsa_i15_oaep_decrypt); test_RSA_keygen("RSA i15 keygen", &br_rsa_i15_keygen, + &br_rsa_i15_compute_modulus, &br_rsa_i15_compute_pubexp, + &br_rsa_i15_compute_privexp, &br_rsa_i15_public, &br_rsa_i15_pkcs1_sign, &br_rsa_i15_pkcs1_vrfy); } @@ -5857,6 +5924,8 @@ test_RSA_i31(void) test_RSA_OAEP("RSA i31 OAEP", &br_rsa_i31_oaep_encrypt, &br_rsa_i31_oaep_decrypt); test_RSA_keygen("RSA i31 keygen", &br_rsa_i31_keygen, + &br_rsa_i31_compute_modulus, &br_rsa_i31_compute_pubexp, + &br_rsa_i31_compute_privexp, &br_rsa_i31_public, &br_rsa_i31_pkcs1_sign, &br_rsa_i31_pkcs1_vrfy); } @@ -5896,7 +5965,10 @@ test_RSA_i62(void) test_RSA_core("RSA i62 core", pub, priv); test_RSA_sign("RSA i62 sign", priv, sign, vrfy); test_RSA_OAEP("RSA i62 OAEP", menc, mdec); - test_RSA_keygen("RSA i62 keygen", kgen, sign, vrfy); + test_RSA_keygen("RSA i62 keygen", kgen, + &br_rsa_i31_compute_modulus, &br_rsa_i31_compute_pubexp, + &br_rsa_i31_compute_privexp, pub, + sign, vrfy); } else { if (priv || sign || vrfy || menc || mdec || kgen) { fprintf(stderr, "Inconsistent i62 availability\n"); @@ -7209,7 +7281,6 @@ test_EC_P256_carry(const br_ec_impl *impl) static void test_EC_KAT(const char *name, const br_ec_impl *impl, uint32_t curve_mask) { - printf("Test %s: ", name); fflush(stdout); @@ -7237,6 +7308,155 @@ test_EC_KAT(const char *name, const br_ec_impl *impl, uint32_t curve_mask) fflush(stdout); } +static void +test_EC_keygen(const char *name, const br_ec_impl *impl, uint32_t curves) +{ + int curve; + br_hmac_drbg_context rng; + + printf("Test %s keygen: ", name); + fflush(stdout); + + br_hmac_drbg_init(&rng, &br_sha256_vtable, "seed for EC keygen", 18); + br_hmac_drbg_update(&rng, name, strlen(name)); + + for (curve = -1; curve <= 35; curve ++) { + br_ec_private_key sk; + br_ec_public_key pk; + unsigned char kbuf_priv[BR_EC_KBUF_PRIV_MAX_SIZE]; + unsigned char kbuf_pub[BR_EC_KBUF_PUB_MAX_SIZE]; + + if (curve < 0 || curve >= 32 || ((curves >> curve) & 1) == 0) { + if (br_ec_keygen(&rng.vtable, impl, + &sk, kbuf_priv, curve) != 0) + { + fprintf(stderr, "br_ec_keygen() did not" + " reject unsupported curve %d\n", + curve); + exit(EXIT_FAILURE); + } + sk.curve = curve; + if (br_ec_compute_pub(impl, NULL, NULL, &sk) != 0) { + fprintf(stderr, "br_ec_keygen() did not" + " reject unsupported curve %d\n", + curve); + exit(EXIT_FAILURE); + } + } else { + size_t len, u; + unsigned char tmp_priv[sizeof kbuf_priv]; + unsigned char tmp_pub[sizeof kbuf_pub]; + unsigned z; + + len = br_ec_keygen(&rng.vtable, impl, + NULL, NULL, curve); + if (len == 0) { + fprintf(stderr, "br_ec_keygen() rejects" + " supported curve %d\n", curve); + exit(EXIT_FAILURE); + } + if (len > sizeof kbuf_priv) { + fprintf(stderr, "oversized kbuf_priv\n"); + exit(EXIT_FAILURE); + } + memset(kbuf_priv, 0, sizeof kbuf_priv); + if (br_ec_keygen(&rng.vtable, impl, + NULL, kbuf_priv, curve) != len) + { + fprintf(stderr, "kbuf_priv length mismatch\n"); + exit(EXIT_FAILURE); + } + z = 0; + for (u = 0; u < len; u ++) { + z |= kbuf_priv[u]; + } + if (z == 0) { + fprintf(stderr, "kbuf_priv not initialized\n"); + exit(EXIT_FAILURE); + } + for (u = len; u < sizeof kbuf_priv; u ++) { + if (kbuf_priv[u] != 0) { + fprintf(stderr, "kbuf_priv overflow\n"); + exit(EXIT_FAILURE); + } + } + if (br_ec_keygen(&rng.vtable, impl, + NULL, tmp_priv, curve) != len) + { + fprintf(stderr, "tmp_priv length mismatch\n"); + exit(EXIT_FAILURE); + } + if (memcmp(kbuf_priv, tmp_priv, len) == 0) { + fprintf(stderr, "keygen stutter\n"); + exit(EXIT_FAILURE); + } + memset(&sk, 0, sizeof sk); + if (br_ec_keygen(&rng.vtable, impl, + &sk, kbuf_priv, curve) != len) + { + fprintf(stderr, + "kbuf_priv length mismatch (2)\n"); + exit(EXIT_FAILURE); + } + if (sk.curve != curve || sk.x != kbuf_priv + || sk.xlen != len) + { + fprintf(stderr, "sk not initialized\n"); + exit(EXIT_FAILURE); + } + + len = br_ec_compute_pub(impl, NULL, NULL, &sk); + if (len > sizeof kbuf_pub) { + fprintf(stderr, "oversized kbuf_pub\n"); + exit(EXIT_FAILURE); + } + memset(kbuf_pub, 0, sizeof kbuf_pub); + if (br_ec_compute_pub(impl, NULL, + kbuf_pub, &sk) != len) + { + fprintf(stderr, "kbuf_pub length mismatch\n"); + exit(EXIT_FAILURE); + } + for (u = len; u < sizeof kbuf_pub; u ++) { + if (kbuf_pub[u] != 0) { + fprintf(stderr, "kbuf_pub overflow\n"); + exit(EXIT_FAILURE); + } + } + memset(&pk, 0, sizeof pk); + if (br_ec_compute_pub(impl, &pk, + tmp_pub, &sk) != len) + { + fprintf(stderr, "tmp_pub length mismatch\n"); + exit(EXIT_FAILURE); + } + if (memcmp(kbuf_pub, tmp_pub, len) != 0) { + fprintf(stderr, "pubkey mismatch\n"); + exit(EXIT_FAILURE); + } + if (pk.curve != curve || pk.q != tmp_pub + || pk.qlen != len) + { + fprintf(stderr, "pk not initialized\n"); + exit(EXIT_FAILURE); + } + + if (impl->mulgen(kbuf_pub, + sk.x, sk.xlen, curve) != len + || memcmp(pk.q, kbuf_pub, len) != 0) + { + fprintf(stderr, "wrong pubkey\n"); + exit(EXIT_FAILURE); + } + } + printf("."); + fflush(stdout); + } + + printf(" done.\n"); + fflush(stdout); +} + static void test_EC_prime_i15(void) { @@ -7244,6 +7464,10 @@ test_EC_prime_i15(void) (uint32_t)1 << BR_EC_secp256r1 | (uint32_t)1 << BR_EC_secp384r1 | (uint32_t)1 << BR_EC_secp521r1); + test_EC_keygen("EC_prime_i15", &br_ec_prime_i15, + (uint32_t)1 << BR_EC_secp256r1 + | (uint32_t)1 << BR_EC_secp384r1 + | (uint32_t)1 << BR_EC_secp521r1); } static void @@ -7253,6 +7477,10 @@ test_EC_prime_i31(void) (uint32_t)1 << BR_EC_secp256r1 | (uint32_t)1 << BR_EC_secp384r1 | (uint32_t)1 << BR_EC_secp521r1); + test_EC_keygen("EC_prime_i31", &br_ec_prime_i31, + (uint32_t)1 << BR_EC_secp256r1 + | (uint32_t)1 << BR_EC_secp384r1 + | (uint32_t)1 << BR_EC_secp521r1); } static void @@ -7260,6 +7488,8 @@ test_EC_p256_m15(void) { test_EC_KAT("EC_p256_m15", &br_ec_p256_m15, (uint32_t)1 << BR_EC_secp256r1); + test_EC_keygen("EC_p256_m15", &br_ec_p256_m15, + (uint32_t)1 << BR_EC_secp256r1); } static void @@ -7267,6 +7497,8 @@ test_EC_p256_m31(void) { test_EC_KAT("EC_p256_m31", &br_ec_p256_m31, (uint32_t)1 << BR_EC_secp256r1); + test_EC_keygen("EC_p256_m31", &br_ec_p256_m31, + (uint32_t)1 << BR_EC_secp256r1); } const struct { @@ -7353,24 +7585,32 @@ static void test_EC_c25519_i15(void) { test_EC_c25519("EC_c25519_i15", &br_ec_c25519_i15); + test_EC_keygen("EC_c25519_i15", &br_ec_c25519_i15, + (uint32_t)1 << BR_EC_curve25519); } static void test_EC_c25519_i31(void) { test_EC_c25519("EC_c25519_i31", &br_ec_c25519_i31); + test_EC_keygen("EC_c25519_i31", &br_ec_c25519_i31, + (uint32_t)1 << BR_EC_curve25519); } static void test_EC_c25519_m15(void) { test_EC_c25519("EC_c25519_m15", &br_ec_c25519_m15); + test_EC_keygen("EC_c25519_m15", &br_ec_c25519_m15, + (uint32_t)1 << BR_EC_curve25519); } static void test_EC_c25519_m31(void) { test_EC_c25519("EC_c25519_m31", &br_ec_c25519_m31); + test_EC_keygen("EC_c25519_m31", &br_ec_c25519_m31, + (uint32_t)1 << BR_EC_curve25519); } static const unsigned char EC_P256_PUB_POINT[] = { diff --git a/tools/brssl.h b/tools/brssl.h index 22fd712..a23ba00 100644 --- a/tools/brssl.h +++ b/tools/brssl.h @@ -298,11 +298,22 @@ int uses_ecdhe(unsigned suite); */ void list_names(void); +/* + * Print out all known elliptic curve names. + */ +void list_curves(void); + /* * Get the symbolic name for an elliptic curve (by ID). */ const char *ec_curve_name(int curve); +/* + * Get a curve by symbolic name. If the name is not recognized, -1 is + * returned. + */ +int get_curve_by_name(const char *str); + /* * Get the symbolic name for a hash function name (by ID). */ diff --git a/tools/names.c b/tools/names.c index 3ea7f1b..f9487fd 100644 --- a/tools/names.c +++ b/tools/names.c @@ -322,68 +322,99 @@ const cipher_suite cipher_suites[] = { static const struct { int id; const char *name; + const char *sid[4]; } curves[] = { { BR_EC_sect163k1, - "sect163k1" }, + "sect163k1", + { "sect163k1", "K-163", NULL, NULL } }, { BR_EC_sect163r1, - "sect163r1" }, + "sect163r1", + { "sect163r1", NULL, NULL, NULL } }, { BR_EC_sect163r2, - "sect163r2" }, + "sect163r2", + { "sect163r2", "B-163", NULL, NULL } }, { BR_EC_sect193r1, - "sect193r1" }, + "sect193r1", + { "sect193r1", NULL, NULL, NULL } }, { BR_EC_sect193r2, - "sect193r2" }, + "sect193r2", + { "sect193r2", NULL, NULL, NULL } }, { BR_EC_sect233k1, - "sect233k1" }, + "sect233k1", + { "sect233k1", "K-233", NULL, NULL } }, { BR_EC_sect233r1, - "sect233r1" }, + "sect233r1", + { "sect233r1", "B-233", NULL, NULL } }, { BR_EC_sect239k1, - "sect239k1" }, + "sect239k1", + { "sect239k1", NULL, NULL, NULL } }, { BR_EC_sect283k1, - "sect283k1" }, + "sect283k1", + { "sect283k1", "K-283", NULL, NULL } }, { BR_EC_sect283r1, - "sect283r1" }, + "sect283r1", + { "sect283r1", "B-283", NULL, NULL } }, { BR_EC_sect409k1, - "sect409k1" }, + "sect409k1", + { "sect409k1", "K-409", NULL, NULL } }, { BR_EC_sect409r1, - "sect409r1" }, + "sect409r1", + { "sect409r1", "B-409", NULL, NULL } }, { BR_EC_sect571k1, - "sect571k1" }, + "sect571k1", + { "sect571k1", "K-571", NULL, NULL } }, { BR_EC_sect571r1, - "sect571r1" }, + "sect571r1", + { "sect571r1", "B-571", NULL, NULL } }, { BR_EC_secp160k1, - "secp160k1" }, + "secp160k1", + { "secp160k1", NULL, NULL, NULL } }, { BR_EC_secp160r1, - "secp160r1" }, + "secp160r1", + { "secp160r1", NULL, NULL, NULL } }, { BR_EC_secp160r2, - "secp160r2" }, + "secp160r2", + { "secp160r2", NULL, NULL, NULL } }, { BR_EC_secp192k1, - "secp192k1" }, + "secp192k1", + { "secp192k1", NULL, NULL, NULL } }, { BR_EC_secp192r1, - "secp192r1" }, + "secp192r1", + { "secp192r1", "P-192", NULL, NULL } }, { BR_EC_secp224k1, - "secp224k1" }, + "secp224k1", + { "secp224k1", NULL, NULL, NULL } }, { BR_EC_secp224r1, - "secp224r1" }, + "secp224r1", + { "secp224r1", "P-224", NULL, NULL } }, { BR_EC_secp256k1, - "secp256k1" }, + "secp256k1", + { "secp256k1", NULL, NULL, NULL } }, { BR_EC_secp256r1, - "secp256r1 (P-256)" }, + "secp256r1 (P-256)", + { "secp256r1", "P-256", "prime256v1", NULL } }, { BR_EC_secp384r1, - "secp384r1 (P-384)" }, + "secp384r1 (P-384)", + { "secp384r1", "P-384", NULL, NULL } }, { BR_EC_secp521r1, - "secp521r1 (P-521)" }, + "secp521r1 (P-521)", + { "secp521r1", "P-521", NULL, NULL } }, { BR_EC_brainpoolP256r1, - "brainpoolP256r1" }, + "brainpoolP256r1", + { "brainpoolP256r1", NULL, NULL, NULL } }, { BR_EC_brainpoolP384r1, - "brainpoolP384r1" }, + "brainpoolP384r1", + { "brainpoolP384r1", NULL, NULL, NULL } }, { BR_EC_brainpoolP512r1, - "brainpoolP512r1" }, + "brainpoolP512r1", + { "brainpoolP512r1", NULL, NULL, NULL } }, { BR_EC_curve25519, - "Curve25519" }, + "Curve25519", + { "curve25519", "c25519", NULL, NULL } }, { BR_EC_curve448, - "Curve448" }, - { 0, 0 } + "Curve448", + { "curve448", "c448", NULL, NULL } }, + { 0, 0, { 0, 0, 0, 0 } } }; static const struct { @@ -627,6 +658,31 @@ list_names(void) } } +/* see brssl.h */ +void +list_curves(void) +{ + size_t u; + for (u = 0; curves[u].name; u ++) { + size_t v; + + for (v = 0; curves[u].sid[v]; v ++) { + if (v == 0) { + printf(" "); + } else if (v == 1) { + printf(" ("); + } else { + printf(", "); + } + printf("%s", curves[u].sid[v]); + } + if (v > 1) { + printf(")"); + } + printf("\n"); + } +} + static int is_ign(int c) { @@ -956,6 +1012,22 @@ ec_curve_name(int curve) } } +/* see brssl.h */ +int +get_curve_by_name(const char *str) +{ + size_t u, v; + + for (u = 0; curves[u].name; u ++) { + for (v = 0; curves[u].sid[v]; v ++) { + if (eqstr(curves[u].sid[v], str)) { + return curves[u].id; + } + } + } + return -1; +} + /* see brssl.h */ const char * hash_function_name(int id) diff --git a/tools/skey.c b/tools/skey.c index 0f672bf..90ecf63 100644 --- a/tools/skey.c +++ b/tools/skey.c @@ -31,6 +31,15 @@ #include "brssl.h" #include "bearssl.h" +typedef struct { + int print_text; + int print_C; + const char *rawder; + const char *rawpem; + const char *pk8der; + const char *pk8pem; +} outspec; + static void print_int_text(const char *name, const unsigned char *buf, size_t len) { @@ -63,17 +72,70 @@ print_int_C(const char *name, const unsigned char *buf, size_t len) printf("\n};\n"); } -static void -print_rsa(const br_rsa_private_key *sk, int print_text, int print_C) +static int +write_to_file(const char *name, const void *data, size_t len) +{ + FILE *f; + + f = fopen(name, "wb"); + if (f == NULL) { + fprintf(stderr, + "ERROR: cannot open file '%s' for writing\n", + name); + return 0; + } + if (fwrite(data, 1, len, f) != len) { + fclose(f); + fprintf(stderr, + "ERROR: cannot write to file '%s'\n", + name); + return 0; + } + fclose(f); + return 1; +} + +static int +write_to_pem_file(const char *name, + const void *data, size_t len, const char *banner) +{ + void *pem; + size_t pemlen; + int r; + + pemlen = br_pem_encode(NULL, NULL, len, banner, 0); + pem = xmalloc(pemlen + 1); + br_pem_encode(pem, data, len, banner, 0); + r = write_to_file(name, pem, pemlen); + xfree(pem); + return r; +} + +static int +print_rsa(const br_rsa_private_key *sk, outspec *os) { - if (print_text) { + int ret; + unsigned char *n, *d, *buf; + uint32_t e; + size_t nlen, dlen, len; + br_rsa_compute_modulus cm; + br_rsa_compute_pubexp ce; + br_rsa_compute_privexp cd; + br_rsa_public_key pk; + unsigned char ebuf[4]; + + n = NULL; + d = NULL; + buf = NULL; + ret = 1; + if (os->print_text) { print_int_text("p ", sk->p, sk->plen); print_int_text("q ", sk->q, sk->qlen); print_int_text("dp", sk->dp, sk->dplen); print_int_text("dq", sk->dq, sk->dqlen); print_int_text("iq", sk->iq, sk->iqlen); } - if (print_C) { + if (os->print_C) { print_int_C("RSA_P", sk->p, sk->plen); print_int_C("RSA_Q", sk->q, sk->qlen); print_int_C("RSA_DP", sk->dp, sk->dplen); @@ -88,21 +150,174 @@ print_rsa(const br_rsa_private_key *sk, int print_text, int print_C) printf("\t(unsigned char *)RSA_IQ, sizeof RSA_IQ\n"); printf("};\n"); } + + if (os->rawder == NULL && os->rawpem == NULL + && os->pk8der == NULL && os->pk8pem == NULL) + { + return ret; + } + + cm = br_rsa_compute_modulus_get_default(); + ce = br_rsa_compute_pubexp_get_default(); + cd = br_rsa_compute_privexp_get_default(); + nlen = cm(NULL, sk); + if (nlen == 0) { + goto print_RSA_error; + } + n = xmalloc(nlen); + if (cm(n, sk) != nlen) { + goto print_RSA_error; + } + e = ce(sk); + if (e == 0) { + goto print_RSA_error; + } + dlen = cd(NULL, sk, e); + if (dlen == 0) { + goto print_RSA_error; + } + d = xmalloc(dlen); + if (cd(d, sk, e) != dlen) { + goto print_RSA_error; + } + ebuf[0] = e >> 24; + ebuf[1] = e >> 16; + ebuf[2] = e >> 8; + ebuf[3] = e; + pk.n = n; + pk.nlen = nlen; + pk.e = ebuf; + pk.elen = sizeof ebuf; + + if (os->rawder != NULL || os->rawpem != NULL) { + len = br_encode_rsa_raw_der(NULL, sk, &pk, d, dlen); + if (len == 0) { + goto print_RSA_error; + } + buf = xmalloc(len); + if (br_encode_rsa_raw_der(buf, sk, &pk, d, dlen) != len) { + goto print_RSA_error; + } + if (os->rawder != NULL) { + ret &= write_to_file(os->rawder, buf, len); + } + if (os->rawpem != NULL) { + ret &= write_to_pem_file(os->rawpem, + buf, len, "RSA PRIVATE KEY"); + } + xfree(buf); + buf = NULL; + } + + if (os->pk8der != NULL || os->pk8pem != NULL) { + len = br_encode_rsa_pkcs8_der(NULL, sk, &pk, d, dlen); + if (len == 0) { + goto print_RSA_error; + } + buf = xmalloc(len); + if (br_encode_rsa_pkcs8_der(buf, sk, &pk, d, dlen) != len) { + goto print_RSA_error; + } + if (os->pk8der != NULL) { + ret &= write_to_file(os->pk8der, buf, len); + } + if (os->pk8pem != NULL) { + ret &= write_to_pem_file(os->pk8pem, + buf, len, "PRIVATE KEY"); + } + xfree(buf); + buf = NULL; + } + +print_RSA_exit: + xfree(n); + xfree(d); + xfree(buf); + return ret; + +print_RSA_error: + fprintf(stderr, "ERROR: cannot encode RSA key\n"); + ret = 0; + goto print_RSA_exit; } -static void -print_ec(const br_ec_private_key *sk, int print_text, int print_C) +static int +print_ec(const br_ec_private_key *sk, outspec *os) { - if (print_text) { + br_ec_public_key pk; + unsigned kbuf[BR_EC_KBUF_PUB_MAX_SIZE]; + unsigned char *buf; + size_t len; + int r; + + if (os->print_text) { print_int_text("x", sk->x, sk->xlen); } - if (print_C) { + if (os->print_C) { print_int_C("EC_X", sk->x, sk->xlen); printf("\nstatic const br_ec_private_key EC = {\n"); printf("\t%d,\n", sk->curve); printf("\t(unsigned char *)EC_X, sizeof EC_X\n"); printf("};\n"); } + + if (os->rawder == NULL && os->rawpem == NULL + && os->pk8der == NULL && os->pk8pem == NULL) + { + return 1; + } + if (br_ec_compute_pub(br_ec_get_default(), &pk, kbuf, sk) == 0) { + fprintf(stderr, + "ERROR: cannot re-encode (unsupported curve)\n"); + return 0; + } + + r = 1; + if (os->rawder != NULL || os->rawpem != NULL) { + len = br_encode_ec_raw_der(NULL, sk, &pk); + if (len == 0) { + fprintf(stderr, "ERROR: cannot re-encode" + " (unsupported curve)\n"); + return 0; + } + buf = xmalloc(len); + if (br_encode_ec_raw_der(buf, sk, &pk) != len) { + fprintf(stderr, "ERROR: re-encode failure\n"); + xfree(buf); + return 0; + } + if (os->rawder != NULL) { + r &= write_to_file(os->rawder, buf, len); + } + if (os->rawpem != NULL) { + r &= write_to_pem_file(os->rawpem, + buf, len, "EC PRIVATE KEY"); + } + xfree(buf); + } + if (os->pk8der != NULL || os->pk8pem != NULL) { + len = br_encode_ec_pkcs8_der(NULL, sk, &pk); + if (len == 0) { + fprintf(stderr, "ERROR: cannot re-encode" + " (unsupported curve)\n"); + return 0; + } + buf = xmalloc(len); + if (br_encode_ec_pkcs8_der(buf, sk, &pk) != len) { + fprintf(stderr, "ERROR: re-encode failure\n"); + xfree(buf); + return 0; + } + if (os->pk8der != NULL) { + r &= write_to_file(os->pk8der, buf, len); + } + if (os->pk8pem != NULL) { + r &= write_to_pem_file(os->pk8pem, + buf, len, "PRIVATE KEY"); + } + xfree(buf); + } + return r; } static int @@ -158,7 +373,7 @@ parse_rsa_spec(const char *kgen_spec, unsigned *size, uint32_t *pubexp) } static int -keygen_rsa(unsigned size, uint32_t pubexp, int print_text, int print_C) +keygen_rsa(unsigned size, uint32_t pubexp, outspec *os) { br_hmac_drbg_context rng; br_prng_seeder seeder; @@ -183,17 +398,72 @@ keygen_rsa(unsigned size, uint32_t pubexp, int print_text, int print_C) if (!r) { fprintf(stderr, "ERROR: RSA key pair generation failed\n"); } else { - print_rsa(&sk, print_text, print_C); + r = print_rsa(&sk, os); } xfree(kbuf_priv); return r; } static int -decode_key(const unsigned char *buf, size_t len, int print_text, int print_C) +parse_ec_spec(const char *kgen_spec, int *curve) +{ + const char *p; + + *curve = 0; + p = kgen_spec; + if (*p != 'e' && *p != 'E') { + return 0; + } + p ++; + if (*p != 'c' && *p != 'C') { + return 0; + } + p ++; + if (*p == 0) { + *curve = BR_EC_secp256r1; + return 1; + } + if (*p != ':') { + return 0; + } + *curve = get_curve_by_name(p); + return *curve > 0; +} + +static int +keygen_ec(int curve, outspec *os) +{ + br_hmac_drbg_context rng; + br_prng_seeder seeder; + const br_ec_impl *impl; + br_ec_private_key sk; + unsigned char kbuf_priv[BR_EC_KBUF_PRIV_MAX_SIZE]; + size_t len; + + seeder = br_prng_seeder_system(NULL); + if (seeder == 0) { + fprintf(stderr, "ERROR: no system source of randomness\n"); + return 0; + } + br_hmac_drbg_init(&rng, &br_sha256_vtable, NULL, 0); + if (!seeder(&rng.vtable)) { + fprintf(stderr, "ERROR: system source of randomness failed\n"); + return 0; + } + impl = br_ec_get_default(); + len = br_ec_keygen(&rng.vtable, impl, &sk, kbuf_priv, curve); + if (len == 0) { + fprintf(stderr, "ERROR: curve is not supported\n"); + return 0; + } + return print_ec(&sk, os); +} + +static int +decode_key(const unsigned char *buf, size_t len, outspec *os) { br_skey_decoder_context dc; - int err; + int err, ret; br_skey_decoder_init(&dc); br_skey_decoder_push(&dc, buf, len); @@ -208,8 +478,9 @@ decode_key(const unsigned char *buf, size_t len, int print_text, int print_C) } else { fprintf(stderr, " (unknown)\n"); } - return -1; + return 0; } + ret = 1; switch (br_skey_decoder_key_type(&dc)) { const br_rsa_private_key *rk; const br_ec_private_key *ek; @@ -217,23 +488,24 @@ decode_key(const unsigned char *buf, size_t len, int print_text, int print_C) case BR_KEYTYPE_RSA: rk = br_skey_decoder_get_rsa(&dc); printf("RSA key (%lu bits)\n", (unsigned long)rk->n_bitlen); - print_rsa(rk, print_text, print_C); + ret = print_rsa(rk, os); break; case BR_KEYTYPE_EC: ek = br_skey_decoder_get_ec(&dc); printf("EC key (curve = %d: %s)\n", ek->curve, ec_curve_name(ek->curve)); - print_ec(ek, print_text, print_C); + ret = print_ec(ek, os); break; default: fprintf(stderr, "Unknown key type: %d\n", br_skey_decoder_key_type(&dc)); - return -1; + ret = 0; + break; } - return 0; + return ret; } static void @@ -244,19 +516,31 @@ usage_skey(void) fprintf(stderr, "options:\n"); fprintf(stderr, -" -q suppress verbose messages\n"); +" -q suppress verbose messages\n"); + fprintf(stderr, +" -text print private key details (human-readable)\n"); fprintf(stderr, -" -text print public key details (human-readable)\n"); +" -C print private key details (C code)\n"); fprintf(stderr, -" -C print public key details (C code)\n"); +" -rawder file save private key in 'file' (raw format, DER)\n"); fprintf(stderr, -" -gen spec generate a new key using the provided key specification\n"); +" -rawpem file save private key in 'file' (raw format, PEM)\n"); + fprintf(stderr, +" -pk8der file save private key in 'file' (PKCS#8 format, DER)\n"); + fprintf(stderr, +" -pk8pem file save private key in 'file' (PKCS#8 format, PEM)\n"); + fprintf(stderr, +" -gen spec generate a new key using the provided key specification\n"); + fprintf(stderr, +" -list list known elliptic curve names\n"); fprintf(stderr, "Key specification begins with a key type, followed by optional parameters\n"); fprintf(stderr, "that depend on the key type, separated by colon characters:\n"); fprintf(stderr, " rsa[:size[:pubexep]] RSA key (defaults: size = 2048, pubexp = 3)\n"); + fprintf(stderr, +" ec[:curvename] EC key (default curve: secp256r1)\n"); } /* see brssl.h */ @@ -266,7 +550,7 @@ do_skey(int argc, char *argv[]) int retcode; int verbose; int i, num_files; - int print_text, print_C; + outspec os; unsigned char *buf; size_t len; pem_object *pos; @@ -274,8 +558,12 @@ do_skey(int argc, char *argv[]) retcode = 0; verbose = 1; - print_text = 0; - print_C = 0; + os.print_text = 0; + os.print_C = 0; + os.rawder = NULL; + os.rawpem = NULL; + os.pk8der = NULL; + os.pk8pem = NULL; num_files = 0; buf = NULL; pos = NULL; @@ -294,9 +582,69 @@ do_skey(int argc, char *argv[]) } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) { verbose = 0; } else if (eqstr(arg, "-text")) { - print_text = 1; + os.print_text = 1; } else if (eqstr(arg, "-C")) { - print_C = 1; + os.print_C = 1; + } else if (eqstr(arg, "-rawder")) { + if (++ i >= argc) { + fprintf(stderr, + "ERROR: no argument for '-rawder'\n"); + usage_skey(); + goto skey_exit_error; + } + if (os.rawder != NULL) { + fprintf(stderr, + "ERROR: multiple '-rawder' options\n"); + usage_skey(); + goto skey_exit_error; + } + os.rawder = argv[i]; + argv[i] = NULL; + } else if (eqstr(arg, "-rawpem")) { + if (++ i >= argc) { + fprintf(stderr, + "ERROR: no argument for '-rawpem'\n"); + usage_skey(); + goto skey_exit_error; + } + if (os.rawpem != NULL) { + fprintf(stderr, + "ERROR: multiple '-rawpem' options\n"); + usage_skey(); + goto skey_exit_error; + } + os.rawpem = argv[i]; + argv[i] = NULL; + } else if (eqstr(arg, "-pk8der")) { + if (++ i >= argc) { + fprintf(stderr, + "ERROR: no argument for '-pk8der'\n"); + usage_skey(); + goto skey_exit_error; + } + if (os.pk8der != NULL) { + fprintf(stderr, + "ERROR: multiple '-pk8der' options\n"); + usage_skey(); + goto skey_exit_error; + } + os.pk8der = argv[i]; + argv[i] = NULL; + } else if (eqstr(arg, "-pk8pem")) { + if (++ i >= argc) { + fprintf(stderr, + "ERROR: no argument for '-pk8pem'\n"); + usage_skey(); + goto skey_exit_error; + } + if (os.pk8pem != NULL) { + fprintf(stderr, + "ERROR: multiple '-pk8pem' options\n"); + usage_skey(); + goto skey_exit_error; + } + os.pk8pem = argv[i]; + argv[i] = NULL; } else if (eqstr(arg, "-gen")) { if (++ i >= argc) { fprintf(stderr, @@ -312,6 +660,9 @@ do_skey(int argc, char *argv[]) } kgen_spec = argv[i]; argv[i] = NULL; + } else if (eqstr(arg, "-list")) { + list_curves(); + goto skey_exit; } else { fprintf(stderr, "ERROR: unknown option: '%s'\n", arg); usage_skey(); @@ -321,6 +672,7 @@ do_skey(int argc, char *argv[]) if (kgen_spec != NULL) { unsigned rsa_size; uint32_t rsa_pubexp; + int curve; if (num_files != 0) { fprintf(stderr, @@ -330,7 +682,13 @@ do_skey(int argc, char *argv[]) } if (parse_rsa_spec(kgen_spec, &rsa_size, &rsa_pubexp)) { - keygen_rsa(rsa_size, rsa_pubexp, print_text, print_C); + if (!keygen_rsa(rsa_size, rsa_pubexp, &os)) { + goto skey_exit_error; + } + } else if (parse_ec_spec(kgen_spec, &curve)) { + if (!keygen_ec(curve, &os)) { + goto skey_exit_error; + } } else { fprintf(stderr, "ERROR: unknown key specification: '%s'\n", @@ -360,7 +718,7 @@ do_skey(int argc, char *argv[]) fprintf(stderr, "File '%s': ASN.1/DER object\n", fname); } - if (decode_key(buf, len, print_text, print_C) < 0) { + if (!decode_key(buf, len, &os)) { goto skey_exit_error; } } else { @@ -382,9 +740,8 @@ do_skey(int argc, char *argv[]) || eqstr(name, "EC PRIVATE KEY") || eqstr(name, "PRIVATE KEY")) { - if (decode_key(pos[u].data, - pos[u].data_len, - print_text, print_C) < 0) + if (!decode_key(pos[u].data, + pos[u].data_len, &os)) { goto skey_exit_error; }