about summary refs log tree commit diff
path: root/perl
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2015-02-04T15·43+0100
committerEelco Dolstra <eelco.dolstra@logicblox.com>2015-02-04T16·10+0100
commite0def5bc4b41ad09ce3f188bf522814ef3389e1f (patch)
tree70b894e41d8b682a166872d28d720e438aea8dda /perl
parent0d1dafa0c4ef8adc27315653df8a170c0cf33985 (diff)
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.
Diffstat (limited to 'perl')
-rw-r--r--perl/lib/Nix/Config.pm.in27
-rw-r--r--perl/lib/Nix/Crypto.pm42
-rw-r--r--perl/lib/Nix/Manifest.pm19
-rw-r--r--perl/lib/Nix/Store.pm1
-rw-r--r--perl/lib/Nix/Store.xs40
-rw-r--r--perl/local.mk11
6 files changed, 74 insertions, 66 deletions
diff --git a/perl/lib/Nix/Config.pm.in b/perl/lib/Nix/Config.pm.in
index bc51310e5a..388acd2e61 100644
--- a/perl/lib/Nix/Config.pm.in
+++ b/perl/lib/Nix/Config.pm.in
@@ -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;
diff --git a/perl/lib/Nix/Crypto.pm b/perl/lib/Nix/Crypto.pm
deleted file mode 100644
index 0286e88d3d..0000000000
--- a/perl/lib/Nix/Crypto.pm
+++ /dev/null
@@ -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;
diff --git a/perl/lib/Nix/Manifest.pm b/perl/lib/Nix/Manifest.pm
index 9b7e89fa42..ec3e48fcfb 100644
--- a/perl/lib/Nix/Manifest.pm
+++ b/perl/lib/Nix/Manifest.pm
@@ -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;
diff --git a/perl/lib/Nix/Store.pm b/perl/lib/Nix/Store.pm
index 89cfaefa5f..233a432ee0 100644
--- a/perl/lib/Nix/Store.pm
+++ b/perl/lib/Nix/Store.pm
@@ -17,6 +17,7 @@ our @EXPORT = qw(
     queryPathFromHashPart
     topoSortPaths computeFSClosure followLinksToStorePath exportPaths importPaths
     hashPath hashFile hashString
+    signString checkSignature
     addToStore makeFixedOutputPath
     derivationFromPath
 );
diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs
index ff90616d37..792d2f6499 100644
--- a/perl/lib/Nix/Store.xs
+++ b/perl/lib/Nix/Store.xs
@@ -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 {
diff --git a/perl/local.mk b/perl/local.mk
index 73d8a7c952..132676f533 100644
--- a/perl/local.mk
+++ b/perl/local.mk
@@ -5,8 +5,7 @@ nix_perl_sources := \
   $(d)/lib/Nix/SSH.pm \
   $(d)/lib/Nix/CopyClosure.pm \
   $(d)/lib/Nix/Config.pm.in \
-  $(d)/lib/Nix/Utils.pm \
-  $(d)/lib/Nix/Crypto.pm
+  $(d)/lib/Nix/Utils.pm
 
 nix_perl_modules := $(nix_perl_sources:.in=)
 
@@ -23,16 +22,18 @@ ifeq ($(perlbindings), yes)
 
   Store_SOURCES := $(Store_DIR)/Store.cc
 
-  Store_LIBS = libstore libutil
-
   Store_CXXFLAGS = \
     -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
 
+  Store_LIBS = libstore libutil
+
+  Store_LDFLAGS := $(SODIUM_LIBS)
+
   ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
     archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
     libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
-    Store_LDFLAGS = $(shell find ${archlib} -name ${libperl})
+    Store_LDFLAGS += $(shell find ${archlib} -name ${libperl})
   endif
 
   Store_ALLOW_UNDEFINED = 1