about summary refs log tree commit diff
path: root/tvix/nix-compat
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/nix-compat')
-rw-r--r--tvix/nix-compat/Cargo.toml2
-rw-r--r--tvix/nix-compat/src/narinfo/signature.rs54
2 files changed, 56 insertions, 0 deletions
diff --git a/tvix/nix-compat/Cargo.toml b/tvix/nix-compat/Cargo.toml
index db6c772019c8..115c1b739764 100644
--- a/tvix/nix-compat/Cargo.toml
+++ b/tvix/nix-compat/Cargo.toml
@@ -12,6 +12,8 @@ async = ["futures-util"]
 bitflags = "2.4.1"
 bstr = { version = "1.6.0", features = ["alloc", "unicode", "serde"] }
 data-encoding = "2.3.3"
+ed25519 = "2.2.3"
+ed25519-dalek = "2.1.0"
 futures-util = { version = "0.3.28", features = ["io"], optional = true }
 glob = "0.3.0"
 nom = "7.1.3"
diff --git a/tvix/nix-compat/src/narinfo/signature.rs b/tvix/nix-compat/src/narinfo/signature.rs
index 5a7481591ce5..a0e56f93488e 100644
--- a/tvix/nix-compat/src/narinfo/signature.rs
+++ b/tvix/nix-compat/src/narinfo/signature.rs
@@ -35,6 +35,13 @@ impl<'a> Signature<'a> {
     pub fn bytes(&self) -> &[u8; 64] {
         &self.bytes
     }
+
+    /// For a given fingerprint and ed25519 verifying key, ensure if the signature is valid.
+    pub fn verify(&self, fingerprint: &[u8], verifying_key: &ed25519_dalek::VerifyingKey) -> bool {
+        let signature = ed25519_dalek::Signature::from_bytes(self.bytes());
+
+        verifying_key.verify_strict(fingerprint, &signature).is_ok()
+    }
 }
 
 #[derive(Debug, thiserror::Error)]
@@ -52,3 +59,50 @@ impl Display for Signature<'_> {
         write!(w, "{}:{}", self.name, BASE64.encode(&self.bytes))
     }
 }
+
+#[cfg(test)]
+mod test {
+    use data_encoding::BASE64;
+    use ed25519_dalek::VerifyingKey;
+    use lazy_static::lazy_static;
+
+    use super::Signature;
+    use test_case::test_case;
+
+    const FINGERPRINT: &'static str = "1;/nix/store/syd87l2rxw8cbsxmxl853h0r6pdwhwjr-curl-7.82.0-bin;sha256:1b4sb93wp679q4zx9k1ignby1yna3z7c4c2ri3wphylbc2dwsys0;196040;/nix/store/0jqd0rlxzra1rs38rdxl43yh6rxchgc6-curl-7.82.0,/nix/store/6w8g7njm4mck5dmjxws0z1xnrxvl81xa-glibc-2.34-115,/nix/store/j5jxw3iy7bbz4a57fh9g2xm2gxmyal8h-zlib-1.2.12,/nix/store/yxvjs9drzsphm9pcf42a4byzj1kb9m7k-openssl-1.1.1n";
+
+    // The signing key labelled as `cache.nixos.org-1`,
+    lazy_static! {
+        static ref PUB_CACHE_NIXOS_ORG_1: VerifyingKey = ed25519_dalek::VerifyingKey::from_bytes(
+            BASE64
+                .decode(b"6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=")
+                .unwrap()[..]
+                .try_into()
+                .unwrap()
+        )
+        .unwrap();
+        static ref PUB_TEST_1: VerifyingKey = ed25519_dalek::VerifyingKey::from_bytes(
+            BASE64
+                .decode(b"tLAEn+EeaBUJYqEpTd2yeerr7Ic6+0vWe+aXL/vYUpE=")
+                .unwrap()[..]
+                .try_into()
+                .unwrap()
+        )
+        .unwrap();
+    }
+
+    #[test_case(&PUB_CACHE_NIXOS_ORG_1, &"cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", FINGERPRINT, true; "valid cache.nixos.org-1")]
+    #[test_case(&PUB_CACHE_NIXOS_ORG_1, &"cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", FINGERPRINT, true; "valid test1")]
+    #[test_case(&PUB_CACHE_NIXOS_ORG_1, &"cache.nixos.org-2:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", FINGERPRINT, true; "valid cache.nixos.org different name")]
+    #[test_case(&PUB_CACHE_NIXOS_ORG_1, &"cache.nixos.org-1:TsTTb000000000000000000000000ytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", FINGERPRINT, false; "fail invalid cache.nixos.org-1 signature")]
+    #[test_case(&PUB_CACHE_NIXOS_ORG_1, &"cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", &FINGERPRINT[0..5], false; "fail valid sig but wrong fp cache.nixos.org-1")]
+    fn verify_sigs(
+        verifying_key: &VerifyingKey,
+        sig_str: &'static str,
+        fp: &str,
+        expect_valid: bool,
+    ) {
+        let sig = Signature::parse(sig_str).expect("must parse");
+        assert_eq!(expect_valid, sig.verify(fp.as_bytes(), verifying_key));
+    }
+}