use crate::codec::{parse_binding_request, TransactionId, BINDING_REQUEST};
use crate::credential::signature::StunSigner;
use crate::credential::wire::{
is_valid_signature_der_len, is_valid_spki_der, write_attr, write_header, CredentialError,
ATTR_DIG_IDENTITY, ATTR_DIG_SIGNATURE, ATTR_NONCE, CREDENTIAL_VERSION,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RequestKind<'a> {
Bare,
Identity {
spki: &'a [u8],
},
Signed {
spki: &'a [u8],
nonce: &'a [u8],
signature: &'a [u8],
},
}
pub fn classify_request(
datagram: &[u8],
) -> Result<(TransactionId, RequestKind<'_>), CredentialError> {
let txid = parse_binding_request(datagram)?;
let msg_len = u16::from_be_bytes([datagram[2], datagram[3]]) as usize;
let area = &datagram[20..20 + msg_len];
let kind = classify_attrs(area)?;
Ok((txid, kind))
}
fn classify_attrs(area: &[u8]) -> Result<RequestKind<'_>, CredentialError> {
let mut identity: Option<&[u8]> = None;
let mut nonce: Option<&[u8]> = None;
let mut signature: Option<&[u8]> = None;
let mut signature_seen = false;
let mut off = 0usize;
while off + 4 <= area.len() {
if signature_seen {
return Err(CredentialError::Malformed);
}
let attr_type = u16::from_be_bytes([area[off], area[off + 1]]);
let attr_len = u16::from_be_bytes([area[off + 2], area[off + 3]]) as usize;
let val_start = off + 4;
let val_end = val_start + attr_len;
if val_end > area.len() {
return Err(CredentialError::Malformed);
}
let value = &area[val_start..val_end];
match attr_type {
ATTR_DIG_IDENTITY => {
if identity.is_some() {
return Err(CredentialError::Malformed);
}
if value.len() != 1 + crate::credential::wire::P256_SPKI_LEN
|| value[0] != CREDENTIAL_VERSION
|| !is_valid_spki_der(&value[1..])
{
return Err(CredentialError::Malformed);
}
identity = Some(&value[1..]);
}
ATTR_NONCE => {
if nonce.is_some() {
return Err(CredentialError::Malformed);
}
nonce = Some(value);
}
ATTR_DIG_SIGNATURE => {
if value.is_empty()
|| value[0] != CREDENTIAL_VERSION
|| !is_valid_signature_der_len(&value[1..])
{
return Err(CredentialError::Malformed);
}
signature = Some(&value[1..]);
signature_seen = true;
}
_ => {} }
off = val_end + ((4 - (attr_len % 4)) % 4);
}
match (identity, nonce, signature) {
(None, None, None) => Ok(RequestKind::Bare),
(Some(spki), None, None) => Ok(RequestKind::Identity { spki }),
(Some(spki), Some(nonce), Some(signature)) => Ok(RequestKind::Signed {
spki,
nonce,
signature,
}),
_ => Err(CredentialError::Malformed), }
}
pub fn encode_identity_request(txid: &TransactionId, spki_der: &[u8]) -> Vec<u8> {
debug_assert!(
is_valid_spki_der(spki_der),
"encode_identity_request requires a valid 91-byte P-256 SPKI DER"
);
let mut identity_value = Vec::with_capacity(1 + spki_der.len());
identity_value.push(CREDENTIAL_VERSION);
identity_value.extend_from_slice(spki_der);
let mut attrs = Vec::new();
write_attr(&mut attrs, ATTR_DIG_IDENTITY, &identity_value);
let mut msg = Vec::with_capacity(20 + attrs.len());
write_header(&mut msg, BINDING_REQUEST, attrs.len() as u16, txid);
msg.extend_from_slice(&attrs);
msg
}
pub fn encode_signed_request(
txid: &TransactionId,
nonce_attr_value: &[u8],
signer: &dyn StunSigner,
) -> Vec<u8> {
let spki = signer.spki_der();
let message = crate::credential::signature::signing_message(txid, nonce_attr_value, spki);
let sig_der = signer.sign(&message);
let mut identity_value = Vec::with_capacity(1 + spki.len());
identity_value.push(CREDENTIAL_VERSION);
identity_value.extend_from_slice(spki);
let mut signature_value = Vec::with_capacity(1 + sig_der.len());
signature_value.push(CREDENTIAL_VERSION);
signature_value.extend_from_slice(&sig_der);
let mut attrs = Vec::new();
write_attr(&mut attrs, ATTR_DIG_IDENTITY, &identity_value);
write_attr(&mut attrs, ATTR_NONCE, nonce_attr_value);
write_attr(&mut attrs, ATTR_DIG_SIGNATURE, &signature_value);
let mut msg = Vec::with_capacity(20 + attrs.len());
write_header(&mut msg, BINDING_REQUEST, attrs.len() as u16, txid);
msg.extend_from_slice(&attrs);
msg
}