std1.0.0-beta-7 SafeC docs

Source crypto/rsa.sc

View in the repository

// SafeC Standard Library — RSA signatures (see rsa.h).
#pragma once
#include <std/crypto/rsa.h>
#include <std/crypto/asn1.h>
#include <std/crypto/ct.h>
#include <std/mem.h>

namespace std {

// DigestInfo DER prefixes (RFC 8017 §9.2 note 1).
static unsigned char rsa_di_sha1_[15] = { 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14 };
static unsigned char rsa_di_sha224_[19] = { 0x30,0x2d,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x04,0x05,0x00,0x04,0x1c };
static unsigned char rsa_di_sha256_[19] = { 0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20 };
static unsigned char rsa_di_sha384_[19] = { 0x30,0x41,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,0x30 };
static unsigned char rsa_di_sha512_[19] = { 0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40 };
// rsaEncryption 1.2.840.113549.1.1.1
static unsigned char rsa_oid_enc_[9] = { 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01 };

static unsigned long rsa_bits_(const &BnMont m) {
    unsigned long bits = (unsigned long)m.n * 32UL;
    unsigned int top = m.m[m.n - 1];
    unsigned int b = 0x80000000U;
    while (b != 0U && (top & b) == 0U) { bits = bits - 1UL; b = b >> 1; }
    return bits;
}

int RsaPublicKey::init_der_n_(const &unsigned char der, unsigned long len) {
    struct Asn1_ in;
    unsafe { in = asn1_view_((const unsigned char*)der, len); }
    struct Asn1_ seq;
    if (!asn1_get_(&in, (unsigned char)ASN1_TAG_SEQUENCE, &seq)) { return 0; }
    struct Asn1_ nn;
    struct Asn1_ ee;
    if (!asn1_uint_(&seq, &nn) || !asn1_uint_(&seq, &ee)) { return 0; }
    if (nn.len > (unsigned long)RSA_MAX_BYTES || ee.len > 8UL || ee.len == 0UL) { return 0; }
    unsafe {
        if ((ee.p[ee.len - 1UL] & (unsigned char)1) == (unsigned char)0) { return 0; }
        if (ee.len == 1UL && ee.p[0] == (unsigned char)1) { return 0; }
        if (!self.n.init_n_((const &unsigned char)nn.p, nn.len)) { return 0; }
        unsigned long i = 0UL;
        while (i < ee.len) { self.e[i] = ee.p[i]; i = i + 1UL; }
    }
    self.e_len = ee.len;
    self.bits = rsa_bits_(&self.n);
    if (self.bits < (unsigned long)RSA_MIN_BITS) { return 0; }
    return 1;
}

int RsaPublicKey::init_der([]const unsigned char der_s) {
    unsigned long len = der_s.len;
    const &unsigned char der = crypto_in_(der_s, 0UL);
    return self.init_der_n_(der, len);
}

// s^e mod n into em (k bytes). 0 if sig is the wrong length or >= n.
static int rsa_public_op_(const &RsaPublicKey key, const &unsigned char sig, unsigned long sig_len,
                          &unsigned char em) {
    if (sig_len != key.n.bytes) { return 0; }
    return bn_mod_exp_public_n_(&key.n, sig, sig_len, &key.e[0], key.e_len, em);
}

static int rsa_digest_info_(int alg, &unsigned char* prefix, &unsigned long plen) {
    unsafe {
        if (alg == DIGEST_SHA1) { *prefix = &rsa_di_sha1_[0]; *plen = 15UL; return 1; }
        if (alg == DIGEST_SHA224) { *prefix = &rsa_di_sha224_[0]; *plen = 19UL; return 1; }
        if (alg == DIGEST_SHA256) { *prefix = &rsa_di_sha256_[0]; *plen = 19UL; return 1; }
        if (alg == DIGEST_SHA384) { *prefix = &rsa_di_sha384_[0]; *plen = 19UL; return 1; }
        if (alg == DIGEST_SHA512) { *prefix = &rsa_di_sha512_[0]; *plen = 19UL; return 1; }
    }
    return 0;
}

// EMSA-PKCS1-v1_5 encoding of 'hash' into em[0..k). 0 if it doesn't fit.
static int rsa_pkcs1_encode_(int alg, const &unsigned char hash, unsigned long hash_len,
                             &unsigned char em, unsigned long k) {
    unsigned char* prefix;
    unsigned long plen = 0UL;
    if (!rsa_digest_info_(alg, &prefix, &plen)) { return 0; }
    if (hash_len != digest_size(alg)) { return 0; }
    unsigned long tlen = plen + hash_len;
    if (k < tlen + 11UL) { return 0; }
    unsafe {
        unsigned char* e = (unsigned char*)em;
        const unsigned char* h = (const unsigned char*)hash;
        e[0] = (unsigned char)0;
        e[1] = (unsigned char)1;
        unsigned long i = 2UL;
        while (i < k - tlen - 1UL) { e[i] = (unsigned char)0xFF; i = i + 1UL; }
        e[i] = (unsigned char)0;
        i = i + 1UL;
        unsigned long j = 0UL;
        while (j < plen) { e[i + j] = prefix[j]; j = j + 1UL; }
        j = 0UL;
        while (j < hash_len) { e[i + plen + j] = h[j]; j = j + 1UL; }
    }
    return 1;
}

int rsa_verify_pkcs1_n_(const &RsaPublicKey key, int alg, const &unsigned char hash, unsigned long hash_len,
                     const &unsigned char sig, unsigned long sig_len) {
    unsigned char em[RSA_MAX_BYTES];
    unsigned char want[RSA_MAX_BYTES];
    unsigned long k = key.n.bytes;
    if (!rsa_public_op_(key, sig, sig_len, &em[0])) { return 0; }
    if (!rsa_pkcs1_encode_(alg, hash, hash_len, &want[0], k)) { return 0; }
    return ct_eq_n_(&em[0], &want[0], k);
}

int rsa_verify_pkcs1(const &RsaPublicKey key, int alg, []const unsigned char hash_s, []const unsigned char sig_s) {
    unsigned long hash_len = hash_s.len;
    const &unsigned char hash = crypto_in_(hash_s, 0UL);
    unsigned long sig_len = sig_s.len;
    const &unsigned char sig = crypto_in_(sig_s, 0UL);
    return rsa_verify_pkcs1_n_(key, alg, hash, hash_len, sig, sig_len);
}

int rsa_verify_pss_n_(const &RsaPublicKey key, int alg, const &unsigned char hash, unsigned long hash_len,
                   const &unsigned char sig, unsigned long sig_len, unsigned long salt_len) {
    unsigned char out[RSA_MAX_BYTES];
    unsigned long hl = digest_size(alg);
    if (hl == 0UL || hash_len != hl) { return 0; }
    if (!rsa_public_op_(key, sig, sig_len, &out[0])) { return 0; }
    unsigned long k = key.n.bytes;
    unsigned long em_bits = key.bits - 1UL;
    unsigned long em_len = (em_bits + 7UL) / 8UL;
    unsigned long off = k - em_len;         // 0 or 1 leading byte
    if (off == 1UL && out[0] != (unsigned char)0) { return 0; }
    if (em_len < hl + salt_len + 2UL) { return 0; }
    unsigned char* em;
    unsafe { em = (unsigned char*)&out[0] + off; }
    unsafe {
        if (em[em_len - 1UL] != (unsigned char)0xbc) { return 0; }
        unsigned long db_len = em_len - hl - 1UL;
        unsigned char* h = em + db_len;
        unsigned long top = 8UL * em_len - em_bits;   // 0..7 bits that must be zero
        unsigned char top_mask = (unsigned char)(0xFFU >> (unsigned int)top);
        if ((em[0] & (unsigned char)~top_mask) != (unsigned char)0) { return 0; }
        mgf1_xor_n_(alg, (const &unsigned char)h, hl, (&unsigned char)em, db_len);
        em[0] = em[0] & top_mask;
        unsigned long ps = db_len - salt_len - 1UL;
        unsigned long i = 0UL;
        while (i < ps) { if (em[i] != (unsigned char)0) { return 0; } i = i + 1UL; }
        if (em[ps] != (unsigned char)1) { return 0; }
        // H' = Hash(0x00 * 8 || mHash || salt)
        unsigned char zeros[8];
        i = 0UL;
        while (i < 8UL) { zeros[i] = (unsigned char)0; i = i + 1UL; }
        struct DigestCtx c = DigestCtx::new(alg);
        c.update_n_(&zeros[0], 8UL);
        c.update_n_(hash, hl);
        if (salt_len > 0UL) { c.update_n_((const &unsigned char)(em + ps + 1UL), salt_len); }
        unsigned char h2[DIGEST_MAX_SIZE];
        c.finish_n_(&h2[0]);
        return ct_eq_n_((const &unsigned char)h, &h2[0], hl);
    }
}

int rsa_verify_pss(const &RsaPublicKey key, int alg, []const unsigned char hash_s, []const unsigned char sig_s, unsigned long salt_len) {
    unsigned long hash_len = hash_s.len;
    const &unsigned char hash = crypto_in_(hash_s, 0UL);
    unsigned long sig_len = sig_s.len;
    const &unsigned char sig = crypto_in_(sig_s, 0UL);
    return rsa_verify_pss_n_(key, alg, hash, hash_len, sig, sig_len, salt_len);
}

// ── Private keys ──────────────────────────────────────────────────────────────

int RsaPrivateKey::init_der_n_(const &unsigned char der, unsigned long len) {
    struct Asn1_ in;
    unsafe { in = asn1_view_((const unsigned char*)der, len); }
    struct Asn1_ seq;
    if (!asn1_get_(&in, (unsigned char)ASN1_TAG_SEQUENCE, &seq)) { return 0; }
    int version = -1;
    if (!asn1_small_int_(&seq, &version) || version != 0) { return 0; }
    // PKCS#8: AlgorithmIdentifier then an OCTET STRING with the PKCS#1 key.
    if (asn1_peek_(&seq) == ASN1_TAG_SEQUENCE) {
        struct Asn1_ algId;
        struct Asn1_ oid;
        struct Asn1_ inner;
        if (!asn1_get_(&seq, (unsigned char)ASN1_TAG_SEQUENCE, &algId)) { return 0; }
        if (!asn1_get_(&algId, (unsigned char)ASN1_TAG_OID, &oid)) { return 0; }
        unsafe { if (!asn1_oid_is_(&oid, &rsa_oid_enc_[0], 9UL)) { return 0; } }
        if (!asn1_get_(&seq, (unsigned char)ASN1_TAG_OCTET_STRING, &inner)) { return 0; }
        unsafe { return self.init_der_n_((const &unsigned char)inner.p, inner.len); }
    }
    struct Asn1_ nn;
    struct Asn1_ ee;
    struct Asn1_ dd;
    if (!asn1_uint_(&seq, &nn) || !asn1_uint_(&seq, &ee) || !asn1_uint_(&seq, &dd)) { return 0; }
    if (nn.len > (unsigned long)RSA_MAX_BYTES || ee.len > 8UL || ee.len == 0UL || dd.len > nn.len) { return 0; }
    unsafe {
        if (!self.pub.n.init_n_((const &unsigned char)nn.p, nn.len)) { return 0; }
        unsigned long i = 0UL;
        while (i < ee.len) { self.pub.e[i] = ee.p[i]; i = i + 1UL; }
        self.pub.e_len = ee.len;
        // d left-padded to the modulus length: the exponentiation's work
        // then depends only on the key size.
        unsigned long k = self.pub.n.bytes;
        i = 0UL;
        while (i < k) { self.d[i] = (unsigned char)0; i = i + 1UL; }
        i = 0UL;
        while (i < dd.len) { self.d[k - dd.len + i] = dd.p[i]; i = i + 1UL; }
        self.d_len = k;
    }
    self.pub.bits = rsa_bits_(&self.pub.n);
    if (self.pub.bits < (unsigned long)RSA_MIN_BITS) { return 0; }
    return 1;
}

int RsaPrivateKey::init_der([]const unsigned char der_s) {
    unsigned long len = der_s.len;
    const &unsigned char der = crypto_in_(der_s, 0UL);
    return self.init_der_n_(der, len);
}

void RsaPrivateKey::destroy() {
    mem_zero_secure(self.d[0..(unsigned long)RSA_MAX_BYTES]);
    self.d_len = 0UL;
}

// sig = em^d mod n, then checked with e (a faulty computation must not
// leave the process: RSA's CRT-less private op can't leak factors this
// way, but checking is cheap).
static int rsa_private_op_(const &RsaPrivateKey key, const &unsigned char em, &unsigned char sig) {
    unsigned long k = key.pub.n.bytes;
    if (!bn_mod_exp_secret_n_(&key.pub.n, em, k, &key.d[0], key.d_len, sig)) { return 0; }
    unsigned char back[RSA_MAX_BYTES];
    if (!bn_mod_exp_public_n_(&key.pub.n, sig, k, &key.pub.e[0], key.pub.e_len, &back[0])) { return 0; }
    return ct_eq_n_(&back[0], em, k);
}

int rsa_sign_pkcs1_n_(const &RsaPrivateKey key, int alg, const &unsigned char hash, unsigned long hash_len,
                   &unsigned char sig, unsigned long sig_cap, &unsigned long sig_len) {
    unsigned long k = key.pub.n.bytes;
    if (sig_cap < k) { return 0; }
    unsigned char em[RSA_MAX_BYTES];
    if (!rsa_pkcs1_encode_(alg, hash, hash_len, &em[0], k)) { return 0; }
    if (!rsa_private_op_(key, &em[0], sig)) { return 0; }
    *sig_len = k;
    return 1;
}

int rsa_sign_pkcs1(const &RsaPrivateKey key, int alg, []const unsigned char hash_s, []unsigned char sig_s, &unsigned long sig_len) {
    unsigned long hash_len = hash_s.len;
    const &unsigned char hash = crypto_in_(hash_s, 0UL);
    unsigned long sig_cap = sig_s.len;
    &unsigned char sig = crypto_out_(sig_s, 0UL);
    return rsa_sign_pkcs1_n_(key, alg, hash, hash_len, sig, sig_cap, sig_len);
}

int rsa_sign_pss_n_(const &RsaPrivateKey key, int alg, const &unsigned char hash, unsigned long hash_len,
                 &RngCtx rng, &unsigned char sig, unsigned long sig_cap, &unsigned long sig_len) {
    unsigned long hl = digest_size(alg);
    unsigned long k = key.pub.n.bytes;
    if (hl == 0UL || hash_len != hl || sig_cap < k) { return 0; }
    unsigned long em_bits = key.pub.bits - 1UL;
    unsigned long em_len = (em_bits + 7UL) / 8UL;
    unsigned long salt_len = hl;
    if (em_len < hl + salt_len + 2UL) { return 0; }
    unsigned char out[RSA_MAX_BYTES];
    unsigned long off = k - em_len;
    unsigned long i = 0UL;
    while (i < k) { out[i] = (unsigned char)0; i = i + 1UL; }
    unsafe {
        unsigned char* em = (unsigned char*)&out[0] + off;
        unsigned long db_len = em_len - hl - 1UL;
        unsigned char* salt = em + db_len - salt_len;
        rng.fill_n_((&unsigned char)salt, salt_len);
        // H = Hash(0x00 * 8 || mHash || salt)
        unsigned char zeros[8];
        i = 0UL;
        while (i < 8UL) { zeros[i] = (unsigned char)0; i = i + 1UL; }
        struct DigestCtx c = DigestCtx::new(alg);
        c.update_n_(&zeros[0], 8UL);
        c.update_n_(hash, hl);
        c.update_n_((const &unsigned char)salt, salt_len);
        unsigned char* h = em + db_len;
        c.finish_n_((&unsigned char)h);
        em[db_len - salt_len - 1UL] = (unsigned char)1;   // DB = PS || 0x01 || salt
        mgf1_xor_n_(alg, (const &unsigned char)h, hl, (&unsigned char)em, db_len);
        unsigned long top = 8UL * em_len - em_bits;
        em[0] = em[0] & (unsigned char)(0xFFU >> (unsigned int)top);
        em[em_len - 1UL] = (unsigned char)0xbc;
    }
    if (!rsa_private_op_(key, &out[0], sig)) { return 0; }
    *sig_len = k;
    return 1;
}

int rsa_sign_pss(const &RsaPrivateKey key, int alg, []const unsigned char hash_s, &RngCtx rng, []unsigned char sig_s, &unsigned long sig_len) {
    unsigned long hash_len = hash_s.len;
    const &unsigned char hash = crypto_in_(hash_s, 0UL);
    unsigned long sig_cap = sig_s.len;
    &unsigned char sig = crypto_out_(sig_s, 0UL);
    return rsa_sign_pss_n_(key, alg, hash, hash_len, rng, sig, sig_cap, sig_len);
}

} // namespace std