Skip to content

Commit

Permalink
core: add signatureToText function and tests
Browse files Browse the repository at this point in the history
- make NarSignature's Show instance more legible
- add Arbitrary instance for NarSignature
- add roundtrip quickcheck tests for NarSignature encoding
  • Loading branch information
squalus committed Nov 24, 2023
1 parent ee10c59 commit 7850a38
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 17 deletions.
41 changes: 24 additions & 17 deletions hnix-store-core/src/System/Nix/Signature.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NamedFieldPuns #-}
{-|
Description : Nix-relevant interfaces to NaCl signatures.
-}
Expand All @@ -8,22 +9,23 @@ module System.Nix.Signature
, NarSignature(..)
, signatureParser
, parseSignature
, signatureToText
) where

import Crypto.Error (CryptoFailable(..))
import Data.ByteString (ByteString)
import Data.Text (Text)
import GHC.Generics (Generic)
import System.Nix.Base (decodeWith, encodeWith, BaseEncoding(Base64))

import qualified Crypto.PubKey.Ed25519
import Crypto.Error (CryptoFailable(..))
import qualified Crypto.PubKey.Ed25519 as Ed25519
import qualified Data.Attoparsec.Text
import qualified Data.Char
import qualified Data.ByteArray
import qualified Data.ByteString as BS
import Data.Text (Text)
import qualified System.Nix.Base
import System.Nix.Base (BaseEncoding(Base64))
import qualified Data.Char
import qualified Data.Text

-- | An ed25519 signature.
newtype Signature = Signature Crypto.PubKey.Ed25519.Signature
newtype Signature = Signature Ed25519.Signature
deriving (Eq, Generic, Show)

-- | A detached signature attesting to a nix archive's validity.
Expand All @@ -33,29 +35,34 @@ data NarSignature = NarSignature
, -- | The archive's signature.
sig :: !Signature
}
deriving (Eq, Generic, Ord, Show)
deriving (Eq, Generic, Ord)

instance Ord Signature where
compare (Signature x) (Signature y) = let
xBS = Data.ByteArray.convert x :: BS.ByteString
yBS = Data.ByteArray.convert y :: BS.ByteString
xBS = Data.ByteArray.convert x :: ByteString
yBS = Data.ByteArray.convert y :: ByteString
in compare xBS yBS

signatureParser :: Data.Attoparsec.Text.Parser NarSignature
signatureParser = do
publicKey <- Data.Attoparsec.Text.takeWhile1 (/= ':')
_ <- Data.Attoparsec.Text.string ":"
encodedSig <- Data.Attoparsec.Text.takeWhile1 (\c -> Data.Char.isAlphaNum c || c == '+' || c == '/' || c == '=')
decodedSig <- case System.Nix.Base.decodeWith Base64 encodedSig of
decodedSig <- case decodeWith Base64 encodedSig of
Left e -> fail e
Right decodedSig -> pure decodedSig
sig <- case Crypto.PubKey.Ed25519.signature decodedSig of
sig <- case Ed25519.signature decodedSig of
CryptoFailed e -> (fail . show) e
CryptoPassed sig -> pure sig
pure $ NarSignature publicKey (Signature sig)

parseSignature
:: Text -> Either String NarSignature
parseSignature =
Data.Attoparsec.Text.parseOnly signatureParser
parseSignature :: Text -> Either String NarSignature
parseSignature = Data.Attoparsec.Text.parseOnly signatureParser

signatureToText :: NarSignature -> Text
signatureToText NarSignature {publicKey, sig=Signature sig'} = let
b64Encoded = encodeWith Base64 (Data.ByteArray.convert sig' :: ByteString)
in mconcat [ publicKey, ":", b64Encoded ]

instance Show NarSignature where
show narSig = Data.Text.unpack (signatureToText narSig)
2 changes: 2 additions & 0 deletions hnix-store-tests/hnix-store-tests.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ library
, System.Nix.Arbitrary.Derivation
, System.Nix.Arbitrary.DerivedPath
, System.Nix.Arbitrary.Hash
, System.Nix.Arbitrary.Signature
, System.Nix.Arbitrary.Store.Types
, System.Nix.Arbitrary.StorePath
, Test.Hspec.Nix
Expand All @@ -67,6 +68,7 @@ test-suite props
DerivationSpec
DerivedPathSpec
StorePathSpec
SignatureSpec
hs-source-dirs:
tests
build-tool-depends:
Expand Down
1 change: 1 addition & 0 deletions hnix-store-tests/src/System/Nix/Arbitrary.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import System.Nix.Arbitrary.ContentAddress ()
import System.Nix.Arbitrary.Derivation ()
import System.Nix.Arbitrary.DerivedPath ()
import System.Nix.Arbitrary.Hash ()
import System.Nix.Arbitrary.Signature ()
import System.Nix.Arbitrary.Store.Types ()
import System.Nix.Arbitrary.StorePath ()
36 changes: 36 additions & 0 deletions hnix-store-tests/src/System/Nix/Arbitrary/Signature.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- due to recent generic-arbitrary
{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
{-# OPTIONS_GHC -Wno-orphans #-}
{-# LANGUAGE OverloadedStrings #-}
module System.Nix.Arbitrary.Signature where

import qualified Crypto.PubKey.Ed25519
import Crypto.Random (drgNewTest, withDRG)
import qualified Data.ByteString as BS
import qualified Data.Text as Text
import Test.QuickCheck.Arbitrary.Generic (GenericArbitrary(..))
import Test.QuickCheck.Instances ()
import Test.QuickCheck

import System.Nix.Signature

instance Arbitrary Crypto.PubKey.Ed25519.Signature where
arbitrary = do
seeds <- (,,,,) <$> arbitraryBoundedRandom <*> arbitraryBoundedRandom <*> arbitraryBoundedRandom <*> arbitraryBoundedRandom <*> arbitraryBoundedRandom
let drg = drgNewTest seeds
(secretKey, _) = withDRG drg Crypto.PubKey.Ed25519.generateSecretKey
publicKey = Crypto.PubKey.Ed25519.toPublic secretKey
msg :: BS.ByteString = "msg"
pure $ Crypto.PubKey.Ed25519.sign secretKey publicKey msg

deriving via GenericArbitrary Signature
instance Arbitrary Signature

instance Arbitrary NarSignature where
arbitrary = do
name <- Text.pack . getPrintableString <$> suchThat arbitrary (\(PrintableString str) -> validName str)
NarSignature name <$> arbitrary

validName :: String -> Bool
validName txt = not (null txt) && not (elem ':' txt)

13 changes: 13 additions & 0 deletions hnix-store-tests/tests/SignatureSpec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module SignatureSpec where

import Test.Hspec (Spec, describe)
import Test.Hspec.Nix (roundtrips)
import Test.Hspec.QuickCheck (prop)

import System.Nix.Signature (signatureToText, parseSignature)
import System.Nix.Arbitrary ()

spec :: Spec
spec = do
describe "Signature" $ do
prop "roundtrips" $ roundtrips signatureToText parseSignature

0 comments on commit 7850a38

Please sign in to comment.