mirror of
https://github.com/NixOS/nix
synced 2025-07-06 21:41:48 +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
|
@ -1,5 +1,7 @@
|
|||
package Nix::Config;
|
||||
|
||||
use MIME::Base64;
|
||||
|
||||
$version = "@PACKAGE_VERSION@";
|
||||
|
||||
$binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
|
@ -19,24 +21,31 @@ $useBindings = "@perlbindings@" eq "yes";
|
|||
|
||||
%config = ();
|
||||
|
||||
%binaryCachePublicKeys = ();
|
||||
|
||||
sub readConfig {
|
||||
if (defined $ENV{'_NIX_OPTIONS'}) {
|
||||
foreach my $s (split '\n', $ENV{'_NIX_OPTIONS'}) {
|
||||
my ($n, $v) = split '=', $s, 2;
|
||||
$config{$n} = $v;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
my $config = "$confDir/nix.conf";
|
||||
return unless -f $config;
|
||||
|
||||
open CONFIG, "<$config" or die "cannot open ‘$config’";
|
||||
while (<CONFIG>) {
|
||||
/^\s*([\w\-\.]+)\s*=\s*(.*)$/ or next;
|
||||
$config{$1} = $2;
|
||||
}
|
||||
close CONFIG;
|
||||
}
|
||||
|
||||
my $config = "$confDir/nix.conf";
|
||||
return unless -f $config;
|
||||
|
||||
open CONFIG, "<$config" or die "cannot open ‘$config’";
|
||||
while (<CONFIG>) {
|
||||
/^\s*([\w\-\.]+)\s*=\s*(.*)$/ or next;
|
||||
$config{$1} = $2;
|
||||
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);
|
||||
}
|
||||
close CONFIG;
|
||||
}
|
||||
|
||||
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::Path;
|
||||
use Fcntl ':flock';
|
||||
use MIME::Base64;
|
||||
use Nix::Config;
|
||||
use Nix::Crypto;
|
||||
use Nix::Store;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch deleteOldManifests parseNARInfo);
|
||||
|
@ -440,22 +441,20 @@ sub parseNARInfo {
|
|||
}
|
||||
my ($sigVersion, $keyName, $sig64) = split ";", $sig;
|
||||
$sigVersion //= 0;
|
||||
if ($sigVersion != 1) {
|
||||
if ($sigVersion != 2) {
|
||||
warn "NAR info file ‘$location’ has unsupported version $sigVersion; ignoring\n";
|
||||
return undef;
|
||||
}
|
||||
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";
|
||||
return undef;
|
||||
}
|
||||
if (! -f $publicKeyFile) {
|
||||
die "binary cache public key file ‘$publicKeyFile’ does not exist\n";
|
||||
return undef;
|
||||
}
|
||||
if (!isValidSignature($publicKeyFile, $sig64, $signedData)) {
|
||||
warn "NAR info file ‘$location’ has an invalid signature; ignoring\n";
|
||||
|
||||
if (!checkSignature($publicKey, decode_base64($sig64), $signedData)) {
|
||||
warn "NAR info file ‘$location’ has an incorrect signature; ignoring\n";
|
||||
return undef;
|
||||
}
|
||||
$res->{signedBy} = $keyName;
|
||||
|
|
|
@ -17,6 +17,7 @@ our @EXPORT = qw(
|
|||
queryPathFromHashPart
|
||||
topoSortPaths computeFSClosure followLinksToStorePath exportPaths importPaths
|
||||
hashPath hashFile hashString
|
||||
signString checkSignature
|
||||
addToStore makeFixedOutputPath
|
||||
derivationFromPath
|
||||
);
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <misc.hh>
|
||||
#include <util.hh>
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
|
||||
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)
|
||||
PPCODE:
|
||||
try {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue