///|
/// The smallest modulus this package will accept.
///
/// 1024-bit RSA is considered broken for signatures and 2048 is the current
/// floor in every serious guideline. Refusing a short key is a security control
/// in its own right: a key that is too small verifies signatures perfectly well
/// and offers no protection.
pub let minimum_modulus_bits : Int = 2048
///|
let algorithm_tag : String = "rsa-sha256"
///|
/// An RSA public key that verifies RSASSA-PKCS1-v1_5 signatures over SHA-256.
pub struct PublicKey {
modulus : @bigint.BigInt
exponent : @bigint.BigInt
/// Length of the modulus in bytes. This is `k` in RFC 8017 and fixes the
/// exact length every signature and encoded message must have.
size : Int
}
///|
/// Parses a trusted public key.
///
/// The encoding is `rsa-sha256::`, big-endian and
/// unsigned. The algorithm tag is part of the stored key so that migrating to a
/// different scheme later does not require a release that existing
/// installations are unable to accept.
pub fn PublicKey::parse(text : String) -> PublicKey raise KeyError {
let parts = text.split(":").collect()
guard parts.length() == 3 else {
raise Malformed(
detail="expected \{algorithm_tag}::",
)
}
let tag = parts[0].to_owned()
guard tag == algorithm_tag else { raise UnknownAlgorithm(tag~) }
let modulus_bytes = decode_hex(parts[1].to_owned())
let exponent_bytes = decode_hex(parts[2].to_owned())
// A leading zero byte would let two different texts denote the same modulus
// with different values of `k`, and `k` decides which signature lengths are
// accepted. Requiring a canonical encoding keeps that mapping one to one.
guard modulus_bytes[0] != b'\x00' else { raise NonCanonicalModulus }
let modulus = @bigint.BigInt::from_octets(modulus_bytes[:])
let bits = modulus.bit_length()
guard bits >= minimum_modulus_bits else {
raise ModulusTooSmall(bits~, minimum=minimum_modulus_bits)
}
let exponent = @bigint.BigInt::from_octets(exponent_bytes[:])
// e = 1 would make the signature equal to the encoded message, so anyone
// could produce one. Even exponents are not valid RSA exponents at all.
guard exponent.compare_int(3) >= 0 else {
raise InvalidExponent(detail="must be at least 3")
}
guard exponent.compare(modulus) < 0 else {
raise InvalidExponent(detail="must be smaller than the modulus")
}
guard exponent.is_odd() else { raise InvalidExponent(detail="must be odd") }
PublicKey::{ modulus, exponent, size: modulus_bytes.length() }
}
///|
/// Returns the modulus length in bytes.
pub fn PublicKey::size(self : PublicKey) -> Int {
self.size
}
///|
/// Returns the modulus length in bits.
pub fn PublicKey::bits(self : PublicKey) -> Int {
self.modulus.bit_length()
}
///|
fn @bigint.BigInt::is_odd(self : @bigint.BigInt) -> Bool {
(self % @bigint.BigInt::from_int(2)).compare_int(0) != 0
}