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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
| | Fix CVE-2018-0495:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0495
https://www.nccgroup.trust/us/our-research/technical-advisory-return-of-the-hidden-number-problem/
Patch copied from upstream source repository:
https://github.com/openssl/openssl/commit/0c27d793745c7837b13646302b6890a556b7017a
From 0c27d793745c7837b13646302b6890a556b7017a Mon Sep 17 00:00:00 2001
From: Matt Caswell <matt@openssl.org>
Date: Fri, 25 May 2018 12:10:13 +0100
Subject: [PATCH] Add blinding to an ECDSA signature
Keegan Ryan (NCC Group) has demonstrated a side channel attack on an
ECDSA signature operation. During signing the signer calculates:
s:= k^-1 * (m + r * priv_key) mod order
The addition operation above provides a sufficient signal for a
flush+reload attack to derive the private key given sufficient signature
operations.
As a mitigation (based on a suggestion from Keegan) we add blinding to
the operation so that:
s := k^-1 * blind^-1 (blind * m + blind * r * priv_key) mod order
Since this attack is a localhost side channel only no CVE is assigned.
Reviewed-by: Rich Salz <rsalz@openssl.org>
---
CHANGES | 4 +++
crypto/ec/ecdsa_ossl.c | 70 +++++++++++++++++++++++++++++++++++++-----
2 files changed, 67 insertions(+), 7 deletions(-)
diff --git a/CHANGES b/CHANGES
index bfd0bcd402..b749d9ed96 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,10 @@
Changes between 1.1.0h and 1.1.0i [xx XXX xxxx]
+ *) Add blinding to an ECDSA signature to protect against side channel attacks
+ discovered by Keegan Ryan (NCC Group).
+ [Matt Caswell]
+
*) When unlocking a pass phrase protected PEM file or PKCS#8 container, we
now allow empty (zero character) pass phrases.
[Richard Levitte]
diff --git a/crypto/ec/ecdsa_ossl.c b/crypto/ec/ecdsa_ossl.c
index 72e2f0f28b..449be0e92a 100644
--- a/crypto/ec/ecdsa_ossl.c
+++ b/crypto/ec/ecdsa_ossl.c
@@ -210,7 +210,8 @@ ECDSA_SIG *ossl_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
EC_KEY *eckey)
{
int ok = 0, i;
- BIGNUM *kinv = NULL, *s, *m = NULL, *tmp = NULL;
+ BIGNUM *kinv = NULL, *s, *m = NULL, *tmp = NULL, *blind = NULL;
+ BIGNUM *blindm = NULL;
const BIGNUM *order, *ckinv;
BN_CTX *ctx = NULL;
const EC_GROUP *group;
@@ -243,8 +244,18 @@ ECDSA_SIG *ossl_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
}
s = ret->s;
- if ((ctx = BN_CTX_new()) == NULL ||
- (tmp = BN_new()) == NULL || (m = BN_new()) == NULL) {
+ ctx = BN_CTX_secure_new();
+ if (ctx == NULL) {
+ ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ BN_CTX_start(ctx);
+ tmp = BN_CTX_get(ctx);
+ m = BN_CTX_get(ctx);
+ blind = BN_CTX_get(ctx);
+ blindm = BN_CTX_get(ctx);
+ if (blindm == NULL) {
ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_MALLOC_FAILURE);
goto err;
}
@@ -284,18 +295,64 @@ ECDSA_SIG *ossl_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
}
}
- if (!BN_mod_mul(tmp, priv_key, ret->r, order, ctx)) {
+ /*
+ * The normal signature calculation is:
+ *
+ * s := k^-1 * (m + r * priv_key) mod order
+ *
+ * We will blind this to protect against side channel attacks
+ *
+ * s := k^-1 * blind^-1 * (blind * m + blind * r * priv_key) mod order
+ */
+
+ /* Generate a blinding value */
+ do {
+ if (!BN_rand(blind, BN_num_bits(order) - 1, BN_RAND_TOP_ANY,
+ BN_RAND_BOTTOM_ANY))
+ goto err;
+ } while (BN_is_zero(blind));
+ BN_set_flags(blind, BN_FLG_CONSTTIME);
+ BN_set_flags(blindm, BN_FLG_CONSTTIME);
+ BN_set_flags(tmp, BN_FLG_CONSTTIME);
+
+ /* tmp := blind * priv_key * r mod order */
+ if (!BN_mod_mul(tmp, blind, priv_key, order, ctx)) {
ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
goto err;
}
- if (!BN_mod_add_quick(s, tmp, m, order)) {
+ if (!BN_mod_mul(tmp, tmp, ret->r, order, ctx)) {
ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
goto err;
}
+
+ /* blindm := blind * m mod order */
+ if (!BN_mod_mul(blindm, blind, m, order, ctx)) {
+ ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+ goto err;
+ }
+
+ /* s : = (blind * priv_key * r) + (blind * m) mod order */
+ if (!BN_mod_add_quick(s, tmp, blindm, order)) {
+ ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+ goto err;
+ }
+
+ /* s:= s * blind^-1 mod order */
+ if (BN_mod_inverse(blind, blind, order, ctx) == NULL) {
+ ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+ goto err;
+ }
+ if (!BN_mod_mul(s, s, blind, order, ctx)) {
+ ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+ goto err;
+ }
+
+ /* s := s * k^-1 mod order */
if (!BN_mod_mul(s, s, ckinv, order, ctx)) {
ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
goto err;
}
+
if (BN_is_zero(s)) {
/*
* if kinv and r have been supplied by the caller don't to
@@ -317,9 +374,8 @@ ECDSA_SIG *ossl_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
ECDSA_SIG_free(ret);
ret = NULL;
}
+ BN_CTX_end(ctx);
BN_CTX_free(ctx);
- BN_clear_free(m);
- BN_clear_free(tmp);
BN_clear_free(kinv);
return ret;
}
--
2.17.1
|