summary refs log tree commit diff
path: root/gnu/packages/patches/nss-CVE-2020-12399.patch
blob: 0d91b655e2fb5b1fa15bee6a04922cc2cb84941c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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;