|
| 1 | +{-# LANGUAGE OverloadedStrings #-} |
| 2 | +-- Inspired by https://cl.tvl.fyi/c/depot/+/10081/1/tvix/nix-compat/src/narinfo/signature.rs |
| 3 | +-- and https://github.com/nix-community/go-nix/pull/93 |
| 4 | +-- by @flokli and @zimbatm |
| 5 | + |
| 6 | +module Signature where |
| 7 | + |
| 8 | +import qualified Data.ByteString as BS |
| 9 | +import Test.Hspec |
| 10 | +import Data.Text (Text) |
| 11 | +import qualified Crypto.PubKey.Ed25519 |
| 12 | +import qualified System.Nix.Base |
| 13 | +import System.Nix.Base (BaseEncoding(Base64)) |
| 14 | +import Crypto.Error (CryptoFailable(..)) |
| 15 | + |
| 16 | +import System.Nix.Signature |
| 17 | + |
| 18 | +spec_signature :: Spec |
| 19 | +spec_signature = do |
| 20 | + |
| 21 | + describe "signature parser" $ do |
| 22 | + |
| 23 | + it "parses names" $ do |
| 24 | + shouldParseName "cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" "cache.nixos.org-1" |
| 25 | + |
| 26 | + it "fails on invalid signatures" $ do |
| 27 | + shouldNotParse "" |
| 28 | + shouldNotParse "asdf" |
| 29 | + shouldNotParse "cache.nixos.org-1:" |
| 30 | + shouldNotParse ":TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" |
| 31 | + shouldNotParse "cache.nixos.org-1TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" |
| 32 | + shouldNotParse "cache.nixos.org-1:sTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" |
| 33 | + |
| 34 | + it "parses verifying signatures" $ do |
| 35 | + shouldVerify "cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" pubkeyNixosOrg fingerprint |
| 36 | + shouldVerify "cache.nixos.org-2:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" pubkeyNixosOrg fingerprint |
| 37 | + |
| 38 | + it "parses non-verifying signatures" $ do |
| 39 | + shouldNotVerify "cache.nixos.org-1:TsTTb000000000000000000000000ytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" pubkeyNixosOrg fingerprint |
| 40 | + |
| 41 | +fingerprint :: BS.ByteString |
| 42 | +fingerprint = "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"; |
| 43 | + |
| 44 | +forceDecodeB64Pubkey :: Text -> Crypto.PubKey.Ed25519.PublicKey |
| 45 | +forceDecodeB64Pubkey b64EncodedPubkey = let |
| 46 | + decoded = case System.Nix.Base.decodeWith Base64 b64EncodedPubkey of |
| 47 | + Left err -> error err |
| 48 | + Right x -> x |
| 49 | + in case Crypto.PubKey.Ed25519.publicKey decoded of |
| 50 | + CryptoFailed err -> (error . show) err |
| 51 | + CryptoPassed x -> x |
| 52 | + |
| 53 | +pubkeyNixosOrg :: Crypto.PubKey.Ed25519.PublicKey |
| 54 | +pubkeyNixosOrg = forceDecodeB64Pubkey "6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" |
| 55 | + |
| 56 | +shouldNotParse :: Text -> Expectation |
| 57 | +shouldNotParse encoded = case parseSignature encoded of |
| 58 | + Left _ -> pure () |
| 59 | + Right _ -> expectationFailure "should not have parsed" |
| 60 | + |
| 61 | +shouldParseName :: Text -> Text -> Expectation |
| 62 | +shouldParseName encoded name = case parseSignature encoded of |
| 63 | + Left err -> expectationFailure err |
| 64 | + Right narSig -> shouldBe name (publicKey narSig) |
| 65 | + |
| 66 | +shouldVerify :: Text -> Crypto.PubKey.Ed25519.PublicKey -> BS.ByteString -> Expectation |
| 67 | +shouldVerify encoded pubkey msg = case parseSignature encoded of |
| 68 | + Left err -> expectationFailure err |
| 69 | + Right narSig -> let |
| 70 | + (Signature sig') = sig narSig |
| 71 | + in sig' `shouldSatisfy` Crypto.PubKey.Ed25519.verify pubkey msg |
| 72 | + |
| 73 | +shouldNotVerify :: Text -> Crypto.PubKey.Ed25519.PublicKey -> BS.ByteString -> Expectation |
| 74 | +shouldNotVerify encoded pubkey msg = case parseSignature encoded of |
| 75 | + Left err -> expectationFailure err |
| 76 | + Right narSig -> let |
| 77 | + (Signature sig') = sig narSig |
| 78 | + in sig' `shouldNotSatisfy` Crypto.PubKey.Ed25519.verify pubkey msg |
0 commit comments