From 5fc737b02e5530131d4fbac620d38499be3fce80 Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Mon, 27 Nov 2023 17:43:10 +0200 Subject: feat(nix-compat/narinfo): add PubKey::verify(fp, sig) This makes it easy for each PubKey to check if a given Signature is correct for a given fingerprint. Change-Id: I56e6211d133f74f390fd1ae3ae799eef12221904 Reviewed-on: https://cl.tvl.fyi/c/depot/+/10151 Reviewed-by: tazjin Autosubmit: flokli Tested-by: BuildkiteCI --- tvix/nix-compat/src/narinfo/public_keys.rs | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'tvix/nix-compat/src/narinfo') diff --git a/tvix/nix-compat/src/narinfo/public_keys.rs b/tvix/nix-compat/src/narinfo/public_keys.rs index 90759110827e..dd82c8c22eda 100644 --- a/tvix/nix-compat/src/narinfo/public_keys.rs +++ b/tvix/nix-compat/src/narinfo/public_keys.rs @@ -6,6 +6,8 @@ use std::fmt::Display; use data_encoding::BASE64; use ed25519_dalek::{VerifyingKey, PUBLIC_KEY_LENGTH}; +use super::Signature; + /// This represents a ed25519 public key and "name". /// These are normally passed in the `trusted-public-keys` Nix config option, /// and consist of a name and base64-encoded ed25519 pubkey, separated by a `:`. @@ -60,6 +62,19 @@ impl PubKey { pub fn name(&self) -> &str { &self.name } + + /// Verify the passed in signature is a correct signature for the passed in fingerprint and is signed + /// by the key material referred to by [Self], + /// which means the name in the signature has to match, + /// and the signature bytes themselves need to be a valid signature made by + /// the signing key identified by [Self::verifying key]. + pub fn verify(&self, fingerprint: &str, signature: &Signature) -> bool { + if self.name() != signature.name() { + return false; + } + + return signature.verify(fingerprint.as_bytes(), &self.verifying_key); + } } #[derive(Debug, thiserror::Error)] @@ -93,7 +108,10 @@ mod test { use ed25519_dalek::PUBLIC_KEY_LENGTH; use test_case::test_case; + use crate::narinfo::Signature; + use super::PubKey; + 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"; #[test_case("cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=", "cache.nixos.org-1", BASE64.decode(b"6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=").unwrap()[..].try_into().unwrap(); "cache.nixos.org")] #[test_case("cheesecake:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=", "cheesecake", BASE64.decode(b"6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=").unwrap()[..].try_into().unwrap(); "cache.nixos.org different name")] @@ -114,4 +132,18 @@ mod test { fn parse_fail(input: &'static str) { PubKey::parse(input).expect_err("must fail"); } + + #[test_case("cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=", FINGERPRINT, "cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", true; "correct cache.nixos.org")] + #[test_case("cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=", FINGERPRINT, "cache.nixos.org:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", false; "wrong name mismatch")] + fn verify( + pubkey_str: &'static str, + fingerprint: &'static str, + signature_str: &'static str, + expected: bool, + ) { + let pubkey = PubKey::parse(pubkey_str).expect("must parse"); + let signature = Signature::parse(signature_str).expect("must parse"); + + assert_eq!(expected, pubkey.verify(fingerprint, &signature)); + } } -- cgit 1.4.1