///|
/// An RSA public key: modulus `n` and public exponent `e` (RFC 8017). The RS256
/// verification key. Core's `BigInt` carries the modular arithmetic, so the
/// signature scheme is a straight transcription of PKCS#1 v1.5 with no vendored C.
pub(all) struct RsaPublicKey {
n : BigInt
e : BigInt
}
///|
/// An RSA private key: modulus `n`, public exponent `e`, and private exponent `d`
/// (the CRT parameters are not needed for the plain `m^d mod n` path). The RS256
/// signing key.
pub(all) struct RsaPrivateKey {
n : BigInt
e : BigInt
d : BigInt
}
///|
/// Build a public key from hex-encoded modulus and exponent — e.g. openssl's
/// `rsa -modulus` output and `10001`.
pub fn RsaPublicKey::from_hex(n_hex : String, e_hex : String) -> RsaPublicKey {
{
n: BigInt::from_string(n_hex, radix=16),
e: BigInt::from_string(e_hex, radix=16),
}
}
///|
/// Build a private key from hex-encoded modulus, public exponent, and private
/// exponent.
pub fn RsaPrivateKey::from_hex(
n_hex : String,
e_hex : String,
d_hex : String,
) -> RsaPrivateKey {
{
n: BigInt::from_string(n_hex, radix=16),
e: BigInt::from_string(e_hex, radix=16),
d: BigInt::from_string(d_hex, radix=16),
}
}
///|
/// The public half of a private key — for verifying what it signs.
pub fn RsaPrivateKey::public_key(self : RsaPrivateKey) -> RsaPublicKey {
{ n: self.n, e: self.e, }
}
///|
/// The modulus size in octets: the width of an RSA signature and of the encoded
/// message block.
fn modulus_len(n : BigInt) -> Int {
n.to_octets().length()
}
///|
/// The ASN.1 DER `DigestInfo` prefix for a SHA-256 digest (RFC 8017 §9.2): the
/// `id-sha256` `AlgorithmIdentifier` followed by the `OCTET STRING` header for the
/// 32-byte hash.
let sha256_digest_prefix : Bytes = b"\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"
///|
/// EMSA-PKCS1-v1_5 encode `msg`'s SHA-256 digest to `em_len` octets (RFC 8017
/// §9.2): `0x00 || 0x01 || PS || 0x00 || DigestInfo(H(msg))`, where `PS` is a run
/// of `0xFF` filling the block. This is the exact block RS256 signs over.
fn emsa_pkcs1_v15_sha256(msg : Bytes, em_len : Int) -> Bytes {
let digest = sha256(msg)
let t_len = sha256_digest_prefix.length() + digest.length()
let ps_len = em_len - t_len - 3
let em = Buffer()
em.write_byte(b'\x00')
em.write_byte(b'\x01')
for _i in 0.. Bytes {
let em_len = modulus_len(key.n)
let em = emsa_pkcs1_v15_sha256(msg, em_len)
let m = BigInt::from_octets(em[:])
let s = m.pow(key.d, modulus=key.n)
s.to_octets(length=em_len)
}
///|
/// RSASSA-PKCS1-v1_5 verify with SHA-256 — the RS256 verification primitive (RFC
/// 8017 §8.2.2): recover `m = s^e mod n` and compare it, in constant time, to the
/// expected EMSA-PKCS1-v1_5 encoding of `msg`. Rejects a signature that is not the
/// modulus width or is `>= n`.
pub fn rsa_pkcs1_sha256_verify(
msg : Bytes,
sig : Bytes,
key : RsaPublicKey,
) -> Bool {
let em_len = modulus_len(key.n)
if sig.length() != em_len {
return false
}
let s = BigInt::from_octets(sig[:])
if s >= key.n {
return false
}
let m = s.pow(key.e, modulus=key.n)
let em = m.to_octets(length=em_len)
let expected = emsa_pkcs1_v15_sha256(msg, em_len)
constant_time_eq(em, expected)
}