{-# LANGUAGE OverloadedStrings #-}
-- Test case from https://code.tvl.fyi/commit/tvix/nix-compat/src/narinfo/fingerprint.rs?id=a834966efd64c1b2306241c3ef20f4258f6b9c4e

module Fingerprint where

import Crypto.Error (CryptoFailable(..))
import Data.Default.Class
import System.Nix.Base (decodeWith, BaseEncoding(Base64))
import System.Nix.Fingerprint
import System.Nix.Signature
import System.Nix.StorePath
import System.Nix.StorePath.Metadata
import System.Nix.Hash (mkNamedDigest)
import Data.Text (Text)
import Data.Time.Clock (UTCTime(..))
import Data.Time.Calendar.OrdinalDate (fromOrdinalDate)
import Test.Hspec

import qualified Crypto.PubKey.Ed25519 as Ed25519
import qualified Data.HashSet as HashSet
import qualified Data.Set as Set
import qualified Data.Text.Encoding as Text

spec_fingerprint :: Spec
spec_fingerprint = do

  describe "fingerprint" $ do

    it "is valid for example metadata" $
      metadataFingerprint def exampleStorePath exampleMetadata `shouldBe` exampleFingerprint

    it "allows a successful signature verification" $ do
      let msg = Text.encodeUtf8 $ metadataFingerprint def exampleStorePath exampleMetadata
          Signature sig' =
            case
              sig
              <$> filter (\(NarSignature publicKey _) -> publicKey == "cache.nixos.org-1")
              (Set.toList (metadataSigs exampleMetadata))
            of
              (x:_) -> x
              _ -> error "impossible"
      sig' `shouldSatisfy` Ed25519.verify pubkey msg

exampleFingerprint :: Text
exampleFingerprint = "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";

exampleStorePath :: StorePath
exampleStorePath = forceRight $ parsePath def "/nix/store/syd87l2rxw8cbsxmxl853h0r6pdwhwjr-curl-7.82.0-bin"

exampleMetadata :: Metadata StorePath
exampleMetadata = Metadata
  { metadataDeriverPath = Just $ forceRight $ parsePath def "/nix/store/5rwxzi7pal3qhpsyfc16gzkh939q1np6-curl-7.82.0.drv"
  , metadataNarHash = forceRight $ mkNamedDigest "sha256" "1b4sb93wp679q4zx9k1ignby1yna3z7c4c2ri3wphylbc2dwsys0"
  , metadataReferences = HashSet.fromList $ forceRight . parsePath def <$> ["/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"]
  , metadataRegistrationTime = UTCTime (fromOrdinalDate 0 0) 0
  , metadataNarBytes = Just 196040
  , metadataTrust = BuiltElsewhere
  , metadataSigs = Set.fromList $ forceRight . parseNarSignature <$> ["cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", "test1:519iiVLx/c4Rdt5DNt6Y2Jm6hcWE9+XY69ygiWSZCNGVcmOcyL64uVAJ3cV8vaTusIZdbTnYo9Y7vDNeTmmMBQ=="]
  , metadataContentAddress = Nothing
  }

pubkey :: Ed25519.PublicKey
pubkey = forceDecodeB64Pubkey "6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="

forceDecodeB64Pubkey :: Text -> Ed25519.PublicKey
forceDecodeB64Pubkey b64EncodedPubkey = let
  decoded = forceRight $ decodeWith Base64 b64EncodedPubkey
  in case Ed25519.publicKey decoded of
    CryptoFailed err -> (error . show) err
    CryptoPassed x -> x

forceRight :: Either a b -> b
forceRight = \case
  Right x -> x
  _ -> error "forceRight failed"

