From 7bc396bf353c5550c49b3f8791b34072ba417d90 Mon Sep 17 00:00:00 2001 From: Mark H Weaver Date: Tue, 2 Jun 2020 14:05:46 -0400 Subject: 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. --- gnu/local.mk | 1 + gnu/packages/nss.scm | 9 ++ gnu/packages/patches/nss-CVE-2020-12399.patch | 138 ++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 gnu/packages/patches/nss-CVE-2020-12399.patch 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: + +but with "nss/" inserted into the file name to patch. + +# HG changeset patch +# User Robert Relyea +# 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(¶ms->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; + -- cgit v1.2.3