From e45c952703f073028f9e6629d7e2e51706464759 Mon Sep 17 00:00:00 2001 From: Victor Embacher Date: Wed, 20 Mar 2024 12:02:45 +0100 Subject: [PATCH] Refactored to use a single library for JSON canonicalization. Signed-off-by: Victor Embacher --- Cargo.toml | 1 - src/cosign/bundle.rs | 14 +++++------ src/registry/oci_caching_client.rs | 38 +++++++++++++++++------------- src/rekor/models/checkpoint.rs | 10 ++++---- src/rekor/models/log_entry.rs | 17 ++++++------- 5 files changed, 42 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a9de331c82..be1c652c49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,6 @@ ed25519-dalek = { version = "2.0.0-rc.2", features = ["pkcs8", "rand_core"] } elliptic-curve = { version = "0.13.5", features = ["arithmetic", "pem"] } lazy_static = "1.4.0" oci-distribution = { version = "0.10", default-features = false, optional = true } -olpc-cjson = "0.1" openidconnect = { version = "3.0", default-features = false, features = [ "reqwest", ], optional = true } diff --git a/src/cosign/bundle.rs b/src/cosign/bundle.rs index e484a64796..795e9bc7cb 100644 --- a/src/cosign/bundle.rs +++ b/src/cosign/bundle.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use olpc_cjson::CanonicalFormatter; +use json_syntax::Print; use serde::{Deserialize, Serialize}; use std::cmp::PartialEq; @@ -78,17 +78,15 @@ impl Bundle { bundle: &Bundle, rekor_pub_key: &CosignVerificationKey, ) -> Result<()> { - let mut buf = Vec::new(); - let mut ser = serde_json::Serializer::with_formatter(&mut buf, CanonicalFormatter::new()); - bundle.payload.serialize(&mut ser).map_err(|e| { - SigstoreError::UnexpectedError(format!( - "Cannot create canonical JSON representation of bundle: {e:?}" - )) + let mut body = json_syntax::to_value(&bundle.payload).map_err(|_| { + SigstoreError::UnexpectedError("failed to serialize with json_syntax".to_string()) })?; + body.canonicalize(); + let encoded = body.compact_print().to_string(); rekor_pub_key.verify_signature( Signature::Base64Encoded(bundle.signed_entry_timestamp.as_bytes()), - &buf, + encoded.as_bytes(), )?; Ok(()) } diff --git a/src/registry/oci_caching_client.rs b/src/registry/oci_caching_client.rs index 035bd3ad93..93122fe722 100644 --- a/src/registry/oci_caching_client.rs +++ b/src/registry/oci_caching_client.rs @@ -18,7 +18,7 @@ use crate::errors::{Result, SigstoreError}; use async_trait::async_trait; use cached::proc_macro::cached; -use olpc_cjson::CanonicalFormatter; +use json_syntax::Print; use serde::Serialize; use sha2::{Digest, Sha256}; use tracing::{debug, error}; @@ -103,15 +103,18 @@ impl<'a> PullSettings<'a> { // Because of that the method will return the '0' value when something goes // wrong during the serialization operation. This is very unlikely to happen pub fn hash(&self) -> String { - let mut buf = Vec::new(); - let mut ser = serde_json::Serializer::with_formatter(&mut buf, CanonicalFormatter::new()); - if let Err(e) = self.serialize(&mut ser) { - error!(err=?e, settings=?self, "Cannot perform canonical serialization"); - return "0".to_string(); - } + let mut body = match json_syntax::to_value(self) { + Ok(body) => body, + Err(_e) => { + error!(err=?_e, settings=?self, "Cannot perform canonical serialization"); + return "0".to_string(); + } + }; + body.canonicalize(); + let encoded = body.compact_print().to_string(); let mut hasher = Sha256::new(); - hasher.update(&buf); + hasher.update(encoded.as_bytes()); let result = hasher.finalize(); result .iter() @@ -194,15 +197,18 @@ impl PullManifestSettings { // Because of that the method will return the '0' value when something goes // wrong during the serialization operation. This is very unlikely to happen pub fn hash(&self) -> String { - let mut buf = Vec::new(); - let mut ser = serde_json::Serializer::with_formatter(&mut buf, CanonicalFormatter::new()); - if let Err(e) = self.serialize(&mut ser) { - error!(err=?e, settings=?self, "Cannot perform canonical serialization"); - return "0".to_string(); - } + let mut body = match json_syntax::to_value(self) { + Ok(body) => body, + Err(_e) => { + error!(err=?_e, settings=?self, "Cannot perform canonical serialization"); + return "0".to_string(); + } + }; + body.canonicalize(); + let encoded = body.compact_print().to_string(); let mut hasher = Sha256::new(); - hasher.update(&buf); + hasher.update(encoded.as_bytes()); let result = hasher.finalize(); result .iter() @@ -243,7 +249,7 @@ async fn pull_manifest_cached( impl ClientCapabilitiesDeps for OciCachingClient {} #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(target_arch = "wasm32", async_trait(? Send))] impl ClientCapabilities for OciCachingClient { async fn fetch_manifest_digest( &mut self, diff --git a/src/rekor/models/checkpoint.rs b/src/rekor/models/checkpoint.rs index 5d57238871..9ae58280bd 100644 --- a/src/rekor/models/checkpoint.rs +++ b/src/rekor/models/checkpoint.rs @@ -7,6 +7,7 @@ use base64::prelude::BASE64_STANDARD; use base64::Engine; use digest::Output; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::Write; use std::fmt::{Display, Formatter}; use std::str::FromStr; @@ -90,11 +91,10 @@ impl CheckpointNote { // Output is the part of the checkpoint that is signed. fn marshal(&self) -> String { let hash_b64 = BASE64_STANDARD.encode(self.hash); - let other_content: String = self - .other_content - .iter() - .map(|c| format!("{c}\n")) - .collect(); + let other_content: String = self.other_content.iter().fold(String::new(), |mut acc, c| { + writeln!(acc, "{c}").expect("failed to write to string"); + acc + }); format!( "{}\n{}\n{hash_b64}\n{other_content}", self.origin, self.size diff --git a/src/rekor/models/log_entry.rs b/src/rekor/models/log_entry.rs index fcc8528d9b..13250b0623 100644 --- a/src/rekor/models/log_entry.rs +++ b/src/rekor/models/log_entry.rs @@ -21,7 +21,7 @@ use crate::crypto::CosignVerificationKey; use crate::errors::SigstoreError::UnexpectedError; use crate::rekor::models::checkpoint::Checkpoint; use crate::rekor::models::InclusionProof as InclusionProof2; -use olpc_cjson::CanonicalFormatter; +use json_syntax::Print; use serde::{Deserialize, Serialize}; use serde_json::{json, Error, Value}; use std::collections::HashMap; @@ -159,13 +159,14 @@ impl LogEntry { }) .and_then(|proof| { // encode as canonical JSON - let mut encoded_entry = Vec::new(); - let mut ser = serde_json::Serializer::with_formatter( - &mut encoded_entry, - CanonicalFormatter::new(), - ); - self.body.serialize(&mut ser)?; - proof.verify(&encoded_entry, rekor_key) + let mut body = json_syntax::to_value(&self.body).map_err(|_| { + SigstoreError::UnexpectedError( + "failed to serialize with json_syntax".to_string(), + ) + })?; + body.canonicalize(); + let encoded_entry = body.compact_print().to_string(); + proof.verify(encoded_entry.as_bytes(), rekor_key) }) } }