about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRyan Lahfa <tvl@lahfa.xyz>2024-01-20T01·54+0100
committerclbot <clbot@tvl.fyi>2024-01-20T17·14+0000
commite98ea31bbd17e71566d56810fa9c1d08960461ad (patch)
tree178b2a33746fe565b1ff38697fcee47ebdee08ad
parenta27edf6405070a37b959572e20a0fa28a7a71a33 (diff)
fix(nix-compat): accept SRI hashes of invalid length r/7429
In cl/10468, we accepted SRI hashes of invalid padding while checking
their trailing bits.

In this commit, we accept SRI hashes of invalid padding and invalid length, as Nix does.

Real world example: `pkgs.javaPackages.openjfx11.deps`
<https://github.com/NixOS/nixpkgs/blob/849e4dc5ff0ae70fb7a0df19dbbf633e408c0f32/pkgs/development/compilers/openjdk/openjfx/11.nix#L71>
in nixpkgs.

Change-Id: I834437e7b94dab9fbb030163f7a2741f52bbf03a
Reviewed-on: https://cl.tvl.fyi/c/depot/+/10668
Autosubmit: raitobezarius <tvl@lahfa.xyz>
Tested-by: BuildkiteCI
Reviewed-by: flokli <flokli@flokli.de>
-rw-r--r--tvix/nix-compat/src/nixhash/mod.rs38
1 files changed, 36 insertions, 2 deletions
diff --git a/tvix/nix-compat/src/nixhash/mod.rs b/tvix/nix-compat/src/nixhash/mod.rs
index 91f47ecc6320..e148eb72ee9c 100644
--- a/tvix/nix-compat/src/nixhash/mod.rs
+++ b/tvix/nix-compat/src/nixhash/mod.rs
@@ -237,8 +237,17 @@ pub fn from_sri_str(s: &str) -> Result<NixHash> {
     // strip all padding characters.
     let encoded_digest = encoded_digest.trim_end_matches('=');
 
-    if encoded_digest.len() == BASE64_NOPAD.encode_len(algo.digest_length()) {
-        let digest = BASE64_NOPAD
+    // If we are using BASE64_NOPAD, we must also disable the trailing bit checking otherwise we
+    // are bound to get invalid length for our inputs.
+    // See the `weird_sha256` example below.
+    let mut spec = BASE64_NOPAD.specification();
+    spec.check_trailing_bits = false;
+    let encoder = spec
+        .encoding()
+        .expect("Tvix bug: failed to get the special base64 encoder for Nix SRI hashes");
+
+    if encoded_digest.len() == encoder.encode_len(algo.digest_length()) {
+        let digest = encoder
             .decode(encoded_digest.as_bytes())
             .map_err(Error::InvalidBase64Encoding)?;
 
@@ -493,4 +502,29 @@ mod tests {
         // not passing SRI, but hash algo out of band should fail
         nixhash::from_str(broken_base64, Some("sha256")).expect_err("must fail");
     }
+
+    /// As we decided to pass our hashes by trimming `=` completely,
+    /// we need to take into account hashes with padding requirements which
+    /// contains trailing bits which would be checked by `BASE64_NOPAD` and would
+    /// make the verification crash.
+    ///
+    /// This base64 has a trailing non-zero bit at bit 42.
+    #[test]
+    fn sha256_weird_base64() {
+        let weird_base64 = "syceJMUEknBDCHK8eGs6rUU3IQn+HnQfURfCrDxYPa9=";
+        let expected_digest =
+            hex!("b3271e24c5049270430872bc786b3aad45372109fe1e741f5117c2ac3c583daf");
+
+        let nix_hash = nixhash::from_str(&format!("sha256-{}", &weird_base64), Some("sha256"))
+            .expect("must succeed");
+        assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
+
+        // not passing hash algo out of band should succeed
+        let nix_hash =
+            nixhash::from_str(&format!("sha256-{}", &weird_base64), None).expect("must succeed");
+        assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
+
+        // not passing SRI, but hash algo out of band should fail
+        nixhash::from_str(weird_base64, Some("sha256")).expect_err("must fail");
+    }
 }