use super::crypto::ecdsa::Curve;
use super::crypto::hash::{Digest, Sha256, Sha384};
use super::crypto::{ecdsa, ed25519, rsa};
use super::key_schedule::{CipherSuite, SuiteHash};
use super::x509::der::{
expect_sequence, read_element, OID_EC_PUBLIC_KEY, OID_ED25519, OID_P256, OID_P384, OID_P521,
OID_RSA_ENCRYPTION,
};
use super::{Identity, TlsError, TlsResult};
use alloc::vec::Vec;
enum ParsedKey {
Rsa { n: Vec<u8>, e: Vec<u8>, d: Vec<u8> },
Ed25519([u8; 32]),
Ec { curve: Curve, d: Vec<u8> },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum IdentityKeyType {
Rsa,
Ecdsa(Curve),
Ed25519,
}
pub(crate) fn key_type(key_der: &[u8]) -> TlsResult<IdentityKeyType> {
match parse_private_key(key_der)? {
ParsedKey::Rsa { .. } => Ok(IdentityKeyType::Rsa),
ParsedKey::Ec { curve, .. } => Ok(IdentityKeyType::Ecdsa(curve)),
ParsedKey::Ed25519(_) => Ok(IdentityKeyType::Ed25519),
}
}
pub(crate) fn identity_key_type(identity: &Identity) -> TlsResult<IdentityKeyType> {
key_type(&identity.private_key)
}
pub(crate) fn key_matches_certificate(leaf: &super::x509::Certificate, key_der: &[u8]) -> bool {
use super::x509::der::{
parse_rsa_public_key, OID_EC_PUBLIC_KEY, OID_ED25519, OID_RSA_ENCRYPTION,
};
const CHECK: &[u8] = b"courierust: private key / certificate consistency check";
let key = match parse_private_key(key_der) {
Ok(key) => key,
Err(_) => return false,
};
let spki = &leaf.spki;
if spki.oid == OID_RSA_ENCRYPTION {
let ParsedKey::Rsa { n, e, d } = key else {
return false;
};
let Some((cert_n, cert_e)) = parse_rsa_public_key(&spki.key) else {
return false;
};
let mut h = Sha256::new();
h.update(CHECK);
let digest = h.finalize();
let Some(signature) = rsa::sign_pkcs1v15(&n, &e, &d, rsa::DIGEST_INFO_SHA256, &digest)
else {
return false;
};
return rsa::verify_rsa_pkcs1v15(
&rsa::RsaPublicKey {
n: cert_n,
e: cert_e,
},
false,
&digest,
&signature,
);
}
if spki.oid == OID_ED25519 {
let ParsedKey::Ed25519(seed) = key else {
return false;
};
let Ok(public) = <[u8; 32]>::try_from(spki.key.as_slice()) else {
return false;
};
let signature = ed25519::sign(&seed, CHECK);
return ed25519::verify(&public, CHECK, &signature);
}
if spki.oid == OID_EC_PUBLIC_KEY {
let ParsedKey::Ec { curve, d } = key else {
return false;
};
if spki.ec_curve != Some(curve) {
return false;
}
let coord_len = curve.coord_len();
if spki.key.len() != 1 + 2 * coord_len || spki.key[0] != 0x04 {
return false;
}
let (qx, qy) = (&spki.key[1..1 + coord_len], &spki.key[1 + coord_len..]);
let digest = match curve {
Curve::P256 => {
let mut h = Sha256::new();
h.update(CHECK);
h.finalize()
}
Curve::P384 => {
let mut h = Sha384::new();
h.update(CHECK);
h.finalize()
}
Curve::P521 => {
let mut h = ed25519::Sha512::new();
h.update(CHECK);
h.finalize()
}
};
let Some((r, s)) = ecdsa::sign(curve, &d, &digest) else {
return false;
};
return ecdsa::verify_der(curve, qx, qy, &digest, &encode_ecdsa_sig(&r, &s));
}
false
}
pub(crate) fn tls13_suite_hash_pref(identity: &Identity) -> Option<SuiteHash> {
match identity_key_type(identity).ok()? {
IdentityKeyType::Rsa | IdentityKeyType::Ed25519 => None,
IdentityKeyType::Ecdsa(Curve::P256) => Some(SuiteHash::Sha256),
IdentityKeyType::Ecdsa(Curve::P384) => Some(SuiteHash::Sha384),
IdentityKeyType::Ecdsa(Curve::P521) => None,
}
}
pub(crate) fn sign_tls12_server_key_exchange(
identity: &Identity,
message: &[u8],
) -> TlsResult<Option<(u8, u8, Vec<u8>)>> {
let key = parse_private_key(&identity.private_key)?;
match key {
ParsedKey::Rsa { n, e, d } => {
let mut h = Sha256::new();
let digest = {
h.update(message);
h.finalize()
};
rsa::sign_pkcs1v15(&n, &e, &d, rsa::DIGEST_INFO_SHA256, &digest)
.map(|sig| (4, 1, sig))
.map(Some)
.ok_or_else(|| TlsError::Certificate("RSA signing failed".into()))
}
ParsedKey::Ec { curve, d } => {
let (hash_alg, digest) = match curve {
Curve::P256 => {
let mut h = Sha256::new();
h.update(message);
(4, h.finalize())
}
Curve::P384 => {
let mut h = Sha384::new();
h.update(message);
(5, h.finalize())
}
Curve::P521 => {
let mut h = ed25519::Sha512::new();
h.update(message);
(6, h.finalize())
}
};
match ecdsa::sign(curve, &d, &digest) {
Some((r, s)) => {
let der = encode_ecdsa_sig(&r, &s);
Ok(Some((hash_alg, 3, der)))
}
None => Err(TlsError::Certificate("ECDSA signing failed".into())),
}
}
ParsedKey::Ed25519(seed) => {
let sig = ed25519::sign(&seed, message);
Ok(Some((0x08, 0x07, sig.to_vec())))
}
}
}
pub(crate) fn sign_cert_verify(
identity: &Identity,
message: &[u8],
suite: CipherSuite,
) -> TlsResult<Option<(u16, Vec<u8>)>> {
let key = parse_private_key(&identity.private_key)?;
match key {
ParsedKey::Rsa { n, e, d } => {
let (scheme_pss, scheme_pkcs1, salt_len, digest_info) = match suite.hash() {
SuiteHash::Sha256 => (0x0804, 0x0401, 32, rsa::DIGEST_INFO_SHA256),
SuiteHash::Sha384 => (0x0805, 0x0501, 48, rsa::DIGEST_INFO_SHA384),
};
let mut h: super::crypto::hash::BoxDigest = match suite.hash() {
SuiteHash::Sha256 => Box::<Sha256>::default(),
SuiteHash::Sha384 => Box::<Sha384>::default(),
};
if let Some(sig) = rsa::sign_pss(h.as_mut(), &n, &e, &d, message, salt_len) {
return Ok(Some((scheme_pss, sig)));
}
let mut h: super::crypto::hash::BoxDigest = match suite.hash() {
SuiteHash::Sha256 => Box::<Sha256>::default(),
SuiteHash::Sha384 => Box::<Sha384>::default(),
};
let digest = {
h.update(message);
h.finalize()
};
if let Some(sig) = rsa::sign_pkcs1v15(&n, &e, &d, digest_info, &digest) {
return Ok(Some((scheme_pkcs1, sig)));
}
Err(TlsError::Certificate("RSA signing failed".into()))
}
ParsedKey::Ed25519(seed) => {
let sig = ed25519::sign(&seed, message);
Ok(Some((0x0807, sig.to_vec())))
}
ParsedKey::Ec { curve, d } => {
let (scheme, digest) = match (curve, suite.hash()) {
(Curve::P256, SuiteHash::Sha256) => {
let mut h = Sha256::new();
h.update(message);
(0x0403, h.finalize())
}
(Curve::P384, SuiteHash::Sha384) => {
let mut h = Sha384::new();
h.update(message);
(0x0503, h.finalize())
}
(Curve::P521, _) => {
return Err(TlsError::Certificate(
"P-521 identity cannot sign a TLS 1.3 CertificateVerify \
(no SHA-512 cipher suite)"
.into(),
))
}
_ => {
return Err(TlsError::Certificate(
"ECDSA identity curve incompatible with the negotiated \
cipher suite"
.into(),
))
}
};
match ecdsa::sign(curve, &d, &digest) {
Some((r, s)) => {
let der = encode_ecdsa_sig(&r, &s);
Ok(Some((scheme, der)))
}
None => Err(TlsError::Certificate("ECDSA signing failed".into())),
}
}
}
}
fn encode_ecdsa_sig(r: &[u8], s: &[u8]) -> Vec<u8> {
fn enc_int(v: &[u8]) -> Vec<u8> {
let mut body = v.to_vec();
while body.len() > 1 && body[0] == 0 {
body.remove(0);
}
if body[0] & 0x80 != 0 {
body.insert(0, 0);
}
let mut out = Vec::with_capacity(2 + body.len());
out.push(0x02);
out.push(body.len() as u8);
out.extend_from_slice(&body);
out
}
let r_der = enc_int(r);
let s_der = enc_int(s);
let body_len = r_der.len() + s_der.len();
let mut out = Vec::with_capacity(4 + body_len);
out.push(0x30);
if body_len < 128 {
out.push(body_len as u8);
} else {
out.push(0x81);
out.push(body_len as u8);
}
out.extend_from_slice(&r_der);
out.extend_from_slice(&s_der);
out
}
fn parse_private_key(der: &[u8]) -> TlsResult<ParsedKey> {
if let Some(k) = parse_pkcs8(der) {
return Ok(k);
}
if let Some(k) = parse_pkcs1_rsa(der) {
return Ok(k);
}
if let Some(k) = parse_sec1_ec(der, None) {
return Ok(k);
}
Err(TlsError::Certificate(
"unsupported private key format".into(),
))
}
fn parse_pkcs8(der: &[u8]) -> Option<ParsedKey> {
let mut pos = 0usize;
let seq = expect_sequence(der, &mut pos)?;
if pos != der.len() {
return None;
}
let mut p = 0usize;
let version = read_element(seq, &mut p)?;
if version.tag != 0x02 {
return None;
}
let alg = read_element(seq, &mut p)?;
if alg.tag != 0x30 {
return None;
}
let mut a = 0usize;
let oid = read_element(alg.content, &mut a)?;
if oid.tag != 0x06 {
return None;
}
let key = read_element(seq, &mut p)?;
if key.tag != 0x04 {
return None;
}
if oid.content == OID_RSA_ENCRYPTION {
return parse_pkcs1_rsa(key.content);
}
if oid.content == OID_ED25519 {
let seed_bytes = if key.content.len() == 32 {
key.content
} else if key.content.len() == 34 && key.content[0] == 0x04 && key.content[1] == 0x20 {
&key.content[2..]
} else {
return None;
};
let mut seed = [0u8; 32];
seed.copy_from_slice(seed_bytes);
return Some(ParsedKey::Ed25519(seed));
}
if oid.content == OID_EC_PUBLIC_KEY {
let params = read_element(alg.content, &mut a)?;
if params.tag != 0x06 {
return None;
}
return parse_sec1_ec(key.content, curve_from_oid(params.content));
}
None
}
fn curve_from_oid(oid: &[u8]) -> Option<Curve> {
match oid {
OID_P256 => Some(Curve::P256),
OID_P384 => Some(Curve::P384),
OID_P521 => Some(Curve::P521),
_ => None,
}
}
fn parse_sec1_ec(der: &[u8], curve: Option<Curve>) -> Option<ParsedKey> {
let mut pos = 0usize;
let seq = expect_sequence(der, &mut pos)?;
if pos != der.len() {
return None;
}
let mut p = 0usize;
let ver = read_element(seq, &mut p)?;
if ver.tag != 0x02 {
return None;
}
let d_oct = read_element(seq, &mut p)?;
if d_oct.tag != 0x04 {
return None;
}
let mut curve = curve;
while let Some(el) = read_element(seq, &mut p) {
if el.tag == 0xa0 {
let mut pp = 0usize;
let oid = read_element(el.content, &mut pp)?;
if oid.tag == 0x06 {
curve = curve_from_oid(oid.content);
}
}
}
let curve = curve?;
let coord_len = curve.coord_len();
if d_oct.content.is_empty() || d_oct.content.len() > coord_len {
return None;
}
let mut d = vec![0u8; coord_len];
d[coord_len - d_oct.content.len()..].copy_from_slice(d_oct.content);
Some(ParsedKey::Ec { curve, d })
}
fn parse_pkcs1_rsa(der: &[u8]) -> Option<ParsedKey> {
let mut pos = 0usize;
let seq = expect_sequence(der, &mut pos)?;
if pos != der.len() {
return None;
}
let mut p = 0usize;
let ver = read_element(seq, &mut p)?;
if ver.tag != 0x02 {
return None;
}
let n = read_element(seq, &mut p)?;
if n.tag != 0x02 {
return None;
}
let e = read_element(seq, &mut p)?;
if e.tag != 0x02 {
return None;
}
let d = read_element(seq, &mut p)?;
if d.tag != 0x02 {
return None;
}
let nv = strip_int(n.content);
let ev = strip_int(e.content);
let dv = strip_int(d.content);
if ev.is_empty() {
return None;
}
Some(ParsedKey::Rsa {
n: nv.to_vec(),
e: ev.to_vec(),
d: dv.to_vec(),
})
}
fn strip_int(v: &[u8]) -> &[u8] {
if v.len() > 1 && v[0] == 0 {
&v[1..]
} else {
v
}
}
#[cfg(test)]
mod tests {
use super::*;
fn hex(s: &str) -> Vec<u8> {
let s = s.replace(' ', "");
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
.collect()
}
fn to_hex(v: &[u8]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut s = String::with_capacity(v.len() * 2);
for &b in v {
s.push(HEX[(b >> 4) as usize] as char);
s.push(HEX[(b & 0x0f) as usize] as char);
}
s
}
#[test]
fn ed25519_sign_verify_roundtrip() {
let seed: [u8; 32] =
hex("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
.try_into()
.unwrap();
let msg = b"";
let sig = ed25519::sign(&seed, msg);
assert_eq!(
to_hex(&sig),
"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155\
5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"
);
let pk: [u8; 32] = hex("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a")
.try_into()
.unwrap();
assert!(ed25519::verify(&pk, msg, &sig));
assert!(!ed25519::verify(&pk, b"tampered", &sig));
}
#[test]
fn ecdsa_p256_sign_verify_roundtrip() {
let d: Vec<u8> = hex("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721");
let digest: Vec<u8> =
hex("af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf");
let (r, s) = ecdsa::sign(Curve::P256, &d, &digest).expect("sign");
let qx = hex("60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6");
let qy = hex("7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299");
let der = encode_ecdsa_sig(&r, &s);
assert!(super::super::crypto::ecdsa::verify_der(
Curve::P256,
&qx,
&qy,
&digest,
&der
));
let mut bad = digest;
bad[0] ^= 1;
assert!(!super::super::crypto::ecdsa::verify_der(
Curve::P256,
&qx,
&qy,
&bad,
&der
));
}
#[test]
fn rsa_sign_verify_roundtrip() {
let n: Vec<u8> = hex(
"a643f09b73976142b45694f8a8ae222e00926aae43f8ac9ed9e3828535e19e8d\
57e435a703e47fd795ba13836faa2121e40abe6768b16a3c930e004f2c0e73f2\
56e61598ea9fb2e3501ecef756e5465d99a1435a38997167ec54152a777dd2d9\
2035cfd55e444fb1a14b804ff40b8a23d46c9fab0a451d21af837f5799d57809",
);
let d: Vec<u8> = hex(
"775819235c4b72f2f0839d97076d46f7824d96e9d3bc721bec06d4af4dc7cf89\
61675be3b0759a16635117a4a6c895d3bfdebe6177d2b1911d75555f7f1e38b6\
b38050ddc7c619086cca42cf319313c7adf92a4a8e17c3e7f6789208bbf65c09\
cacd0b3cb16eb3b70838379844509fae17818045f34953e5201fdf1c65a1a5a1",
);
let msg = b"TLS 1.3 server CertificateVerify";
let e = vec![0x01u8, 0x00, 0x01];
let mut h = Sha256::new();
let digest = {
h.update(msg);
h.finalize()
};
let pkcs1_sig =
rsa::sign_pkcs1v15(&n, &e, &d, rsa::DIGEST_INFO_SHA256, &digest).expect("sign pkcs1");
let key = super::super::crypto::rsa::RsaPublicKey {
n: n.clone(),
e: e.clone(),
};
assert!(key.verify_pkcs1v15(rsa::DIGEST_INFO_SHA256, &digest, &pkcs1_sig));
let mut h = Sha256::new();
let pss_sig = rsa::sign_pss(&mut h, &n, &e, &d, msg, 32).expect("sign pss");
let mut h = Sha256::new();
assert!(key.verify_pss(&mut h, msg, 32, &pss_sig));
for _ in 0..3 {
let again = rsa::sign_pkcs1v15(&n, &e, &d, rsa::DIGEST_INFO_SHA256, &digest)
.expect("sign pkcs1");
assert_eq!(again, pkcs1_sig, "blinding must not alter the signature");
}
}
}