1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! The public key a certificate carries.
//!
//! The key stays as it was encoded: re-encoding it here would change what a
//! key identifier is computed over. [`rsa`] and [`ec`] read its structure in
//! place; whether a key is usable remains the algorithm registry's decision.
use crate::algorithm::{self, AlgorithmIdentifier};
use crate::error::{Error, ErrorCode, Located, Result};
use structured_asn1::memory::Work;
use structured_asn1::{BitString, Element, Oid, Reader, Tag, universal};
pub mod ec;
pub mod rsa;
/// A public key and the algorithm it belongs to.
#[derive(Debug, Clone, Copy)]
pub struct PublicKeyInfo<'a> {
algorithm: AlgorithmIdentifier<'a>,
key: BitString<'a>,
der: &'a [u8],
// Document offset of the first key octet, after the unused-bits octet.
key_offset: u64,
}
impl<'a> PublicKeyInfo<'a> {
/// Which algorithm this key belongs to.
#[must_use]
pub fn algorithm(self) -> AlgorithmIdentifier<'a> {
self.algorithm
}
/// The key itself.
#[must_use]
pub fn key(self) -> BitString<'a> {
self.key
}
/// The key bytes, where the key is a whole number of octets, which it is
/// for every algorithm a certificate carries.
#[must_use]
pub fn key_octets(self) -> Option<&'a [u8]> {
self.key.octets()
}
/// Where the first key octet sits in the whole document.
#[must_use]
pub fn key_offset(self) -> u64 {
self.key_offset
}
/// Whole key octets, or a located refusal of a padded bit string.
pub(crate) fn whole_octets(self, field: &'static str) -> Result<&'a [u8]> {
self.key_octets()
.ok_or_else(|| Error::new(ErrorCode::MalformedValue, field, self.key_offset))
}
/// The whole structure as it was encoded.
///
/// What a fingerprint over the key information is taken of. The key
/// identifier the profile describes is a digest of [`Self::key_octets`]
/// instead, and the two are different values over different bytes.
#[must_use]
pub fn as_bytes(self) -> &'a [u8] {
self.der
}
/// Reads SPKI from an element in the caller's reader, retaining its budget.
///
/// The structure follows RFC 5280 section 4.1.2.7:
/// <https://www.rfc-editor.org/rfc/rfc5280.html#section-4.1.2.7>.
/// Algorithm-specific restrictions belong to the cryptographic consumer.
/// No key bytes are copied or re-encoded, and no new parser budget is created.
///
/// # Errors
///
/// Reports malformed structure, trailing fields or an exhausted parent budget.
pub fn read_element(
parent: &Reader<'a>,
element: &Element<'a>,
field: &'static str,
) -> Result<PublicKeyInfo<'a>> {
let fail = |code| Error::new(code, field, element.span().start);
if element.tag() != Tag::constructed(universal::SEQUENCE) {
return Err(fail(ErrorCode::Encoding(
structured_asn1::ErrorCode::UnexpectedTag,
)));
}
let mut inside = parent.enter(element).field(field)?;
let algorithm_element = inside
.next_element()
.field(field)?
.ok_or_else(|| fail(ErrorCode::MissingField))?;
let algorithm = algorithm::read(&inside, &algorithm_element, field)?;
let key_element = inside
.expect(Tag::primitive(universal::BIT_STRING))
.field(field)?;
let key = inside.bit_string(&key_element).field(field)?;
if !inside.is_empty() {
return Err(fail(ErrorCode::TrailingData));
}
Ok(PublicKeyInfo {
algorithm,
key,
der: element.as_bytes(),
// A primitive BIT STRING: one unused-bits octet, then the bits.
key_offset: key_element.contents_span().start + 1,
})
}
}
/// Paid identity of two OIDs: the length check, then the octets compared.
pub(crate) fn same_oid<W: Work>(
left: Oid<'_>,
right: Oid<'_>,
execution: &W,
field: &'static str,
at: u64,
) -> Result<bool> {
let charge = |amount| {
execution
.charge_work(amount)
.map_err(|reason| Error::resource(reason, field, at))
};
charge(1)?;
if left.as_bytes().len() != right.as_bytes().len() {
return Ok(false);
}
charge(left.as_bytes().len() as u64)?;
Ok(left == right)
}
#[cfg(test)]
mod tests;