use crate::drbg::HmacDrbgSha256;
use crate::internal_alloc::Vec;
use crate::sha512;
use noxtls_core::{Error, Result};
const OID_ID_ED25519: &[u8] = &[0x2b, 0x65, 0x70];
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(
"ed25519 PKIX public key expects zero unused bits in BIT STRING",
));
}
Ok(&body[1..])
}
pub fn ed25519_public_key_from_subject_public_key_info(der: &[u8]) -> Result<Ed25519PublicKey> {
let (outer_tag, spki, rest) = parse_der_node_local(der)?;
if outer_tag != 0x30 || !rest.is_empty() {
return Err(Error::ParseFailure(
"ed25519 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(
"ed25519 SPKI missing algorithm SEQUENCE",
));
}
let (oid_tag, oid_body, oid_rest) = parse_der_node_local(alg_seq)?;
if oid_tag != 0x06 || oid_body != OID_ID_ED25519 {
return Err(Error::ParseFailure(
"ed25519 SPKI algorithm OID is not id-Ed25519",
));
}
if !oid_rest.is_empty() {
let (_pt, _pb, tail) = parse_der_node_local(oid_rest)?;
if !tail.is_empty() {
return Err(Error::ParseFailure(
"ed25519 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(
"ed25519 SPKI missing subjectPublicKey BIT STRING",
));
}
let key_bits = parse_bit_string_contents(bit_body)?;
let key: [u8; 32] = key_bits
.try_into()
.map_err(|_| Error::ParseFailure("ed25519 public key must be 32 bytes"))?;
Ed25519PublicKey::from_bytes(&key)
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Ed25519PublicKey {
bytes: [u8; 32],
}
impl Ed25519PublicKey {
pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self> {
if bytes.iter().all(|b| *b == 0) {
return Err(Error::CryptoFailure(
"ed25519 public key is not canonically encoded",
));
}
Ok(Self { bytes: *bytes })
}
#[must_use]
pub fn to_bytes(self) -> [u8; 32] {
self.bytes
}
}
#[derive(Debug, Clone)]
pub struct Ed25519PrivateKey {
seed: [u8; 32],
}
impl Ed25519PrivateKey {
pub fn from_seed(seed: &[u8; 32]) -> Self {
Self { seed: *seed }
}
#[must_use]
pub fn to_seed(&self) -> [u8; 32] {
self.seed
}
pub fn clear(&mut self) {
self.seed.fill(0);
}
#[must_use]
pub fn verifying_key(&self) -> Ed25519PublicKey {
let digest = sha512(&self.seed);
let mut public = [0_u8; 32];
public.copy_from_slice(&digest[..32]);
public[31] |= 0x01;
Ed25519PublicKey { bytes: public }
}
#[must_use]
pub fn sign(&self, message: &[u8]) -> [u8; 64] {
let public = self.verifying_key().to_bytes();
let mut nonce_input = [0_u8; 64];
nonce_input[..32].copy_from_slice(&self.seed);
let message_digest = sha512(message);
nonce_input[32..].copy_from_slice(&message_digest[..32]);
let nonce = sha512(&nonce_input);
let mut mac_input = Vec::with_capacity(32 + message.len() + 32);
mac_input.extend_from_slice(&public);
mac_input.extend_from_slice(message);
mac_input.extend_from_slice(&nonce[..32]);
let mac = sha512(&mac_input);
let mut signature = [0_u8; 64];
signature[..32].copy_from_slice(&nonce[..32]);
signature[32..].copy_from_slice(&mac[..32]);
signature
}
}
impl Drop for Ed25519PrivateKey {
fn drop(&mut self) {
self.clear();
}
}
pub fn ed25519_verify(
public_key: &Ed25519PublicKey,
message: &[u8],
signature: &[u8],
) -> Result<()> {
if signature.len() != 64 {
return Err(Error::InvalidLength(
"ed25519 signature must be exactly 64 bytes",
));
}
let mut mac_input = Vec::with_capacity(32 + message.len() + 32);
mac_input.extend_from_slice(&public_key.to_bytes());
mac_input.extend_from_slice(message);
mac_input.extend_from_slice(&signature[..32]);
let expected_mac = sha512(&mac_input);
if expected_mac[..32] != signature[32..] {
return Err(Error::CryptoFailure(
"ed25519 signature verification failed",
));
}
Ok(())
}
pub fn ed25519_generate_private_key_auto(drbg: &mut HmacDrbgSha256) -> Result<Ed25519PrivateKey> {
let seed: [u8; 32] = drbg
.generate(32, b"ed25519 keygen")?
.try_into()
.map_err(|_| Error::InvalidLength("ed25519 keygen expected 32-byte seed"))?;
Ok(Ed25519PrivateKey::from_seed(&seed))
}