summary refs log tree commit diff
path: root/gnu
diff options
context:
space:
mode:
authorMark H Weaver <mhw@netris.org>2020-06-02 14:05:46 -0400
committerMark H Weaver <mhw@netris.org>2020-06-02 14:05:46 -0400
commit7bc396bf353c5550c49b3f8791b34072ba417d90 (patch)
treed205b805297d2346fff6a4aabbdf2d536a7b98b5 /gnu
parent8f7cf3b50cda98afab1bef89bef2d6c01aa2b626 (diff)
downloadguix-7bc396bf353c5550c49b3f8791b34072ba417d90.tar.gz
gnu: nss: Fix CVE-2020-12399 via graft.
* gnu/packages/patches/nss-CVE-2020-12399.patch: New file.
* gnu/local.mk (dist_patch_DATA): Add it.
* gnu/packages/nss.scm (nss/fixed): New variable.
(nss)[replacement]: Add field.
Diffstat (limited to 'gnu')
-rw-r--r--gnu/local.mk1
-rw-r--r--gnu/packages/nss.scm9
-rw-r--r--gnu/packages/patches/nss-CVE-2020-12399.patch138
3 files changed, 148 insertions, 0 deletions
diff --git a/gnu/local.mk b/gnu/local.mk
index 6d775828e0..4a8a855502 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1297,6 +1297,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/ngircd-handle-zombies.patch		\
   %D%/packages/patches/network-manager-plugin-path.patch	\
   %D%/packages/patches/nsis-env-passthru.patch			\
+  %D%/packages/patches/nss-CVE-2020-12399.patch			\
   %D%/packages/patches/nss-increase-test-timeout.patch		\
   %D%/packages/patches/nss-pkgconfig.patch			\
   %D%/packages/patches/ntfs-3g-CVE-2019-9755.patch		\
