use aws_lc_rs::digest::{self, SHA256, SHA384};
use aws_lc_rs::signature::{
ECDSA_P256_SHA256_ASN1, ECDSA_P384_SHA384_ASN1, ED25519, RSA_PKCS1_2048_8192_SHA256,
RSA_PKCS1_2048_8192_SHA384, RsaPublicKeyComponents, UnparsedPublicKey,
};
use ciborium::Value;
use x509_cert::Certificate;
use x509_cert::certificate::Version;
use x509_cert::der::Decode;
use x509_cert::der::asn1::OctetString;
use x509_cert::der::oid::{AssociatedOid, ObjectIdentifier};
use x509_cert::ext::pkix::{BasicConstraints, ExtendedKeyUsage};
use crate::Passki;
use crate::registration::ParsedAttestation;
use crate::types::*;
const OID_FIDO_AAGUID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.45724.1.1.4");
const OID_ANDROID_KEY: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.11129.2.1.17");
const OID_SUBJECT_ALT_NAME: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.5.29.17");
const OID_TCG_KP_AIK: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.23.133.8.3");
enum PublicKey {
Ec { x: Vec<u8>, y: Vec<u8> },
Rsa { n: Vec<u8>, e: Vec<u8> },
}
impl Passki {
pub(crate) fn verify_attestation(
&self,
attestation_bytes: &[u8],
client_data_hash: &[u8],
) -> Result<ParsedAttestation> {
let (fmt, auth_data, att_stmt) = Self::split_attestation_object(attestation_bytes)?;
let parsed = self.parse_auth_data(&auth_data)?;
let fmt = fmt.ok_or_else(|| PasskiError::new("Missing fmt in attestation"))?;
match fmt.as_str() {
"none" => {}
"packed" => verify_packed(&att_stmt, &auth_data, &parsed, client_data_hash)?,
"fido-u2f" => verify_fido_u2f(&att_stmt, &auth_data, &parsed, client_data_hash)?,
"android-key" => verify_android_key(&att_stmt, &auth_data, &parsed, client_data_hash)?,
"tpm" => verify_tpm(&att_stmt, &auth_data, &parsed, client_data_hash)?,
other => {
return Err(Box::new(PasskiError::new(format!(
"Unsupported attestation format: {}",
other
))));
}
}
Ok(parsed)
}
}
fn verify_packed(
att_stmt: &Value,
auth_data: &[u8],
parsed: &ParsedAttestation,
client_data_hash: &[u8],
) -> Result<()> {
let map = att_map(att_stmt)?;
let alg = att_int(map, "alg")? as i32;
let sig = att_bytes(map, "sig")?;
let mut signed_data = auth_data.to_vec();
signed_data.extend_from_slice(client_data_hash);
match att_x5c(map)? {
Some(x5c) => {
let cert = parse_cert(&x5c[0])?;
verify_cert_signature(&cert, alg, &signed_data, sig)?;
check_cert_version_3(&cert)?;
check_not_ca(&cert)?;
check_aaguid_extension(&cert, &parsed.aaguid)?;
}
None => {
if alg != parsed.algorithm {
return Err(Box::new(PasskiError::new(
"packed self-attestation algorithm does not match credential key",
)));
}
Passki::verify_signature(&parsed.public_key, alg, &signed_data, sig)?;
}
}
Ok(())
}
fn verify_fido_u2f(
att_stmt: &Value,
auth_data: &[u8],
parsed: &ParsedAttestation,
client_data_hash: &[u8],
) -> Result<()> {
let map = att_map(att_stmt)?;
let sig = att_bytes(map, "sig")?;
let x5c =
att_x5c(map)?.ok_or_else(|| PasskiError::new("fido-u2f attestation is missing x5c"))?;
if x5c.len() != 1 {
return Err(Box::new(PasskiError::new(
"fido-u2f attestation must contain exactly one certificate",
)));
}
let cert = parse_cert(&x5c[0])?;
let (x, y) = match cose_public_key(&parsed.public_key)? {
PublicKey::Ec { x, y } => (x, y),
PublicKey::Rsa { .. } => {
return Err(Box::new(PasskiError::new(
"fido-u2f credential key must be an EC key",
)));
}
};
if x.len() != 32 || y.len() != 32 {
return Err(Box::new(PasskiError::new(
"fido-u2f requires 32-byte P-256 coordinates",
)));
}
let mut public_key_u2f = Vec::with_capacity(65);
public_key_u2f.push(0x04);
public_key_u2f.extend_from_slice(&x);
public_key_u2f.extend_from_slice(&y);
let mut verification_data = Vec::new();
verification_data.push(0x00);
verification_data.extend_from_slice(&auth_data[..32]);
verification_data.extend_from_slice(client_data_hash);
verification_data.extend_from_slice(&parsed.credential_id);
verification_data.extend_from_slice(&public_key_u2f);
verify_cert_signature(&cert, ALG_ES256, &verification_data, sig)
}
fn verify_android_key(
att_stmt: &Value,
auth_data: &[u8],
parsed: &ParsedAttestation,
client_data_hash: &[u8],
) -> Result<()> {
let map = att_map(att_stmt)?;
let alg = att_int(map, "alg")? as i32;
let sig = att_bytes(map, "sig")?;
let x5c =
att_x5c(map)?.ok_or_else(|| PasskiError::new("android-key attestation is missing x5c"))?;
let cert = parse_cert(&x5c[0])?;
let mut signed_data = auth_data.to_vec();
signed_data.extend_from_slice(client_data_hash);
verify_cert_signature(&cert, alg, &signed_data, sig)?;
if !cert_key_matches(&cert, &cose_public_key(&parsed.public_key)?)? {
return Err(Box::new(PasskiError::new(
"android-key certificate public key does not match credential key",
)));
}
let extension = cert_extension(&cert, &OID_ANDROID_KEY).ok_or_else(|| {
PasskiError::new(
"android-key attestation certificate is missing the key attestation extension",
)
})?;
verify_android_key_description(extension, client_data_hash)
}
fn verify_tpm(
att_stmt: &Value,
auth_data: &[u8],
parsed: &ParsedAttestation,
client_data_hash: &[u8],
) -> Result<()> {
let map = att_map(att_stmt)?;
if att_text(map, "ver")? != "2.0" {
return Err(Box::new(PasskiError::new("Unsupported TPM version")));
}
let alg = att_int(map, "alg")? as i32;
let sig = att_bytes(map, "sig")?;
let cert_info = att_bytes(map, "certInfo")?;
let pub_area = att_bytes(map, "pubArea")?;
let x5c = att_x5c(map)?.ok_or_else(|| PasskiError::new("tpm attestation is missing x5c"))?;
let (name_alg, tpm_key) = parse_tpmt_public(pub_area)?;
if !public_keys_match(&tpm_key, &cose_public_key(&parsed.public_key)?) {
return Err(Box::new(PasskiError::new(
"tpm pubArea key does not match credential key",
)));
}
let (magic, attest_type, extra_data, attested_name) = parse_tpms_attest(cert_info)?;
if magic != 0xff54_4347 {
return Err(Box::new(PasskiError::new("Invalid TPM_GENERATED_VALUE")));
}
if attest_type != 0x8017 {
return Err(Box::new(PasskiError::new(
"certInfo is not a TPM_ST_ATTEST_CERTIFY",
)));
}
let mut att_to_be_signed = auth_data.to_vec();
att_to_be_signed.extend_from_slice(client_data_hash);
if extra_data != digest_for_alg(alg, &att_to_be_signed)? {
return Err(Box::new(PasskiError::new("certInfo extraData mismatch")));
}
if attested_name != tpm_name(name_alg, pub_area)? {
return Err(Box::new(PasskiError::new(
"certInfo attested name mismatch",
)));
}
let cert = parse_cert(&x5c[0])?;
verify_cert_signature(&cert, alg, cert_info, sig)?;
check_cert_version_3(&cert)?;
if !cert.tbs_certificate.subject.0.is_empty() {
return Err(Box::new(PasskiError::new(
"tpm certificate subject must be empty",
)));
}
if cert_extension(&cert, &OID_SUBJECT_ALT_NAME).is_none() {
return Err(Box::new(PasskiError::new(
"tpm certificate is missing subjectAltName",
)));
}
check_eku_contains(&cert, &OID_TCG_KP_AIK)?;
check_not_ca(&cert)?;
check_aaguid_extension(&cert, &parsed.aaguid)?;
Ok(())
}
fn att_map(att_stmt: &Value) -> Result<&Vec<(Value, Value)>> {
att_stmt
.as_map()
.ok_or_else(|| PasskiError::new("attStmt is not a map").into())
}
fn att_bytes<'a>(map: &'a [(Value, Value)], key: &str) -> Result<&'a [u8]> {
map.iter()
.find(|(k, _)| k.as_text() == Some(key))
.and_then(|(_, v)| v.as_bytes())
.map(Vec::as_slice)
.ok_or_else(|| PasskiError::new(format!("Missing {} in attStmt", key)).into())
}
fn att_int(map: &[(Value, Value)], key: &str) -> Result<i64> {
map.iter()
.find(|(k, _)| k.as_text() == Some(key))
.and_then(|(_, v)| v.as_integer())
.and_then(|i| i.try_into().ok())
.ok_or_else(|| PasskiError::new(format!("Missing {} in attStmt", key)).into())
}
fn att_text<'a>(map: &'a [(Value, Value)], key: &str) -> Result<&'a str> {
map.iter()
.find(|(k, _)| k.as_text() == Some(key))
.and_then(|(_, v)| v.as_text())
.ok_or_else(|| PasskiError::new(format!("Missing {} in attStmt", key)).into())
}
fn att_x5c(map: &[(Value, Value)]) -> Result<Option<Vec<Vec<u8>>>> {
let array = match map.iter().find(|(k, _)| k.as_text() == Some("x5c")) {
Some((_, v)) => v
.as_array()
.ok_or_else(|| PasskiError::new("x5c is not an array"))?,
None => return Ok(None),
};
if array.is_empty() {
return Ok(None);
}
let chain = array
.iter()
.map(|v| {
v.as_bytes()
.cloned()
.ok_or_else(|| PasskiError::new("x5c entry is not a byte string").into())
})
.collect::<Result<Vec<Vec<u8>>>>()?;
Ok(Some(chain))
}
fn parse_cert(der: &[u8]) -> Result<Certificate> {
Certificate::from_der(der)
.map_err(|e| PasskiError::new(format!("Failed to parse certificate: {}", e)).into())
}
fn cert_extension<'a>(cert: &'a Certificate, oid: &ObjectIdentifier) -> Option<&'a [u8]> {
cert.tbs_certificate
.extensions
.as_ref()?
.iter()
.find(|ext| &ext.extn_id == oid)
.map(|ext| ext.extn_value.as_bytes())
}
fn verify_cert_signature(
cert: &Certificate,
alg: i32,
signed_data: &[u8],
signature: &[u8],
) -> Result<()> {
let key = cert
.tbs_certificate
.subject_public_key_info
.subject_public_key
.as_bytes()
.ok_or_else(|| PasskiError::new("Invalid certificate public key encoding"))?;
verify_with_key(alg, key, signed_data, signature)
}
fn verify_with_key(alg: i32, key: &[u8], signed_data: &[u8], signature: &[u8]) -> Result<()> {
match alg {
ALG_ES256 => UnparsedPublicKey::new(&ECDSA_P256_SHA256_ASN1, key)
.verify(signed_data, signature)
.map_err(|_| PasskiError::new("Attestation signature verification failed").into()),
ALG_ES384 => UnparsedPublicKey::new(&ECDSA_P384_SHA384_ASN1, key)
.verify(signed_data, signature)
.map_err(|_| PasskiError::new("Attestation signature verification failed").into()),
ALG_EDDSA => UnparsedPublicKey::new(&ED25519, key)
.verify(signed_data, signature)
.map_err(|_| PasskiError::new("Attestation signature verification failed").into()),
ALG_RS256 => {
let (n, e) = parse_rsa_public_key(key)?;
RsaPublicKeyComponents { n: &n, e: &e }
.verify(&RSA_PKCS1_2048_8192_SHA256, signed_data, signature)
.map_err(|_| PasskiError::new("Attestation signature verification failed").into())
}
ALG_RS384 => {
let (n, e) = parse_rsa_public_key(key)?;
RsaPublicKeyComponents { n: &n, e: &e }
.verify(&RSA_PKCS1_2048_8192_SHA384, signed_data, signature)
.map_err(|_| PasskiError::new("Attestation signature verification failed").into())
}
_ => Err(Box::new(PasskiError::new(format!(
"Unsupported attestation algorithm: {}",
alg
)))),
}
}
fn check_cert_version_3(cert: &Certificate) -> Result<()> {
if cert.tbs_certificate.version != Version::V3 {
return Err(Box::new(PasskiError::new(
"Attestation certificate is not version 3",
)));
}
Ok(())
}
fn check_not_ca(cert: &Certificate) -> Result<()> {
if let Some(value) = cert_extension(cert, &BasicConstraints::OID) {
let bc = BasicConstraints::from_der(value)
.map_err(|e| PasskiError::new(format!("Invalid Basic Constraints: {}", e)))?;
if bc.ca {
return Err(Box::new(PasskiError::new(
"Attestation certificate must not be a CA",
)));
}
}
Ok(())
}
fn check_eku_contains(cert: &Certificate, oid: &ObjectIdentifier) -> Result<()> {
let value = cert_extension(cert, &ExtendedKeyUsage::OID)
.ok_or_else(|| PasskiError::new("Attestation certificate is missing extended key usage"))?;
let eku = ExtendedKeyUsage::from_der(value)
.map_err(|e| PasskiError::new(format!("Invalid extended key usage: {}", e)))?;
if !eku.0.contains(oid) {
return Err(Box::new(PasskiError::new(
"Attestation certificate has wrong extended key usage",
)));
}
Ok(())
}
fn check_aaguid_extension(cert: &Certificate, aaguid: &[u8; 16]) -> Result<()> {
if let Some(value) = cert_extension(cert, &OID_FIDO_AAGUID) {
let wrapped = OctetString::from_der(value)
.map_err(|e| PasskiError::new(format!("Invalid AAGUID extension: {}", e)))?;
if wrapped.as_bytes() != aaguid {
return Err(Box::new(PasskiError::new(
"Attestation certificate AAGUID does not match authenticator data",
)));
}
}
Ok(())
}
fn cert_key_matches(cert: &Certificate, key: &PublicKey) -> Result<bool> {
let cert_key = cert
.tbs_certificate
.subject_public_key_info
.subject_public_key
.as_bytes()
.ok_or_else(|| PasskiError::new("Invalid certificate public key encoding"))?;
Ok(match key {
PublicKey::Ec { x, y } => {
let mut point = Vec::with_capacity(1 + x.len() + y.len());
point.push(0x04);
point.extend_from_slice(x);
point.extend_from_slice(y);
cert_key == point
}
PublicKey::Rsa { n, e } => {
let (cn, ce) = parse_rsa_public_key(cert_key)?;
be_eq(&cn, n) && be_eq(&ce, e)
}
})
}
fn cose_public_key(cose_key_bytes: &[u8]) -> Result<PublicKey> {
let map = Passki::cose_parse(cose_key_bytes)?;
let kty = map
.iter()
.find(|(k, _)| k.as_integer() == Some(1.into()))
.and_then(|(_, v)| v.as_integer())
.and_then(|i| i.try_into().ok())
.ok_or_else(|| PasskiError::new("Missing kty in COSE key"))?;
match kty {
KTY_EC2 => Ok(PublicKey::Ec {
x: Passki::cose_field(&map, -2, "x coordinate")?.to_vec(),
y: Passki::cose_field(&map, -3, "y coordinate")?.to_vec(),
}),
KTY_RSA => Ok(PublicKey::Rsa {
n: Passki::cose_field(&map, -1, "n (modulus)")?.to_vec(),
e: Passki::cose_field(&map, -2, "e (exponent)")?.to_vec(),
}),
_ => Err(Box::new(PasskiError::new(
"Unsupported COSE key type for attestation key matching",
))),
}
}
fn public_keys_match(tpm_key: &PublicKey, cose_key: &PublicKey) -> bool {
match (tpm_key, cose_key) {
(PublicKey::Rsa { n, e }, PublicKey::Rsa { n: cn, e: ce }) => be_eq(n, cn) && be_eq(e, ce),
(PublicKey::Ec { x, y }, PublicKey::Ec { x: cx, y: cy }) => be_eq(x, cx) && be_eq(y, cy),
_ => false,
}
}
fn digest_for_alg(alg: i32, data: &[u8]) -> Result<Vec<u8>> {
let algorithm = match alg {
ALG_ES256 | ALG_RS256 => &SHA256,
ALG_ES384 | ALG_RS384 => &SHA384,
_ => {
return Err(Box::new(PasskiError::new(format!(
"Unsupported attestation algorithm: {}",
alg
))));
}
};
Ok(digest::digest(algorithm, data).as_ref().to_vec())
}
fn be_eq(a: &[u8], b: &[u8]) -> bool {
strip_leading_zeros(a) == strip_leading_zeros(b)
}
fn strip_leading_zeros(bytes: &[u8]) -> &[u8] {
let start = bytes.iter().take_while(|&&b| b == 0).count();
&bytes[start..]
}
fn parse_rsa_public_key(der: &[u8]) -> Result<(Vec<u8>, Vec<u8>)> {
let mut seq = DerReader::new(der).read_sequence()?;
let n = seq.read_integer()?;
let e = seq.read_integer()?;
Ok((
strip_leading_zeros(n).to_vec(),
strip_leading_zeros(e).to_vec(),
))
}
fn verify_android_key_description(extension: &[u8], client_data_hash: &[u8]) -> Result<()> {
let mut kd = DerReader::new(extension).read_sequence()?;
kd.skip()?; kd.skip()?; kd.skip()?; kd.skip()?; let challenge = kd.read_octet_string()?;
if challenge != client_data_hash {
return Err(Box::new(PasskiError::new(
"android-key attestation challenge does not match client data hash",
)));
}
kd.skip()?; let software_enforced = kd.read_sequence_bytes()?;
let tee_enforced = kd.read_sequence_bytes()?;
if authz_has_all_applications(software_enforced)? || authz_has_all_applications(tee_enforced)? {
return Err(Box::new(PasskiError::new(
"android-key attestation must not allow all applications",
)));
}
if !authz_origin_and_purpose_ok(tee_enforced)?
&& !authz_origin_and_purpose_ok(software_enforced)?
{
return Err(Box::new(PasskiError::new(
"android-key attestation has wrong key origin or purpose",
)));
}
Ok(())
}
const KM_TAG_ALL_APPLICATIONS: u32 = 600;
const KM_TAG_ORIGIN: u32 = 702;
const KM_TAG_PURPOSE: u32 = 1;
const KM_ORIGIN_GENERATED: i64 = 0;
const KM_PURPOSE_SIGN: i64 = 2;
fn authz_has_all_applications(list: &[u8]) -> Result<bool> {
let mut reader = DerReader::new(list);
while let Some((tag, _)) = reader.next_context_field()? {
if tag == KM_TAG_ALL_APPLICATIONS {
return Ok(true);
}
}
Ok(false)
}
fn authz_origin_and_purpose_ok(list: &[u8]) -> Result<bool> {
let mut origin_ok = false;
let mut purpose_ok = false;
let mut reader = DerReader::new(list);
while let Some((tag, value)) = reader.next_context_field()? {
if tag == KM_TAG_ORIGIN {
let origin = DerReader::new(value).read_integer_value()?;
origin_ok = origin == KM_ORIGIN_GENERATED;
} else if tag == KM_TAG_PURPOSE {
let mut set = DerReader::new(value).read_set()?;
while let Some(int) = set.next_integer_value()? {
if int == KM_PURPOSE_SIGN {
purpose_ok = true;
}
}
}
}
Ok(origin_ok && purpose_ok)
}
fn parse_tpmt_public(pub_area: &[u8]) -> Result<(u16, PublicKey)> {
let mut reader = BeReader::new(pub_area);
let key_type = reader.u16()?;
let name_alg = reader.u16()?;
reader.u32()?; reader.sized_u16()?;
let key = match key_type {
0x0001 => {
reader.u16()?; reader.u16()?; reader.u16()?; let exponent = reader.u32()?;
let modulus = reader.sized_u16()?;
let exponent = if exponent == 0 { 65537 } else { exponent };
PublicKey::Rsa {
n: modulus.to_vec(),
e: strip_leading_zeros(&exponent.to_be_bytes()).to_vec(),
}
}
0x0023 => {
reader.u16()?; reader.u16()?; reader.u16()?; reader.u16()?; let x = reader.sized_u16()?.to_vec();
let y = reader.sized_u16()?.to_vec();
PublicKey::Ec { x, y }
}
_ => {
return Err(Box::new(PasskiError::new("Unsupported TPM key type")));
}
};
Ok((name_alg, key))
}
fn parse_tpms_attest(cert_info: &[u8]) -> Result<(u32, u16, Vec<u8>, Vec<u8>)> {
let mut reader = BeReader::new(cert_info);
let magic = reader.u32()?;
let attest_type = reader.u16()?;
reader.sized_u16()?; let extra_data = reader.sized_u16()?.to_vec();
reader.take(17)?; reader.take(8)?; let name = reader.sized_u16()?.to_vec(); Ok((magic, attest_type, extra_data, name))
}
fn tpm_name(name_alg: u16, pub_area: &[u8]) -> Result<Vec<u8>> {
let algorithm = match name_alg {
0x000B => &SHA256,
0x000C => &SHA384,
_ => return Err(Box::new(PasskiError::new("Unsupported TPM name algorithm"))),
};
let mut name = name_alg.to_be_bytes().to_vec();
name.extend_from_slice(digest::digest(algorithm, pub_area).as_ref());
Ok(name)
}
struct BeReader<'a> {
bytes: &'a [u8],
pos: usize,
}
impl<'a> BeReader<'a> {
fn new(bytes: &'a [u8]) -> Self {
Self { bytes, pos: 0 }
}
fn take(&mut self, n: usize) -> Result<&'a [u8]> {
let end = self
.pos
.checked_add(n)
.filter(|&end| end <= self.bytes.len())
.ok_or_else(|| PasskiError::new("Truncated TPM structure"))?;
let slice = &self.bytes[self.pos..end];
self.pos = end;
Ok(slice)
}
fn u16(&mut self) -> Result<u16> {
let bytes = self.take(2)?;
Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
}
fn u32(&mut self) -> Result<u32> {
let bytes = self.take(4)?;
Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
fn sized_u16(&mut self) -> Result<&'a [u8]> {
let len = self.u16()? as usize;
self.take(len)
}
}
struct DerReader<'a> {
bytes: &'a [u8],
pos: usize,
}
impl<'a> DerReader<'a> {
fn new(bytes: &'a [u8]) -> Self {
Self { bytes, pos: 0 }
}
fn read_tlv(&mut self) -> Result<(u8, bool, u32, &'a [u8])> {
let first = *self
.bytes
.get(self.pos)
.ok_or_else(|| PasskiError::new("Truncated DER element"))?;
self.pos += 1;
let class = first & 0xC0;
let constructed = first & 0x20 != 0;
let mut tag_number = (first & 0x1F) as u32;
if tag_number == 0x1F {
tag_number = 0;
loop {
let byte = *self
.bytes
.get(self.pos)
.ok_or_else(|| PasskiError::new("Truncated DER tag"))?;
self.pos += 1;
tag_number = (tag_number << 7) | (byte & 0x7F) as u32;
if byte & 0x80 == 0 {
break;
}
}
}
let first_len = *self
.bytes
.get(self.pos)
.ok_or_else(|| PasskiError::new("Truncated DER length"))?;
self.pos += 1;
let len = if first_len & 0x80 == 0 {
first_len as usize
} else {
let count = (first_len & 0x7F) as usize;
let mut len = 0usize;
for _ in 0..count {
let byte = *self
.bytes
.get(self.pos)
.ok_or_else(|| PasskiError::new("Truncated DER length"))?;
self.pos += 1;
len = (len << 8) | byte as usize;
}
len
};
let end = self
.pos
.checked_add(len)
.filter(|&end| end <= self.bytes.len())
.ok_or_else(|| PasskiError::new("DER length exceeds buffer"))?;
let content = &self.bytes[self.pos..end];
self.pos = end;
Ok((class, constructed, tag_number, content))
}
fn read_sequence(&mut self) -> Result<DerReader<'a>> {
Ok(DerReader::new(self.read_sequence_bytes()?))
}
fn read_sequence_bytes(&mut self) -> Result<&'a [u8]> {
let (class, constructed, tag, content) = self.read_tlv()?;
if class != 0x00 || !constructed || tag != 0x10 {
return Err(Box::new(PasskiError::new("Expected DER SEQUENCE")));
}
Ok(content)
}
fn read_set(&mut self) -> Result<DerReader<'a>> {
let (class, constructed, tag, content) = self.read_tlv()?;
if class != 0x00 || !constructed || tag != 0x11 {
return Err(Box::new(PasskiError::new("Expected DER SET")));
}
Ok(DerReader::new(content))
}
fn read_integer(&mut self) -> Result<&'a [u8]> {
let (class, constructed, tag, content) = self.read_tlv()?;
if class != 0x00 || constructed || tag != 0x02 {
return Err(Box::new(PasskiError::new("Expected DER INTEGER")));
}
Ok(content)
}
fn read_integer_value(&mut self) -> Result<i64> {
let content = self.read_integer()?;
if content.is_empty() || content.len() > 8 {
return Err(Box::new(PasskiError::new("Unsupported DER INTEGER width")));
}
let mut value = if content[0] & 0x80 != 0 { -1i64 } else { 0i64 };
for &byte in content {
value = (value << 8) | byte as i64;
}
Ok(value)
}
fn read_octet_string(&mut self) -> Result<&'a [u8]> {
let (class, constructed, tag, content) = self.read_tlv()?;
if class != 0x00 || constructed || tag != 0x04 {
return Err(Box::new(PasskiError::new("Expected DER OCTET STRING")));
}
Ok(content)
}
fn skip(&mut self) -> Result<()> {
self.read_tlv()?;
Ok(())
}
fn next_context_field(&mut self) -> Result<Option<(u32, &'a [u8])>> {
while self.pos < self.bytes.len() {
let (class, _, tag, content) = self.read_tlv()?;
if class == 0x80 {
return Ok(Some((tag, content)));
}
}
Ok(None)
}
fn next_integer_value(&mut self) -> Result<Option<i64>> {
if self.pos >= self.bytes.len() {
return Ok(None);
}
Ok(Some(self.read_integer_value()?))
}
}