about summary refs log tree commit diff
path: root/tvix/store/src
diff options
context:
space:
mode:
authorFlorian Klink <flokli@flokli.de>2023-11-27T16·20+0200
committerflokli <flokli@flokli.de>2023-11-28T16·08+0000
commit4297e33d949d6789a84c95632077897cbecd45de (patch)
treeb79a887916ff8f6271155e2c63fe3f478a3829b3 /tvix/store/src
parent5fc737b02e5530131d4fbac620d38499be3fce80 (diff)
feat(tvix/store/pathinfosvc): add signature verification r/7085
Introduce an Option<Vec<narinfo::PubKey>>, configurable with a
`set_public_keys` method.

If set, this configures NixHTTPPathInfoService to validate signatures.

Change-Id: I157c5e13c41fc9bfd40b0655381fb4cf33900868
Reviewed-on: https://cl.tvl.fyi/c/depot/+/10152
Autosubmit: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
Diffstat (limited to 'tvix/store/src')
-rw-r--r--tvix/store/src/pathinfoservice/nix_http.rs34
1 files changed, 33 insertions, 1 deletions
diff --git a/tvix/store/src/pathinfoservice/nix_http.rs b/tvix/store/src/pathinfoservice/nix_http.rs
index 26e516a90d4f..08e01d2540f0 100644
--- a/tvix/store/src/pathinfoservice/nix_http.rs
+++ b/tvix/store/src/pathinfoservice/nix_http.rs
@@ -6,7 +6,10 @@ use std::{
 
 use data_encoding::BASE64;
 use futures::{Stream, TryStreamExt};
-use nix_compat::{narinfo::NarInfo, nixbase32};
+use nix_compat::{
+    narinfo::{self, NarInfo},
+    nixbase32,
+};
 use reqwest::StatusCode;
 use sha2::{digest::FixedOutput, Digest, Sha256};
 use tonic::async_trait;
@@ -41,6 +44,10 @@ pub struct NixHTTPPathInfoService {
 
     blob_service: Arc<dyn BlobService>,
     directory_service: Arc<dyn DirectoryService>,
+
+    /// An optional list of [narinfo::PubKey].
+    /// If set, the .narinfo files received need to have correct signature by at least one of these.
+    public_keys: Option<Vec<narinfo::PubKey>>,
 }
 
 impl NixHTTPPathInfoService {
@@ -54,8 +61,15 @@ impl NixHTTPPathInfoService {
             http_client: reqwest::Client::new(),
             blob_service,
             directory_service,
+
+            public_keys: None,
         }
     }
+
+    /// Configures [Self] to validate NARInfo fingerprints with the public keys passed.
+    pub fn set_public_keys(&mut self, public_keys: Vec<narinfo::PubKey>) {
+        self.public_keys = Some(public_keys);
+    }
 }
 
 #[async_trait]
@@ -109,6 +123,24 @@ impl PathInfoService for NixHTTPPathInfoService {
             )
         })?;
 
+        // if [self.public_keys] is set, ensure there's at least one valid signature.
+        if let Some(public_keys) = &self.public_keys {
+            let fingerprint = narinfo.fingerprint();
+
+            if !public_keys.iter().any(|pubkey| {
+                narinfo
+                    .signatures
+                    .iter()
+                    .any(|sig| pubkey.verify(&fingerprint, sig))
+            }) {
+                warn!("no valid signature found");
+                Err(io::Error::new(
+                    io::ErrorKind::InvalidData,
+                    "no valid signature found",
+                ))?;
+            }
+        }
+
         // Convert to a (sparse) PathInfo. We still need to populate the node field,
         // and for this we need to download the NAR file.
         // FUTUREWORK: Keep some database around mapping from narsha256 to