diff --git a/gnu/packages/nss.scm b/gnu/packages/nss.scm
index e423bdd3a2..887860157d 100644
--- a/gnu/packages/nss.scm
+++ b/gnu/packages/nss.scm
@@ -73,6 +73,7 @@ in the Mozilla clients.")
   (package
     (name "nss")
     (version "3.50")
+    (replacement nss/fixed)
     (source (origin
               (method url-fetch)
               (uri (let ((version-with-underscores
@@ -191,3 +192,11 @@ applications.  Applications built with NSS can support SSL v2 and v3, TLS,
 PKCS #5, PKCS #7, PKCS #11, PKCS #12, S/MIME, X.509 v3 certificates, and other
 security standards.")
     (license license:mpl2.0)))
+
+(define nss/fixed
+  (package
+    (inherit nss)
+    (source (origin
+              (inherit (package-source nss))
+              (patches (append (search-patches "nss-CVE-2020-12399.patch")
+                               (origin-patches (package-source nss))))))))
diff --git a/gnu/packages/patches/nss-CVE-2020-12399.patch b/gnu/packages/patches/nss-CVE-2020-12399.patch
new file mode 100644
index 0000000000..0d91b655e2
--- /dev/null
+++ b/gnu/packages/patches/nss-CVE-2020-12399.patch
@@ -0,0 +1,138 @@
+Fix CVE-2020-12399 (Timing attack on DSA signature generation: NSS has
+shown timing differences when performing DSA signatures, which was
+exploitable and could eventually leak private keys.)
+
+Copied from upstream:
+<https://hg.mozilla.org/projects/nss/rev/daa823a4a29bcef0fec33a379ec83857429aea2e>
+but with "nss/" inserted into the file name to patch.
+
+# HG changeset patch
+# User Robert Relyea <rrelyea@redhat.com>
+# Date 1589907685 0
+# Node ID daa823a4a29bcef0fec33a379ec83857429aea2e
+# Parent  d2cfb4ccdf167e5ea06d2bb5bc39c50f789929c8
+Bug 1631576 - Force a fixed length for DSA exponentiation r=pereida,bbrumley
+
+Differential Revision: https://phabricator.services.mozilla.com/D72011
+
+diff --git a/nss/lib/freebl/dsa.c b/nss/lib/freebl/dsa.c
+--- a/nss/lib/freebl/dsa.c
++++ b/nss/lib/freebl/dsa.c
+@@ -308,23 +308,24 @@ DSA_NewKeyFromSeed(const PQGParams *para
+     SECItem seedItem;
+     seedItem.data = (unsigned char *)seed;
+     seedItem.len = PQG_GetLength(&params->subPrime);
+     return dsa_NewKeyExtended(params, &seedItem, privKey);
+ }
+ 
+ static SECStatus
+ dsa_SignDigest(DSAPrivateKey *key, SECItem *signature, const SECItem *digest,
+-               const unsigned char *kb)
++               const unsigned char *kbytes)
+ {
+     mp_int p, q, g; /* PQG parameters */
+     mp_int x, k;    /* private key & pseudo-random integer */
+     mp_int r, s;    /* tuple (r, s) is signature) */
+     mp_int t;       /* holding tmp values */
+     mp_int ar;      /* holding blinding values */
++    mp_digit fuzz;  /* blinding multiplier for q */
+     mp_err err = MP_OKAY;
+     SECStatus rv = SECSuccess;
+     unsigned int dsa_subprime_len, dsa_signature_len, offset;
+     SECItem localDigest;
+     unsigned char localDigestData[DSA_MAX_SUBPRIME_LEN];
+     SECItem t2 = { siBuffer, NULL, 0 };
+ 
+     /* FIPS-compliance dictates that digest is a SHA hash. */
+@@ -368,31 +369,46 @@ dsa_SignDigest(DSAPrivateKey *key, SECIt
+     CHECK_MPI_OK(mp_init(&q));
+     CHECK_MPI_OK(mp_init(&g));
+     CHECK_MPI_OK(mp_init(&x));
+     CHECK_MPI_OK(mp_init(&k));
+     CHECK_MPI_OK(mp_init(&r));
+     CHECK_MPI_OK(mp_init(&s));
+     CHECK_MPI_OK(mp_init(&t));
+     CHECK_MPI_OK(mp_init(&ar));
++
+     /*
+     ** Convert stored PQG and private key into MPI integers.
+     */
+     SECITEM_TO_MPINT(key->params.prime, &p);
+     SECITEM_TO_MPINT(key->params.subPrime, &q);
+     SECITEM_TO_MPINT(key->params.base, &g);
+     SECITEM_TO_MPINT(key->privateValue, &x);
+-    OCTETS_TO_MPINT(kb, &k, dsa_subprime_len);
++    OCTETS_TO_MPINT(kbytes, &k, dsa_subprime_len);
++
++    /* k blinding  create a single value that has the high bit set in
++     * the mp_digit*/
++    if (RNG_GenerateGlobalRandomBytes(&fuzz, sizeof(mp_digit)) != SECSuccess) {
++        PORT_SetError(SEC_ERROR_NEED_RANDOM);
++        rv = SECFailure;
++        goto cleanup;
++    }
++    fuzz |= 1ULL << ((sizeof(mp_digit) * PR_BITS_PER_BYTE - 1));
+     /*
+     ** FIPS 186-1, Section 5, Step 1
+     **
+     ** r = (g**k mod p) mod q
+     */
+-    CHECK_MPI_OK(mp_exptmod(&g, &k, &p, &r)); /* r = g**k mod p */
+-    CHECK_MPI_OK(mp_mod(&r, &q, &r));         /* r = r mod q    */
++    CHECK_MPI_OK(mp_mul_d(&q, fuzz, &t)); /* t = q*fuzz */
++    CHECK_MPI_OK(mp_add(&k, &t, &t));     /* t = k+q*fuzz */
++    /* length of t is now fixed, bits in k have been blinded */
++    CHECK_MPI_OK(mp_exptmod(&g, &t, &p, &r)); /* r = g**t mod p */
++    /* r is now g**(k+q*fuzz) == g**k mod p */
++    CHECK_MPI_OK(mp_mod(&r, &q, &r)); /* r = r mod q    */
++
+     /*
+     ** FIPS 186-1, Section 5, Step 2
+     **
+     ** s = (k**-1 * (HASH(M) + x*r)) mod q
+     */
+     if (DSA_NewRandom(NULL, &key->params.subPrime, &t2) != SECSuccess) {
+         PORT_SetError(SEC_ERROR_NEED_RANDOM);
+         rv = SECFailure;
+@@ -406,25 +422,34 @@ dsa_SignDigest(DSAPrivateKey *key, SECIt
+         goto cleanup;
+     }
+     SECITEM_TO_MPINT(t2, &ar); /* ar <-$ Zq */
+     SECITEM_FreeItem(&t2, PR_FALSE);
+ 
+     /* Using mp_invmod on k directly would leak bits from k. */
+     CHECK_MPI_OK(mp_mul(&k, &ar, &k));       /* k = k * ar */
+     CHECK_MPI_OK(mp_mulmod(&k, &t, &q, &k)); /* k = k * t mod q */
+-    CHECK_MPI_OK(mp_invmod(&k, &q, &k));     /* k = k**-1 mod q */
++    /* k is now k*t*ar */
++    CHECK_MPI_OK(mp_invmod(&k, &q, &k)); /* k = k**-1 mod q */
++    /* k is now (k*t*ar)**-1 */
+     CHECK_MPI_OK(mp_mulmod(&k, &t, &q, &k)); /* k = k * t mod q */
+-    SECITEM_TO_MPINT(localDigest, &s);       /* s = HASH(M)     */
++    /* k is now (k*ar)**-1 */
++    SECITEM_TO_MPINT(localDigest, &s); /* s = HASH(M)     */
+     /* To avoid leaking secret bits here the addition is blinded. */
+-    CHECK_MPI_OK(mp_mul(&x, &ar, &x));        /* x = x * ar */
+-    CHECK_MPI_OK(mp_mulmod(&x, &r, &q, &x));  /* x = x * r mod q */
++    CHECK_MPI_OK(mp_mul(&x, &ar, &x)); /* x = x * ar */
++    /* x is now x*ar */
++    CHECK_MPI_OK(mp_mulmod(&x, &r, &q, &x)); /* x = x * r mod q */
++    /* x is now x*r*ar */
+     CHECK_MPI_OK(mp_mulmod(&s, &ar, &q, &t)); /* t = s * ar mod q */
+-    CHECK_MPI_OK(mp_add(&t, &x, &s));         /* s = t + x */
+-    CHECK_MPI_OK(mp_mulmod(&s, &k, &q, &s));  /* s = s * k mod q */
++    /* t is now hash(M)*ar */
++    CHECK_MPI_OK(mp_add(&t, &x, &s)); /* s = t + x */
++    /* s is now (HASH(M)+x*r)*ar */
++    CHECK_MPI_OK(mp_mulmod(&s, &k, &q, &s)); /* s = s * k mod q */
++    /* s is now (HASH(M)+x*r)*ar*(k*ar)**-1 = (k**-1)*(HASH(M)+x*r) */
++
+     /*
+     ** verify r != 0 and s != 0
+     ** mentioned as optional in FIPS 186-1.
+     */
+     if (mp_cmp_z(&r) == 0 || mp_cmp_z(&s) == 0) {
+         PORT_SetError(SEC_ERROR_NEED_RANDOM);
+         rv = SECFailure;
+         goto cleanup;
+