structured_pkix/ocsp/
nonce.rs1use super::{BasicResponse, end};
4use crate::error::{Error, ErrorCode, Located, Result};
5use crate::oid;
6use structured_asn1::memory::Work;
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, W: Work>(
76 extension: &crate::Extension<'a>,
77 execution: &W,
78 field: &'static str,
79) -> Result<&'a [u8]> {
80 let mut reader = extension.reader_scoped(execution).field(field)?;
81 let value = reader
82 .expect(structured_asn1::Tag::primitive(
83 structured_asn1::universal::OCTET_STRING,
84 ))
85 .field(field)?;
86 end(&reader, field)?;
87 let bytes = value.contents();
88 if bytes.is_empty() || bytes.len() > 128 {
91 return Err(Error::new(
92 ErrorCode::MalformedValue,
93 field,
94 extension.span().start,
95 ));
96 }
97 Ok(bytes)
98}
99
100pub(super) fn bind<L: Work, R: Work>(
101 request: Option<RequestNonce<'_>>,
102 response: Option<&[u8]>,
103 left: (&L, &'static str, u64),
104 right: (&R, &'static str, u64),
105) -> Result<NonceBinding> {
106 let (request, response) = match (request, response) {
107 (None, None) => return Ok(NonceBinding::NotRequested),
108 (None, Some(_)) => return Ok(NonceBinding::Unsolicited),
109 (Some(_), None) => return Ok(NonceBinding::Missing),
110 (Some(request), Some(response)) => (request.as_bytes(), response),
111 };
112 left.0
113 .charge_work(1)
114 .map_err(|reason| Error::resource(reason, left.1, left.2))?;
115 right
116 .0
117 .charge_work(1)
118 .map_err(|reason| Error::resource(reason, right.1, right.2))?;
119 if request.len() != response.len() {
120 return Ok(NonceBinding::Differs);
121 }
122 left.0
123 .charge_work(request.len() as u64)
124 .map_err(|reason| Error::resource(reason, left.1, left.2))?;
125 right
126 .0
127 .charge_work(response.len() as u64)
128 .map_err(|reason| Error::resource(reason, right.1, right.2))?;
129 Ok(if request == response {
130 NonceBinding::Matches
131 } else {
132 NonceBinding::Differs
133 })
134}
135
136impl<'a> BasicResponse<'a> {
137 pub fn nonce<W: Work>(&self, execution: &W) -> Result<Option<&'a [u8]>> {
141 match self.extensions.get(oid::OCSP_NONCE, execution)? {
142 Some(extension) => read(extension, execution, "ocspResponse.nonce").map(Some),
143 None => Ok(None),
144 }
145 }
146
147 pub fn bind_nonce<W: Work>(
151 &self,
152 request: Option<RequestNonce<'_>>,
153 execution: &W,
154 ) -> Result<NonceBinding> {
155 let source = self.algorithm.query_in(execution);
156 bind(
157 request,
158 self.nonce(execution)?,
159 (execution, "ocspResponse.nonce", self.span.start),
160 (&source, "ocspResponse.nonce", self.span.start),
161 )
162 }
163}