hnix-store-core-0.9.0.0: src/System/Nix/Signature.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NamedFieldPuns #-}
{-|
Description : Nix-relevant interfaces to NaCl signatures.
-}
module System.Nix.Signature
( Signature(..)
, signatureParser
, parseSignature
, signatureToText
, NamedSignature(..)
, narSignatureParser
, parseNamedSignature
, narSignatureToText
) where
import Crypto.Error (CryptoFailable(..))
import Data.Attoparsec.Text (Parser)
import Data.ByteString (ByteString)
import Data.Text (Text)
import GHC.Generics (Generic)
import System.Nix.Base (decodeWith, encodeWith, BaseEncoding(Base64))
import Crypto.PubKey.Ed25519 qualified as Ed25519
import Data.Attoparsec.Text qualified
import Data.ByteArray qualified
import Data.Char qualified
import Data.Text qualified
-- | An ed25519 signature.
newtype Signature = Signature Ed25519.Signature
deriving (Eq, Generic, Show)
signatureParser :: Parser Signature
signatureParser = do
encodedSig <-
Data.Attoparsec.Text.takeWhile1
(\c -> Data.Char.isAlphaNum c || c == '+' || c == '/' || c == '=')
decodedSig <- case decodeWith Base64 encodedSig of
Left e -> fail e
Right decodedSig -> pure decodedSig
sig <- case Ed25519.signature decodedSig of
CryptoFailed e -> (fail . show) e
CryptoPassed sig -> pure sig
pure $ Signature sig
parseSignature :: Text -> Either String Signature
parseSignature = Data.Attoparsec.Text.parseOnly signatureParser
signatureToText :: Signature -> Text
signatureToText (Signature sig) =
encodeWith Base64 (Data.ByteArray.convert sig :: ByteString)
-- | A detached signature attesting to a nix archive's validity.
data NamedSignature = NamedSignature
{ -- | The name of the public key used to sign the archive.
publicKey :: !Text
, -- | The archive's signature.
sig :: !Signature
}
deriving (Eq, Generic, Ord)
instance Ord Signature where
compare (Signature x) (Signature y) = let
xBS = Data.ByteArray.convert x :: ByteString
yBS = Data.ByteArray.convert y :: ByteString
in compare xBS yBS
narSignatureParser :: Parser NamedSignature
narSignatureParser = do
publicKey <- Data.Attoparsec.Text.takeWhile1 (/= ':')
_ <- Data.Attoparsec.Text.string ":"
sig <- signatureParser
pure $ NamedSignature {..}
parseNamedSignature :: Text -> Either String NamedSignature
parseNamedSignature = Data.Attoparsec.Text.parseOnly narSignatureParser
narSignatureToText :: NamedSignature -> Text
narSignatureToText NamedSignature {..} =
mconcat [ publicKey, ":", signatureToText sig ]
instance Show NamedSignature where
show narSig = Data.Text.unpack (narSignatureToText narSig)