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}
24impl core::error::Error for NonceSizeError {}
25
26impl<'a> RequestNonce<'a> {
27 pub fn from_sent(bytes: &'a [u8]) -> core::result::Result<Self, NonceSizeError> {
33 if bytes.is_empty() || bytes.len() > 128 {
34 return Err(NonceSizeError {
35 length: bytes.len(),
36 });
37 }
38 Ok(Self(bytes))
39 }
40 #[must_use]
45 pub fn new(bytes: &'a [u8]) -> Option<Self> {
46 if bytes.len() < 32 || bytes.len() > 128 {
47 return None;
48 }
49 Some(Self(bytes))
50 }
51
52 #[must_use]
54 pub fn as_bytes(self) -> &'a [u8] {
55 self.0
56 }
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum NonceBinding {
62 NotRequested,
64 Unsolicited,
66 Missing,
68 Matches,
70 Differs,
72}
73
74pub(super) fn read<'a, W: Work>(
75 extension: &crate::Extension<'a>,
76 execution: &W,
77 field: &'static str,
78) -> Result<&'a [u8]> {
79 let mut reader = extension.reader_scoped(execution).field(field)?;
80 let value = reader
81 .expect(structured_asn1::Tag::primitive(
82 structured_asn1::universal::OCTET_STRING,
83 ))
84 .field(field)?;
85 end(&reader, field)?;
86 let bytes = value.contents();
87 if bytes.is_empty() || bytes.len() > 128 {
90 return Err(Error::new(
91 ErrorCode::MalformedValue,
92 field,
93 extension.span().start,
94 ));
95 }
96 Ok(bytes)
97}
98
99pub(super) fn bind<L: Work, R: Work>(
100 request: Option<RequestNonce<'_>>,
101 response: Option<&[u8]>,
102 left: (&L, &'static str, u64),
103 right: (&R, &'static str, u64),
104) -> Result<NonceBinding> {
105 let (request, response) = match (request, response) {
106 (None, None) => return Ok(NonceBinding::NotRequested),
107 (None, Some(_)) => return Ok(NonceBinding::Unsolicited),
108 (Some(_), None) => return Ok(NonceBinding::Missing),
109 (Some(request), Some(response)) => (request.as_bytes(), response),
110 };
111 left.0
112 .charge_work(1)
113 .map_err(|reason| Error::resource(reason, left.1, left.2))?;
114 right
115 .0
116 .charge_work(1)
117 .map_err(|reason| Error::resource(reason, right.1, right.2))?;
118 if request.len() != response.len() {
119 return Ok(NonceBinding::Differs);
120 }
121 left.0
122 .charge_work(request.len() as u64)
123 .map_err(|reason| Error::resource(reason, left.1, left.2))?;
124 right
125 .0
126 .charge_work(response.len() as u64)
127 .map_err(|reason| Error::resource(reason, right.1, right.2))?;
128 Ok(if request == response {
129 NonceBinding::Matches
130 } else {
131 NonceBinding::Differs
132 })
133}
134
135impl<'a> BasicResponse<'a> {
136 pub fn nonce<W: Work>(&self, execution: &W) -> Result<Option<&'a [u8]>> {
140 match self.extensions.get(oid::OCSP_NONCE, execution)? {
141 Some(extension) => read(extension, execution, "ocspResponse.nonce").map(Some),
142 None => Ok(None),
143 }
144 }
145
146 pub fn bind_nonce<W: Work>(
150 &self,
151 request: Option<RequestNonce<'_>>,
152 execution: &W,
153 ) -> Result<NonceBinding> {
154 let source = self.algorithm.query_in(execution);
155 bind(
156 request,
157 self.nonce(execution)?,
158 (execution, "ocspResponse.nonce", self.span.start),
159 (&source, "ocspResponse.nonce", self.span.start),
160 )
161 }
162}