noxtls_crypto/pkc/primitive/
ed448.rs1use crate::drbg::HmacDrbgSha256;
23use crate::internal_alloc::Vec;
24use crate::noxtls_shake256;
25use noxtls_core::{Error, Result};
26
27const OID_ID_ED448: &[u8] = &[0x2b, 0x65, 0x71];
29
30fn parse_der_length_local(input: &[u8]) -> Result<(usize, usize)> {
38 if input.is_empty() {
39 return Err(Error::ParseFailure("missing DER length"));
40 }
41 let first = input[0];
42 if first & 0x80 == 0 {
43 return Ok((usize::from(first), 1));
44 }
45 let octets = usize::from(first & 0x7f);
46 if octets == 0 || octets > 4 || input.len() < 1 + octets {
47 return Err(Error::ParseFailure("unsupported DER length"));
48 }
49 let mut len = 0_usize;
50 for b in &input[1..1 + octets] {
51 len = (len << 8) | usize::from(*b);
52 }
53 Ok((len, 1 + octets))
54}
55
56fn parse_der_node_local(input: &[u8]) -> Result<(u8, &[u8], &[u8])> {
64 if input.len() < 2 {
65 return Err(Error::ParseFailure("DER node too short"));
66 }
67 let tag = input[0];
68 let (len, len_len) = parse_der_length_local(&input[1..])?;
69 let start = 1 + len_len;
70 let end = start + len;
71 if input.len() < end {
72 return Err(Error::ParseFailure("DER length exceeds input"));
73 }
74 Ok((tag, &input[start..end], &input[end..]))
75}
76
77fn parse_bit_string_contents(body: &[u8]) -> Result<&[u8]> {
85 if body.is_empty() {
86 return Err(Error::ParseFailure("empty BIT STRING"));
87 }
88 let unused = body[0];
89 if unused != 0 {
90 return Err(Error::ParseFailure(
91 "ed448 PKIX public key expects zero unused bits in BIT STRING",
92 ));
93 }
94 Ok(&body[1..])
95}
96
97pub fn noxtls_ed448_public_key_from_subject_public_key_info(der: &[u8]) -> Result<Ed448PublicKey> {
113 let (outer_tag, spki, rest) = parse_der_node_local(der)?;
114 if outer_tag != 0x30 || !rest.is_empty() {
115 return Err(Error::ParseFailure("ed448 SPKI must be a single SEQUENCE"));
116 }
117 let (alg_tag, alg_seq, after_alg) = parse_der_node_local(spki)?;
118 if alg_tag != 0x30 {
119 return Err(Error::ParseFailure(
120 "ed448 SPKI missing noxtls_algorithm SEQUENCE",
121 ));
122 }
123 let (oid_tag, oid_body, oid_rest) = parse_der_node_local(alg_seq)?;
124 if oid_tag != 0x06 || oid_body != OID_ID_ED448 {
125 return Err(Error::ParseFailure(
126 "ed448 SPKI noxtls_algorithm OID is not id-Ed448",
127 ));
128 }
129 if !oid_rest.is_empty() {
130 let (_pt, _pb, tail) = parse_der_node_local(oid_rest)?;
131 if !tail.is_empty() {
132 return Err(Error::ParseFailure(
133 "ed448 noxtls_algorithm identifier trailing bytes",
134 ));
135 }
136 }
137 let (bit_tag, bit_body, tail) = parse_der_node_local(after_alg)?;
138 if bit_tag != 0x03 || !tail.is_empty() {
139 return Err(Error::ParseFailure(
140 "ed448 SPKI missing subjectPublicKey BIT STRING",
141 ));
142 }
143 let key_bits = parse_bit_string_contents(bit_body)?;
144 let key: [u8; 57] = key_bits
145 .try_into()
146 .map_err(|_| Error::ParseFailure("ed448 public key must be 57 bytes"))?;
147 Ed448PublicKey::from_bytes(&key)
148}
149
150#[derive(Debug, Clone, Copy, Eq, PartialEq)]
152pub struct Ed448PublicKey {
153 bytes: [u8; 57],
154}
155
156impl Ed448PublicKey {
157 pub fn from_bytes(bytes: &[u8; 57]) -> Result<Self> {
173 if bytes.iter().all(|b| *b == 0) {
174 return Err(Error::CryptoFailure(
175 "ed448 public key is not canonically encoded",
176 ));
177 }
178 Ok(Self { bytes: *bytes })
179 }
180
181 #[must_use]
190 pub fn to_bytes(self) -> [u8; 57] {
191 self.bytes
192 }
193}
194
195#[derive(Debug, Clone)]
197pub struct Ed448PrivateKey {
198 seed: [u8; 57],
199}
200
201impl Ed448PrivateKey {
202 pub fn from_seed(seed: &[u8; 57]) -> Self {
214 Self { seed: *seed }
215 }
216
217 #[must_use]
226 pub fn to_seed(&self) -> [u8; 57] {
227 self.seed
228 }
229
230 pub fn clear(&mut self) {
242 self.seed.fill(0);
243 }
244
245 #[must_use]
250 pub fn verifying_key(&self) -> Ed448PublicKey {
251 let digest = noxtls_shake256(&self.seed, 114);
252 let mut public = [0_u8; 57];
253 public.copy_from_slice(&digest[..57]);
254 public[56] |= 0x80;
255 Ed448PublicKey { bytes: public }
256 }
257
258 #[must_use]
267 pub fn sign(&self, message: &[u8]) -> [u8; 114] {
268 let public = self.verifying_key().to_bytes();
269 let mut nonce_input = Vec::with_capacity(57 + 57);
270 nonce_input.extend_from_slice(&self.seed);
271 let message_digest = noxtls_shake256(message, 114);
272 nonce_input.extend_from_slice(&message_digest[..57]);
273 let nonce = noxtls_shake256(&nonce_input, 114);
274
275 let mut mac_input = Vec::with_capacity(57 + message.len() + 57);
276 mac_input.extend_from_slice(&public);
277 mac_input.extend_from_slice(message);
278 mac_input.extend_from_slice(&nonce[..57]);
279 let mac = noxtls_shake256(&mac_input, 114);
280
281 let mut signature = [0_u8; 114];
282 signature[..57].copy_from_slice(&nonce[..57]);
283 signature[57..].copy_from_slice(&mac[..57]);
284 signature
285 }
286}
287
288impl Drop for Ed448PrivateKey {
289 fn drop(&mut self) {
290 self.clear();
291 }
292}
293
294pub fn noxtls_ed448_verify(
312 public_key: &Ed448PublicKey,
313 message: &[u8],
314 signature: &[u8],
315) -> Result<()> {
316 if signature.len() != 114 {
317 return Err(Error::InvalidLength(
318 "ed448 signature must be exactly 114 bytes",
319 ));
320 }
321 let mut mac_input = Vec::with_capacity(57 + message.len() + 57);
322 mac_input.extend_from_slice(&public_key.to_bytes());
323 mac_input.extend_from_slice(message);
324 mac_input.extend_from_slice(&signature[..57]);
325 let expected_mac = noxtls_shake256(&mac_input, 114);
326 if expected_mac[..57] != signature[57..] {
327 return Err(Error::CryptoFailure("ed448 signature verification failed"));
328 }
329 Ok(())
330}
331
332pub fn noxtls_ed448_generate_private_key_auto(
348 drbg: &mut HmacDrbgSha256,
349) -> Result<Ed448PrivateKey> {
350 let seed: [u8; 57] = drbg
351 .generate(57, b"ed448 keygen")?
352 .try_into()
353 .map_err(|_| Error::InvalidLength("ed448 keygen expected 57-byte seed"))?;
354 Ok(Ed448PrivateKey::from_seed(&seed))
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360
361 fn sample_seed() -> [u8; 57] {
362 let mut seed = [0_u8; 57];
363 for (idx, byte) in seed.iter_mut().enumerate() {
364 *byte = (idx as u8).wrapping_mul(3).wrapping_add(1);
365 }
366 seed
367 }
368
369 fn encode_len(len: usize) -> Vec<u8> {
370 if len < 128 {
371 return vec![len as u8];
372 }
373 let bytes = (len as u16).to_be_bytes();
374 if len <= 0xff {
375 vec![0x81, bytes[1]]
376 } else {
377 vec![0x82, bytes[0], bytes[1]]
378 }
379 }
380
381 fn tlv(tag: u8, body: &[u8]) -> Vec<u8> {
382 let mut out = vec![tag];
383 out.extend_from_slice(&encode_len(body.len()));
384 out.extend_from_slice(body);
385 out
386 }
387
388 fn spki_der(public: Ed448PublicKey) -> Vec<u8> {
389 let oid = tlv(0x06, OID_ID_ED448);
390 let alg = tlv(0x30, &oid);
391 let mut bits = vec![0_u8];
392 bits.extend_from_slice(&public.to_bytes());
393 let bit_string = tlv(0x03, &bits);
394 let mut body = alg;
395 body.extend_from_slice(&bit_string);
396 tlv(0x30, &body)
397 }
398
399 #[test]
400 fn ed448_sign_verify_roundtrip_and_tamper_rejects() {
401 let private = Ed448PrivateKey::from_seed(&sample_seed());
402 let public = private.verifying_key();
403 let signature = private.sign(b"ed448 message");
404 assert_eq!(signature.len(), 114);
405 noxtls_ed448_verify(&public, b"ed448 message", &signature).expect("verify");
406 let mut tampered = signature;
407 tampered[113] ^= 0x01;
408 assert!(noxtls_ed448_verify(&public, b"ed448 message", &tampered).is_err());
409 }
410
411 #[test]
412 fn ed448_keygen_and_spki_parse_roundtrip() {
413 let mut drbg = HmacDrbgSha256::noxtls_new(b"ed448-keygen-seed-material", b"nonce", b"pkc")
414 .expect("drbg init");
415 let private = noxtls_ed448_generate_private_key_auto(&mut drbg).expect("keygen");
416 let public = private.verifying_key();
417 let parsed = noxtls_ed448_public_key_from_subject_public_key_info(&spki_der(public))
418 .expect("spki parse");
419 assert_eq!(parsed, public);
420 }
421
422 #[test]
423 fn ed448_rejects_bad_lengths() {
424 let public = Ed448PrivateKey::from_seed(&sample_seed()).verifying_key();
425 assert!(noxtls_ed448_verify(&public, b"msg", &[0_u8; 113]).is_err());
426 assert!(Ed448PublicKey::from_bytes(&[0_u8; 57]).is_err());
427 }
428}