structured_pkix/ocsp/
nonce.rs1use super::{BasicResponse, end};
4use crate::error::{Error, ErrorCode, Located, Result};
5use crate::oid;
6use structured_asn1::memory::{Memory, MemoryError};
7
8#[derive(Debug, Clone, Copy)]
11pub struct RequestNonce<'a>(&'a [u8]);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct NonceSizeError {
16 pub length: usize,
18}
19impl core::fmt::Display for NonceSizeError {
20 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21 write!(formatter, "nonce length {} is outside 1..128", self.length)
22 }
23}
24#[cfg(feature = "std")]
25impl std::error::Error for NonceSizeError {}
26
27impl<'a> RequestNonce<'a> {
28 pub fn from_sent(bytes: &'a [u8]) -> core::result::Result<Self, NonceSizeError> {
34 if bytes.is_empty() || bytes.len() > 128 {
35 return Err(NonceSizeError {
36 length: bytes.len(),
37 });
38 }
39 Ok(Self(bytes))
40 }
41 #[must_use]
46 pub fn new(bytes: &'a [u8]) -> Option<Self> {
47 if bytes.len() < 32 || bytes.len() > 128 {
48 return None;
49 }
50 Some(Self(bytes))
51 }
52
53 #[must_use]
55 pub fn as_bytes(self) -> &'a [u8] {
56 self.0
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum NonceBinding {
63 NotRequested,
65 Unsolicited,
67 Missing,
69 Matches,
71 Differs,
73}
74
75pub(super) fn read<'a>(extension: &crate::Extension<'a>, execution: &Memory) -> Result<&'a [u8]> {
76 const FIELD: &str = "ocspResponse.nonce";
77 let mut reader = extension.reader(execution).field(FIELD)?;
78 let value = reader
79 .expect(structured_asn1::Tag::primitive(
80 structured_asn1::universal::OCTET_STRING,
81 ))
82 .field(FIELD)?;
83 end(&reader, FIELD)?;
84 let bytes = value.contents();
85 if bytes.is_empty() || bytes.len() > 128 {
88 return Err(Error::new(
89 ErrorCode::MalformedValue,
90 FIELD,
91 extension.span().start,
92 ));
93 }
94 Ok(bytes)
95}
96
97pub(super) fn bind(
98 request: Option<RequestNonce<'_>>,
99 response: Option<&[u8]>,
100 execution: &Memory,
101 at: u64,
102) -> Result<NonceBinding> {
103 let (request, response) = match (request, response) {
104 (None, None) => return Ok(NonceBinding::NotRequested),
105 (None, Some(_)) => return Ok(NonceBinding::Unsolicited),
106 (Some(_), None) => return Ok(NonceBinding::Missing),
107 (Some(request), Some(response)) => (request.as_bytes(), response),
108 };
109 let work = u64::try_from(request.len().max(response.len()))
110 .ok()
111 .and_then(|n| n.checked_mul(2))
112 .and_then(|n| n.checked_add(1))
113 .ok_or_else(|| Error::resource(MemoryError::WorkExceeded, "ocspResponse.nonce", at))?;
114 execution
115 .charge_work(work)
116 .map_err(|reason| Error::resource(reason, "ocspResponse.nonce", at))?;
117 Ok(if request == response {
118 NonceBinding::Matches
119 } else {
120 NonceBinding::Differs
121 })
122}
123
124impl<'a> BasicResponse<'a> {
125 pub fn nonce(&self, execution: &Memory) -> Result<Option<&'a [u8]>> {
129 match self.extensions.get(oid::OCSP_NONCE, execution)? {
130 Some(extension) => read(extension, execution).map(Some),
131 None => Ok(None),
132 }
133 }
134
135 pub fn bind_nonce(
139 &self,
140 request: Option<RequestNonce<'_>>,
141 execution: &Memory,
142 ) -> Result<NonceBinding> {
143 bind(request, self.nonce(execution)?, execution, self.span.start)
144 }
145}