|
2 | 2 |
|
3 | 3 | use http::HeaderMap; |
4 | 4 | use sha2::Digest as _; |
| 5 | +use std::fmt::{Display, Formatter}; |
5 | 6 |
|
6 | 7 | use crate::sha256_digest; |
7 | 8 |
|
@@ -38,6 +39,13 @@ pub struct Digest<'a> { |
38 | 39 | pub digest: &'a str, |
39 | 40 | } |
40 | 41 |
|
| 42 | +impl<'a> Display for Digest<'a> { |
| 43 | + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
| 44 | + let Digest { algorithm, digest } = self; |
| 45 | + write!(f, "{algorithm}:{digest}") |
| 46 | + } |
| 47 | +} |
| 48 | + |
41 | 49 | impl<'a> Digest<'a> { |
42 | 50 | /// Create a new digest from a str. This isn't using `FromStr` because we can't use lifetimes |
43 | 51 | /// properly when implementing the trait |
@@ -106,45 +114,53 @@ pub fn digest_header_value(headers: HeaderMap) -> Result<Option<String>> { |
106 | 114 | /// digest. If both digests are provided, but they use different algorithms, then the header digest |
107 | 115 | /// is returned after validation as according to the spec it is the "canonical" digest for the given |
108 | 116 | /// content. |
109 | | -pub fn validate_digest( |
| 117 | +pub(crate) fn validate_digest( |
110 | 118 | body: &[u8], |
111 | 119 | digest_header: Option<String>, |
112 | 120 | reference_digest: Option<&str>, |
113 | 121 | ) -> Result<String> { |
| 122 | + let digest_header = digest_header.as_ref().map(|s| Digest::new(s)).transpose()?; |
| 123 | + let reference_digest = reference_digest.map(|s| Digest::new(s)).transpose()?; |
114 | 124 | match (digest_header, reference_digest) { |
115 | 125 | // If both digests are equal, then just calculate once |
116 | 126 | (Some(digest), Some(reference)) if digest == reference => { |
117 | | - calculate_and_validate(body, &digest) |
| 127 | + calculate_and_validate(body, digest) |
118 | 128 | } |
119 | 129 | (Some(digest), Some(reference)) => { |
120 | 130 | calculate_and_validate(body, reference)?; |
121 | | - calculate_and_validate(body, &digest) |
| 131 | + calculate_and_validate(body, digest) |
122 | 132 | } |
123 | | - (Some(digest), None) => calculate_and_validate(body, &digest), |
| 133 | + (Some(digest), None) => calculate_and_validate(body, digest), |
124 | 134 | (None, Some(reference)) => calculate_and_validate(body, reference), |
125 | 135 | // If we have neither, just digest the body |
126 | 136 | (None, None) => Ok(sha256_digest(body)), |
127 | 137 | } |
128 | 138 | } |
129 | 139 |
|
130 | 140 | /// Helper for calculating and validating the digest of the given content |
131 | | -fn calculate_and_validate(content: &[u8], digest: &str) -> Result<String> { |
132 | | - let parsed_digest = Digest::new(digest)?; |
| 141 | +fn calculate_and_validate(content: &[u8], parsed_digest: Digest) -> Result<String> { |
133 | 142 | let digest_calculated = match parsed_digest.algorithm { |
134 | 143 | "sha256" => format!("{:x}", sha2::Sha256::digest(content)), |
135 | 144 | "sha384" => format!("{:x}", sha2::Sha384::digest(content)), |
136 | 145 | "sha512" => format!("{:x}", sha2::Sha512::digest(content)), |
137 | 146 | other => return Err(DigestError::UnsupportedAlgorithm(other.to_string())), |
138 | 147 | }; |
139 | | - let hex = format!("{}:{digest_calculated}", parsed_digest.algorithm); |
| 148 | + let hex = Digest { |
| 149 | + algorithm: parsed_digest.algorithm, |
| 150 | + digest: &digest_calculated, |
| 151 | + }; |
140 | 152 | tracing::debug!(%hex, "Computed digest of payload"); |
141 | | - if hex != digest { |
| 153 | + if hex != parsed_digest { |
142 | 154 | return Err(DigestError::VerificationError { |
143 | | - expected: digest.to_owned(), |
144 | | - actual: hex, |
| 155 | + expected: parsed_digest.to_string(), |
| 156 | + actual: Digest { |
| 157 | + algorithm: parsed_digest.algorithm, |
| 158 | + digest: &digest_calculated, |
| 159 | + } |
| 160 | + .to_string(), |
145 | 161 | }); |
146 | 162 | } |
147 | | - Ok(hex) |
| 163 | + Ok(hex.to_string()) |
148 | 164 | } |
149 | 165 |
|
150 | 166 | #[cfg(test)] |
|
0 commit comments