From 4297e33d949d6789a84c95632077897cbecd45de Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Mon, 27 Nov 2023 18:20:10 +0200 Subject: feat(tvix/store/pathinfosvc): add signature verification Introduce an Option>, 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 Tested-by: BuildkiteCI Reviewed-by: tazjin --- tvix/store/src/pathinfoservice/nix_http.rs | 34 +++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'tvix') 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, directory_service: Arc, + + /// 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>, } 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) { + 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 -- cgit 1.4.1