mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-14 02:06:16 +02:00
Use libsodium instead of OpenSSL for binary cache signing
Sodium's Ed25519 signatures are much shorter than OpenSSL's RSA signatures. Public keys are also much shorter, so they're now specified directly in the nix.conf option ‘binary-cache-public-keys’. The new command ‘nix-store --generate-binary-cache-key’ generates and prints a public and secret key.
This commit is contained in:
parent
0d1dafa0c4
commit
e0def5bc4b
15 changed files with 196 additions and 91 deletions
|
@ -7,6 +7,7 @@ HAVE_OPENSSL = @HAVE_OPENSSL@
|
||||||
OPENSSL_LIBS = @OPENSSL_LIBS@
|
OPENSSL_LIBS = @OPENSSL_LIBS@
|
||||||
PACKAGE_NAME = @PACKAGE_NAME@
|
PACKAGE_NAME = @PACKAGE_NAME@
|
||||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||||
|
SODIUM_LIBS = @SODIUM_LIBS@
|
||||||
bash = @bash@
|
bash = @bash@
|
||||||
bindir = @bindir@
|
bindir = @bindir@
|
||||||
bsddiff_compat_include = @bsddiff_compat_include@
|
bsddiff_compat_include = @bsddiff_compat_include@
|
||||||
|
|
|
@ -205,6 +205,10 @@ AC_CHECK_HEADERS([bzlib.h], [true],
|
||||||
PKG_CHECK_MODULES([SQLITE3], [sqlite3 >= 3.6.19], [CXXFLAGS="$SQLITE3_CFLAGS $CXXFLAGS"])
|
PKG_CHECK_MODULES([SQLITE3], [sqlite3 >= 3.6.19], [CXXFLAGS="$SQLITE3_CFLAGS $CXXFLAGS"])
|
||||||
|
|
||||||
|
|
||||||
|
# Look for libsodium, a required dependency.
|
||||||
|
PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
|
||||||
|
|
||||||
|
|
||||||
# Whether to use the Boehm garbage collector.
|
# Whether to use the Boehm garbage collector.
|
||||||
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
||||||
[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=no]]),
|
[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=no]]),
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package Nix::Config;
|
package Nix::Config;
|
||||||
|
|
||||||
|
use MIME::Base64;
|
||||||
|
|
||||||
$version = "@PACKAGE_VERSION@";
|
$version = "@PACKAGE_VERSION@";
|
||||||
|
|
||||||
$binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
$binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||||
|
@ -19,15 +21,15 @@ $useBindings = "@perlbindings@" eq "yes";
|
||||||
|
|
||||||
%config = ();
|
%config = ();
|
||||||
|
|
||||||
|
%binaryCachePublicKeys = ();
|
||||||
|
|
||||||
sub readConfig {
|
sub readConfig {
|
||||||
if (defined $ENV{'_NIX_OPTIONS'}) {
|
if (defined $ENV{'_NIX_OPTIONS'}) {
|
||||||
foreach my $s (split '\n', $ENV{'_NIX_OPTIONS'}) {
|
foreach my $s (split '\n', $ENV{'_NIX_OPTIONS'}) {
|
||||||
my ($n, $v) = split '=', $s, 2;
|
my ($n, $v) = split '=', $s, 2;
|
||||||
$config{$n} = $v;
|
$config{$n} = $v;
|
||||||
}
|
}
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
my $config = "$confDir/nix.conf";
|
my $config = "$confDir/nix.conf";
|
||||||
return unless -f $config;
|
return unless -f $config;
|
||||||
|
|
||||||
|
@ -37,6 +39,13 @@ sub readConfig {
|
||||||
$config{$1} = $2;
|
$config{$1} = $2;
|
||||||
}
|
}
|
||||||
close CONFIG;
|
close CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $s (split(/ /, $config{"binary-cache-public-keys"} // "")) {
|
||||||
|
my ($keyName, $publicKey) = split ":", $s;
|
||||||
|
next unless defined $keyName && defined $publicKey;
|
||||||
|
$binaryCachePublicKeys{$keyName} = decode_base64($publicKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
package Nix::Crypto;
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use MIME::Base64;
|
|
||||||
use Nix::Store;
|
|
||||||
use Nix::Config;
|
|
||||||
use IPC::Open2;
|
|
||||||
|
|
||||||
our @ISA = qw(Exporter);
|
|
||||||
our @EXPORT = qw(signString isValidSignature);
|
|
||||||
|
|
||||||
sub signString {
|
|
||||||
my ($privateKeyFile, $s) = @_;
|
|
||||||
my $hash = hashString("sha256", 0, $s);
|
|
||||||
my ($from, $to);
|
|
||||||
my $pid = open2($from, $to, $Nix::Config::openssl, "rsautl", "-sign", "-inkey", $privateKeyFile);
|
|
||||||
print $to $hash;
|
|
||||||
close $to;
|
|
||||||
local $/ = undef;
|
|
||||||
my $sig = <$from>;
|
|
||||||
close $from;
|
|
||||||
waitpid($pid, 0);
|
|
||||||
die "$0: OpenSSL returned exit code $? while signing hash\n" if $? != 0;
|
|
||||||
my $sig64 = encode_base64($sig, "");
|
|
||||||
return $sig64;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub isValidSignature {
|
|
||||||
my ($publicKeyFile, $sig64, $s) = @_;
|
|
||||||
my ($from, $to);
|
|
||||||
my $pid = open2($from, $to, $Nix::Config::openssl, "rsautl", "-verify", "-inkey", $publicKeyFile, "-pubin");
|
|
||||||
print $to decode_base64($sig64);
|
|
||||||
close $to;
|
|
||||||
my $decoded = <$from>;
|
|
||||||
close $from;
|
|
||||||
waitpid($pid, 0);
|
|
||||||
return 0 if $? != 0;
|
|
||||||
my $hash = hashString("sha256", 0, $s);
|
|
||||||
return $decoded eq $hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
|
@ -8,8 +8,9 @@ use Cwd;
|
||||||
use File::stat;
|
use File::stat;
|
||||||
use File::Path;
|
use File::Path;
|
||||||
use Fcntl ':flock';
|
use Fcntl ':flock';
|
||||||
|
use MIME::Base64;
|
||||||
use Nix::Config;
|
use Nix::Config;
|
||||||
use Nix::Crypto;
|
use Nix::Store;
|
||||||
|
|
||||||
our @ISA = qw(Exporter);
|
our @ISA = qw(Exporter);
|
||||||
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch deleteOldManifests parseNARInfo);
|
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch deleteOldManifests parseNARInfo);
|
||||||
|
@ -440,22 +441,20 @@ sub parseNARInfo {
|
||||||
}
|
}
|
||||||
my ($sigVersion, $keyName, $sig64) = split ";", $sig;
|
my ($sigVersion, $keyName, $sig64) = split ";", $sig;
|
||||||
$sigVersion //= 0;
|
$sigVersion //= 0;
|
||||||
if ($sigVersion != 1) {
|
if ($sigVersion != 2) {
|
||||||
warn "NAR info file ‘$location’ has unsupported version $sigVersion; ignoring\n";
|
warn "NAR info file ‘$location’ has unsupported version $sigVersion; ignoring\n";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
return undef unless defined $keyName && defined $sig64;
|
return undef unless defined $keyName && defined $sig64;
|
||||||
my $publicKeyFile = $Nix::Config::config{"binary-cache-public-key-$keyName"};
|
|
||||||
if (!defined $publicKeyFile) {
|
my $publicKey = $Nix::Config::binaryCachePublicKeys{$keyName};
|
||||||
|
if (!defined $publicKey) {
|
||||||
warn "NAR info file ‘$location’ is signed by unknown key ‘$keyName’; ignoring\n";
|
warn "NAR info file ‘$location’ is signed by unknown key ‘$keyName’; ignoring\n";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
if (! -f $publicKeyFile) {
|
|
||||||
die "binary cache public key file ‘$publicKeyFile’ does not exist\n";
|
if (!checkSignature($publicKey, decode_base64($sig64), $signedData)) {
|
||||||
return undef;
|
warn "NAR info file ‘$location’ has an incorrect signature; ignoring\n";
|
||||||
}
|
|
||||||
if (!isValidSignature($publicKeyFile, $sig64, $signedData)) {
|
|
||||||
warn "NAR info file ‘$location’ has an invalid signature; ignoring\n";
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
$res->{signedBy} = $keyName;
|
$res->{signedBy} = $keyName;
|
||||||
|
|
|
@ -17,6 +17,7 @@ our @EXPORT = qw(
|
||||||
queryPathFromHashPart
|
queryPathFromHashPart
|
||||||
topoSortPaths computeFSClosure followLinksToStorePath exportPaths importPaths
|
topoSortPaths computeFSClosure followLinksToStorePath exportPaths importPaths
|
||||||
hashPath hashFile hashString
|
hashPath hashFile hashString
|
||||||
|
signString checkSignature
|
||||||
addToStore makeFixedOutputPath
|
addToStore makeFixedOutputPath
|
||||||
derivationFromPath
|
derivationFromPath
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include <misc.hh>
|
#include <misc.hh>
|
||||||
#include <util.hh>
|
#include <util.hh>
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
@ -223,6 +225,44 @@ SV * hashString(char * algo, int base32, char * s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SV * signString(SV * secretKey_, char * msg)
|
||||||
|
PPCODE:
|
||||||
|
try {
|
||||||
|
STRLEN secretKeyLen;
|
||||||
|
unsigned char * secretKey = (unsigned char *) SvPV(secretKey_, secretKeyLen);
|
||||||
|
if (secretKeyLen != crypto_sign_SECRETKEYBYTES)
|
||||||
|
throw Error("secret key is not valid");
|
||||||
|
|
||||||
|
unsigned char sig[crypto_sign_BYTES];
|
||||||
|
unsigned long long sigLen;
|
||||||
|
crypto_sign_detached(sig, &sigLen, (unsigned char *) msg, strlen(msg), secretKey);
|
||||||
|
XPUSHs(sv_2mortal(newSVpv((char *) sig, sigLen)));
|
||||||
|
} catch (Error & e) {
|
||||||
|
croak(e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int checkSignature(SV * publicKey_, SV * sig_, char * msg)
|
||||||
|
CODE:
|
||||||
|
try {
|
||||||
|
STRLEN publicKeyLen;
|
||||||
|
unsigned char * publicKey = (unsigned char *) SvPV(publicKey_, publicKeyLen);
|
||||||
|
if (publicKeyLen != crypto_sign_PUBLICKEYBYTES)
|
||||||
|
throw Error("public key is not valid");
|
||||||
|
|
||||||
|
STRLEN sigLen;
|
||||||
|
unsigned char * sig = (unsigned char *) SvPV(sig_, sigLen);
|
||||||
|
if (sigLen != crypto_sign_BYTES)
|
||||||
|
throw Error("signature is not valid");
|
||||||
|
|
||||||
|
RETVAL = crypto_sign_verify_detached(sig, (unsigned char *) msg, strlen(msg), publicKey) == 0;
|
||||||
|
} catch (Error & e) {
|
||||||
|
croak(e.what());
|
||||||
|
}
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
|
|
||||||
SV * addToStore(char * srcPath, int recursive, char * algo)
|
SV * addToStore(char * srcPath, int recursive, char * algo)
|
||||||
PPCODE:
|
PPCODE:
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -5,8 +5,7 @@ nix_perl_sources := \
|
||||||
$(d)/lib/Nix/SSH.pm \
|
$(d)/lib/Nix/SSH.pm \
|
||||||
$(d)/lib/Nix/CopyClosure.pm \
|
$(d)/lib/Nix/CopyClosure.pm \
|
||||||
$(d)/lib/Nix/Config.pm.in \
|
$(d)/lib/Nix/Config.pm.in \
|
||||||
$(d)/lib/Nix/Utils.pm \
|
$(d)/lib/Nix/Utils.pm
|
||||||
$(d)/lib/Nix/Crypto.pm
|
|
||||||
|
|
||||||
nix_perl_modules := $(nix_perl_sources:.in=)
|
nix_perl_modules := $(nix_perl_sources:.in=)
|
||||||
|
|
||||||
|
@ -23,16 +22,18 @@ ifeq ($(perlbindings), yes)
|
||||||
|
|
||||||
Store_SOURCES := $(Store_DIR)/Store.cc
|
Store_SOURCES := $(Store_DIR)/Store.cc
|
||||||
|
|
||||||
Store_LIBS = libstore libutil
|
|
||||||
|
|
||||||
Store_CXXFLAGS = \
|
Store_CXXFLAGS = \
|
||||||
-I$(shell $(perl) -e 'use Config; print $$Config{archlibexp};')/CORE \
|
-I$(shell $(perl) -e 'use Config; print $$Config{archlibexp};')/CORE \
|
||||||
-D_FILE_OFFSET_BITS=64 -Wno-unused-variable -Wno-literal-suffix -Wno-reserved-user-defined-literal
|
-D_FILE_OFFSET_BITS=64 -Wno-unused-variable -Wno-literal-suffix -Wno-reserved-user-defined-literal
|
||||||
|
|
||||||
|
Store_LIBS = libstore libutil
|
||||||
|
|
||||||
|
Store_LDFLAGS := $(SODIUM_LIBS)
|
||||||
|
|
||||||
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
||||||
archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
|
archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
|
||||||
libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
|
libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
|
||||||
Store_LDFLAGS = $(shell find ${archlib} -name ${libperl})
|
Store_LDFLAGS += $(shell find ${archlib} -name ${libperl})
|
||||||
endif
|
endif
|
||||||
|
|
||||||
Store_ALLOW_UNDEFINED = 1
|
Store_ALLOW_UNDEFINED = 1
|
||||||
|
|
|
@ -24,7 +24,7 @@ let
|
||||||
|
|
||||||
buildInputs =
|
buildInputs =
|
||||||
[ curl bison flex perl libxml2 libxslt bzip2
|
[ curl bison flex perl libxml2 libxslt bzip2
|
||||||
tetex dblatex nukeReferences pkgconfig sqlite
|
tetex dblatex nukeReferences pkgconfig sqlite libsodium
|
||||||
docbook5 docbook5_xsl
|
docbook5 docbook5_xsl
|
||||||
] ++ lib.optional (!lib.inNixShell) git;
|
] ++ lib.optional (!lib.inNixShell) git;
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ let
|
||||||
name = "nix";
|
name = "nix";
|
||||||
src = tarball;
|
src = tarball;
|
||||||
|
|
||||||
buildInputs = [ curl perl bzip2 openssl pkgconfig sqlite boehmgc ];
|
buildInputs = [ curl perl bzip2 openssl pkgconfig sqlite boehmgc libsodium ];
|
||||||
|
|
||||||
configureFlags = ''
|
configureFlags = ''
|
||||||
--disable-init-state
|
--disable-init-state
|
||||||
|
|
|
@ -6,11 +6,11 @@ use File::Basename;
|
||||||
use File::Path qw(mkpath);
|
use File::Path qw(mkpath);
|
||||||
use File::stat;
|
use File::stat;
|
||||||
use File::Copy;
|
use File::Copy;
|
||||||
|
use MIME::Base64;
|
||||||
use Nix::Config;
|
use Nix::Config;
|
||||||
use Nix::Store;
|
use Nix::Store;
|
||||||
use Nix::Manifest;
|
use Nix::Manifest;
|
||||||
use Nix::Utils;
|
use Nix::Utils;
|
||||||
use Nix::Crypto;
|
|
||||||
|
|
||||||
binmode STDERR, ":encoding(utf8)";
|
binmode STDERR, ":encoding(utf8)";
|
||||||
|
|
||||||
|
@ -27,8 +27,7 @@ my $writeManifest = 0;
|
||||||
my $manifestPath;
|
my $manifestPath;
|
||||||
my $archivesURL;
|
my $archivesURL;
|
||||||
my $link = 0;
|
my $link = 0;
|
||||||
my $privateKeyFile;
|
my $secretKeyFile;
|
||||||
my $keyName;
|
|
||||||
my @roots;
|
my @roots;
|
||||||
|
|
||||||
for (my $n = 0; $n < scalar @ARGV; $n++) {
|
for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||||
|
@ -61,14 +60,10 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||||
$archivesURL = $ARGV[$n];
|
$archivesURL = $ARGV[$n];
|
||||||
} elsif ($arg eq "--link") {
|
} elsif ($arg eq "--link") {
|
||||||
$link = 1;
|
$link = 1;
|
||||||
} elsif ($arg eq "--key") {
|
} elsif ($arg eq "--key-file") {
|
||||||
$n++;
|
$n++;
|
||||||
die "$0: ‘$arg’ requires an argument\n" unless $n < scalar @ARGV;
|
die "$0: ‘$arg’ requires an argument\n" unless $n < scalar @ARGV;
|
||||||
$privateKeyFile = $ARGV[$n];
|
$secretKeyFile = $ARGV[$n];
|
||||||
} elsif ($arg eq "--key-name") {
|
|
||||||
$n++;
|
|
||||||
die "$0: ‘$arg’ requires an argument\n" unless $n < scalar @ARGV;
|
|
||||||
$keyName = $ARGV[$n];
|
|
||||||
} elsif (substr($arg, 0, 1) eq "-") {
|
} elsif (substr($arg, 0, 1) eq "-") {
|
||||||
die "$0: unknown flag ‘$arg’\n";
|
die "$0: unknown flag ‘$arg’\n";
|
||||||
} else {
|
} else {
|
||||||
|
@ -110,7 +105,7 @@ my %narFiles;
|
||||||
foreach my $storePath (@storePaths) {
|
foreach my $storePath (@storePaths) {
|
||||||
my $pathHash = substr(basename($storePath), 0, 32);
|
my $pathHash = substr(basename($storePath), 0, 32);
|
||||||
my $narInfoFile = "$destDir/$pathHash.narinfo";
|
my $narInfoFile = "$destDir/$pathHash.narinfo";
|
||||||
if (-e $narInfoFile) {
|
if (!$force && -e $narInfoFile) {
|
||||||
my $narInfo = parseNARInfo($storePath, readFile($narInfoFile), 0, $narInfoFile) or die "cannot read ‘$narInfoFile’\n";
|
my $narInfo = parseNARInfo($storePath, readFile($narInfoFile), 0, $narInfoFile) or die "cannot read ‘$narInfoFile’\n";
|
||||||
my $narFile = "$destDir/$narInfo->{url}";
|
my $narFile = "$destDir/$narInfo->{url}";
|
||||||
if (-e $narFile) {
|
if (-e $narFile) {
|
||||||
|
@ -257,9 +252,13 @@ for (my $n = 0; $n < scalar @storePaths2; $n++) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defined $privateKeyFile && defined $keyName) {
|
if (defined $secretKeyFile) {
|
||||||
my $sig = signString($privateKeyFile, $info);
|
my $s = readFile $secretKeyFile;
|
||||||
$info .= "Signature: 1;$keyName;$sig\n";
|
chomp $s;
|
||||||
|
my ($keyName, $secretKey) = split ":", $s;
|
||||||
|
die "invalid secret key file ‘$secretKeyFile’\n" unless defined $keyName && defined $secretKey;
|
||||||
|
my $sig = encode_base64(signString(decode_base64($secretKey), $info), "");
|
||||||
|
$info .= "Signature: 2;$keyName;$sig\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
my $pathHash = substr(basename($storePath), 0, 32);
|
my $pathHash = substr(basename($storePath), 0, 32);
|
||||||
|
|
|
@ -925,18 +925,24 @@ std::vector<const char *> stringsToCharPtrs(const Strings & ss)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string runProgram(Path program, bool searchPath, const Strings & args)
|
string runProgram(Path program, bool searchPath, const Strings & args,
|
||||||
|
const string & input)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
/* Create a pipe. */
|
/* Create a pipe. */
|
||||||
Pipe pipe;
|
Pipe stdout, stdin;
|
||||||
pipe.create();
|
stdout.create();
|
||||||
|
if (!input.empty()) stdin.create();
|
||||||
|
|
||||||
/* Fork. */
|
/* Fork. */
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid = startProcess([&]() {
|
||||||
if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
|
if (dup2(stdout.writeSide, STDOUT_FILENO) == -1)
|
||||||
throw SysError("dupping stdout");
|
throw SysError("dupping stdout");
|
||||||
|
if (!input.empty()) {
|
||||||
|
if (dup2(stdin.readSide, STDIN_FILENO) == -1)
|
||||||
|
throw SysError("dupping stdin");
|
||||||
|
}
|
||||||
|
|
||||||
Strings args_(args);
|
Strings args_(args);
|
||||||
args_.push_front(program);
|
args_.push_front(program);
|
||||||
|
@ -950,9 +956,16 @@ string runProgram(Path program, bool searchPath, const Strings & args)
|
||||||
throw SysError(format("executing ‘%1%’") % program);
|
throw SysError(format("executing ‘%1%’") % program);
|
||||||
});
|
});
|
||||||
|
|
||||||
pipe.writeSide.close();
|
stdout.writeSide.close();
|
||||||
|
|
||||||
string result = drainFD(pipe.readSide);
|
/* FIXME: This can deadlock if the input is too long. */
|
||||||
|
if (!input.empty()) {
|
||||||
|
stdin.readSide.close();
|
||||||
|
writeFull(stdin.writeSide, input);
|
||||||
|
stdin.writeSide.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
string result = drainFD(stdout.readSide);
|
||||||
|
|
||||||
/* Wait for the child to finish. */
|
/* Wait for the child to finish. */
|
||||||
int status = pid.wait(true);
|
int status = pid.wait(true);
|
||||||
|
|
|
@ -285,7 +285,7 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = P
|
||||||
/* Run a program and return its stdout in a string (i.e., like the
|
/* Run a program and return its stdout in a string (i.e., like the
|
||||||
shell backtick operator). */
|
shell backtick operator). */
|
||||||
string runProgram(Path program, bool searchPath = false,
|
string runProgram(Path program, bool searchPath = false,
|
||||||
const Strings & args = Strings());
|
const Strings & args = Strings(), const string & input = "");
|
||||||
|
|
||||||
MakeError(ExecError, Error)
|
MakeError(ExecError, Error)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,6 @@ nix-store_SOURCES := $(wildcard $(d)/*.cc)
|
||||||
|
|
||||||
nix-store_LIBS = libmain libstore libutil libformat
|
nix-store_LIBS = libmain libstore libutil libformat
|
||||||
|
|
||||||
nix-store_LDFLAGS = -lbz2 -pthread
|
nix-store_LDFLAGS = -lbz2 -pthread $(SODIUM_LIBS)
|
||||||
|
|
||||||
nix-store_CXXFLAGS = -DCURL=\"$(curl)\"
|
nix-store_CXXFLAGS = -DCURL=\"$(curl)\"
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
#include <bzlib.h>
|
#include <bzlib.h>
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
using std::cin;
|
using std::cin;
|
||||||
|
@ -1006,6 +1008,32 @@ static void opServe(Strings opFlags, Strings opArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void opGenerateBinaryCacheKey(Strings opFlags, Strings opArgs)
|
||||||
|
{
|
||||||
|
foreach (Strings::iterator, i, opFlags)
|
||||||
|
throw UsageError(format("unknown flag ‘%1%’") % *i);
|
||||||
|
|
||||||
|
if (opArgs.size() != 1) throw UsageError("one argument expected");
|
||||||
|
string keyName = opArgs.front();
|
||||||
|
|
||||||
|
sodium_init();
|
||||||
|
|
||||||
|
unsigned char pk[crypto_sign_PUBLICKEYBYTES];
|
||||||
|
unsigned char sk[crypto_sign_SECRETKEYBYTES];
|
||||||
|
if (crypto_sign_keypair(pk, sk) != 0)
|
||||||
|
throw Error("key generation failed");
|
||||||
|
|
||||||
|
// FIXME: super ugly way to do base64 encoding.
|
||||||
|
auto args = Strings({"-MMIME::Base64", "-0777", "-ne", "print encode_base64($_, '')"});
|
||||||
|
|
||||||
|
string pk64 = runProgram("perl", true, args, string((char *) pk, crypto_sign_PUBLICKEYBYTES));
|
||||||
|
std::cout << keyName << ":" << pk64 << std::endl;
|
||||||
|
|
||||||
|
string sk64 = runProgram("perl", true, args, string((char *) sk, crypto_sign_SECRETKEYBYTES));
|
||||||
|
std::cout << keyName << ":" << sk64 << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Scan the arguments; find the operation, set global flags, put all
|
/* Scan the arguments; find the operation, set global flags, put all
|
||||||
other flags in a list, and put all other arguments in another
|
other flags in a list, and put all other arguments in another
|
||||||
list. */
|
list. */
|
||||||
|
@ -1072,14 +1100,16 @@ int main(int argc, char * * argv)
|
||||||
op = opQueryFailedPaths;
|
op = opQueryFailedPaths;
|
||||||
else if (*arg == "--clear-failed-paths")
|
else if (*arg == "--clear-failed-paths")
|
||||||
op = opClearFailedPaths;
|
op = opClearFailedPaths;
|
||||||
|
else if (*arg == "--serve")
|
||||||
|
op = opServe;
|
||||||
|
else if (*arg == "--generate-binary-cache-key")
|
||||||
|
op = opGenerateBinaryCacheKey;
|
||||||
else if (*arg == "--add-root")
|
else if (*arg == "--add-root")
|
||||||
gcRoot = absPath(getArg(*arg, arg, end));
|
gcRoot = absPath(getArg(*arg, arg, end));
|
||||||
else if (*arg == "--indirect")
|
else if (*arg == "--indirect")
|
||||||
indirectRoot = true;
|
indirectRoot = true;
|
||||||
else if (*arg == "--no-output")
|
else if (*arg == "--no-output")
|
||||||
noOutput = true;
|
noOutput = true;
|
||||||
else if (*arg == "--serve")
|
|
||||||
op = opServe;
|
|
||||||
else if (*arg != "" && arg->at(0) == '-') {
|
else if (*arg != "" && arg->at(0) == '-') {
|
||||||
opFlags.push_back(*arg);
|
opFlags.push_back(*arg);
|
||||||
if (*arg == "--max-freed" || *arg == "--max-links" || *arg == "--max-atime") /* !!! hack */
|
if (*arg == "--max-freed" || *arg == "--max-links" || *arg == "--max-atime") /* !!! hack */
|
||||||
|
|
|
@ -87,3 +87,53 @@ rm $(grep -l "StorePath:.*dependencies-input-2" $cacheDir/*.narinfo)
|
||||||
|
|
||||||
nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
|
nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
|
||||||
grep -q "Downloading" $TEST_ROOT/log
|
grep -q "Downloading" $TEST_ROOT/log
|
||||||
|
|
||||||
|
|
||||||
|
# Create a signed binary cache.
|
||||||
|
clearCache
|
||||||
|
|
||||||
|
declare -a res=($(nix-store --generate-binary-cache-key test.nixos.org-1))
|
||||||
|
publicKey="${res[0]}"
|
||||||
|
secretKey="${res[1]}"
|
||||||
|
echo "$secretKey" > $TEST_ROOT/secret-key
|
||||||
|
|
||||||
|
res=($(nix-store --generate-binary-cache-key test.nixos.org-1))
|
||||||
|
badKey="${res[0]}"
|
||||||
|
|
||||||
|
res=($(nix-store --generate-binary-cache-key foo.nixos.org-1))
|
||||||
|
otherKey="${res[0]}"
|
||||||
|
|
||||||
|
nix-push --dest $cacheDir --key-file $TEST_ROOT/secret-key $outPath
|
||||||
|
|
||||||
|
|
||||||
|
# Downloading should fail if we don't provide a key.
|
||||||
|
clearStore
|
||||||
|
|
||||||
|
rm -f $NIX_STATE_DIR/binary-cache*
|
||||||
|
|
||||||
|
(! nix-store -r $outPath --option binary-caches "file://$cacheDir" --option signed-binary-caches '*' )
|
||||||
|
|
||||||
|
|
||||||
|
# And it should fail if we provide an incorrect key.
|
||||||
|
clearStore
|
||||||
|
|
||||||
|
rm -f $NIX_STATE_DIR/binary-cache*
|
||||||
|
|
||||||
|
(! nix-store -r $outPath --option binary-caches "file://$cacheDir" --option signed-binary-caches '*' --option binary-cache-public-keys "$badKey")
|
||||||
|
|
||||||
|
|
||||||
|
# It should succeed if we provide the correct key.
|
||||||
|
nix-store -r $outPath --option binary-caches "file://$cacheDir" --option signed-binary-caches '*' --option binary-cache-public-keys "$otherKey $publicKey"
|
||||||
|
|
||||||
|
|
||||||
|
# It should fail if we corrupt the .narinfo.
|
||||||
|
clearStore
|
||||||
|
|
||||||
|
for i in $cacheDir/*.narinfo; do
|
||||||
|
grep -v References $i > $i.tmp
|
||||||
|
mv $i.tmp $i
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f $NIX_STATE_DIR/binary-cache*
|
||||||
|
|
||||||
|
(! nix-store -r $outPath --option binary-caches "file://$cacheDir" --option signed-binary-caches '*' --option binary-cache-public-keys "$publicKey")
|
||||||
|
|
Loading…
Reference in a new issue