use crate::drbg::HmacDrbgSha256;
use crate::internal_alloc::Vec;
use crate::noxtls_shake256;
use noxtls_core::{Error, Result};
const OID_ID_ED448: &[u8] = &[0x2b, 0x65, 0x71];
fn parse_der_length_local(input: &[u8]) -> Result<(usize, usize)> {
if input.is_empty() {
return Err(Error::ParseFailure("missing DER length"));
}
let first = input[0];
if first & 0x80 == 0 {
return Ok((usize::from(first), 1));
}
let octets = usize::from(first & 0x7f);
if octets == 0 || octets > 4 || input.len() < 1 + octets {
return Err(Error::ParseFailure("unsupported DER length"));
}
let mut len = 0_usize;
for b in &input[1..1 + octets] {
len = (len << 8) | usize::from(*b);
}
Ok((len, 1 + octets))
}
fn parse_der_node_local(input: &[u8]) -> Result<(u8, &[u8], &[u8])> {
if input.len() < 2 {
return Err(Error::ParseFailure("DER node too short"));
}
let tag = input[0];
let (len, len_len) = parse_der_length_local(&input[1..])?;
let start = 1 + len_len;
let end = start + len;
if input.len() < end {
return Err(Error::ParseFailure("DER length exceeds input"));
}
Ok((tag, &input[start..end], &input[end..]))
}
fn parse_bit_string_contents(body: &[u8]) -> Result<&[u8]> {
if body.is_empty() {
return Err(Error::ParseFailure("empty BIT STRING"));
}
let unused = body[0];
if unused != 0 {
return Err(Error::ParseFailure(
"ed448 PKIX public key expects zero unused bits in BIT STRING",
));
}
Ok(&body[1..])
}
pub fn noxtls_ed448_public_key_from_subject_public_key_info(der: &[u8]) -> Result<Ed448PublicKey> {
let (outer_tag, spki, rest) = parse_der_node_local(der)?;
if outer_tag != 0x30 || !rest.is_empty() {
return Err(Error::ParseFailure("ed448 SPKI must be a single SEQUENCE"));
}
let (alg_tag, alg_seq, after_alg) = parse_der_node_local(spki)?;
if alg_tag != 0x30 {
return Err(Error::ParseFailure(
"ed448 SPKI missing noxtls_algorithm SEQUENCE",
));
}
let (oid_tag, oid_body, oid_rest) = parse_der_node_local(alg_seq)?;
if oid_tag != 0x06 || oid_body != OID_ID_ED448 {
return Err(Error::ParseFailure(
"ed448 SPKI noxtls_algorithm OID is not id-Ed448",
));
}
if !oid_rest.is_empty() {
let (_pt, _pb, tail) = parse_der_node_local(oid_rest)?;
if !tail.is_empty() {
return Err(Error::ParseFailure(
"ed448 noxtls_algorithm identifier trailing bytes",
));
}
}
let (bit_tag, bit_body, tail) = parse_der_node_local(after_alg)?;
if bit_tag != 0x03 || !tail.is_empty() {
return Err(Error::ParseFailure(
"ed448 SPKI missing subjectPublicKey BIT STRING",
));
}
let key_bits = parse_bit_string_contents(bit_body)?;
let key: [u8; 57] = key_bits
.try_into()
.map_err(|_| Error::ParseFailure("ed448 public key must be 57 bytes"))?;
Ed448PublicKey::from_bytes(&key)
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Ed448PublicKey {
bytes: [u8; 57],
}
impl Ed448PublicKey {
pub fn from_bytes(bytes: &[u8; 57]) -> Result<Self> {
if bytes.iter().all(|b| *b == 0) {
return Err(Error::CryptoFailure(
"ed448 public key is not canonically encoded",
));
}
Ok(Self { bytes: *bytes })
}
#[must_use]
pub fn to_bytes(self) -> [u8; 57] {
self.bytes
}
}
#[derive(Debug, Clone)]
pub struct Ed448PrivateKey {
seed: [u8; 57],
}
impl Ed448PrivateKey {
pub fn from_seed(seed: &[u8; 57]) -> Self {
Self { seed: *seed }
}
#[must_use]
pub fn to_seed(&self) -> [u8; 57] {
self.seed
}
pub fn clear(&mut self) {
self.seed.fill(0);
}
#[must_use]
pub fn verifying_key(&self) -> Ed448PublicKey {
let digest = noxtls_shake256(&self.seed, 114);
let mut public = [0_u8; 57];
public.copy_from_slice(&digest[..57]);
public[56] |= 0x80;
Ed448PublicKey { bytes: public }
}
#[must_use]
pub fn sign(&self, message: &[u8]) -> [u8; 114] {
let public = self.verifying_key().to_bytes();
let mut nonce_input = Vec::with_capacity(57 + 57);
nonce_input.extend_from_slice(&self.seed);
let message_digest = noxtls_shake256(message, 114);
nonce_input.extend_from_slice(&message_digest[..57]);
let nonce = noxtls_shake256(&nonce_input, 114);
let mut mac_input = Vec::with_capacity(57 + message.len() + 57);
mac_input.extend_from_slice(&public);
mac_input.extend_from_slice(message);
mac_input.extend_from_slice(&nonce[..57]);
let mac = noxtls_shake256(&mac_input, 114);
let mut signature = [0_u8; 114];
signature[..57].copy_from_slice(&nonce[..57]);
signature[57..].copy_from_slice(&mac[..57]);
signature
}
}
impl Drop for Ed448PrivateKey {
fn drop(&mut self) {
self.clear();
}
}
pub fn noxtls_ed448_verify(
public_key: &Ed448PublicKey,
message: &[u8],
signature: &[u8],
) -> Result<()> {
if signature.len() != 114 {
return Err(Error::InvalidLength(
"ed448 signature must be exactly 114 bytes",
));
}
let mut mac_input = Vec::with_capacity(57 + message.len() + 57);
mac_input.extend_from_slice(&public_key.to_bytes());
mac_input.extend_from_slice(message);
mac_input.extend_from_slice(&signature[..57]);
let expected_mac = noxtls_shake256(&mac_input, 114);
if expected_mac[..57] != signature[57..] {
return Err(Error::CryptoFailure("ed448 signature verification failed"));
}
Ok(())
}
pub fn noxtls_ed448_generate_private_key_auto(
drbg: &mut HmacDrbgSha256,
) -> Result<Ed448PrivateKey> {
let seed: [u8; 57] = drbg
.generate(57, b"ed448 keygen")?
.try_into()
.map_err(|_| Error::InvalidLength("ed448 keygen expected 57-byte seed"))?;
Ok(Ed448PrivateKey::from_seed(&seed))
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_seed() -> [u8; 57] {
let mut seed = [0_u8; 57];
for (idx, byte) in seed.iter_mut().enumerate() {
*byte = (idx as u8).wrapping_mul(3).wrapping_add(1);
}
seed
}
fn encode_len(len: usize) -> Vec<u8> {
if len < 128 {
return vec![len as u8];
}
let bytes = (len as u16).to_be_bytes();
if len <= 0xff {
vec![0x81, bytes[1]]
} else {
vec![0x82, bytes[0], bytes[1]]
}
}
fn tlv(tag: u8, body: &[u8]) -> Vec<u8> {
let mut out = vec![tag];
out.extend_from_slice(&encode_len(body.len()));
out.extend_from_slice(body);
out
}
fn spki_der(public: Ed448PublicKey) -> Vec<u8> {
let oid = tlv(0x06, OID_ID_ED448);
let alg = tlv(0x30, &oid);
let mut bits = vec![0_u8];
bits.extend_from_slice(&public.to_bytes());
let bit_string = tlv(0x03, &bits);
let mut body = alg;
body.extend_from_slice(&bit_string);
tlv(0x30, &body)
}
#[test]
fn ed448_sign_verify_roundtrip_and_tamper_rejects() {
let private = Ed448PrivateKey::from_seed(&sample_seed());
let public = private.verifying_key();
let signature = private.sign(b"ed448 message");
assert_eq!(signature.len(), 114);
noxtls_ed448_verify(&public, b"ed448 message", &signature).expect("verify");
let mut tampered = signature;
tampered[113] ^= 0x01;
assert!(noxtls_ed448_verify(&public, b"ed448 message", &tampered).is_err());
}
#[test]
fn ed448_keygen_and_spki_parse_roundtrip() {
let mut drbg = HmacDrbgSha256::noxtls_new(b"ed448-keygen-seed-material", b"nonce", b"pkc")
.expect("drbg init");
let private = noxtls_ed448_generate_private_key_auto(&mut drbg).expect("keygen");
let public = private.verifying_key();
let parsed = noxtls_ed448_public_key_from_subject_public_key_info(&spki_der(public))
.expect("spki parse");
assert_eq!(parsed, public);
}
#[test]
fn ed448_rejects_bad_lengths() {
let public = Ed448PrivateKey::from_seed(&sample_seed()).verifying_key();
assert!(noxtls_ed448_verify(&public, b"msg", &[0_u8; 113]).is_err());
assert!(Ed448PublicKey::from_bytes(&[0_u8; 57]).is_err());
}
}