Add support for signatures
This commit is contained in:
parent
8e9c9198fe
commit
9e5e5f45f3
@ -146,4 +146,16 @@ void freePathInfo(struct PathInfo * const input)
|
|||||||
freeStrings(&input->sigs);
|
freeStrings(&input->sigs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This can be done in Haskell using the `ed25519` package
|
||||||
|
void signString
|
||||||
|
( char const * const secretKey
|
||||||
|
, char const * const message
|
||||||
|
, struct string * const output
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::string signature = SecretKey(secretKey).signDetached(message);
|
||||||
|
|
||||||
|
copyString(signature, output);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,6 +47,8 @@ executable nix-serve-ng
|
|||||||
cxx-options: -std=c++17
|
cxx-options: -std=c++17
|
||||||
|
|
||||||
build-depends: base < 5
|
build-depends: base < 5
|
||||||
|
, base16
|
||||||
|
, base32
|
||||||
, bsd-sysctl
|
, bsd-sysctl
|
||||||
, bytestring
|
, bytestring
|
||||||
, http-types
|
, http-types
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{ mkDerivation, base, bsd-sysctl, bytestring, http-types, lib
|
{ mkDerivation, base, base16, base32, bsd-sysctl, bytestring
|
||||||
, managed, megaparsec, mtl, network, nixstore, nixutil
|
, http-types, lib, managed, megaparsec, mtl, network, nixstore
|
||||||
, optparse-applicative, vector, wai, wai-extra, warp, warp-tls
|
, nixutil, optparse-applicative, vector, wai, wai-extra, warp
|
||||||
|
, warp-tls
|
||||||
}:
|
}:
|
||||||
mkDerivation {
|
mkDerivation {
|
||||||
pname = "nix-serve-ng";
|
pname = "nix-serve-ng";
|
||||||
@ -9,8 +10,9 @@ mkDerivation {
|
|||||||
isLibrary = false;
|
isLibrary = false;
|
||||||
isExecutable = true;
|
isExecutable = true;
|
||||||
executableHaskellDepends = [
|
executableHaskellDepends = [
|
||||||
base bsd-sysctl bytestring http-types managed megaparsec mtl
|
base base16 base32 bsd-sysctl bytestring http-types managed
|
||||||
network optparse-applicative vector wai wai-extra warp warp-tls
|
megaparsec mtl network optparse-applicative vector wai wai-extra
|
||||||
|
warp warp-tls
|
||||||
];
|
];
|
||||||
executableSystemDepends = [ nixstore nixutil ];
|
executableSystemDepends = [ nixstore nixutil ];
|
||||||
description = "A drop-in replacement for nix-serve that's faster and more stable";
|
description = "A drop-in replacement for nix-serve that's faster and more stable";
|
||||||
|
|||||||
41
src/Main.hs
41
src/Main.hs
@ -18,6 +18,7 @@ import Sysctl (_SO_MAX_CONN)
|
|||||||
import qualified Control.Monad as Monad
|
import qualified Control.Monad as Monad
|
||||||
import qualified Control.Monad.Except as Except
|
import qualified Control.Monad.Except as Except
|
||||||
import qualified Data.ByteString as ByteString
|
import qualified Data.ByteString as ByteString
|
||||||
|
import qualified Data.ByteString.Char8 as ByteString.Char8
|
||||||
import qualified Data.ByteString.Builder as Builder
|
import qualified Data.ByteString.Builder as Builder
|
||||||
import qualified Data.ByteString.Lazy as ByteString.Lazy
|
import qualified Data.ByteString.Lazy as ByteString.Lazy
|
||||||
import qualified Data.Vector as Vector
|
import qualified Data.Vector as Vector
|
||||||
@ -32,9 +33,16 @@ import qualified Nix
|
|||||||
import qualified Options
|
import qualified Options
|
||||||
import qualified Options.Applicative as Options
|
import qualified Options.Applicative as Options
|
||||||
import qualified System.BSD.Sysctl as Sysctl
|
import qualified System.BSD.Sysctl as Sysctl
|
||||||
|
import qualified System.Environment as Environment
|
||||||
|
|
||||||
makeApplication :: Integer -> ByteString -> Application
|
data ApplicationOptions = ApplicationOptions
|
||||||
makeApplication priority storeDirectory request respond = do
|
{ priority :: Integer
|
||||||
|
, storeDirectory :: ByteString
|
||||||
|
, secretKey :: Maybe ByteString
|
||||||
|
}
|
||||||
|
|
||||||
|
makeApplication :: ApplicationOptions -> Application
|
||||||
|
makeApplication ApplicationOptions{..} request respond = do
|
||||||
let stripStore = ByteString.stripPrefix (storeDirectory <> "/")
|
let stripStore = ByteString.stripPrefix (storeDirectory <> "/")
|
||||||
|
|
||||||
let done = Except.throwError
|
let done = Except.throwError
|
||||||
@ -76,7 +84,7 @@ makeApplication priority storeDirectory request respond = do
|
|||||||
Just storePath -> do
|
Just storePath -> do
|
||||||
return storePath
|
return storePath
|
||||||
|
|
||||||
PathInfo{..} <- liftIO (Nix.queryPathInfo storePath)
|
pathInfo@PathInfo{..} <- liftIO (Nix.queryPathInfo storePath)
|
||||||
|
|
||||||
narHash2 <- case ByteString.stripPrefix "sha256:" narHash of
|
narHash2 <- case ByteString.stripPrefix "sha256:" narHash of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
@ -115,6 +123,23 @@ makeApplication priority storeDirectory request respond = do
|
|||||||
Nothing ->
|
Nothing ->
|
||||||
return mempty
|
return mempty
|
||||||
|
|
||||||
|
fingerprint <- case Nix.fingerprintPath storePath pathInfo of
|
||||||
|
Nothing -> internalError "invalid NAR hash"
|
||||||
|
Just builder -> do
|
||||||
|
return (ByteString.Lazy.toStrict (Builder.toLazyByteString builder))
|
||||||
|
|
||||||
|
signatures <- case secretKey of
|
||||||
|
Just key -> do
|
||||||
|
signature <- liftIO (Nix.signString key fingerprint)
|
||||||
|
|
||||||
|
return (Vector.singleton signature)
|
||||||
|
|
||||||
|
Nothing -> do
|
||||||
|
return sigs
|
||||||
|
|
||||||
|
let buildSignature signature =
|
||||||
|
"Sig: " <> Builder.byteString signature <> "\n"
|
||||||
|
|
||||||
let builder =
|
let builder =
|
||||||
"StorePath: "
|
"StorePath: "
|
||||||
<> Builder.byteString storePath
|
<> Builder.byteString storePath
|
||||||
@ -129,6 +154,7 @@ makeApplication priority storeDirectory request respond = do
|
|||||||
<> "\n"
|
<> "\n"
|
||||||
<> referencesBuilder
|
<> referencesBuilder
|
||||||
<> deriverBuilder
|
<> deriverBuilder
|
||||||
|
<> foldMap buildSignature signatures
|
||||||
|
|
||||||
let size =
|
let size =
|
||||||
( ByteString.Lazy.toStrict
|
( ByteString.Lazy.toStrict
|
||||||
@ -197,6 +223,9 @@ toSocket path = do
|
|||||||
|
|
||||||
return socket
|
return socket
|
||||||
|
|
||||||
|
readSecretKey :: FilePath -> IO ByteString
|
||||||
|
readSecretKey = fmap ByteString.Char8.strip . ByteString.readFile
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
options@Options{ priority, verbosity } <- do
|
options@Options{ priority, verbosity } <- do
|
||||||
@ -204,13 +233,17 @@ main = do
|
|||||||
|
|
||||||
storeDirectory <- Nix.getStoreDir
|
storeDirectory <- Nix.getStoreDir
|
||||||
|
|
||||||
|
secretKeyFile <- Environment.lookupEnv "NIX_SECRET_KEY_FILE"
|
||||||
|
|
||||||
|
secretKey <- traverse readSecretKey secretKeyFile
|
||||||
|
|
||||||
let logger =
|
let logger =
|
||||||
case verbosity of
|
case verbosity of
|
||||||
Quiet -> id
|
Quiet -> id
|
||||||
Normal -> RequestLogger.logStdout
|
Normal -> RequestLogger.logStdout
|
||||||
Verbose -> RequestLogger.logStdoutDev
|
Verbose -> RequestLogger.logStdoutDev
|
||||||
|
|
||||||
let application = logger (makeApplication priority storeDirectory)
|
let application = logger (makeApplication ApplicationOptions{..})
|
||||||
|
|
||||||
case options of
|
case options of
|
||||||
Options{ ssl = Disabled, socket = TCP{ host, port } } -> do
|
Options{ ssl = Disabled, socket = TCP{ host, port } } -> do
|
||||||
|
|||||||
51
src/Nix.hsc
51
src/Nix.hsc
@ -1,13 +1,17 @@
|
|||||||
{-# LANGUAGE BlockArguments #-}
|
{-# LANGUAGE BlockArguments #-}
|
||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||||
|
{-# LANGUAGE MultiWayIf #-}
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE TypeApplications #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Nix where
|
module Nix where
|
||||||
|
|
||||||
|
import Control.Applicative (empty)
|
||||||
import Control.Monad.Managed (Managed)
|
import Control.Monad.Managed (Managed)
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.ByteString.Builder (Builder)
|
||||||
import Data.Vector (Vector)
|
import Data.Vector (Vector)
|
||||||
import Data.Word (Word64)
|
import Data.Word (Word64)
|
||||||
import Foreign (Ptr, Storable(..))
|
import Foreign (Ptr, Storable(..))
|
||||||
@ -16,6 +20,9 @@ import Foreign.C (CChar, CLong, CSize, CString)
|
|||||||
import qualified Control.Exception as Exception
|
import qualified Control.Exception as Exception
|
||||||
import qualified Control.Monad.Managed as Managed
|
import qualified Control.Monad.Managed as Managed
|
||||||
import qualified Data.ByteString as ByteString
|
import qualified Data.ByteString as ByteString
|
||||||
|
import qualified Data.ByteString.Base16 as Base16
|
||||||
|
import qualified Data.ByteString.Base32 as Base32
|
||||||
|
import qualified Data.ByteString.Builder as Builder
|
||||||
import qualified Data.Vector as Vector
|
import qualified Data.Vector as Vector
|
||||||
import qualified Data.Vector.Storable as Vector.Storable
|
import qualified Data.Vector.Storable as Vector.Storable
|
||||||
import qualified Foreign
|
import qualified Foreign
|
||||||
@ -208,4 +215,48 @@ queryPathInfo storePath = do
|
|||||||
cPathInfo <- peek output
|
cPathInfo <- peek output
|
||||||
fromCPathInfo cPathInfo
|
fromCPathInfo cPathInfo
|
||||||
|
|
||||||
|
fingerprintPath :: ByteString -> PathInfo -> Maybe Builder
|
||||||
|
fingerprintPath storePath PathInfo{ narHash, narSize, references } = do
|
||||||
|
suffix <- ByteString.stripPrefix "sha256:" narHash
|
||||||
|
|
||||||
|
base32Suffix <- if
|
||||||
|
| ByteString.length suffix == 64
|
||||||
|
, Right digest <- Base16.decodeBase16 suffix ->
|
||||||
|
return (Base32.encodeBase32' digest)
|
||||||
|
| ByteString.length suffix == 52 ->
|
||||||
|
return suffix
|
||||||
|
| otherwise ->
|
||||||
|
empty
|
||||||
|
|
||||||
|
return
|
||||||
|
( "1;"
|
||||||
|
<> Builder.byteString storePath
|
||||||
|
<> ";sha256:"
|
||||||
|
<> Builder.byteString base32Suffix
|
||||||
|
<> ";"
|
||||||
|
<> Builder.word64Dec narSize
|
||||||
|
<> ";"
|
||||||
|
<> referencesBuilder
|
||||||
|
)
|
||||||
|
where
|
||||||
|
referencesBuilder =
|
||||||
|
case Vector.uncons references of
|
||||||
|
Nothing ->
|
||||||
|
mempty
|
||||||
|
Just (r0, rs) ->
|
||||||
|
Builder.byteString r0
|
||||||
|
<> foldMap (\r -> "," <> Builder.byteString r) rs
|
||||||
|
|
||||||
|
foreign import ccall "signString" signString_
|
||||||
|
:: CString -> CString -> Ptr String_ -> IO ()
|
||||||
|
|
||||||
|
signString :: ByteString -> ByteString -> IO ByteString
|
||||||
|
signString secretKey fingerprint =
|
||||||
|
ByteString.useAsCString secretKey \cSecretKey ->
|
||||||
|
ByteString.useAsCString fingerprint \cFingerprint ->
|
||||||
|
Foreign.alloca \output -> do
|
||||||
|
let open = signString_ cSecretKey cFingerprint output
|
||||||
|
let close = freeString output
|
||||||
|
Exception.bracket_ open close do
|
||||||
|
string_ <- peek output
|
||||||
|
fromString_ string_
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user