use super::same_oid;
use crate::error::{Error, ErrorCode, Located, Result};
use crate::path::WorkingKey;
use structured_asn1::memory::Work;
use structured_asn1::{Element, Oid, Tag, universal};
const FIELD: &str = "subjectPublicKey.ec";
const PARAMETERS: &str = "subjectPublicKeyInfo.algorithm.parameters";
#[derive(Debug, Clone, Copy)]
pub enum Curve<'a> {
Named(Oid<'a>),
Implicit,
Specified(Element<'a>),
}
#[derive(Debug, Clone, Copy)]
pub enum Point<'a> {
Uncompressed {
x: &'a [u8],
y: &'a [u8],
},
Compressed {
x: &'a [u8],
odd: bool,
},
}
impl Point<'_> {
#[must_use]
pub fn coordinate_len(self) -> usize {
match self {
Self::Uncompressed { x, .. } | Self::Compressed { x, .. } => x.len(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct PublicKey<'a> {
curve: Curve<'a>,
parameters: Element<'a>,
inherited: bool,
point: Point<'a>,
octets: &'a [u8],
offset: u64,
}
impl<'a> PublicKey<'a> {
#[must_use]
pub fn curve(self) -> Curve<'a> {
self.curve
}
#[must_use]
pub fn parameters(self) -> Element<'a> {
self.parameters
}
#[must_use]
pub fn is_inherited(self) -> bool {
self.inherited
}
#[must_use]
pub fn point(self) -> Point<'a> {
self.point
}
#[must_use]
pub fn as_bytes(self) -> &'a [u8] {
self.octets
}
#[must_use]
pub fn offset(self) -> u64 {
self.offset
}
}
pub fn read<'a, W: Work>(key: WorkingKey<'a>, execution: &W) -> Result<PublicKey<'a>> {
let info = key.key();
let own = info.algorithm();
if !same_oid(
own.algorithm(),
crate::oid::EC_PUBLIC_KEY,
execution,
FIELD,
own.offset(),
)? {
return Err(Error::new(ErrorCode::UnexpectedField, FIELD, own.offset()));
}
let (source, inherited) = match (key.parameter_source(), own.parameters_nested()) {
(Some(source), _) => (source, own.has_no_parameters()),
(None, Some(null)) => (null, false),
(None, None) => {
return Err(Error::new(
ErrorCode::MissingField,
PARAMETERS,
own.offset(),
));
}
};
let (reader, parameters) = source.open(execution, PARAMETERS)?;
let curve = if parameters.tag() == Tag::primitive(universal::OID) {
Curve::Named(reader.oid(¶meters).field(PARAMETERS)?)
} else if parameters.tag() == Tag::primitive(universal::NULL) {
reader.null(¶meters).field(PARAMETERS)?;
Curve::Implicit
} else if parameters.tag() == Tag::constructed(universal::SEQUENCE) {
Curve::Specified(parameters)
} else {
return Err(Error::new(
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
PARAMETERS,
parameters.span().start,
));
};
let octets = info.whole_octets(FIELD)?;
let offset = info.key_offset();
execution
.charge_work(1)
.map_err(|reason| Error::resource(reason, FIELD, offset))?;
let point = match octets {
[0x04, coordinates @ ..] if !coordinates.is_empty() && coordinates.len() % 2 == 0 => {
let (x, y) = coordinates.split_at(coordinates.len() / 2);
Point::Uncompressed { x, y }
}
[prefix @ (0x02 | 0x03), x @ ..] if !x.is_empty() => Point::Compressed {
x,
odd: *prefix == 0x03,
},
_ => return Err(Error::new(ErrorCode::MalformedValue, FIELD, offset)),
};
Ok(PublicKey {
curve,
parameters,
inherited,
point,
octets,
offset,
})
}