summaryrefslogtreecommitdiff
blob: 9fe9f58d4763374293bc7c5369fedc9926f79b89 (plain)
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
From b110ec2277ba33e0935f3d465a5413f669d8aefc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
Date: Tue, 11 Oct 2016 16:15:43 +0200
Subject: [PATCH] Port to OpenSSL 1.1.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

OpenSSL 1.1.0 hid ECDSA structure internals and provided methods
instead.

This patch uses the methods and provides their copies in the case of
older OpenSSL. Because the new OpenSSL API, ECDSA_SIG_set0(), cannot
set curve parameters individually and ECDSA_SIG_get0() returns yet
another reference, it's necessary to duplicate the other unchanged
paramater when calling set_r() or set_s().

This patch also stops exporting ECDSA_METHOD functions that were
removed from the new OpenSSL.

CPAN RT#118330

Signed-off-by: Petr Písař <ppisar@redhat.com>
---
 ECDSA.xs                | 105 ++++++++++++++++++++++++++++++++++++++++--------
 t/Crypt-OpenSSL-ECDSA.t |  13 +++++-
 2 files changed, 100 insertions(+), 18 deletions(-)

diff --git a/ECDSA.xs b/ECDSA.xs
index 4016368..3d6e2d1 100644
--- a/ECDSA.xs
+++ b/ECDSA.xs
@@ -7,9 +7,35 @@
 
 #include <openssl/ecdsa.h>
 #include <openssl/err.h>
+#include <openssl/bn.h>
 
 #include "const-c.inc"
 
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+#include <openssl/ec.h>
+#else
+/* ECDSA_SIG_get0() and ECDSA_SIG_set0() copied from OpenSSL 1.1.0b. */
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+    const BIGNUM **ps) {
+    if (pr != NULL)
+        *pr = sig->r;
+    if (ps != NULL)
+        *ps = sig->s;
+}
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+    if (r == NULL || s == NULL)
+        return 0;
+    BN_clear_free(sig->r);
+    BN_clear_free(sig->s);
+    sig->r = r;
+    sig->s = s;
+    return 1;
+}
+#endif
+
 MODULE = Crypt::OpenSSL::ECDSA		PACKAGE = Crypt::OpenSSL::ECDSA
 
 PROTOTYPES: ENABLE
@@ -17,7 +43,9 @@ INCLUDE: const-xs.inc
 
 BOOT:
     ERR_load_crypto_strings();
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L && OPENSSL_VERSION_NUMBER < 0x10100000L
     ERR_load_ECDSA_strings();
+#endif
 
 #ECDSA_SIG *
 #ECDSA_SIG_new()
@@ -61,10 +89,16 @@ ECDSA_do_verify(const unsigned char *dgst, const ECDSA_SIG *sig, EC_KEY* eckey);
 	OUTPUT:
 		RETVAL
 
-# These ECDSA_METHOD functions only became available in 1.0.2
+# These ECDSA_METHOD functions only became available in 1.0.2,
+# but some of them removed again in 1.1.0.
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
 
+int	  
+ECDSA_size(const EC_KEY *eckey)
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
 const ECDSA_METHOD *
 ECDSA_OpenSSL()
 
@@ -77,9 +111,6 @@ ECDSA_get_default_method()
 int 	  
 ECDSA_set_method(EC_KEY *eckey, const ECDSA_METHOD *meth)
 
-int	  
-ECDSA_size(const EC_KEY *eckey)
-
 ECDSA_METHOD *
 ECDSA_METHOD_new(ECDSA_METHOD *ecdsa_method=0)
 
@@ -95,7 +126,7 @@ ECDSA_METHOD_set_name(ECDSA_METHOD *ecdsa_method, char *name)
 void 
 ERR_load_ECDSA_strings()
 
-
+#endif
 #endif
 
 
@@ -135,11 +166,13 @@ SV *
 get_r(ecdsa_sig)
         ECDSA_SIG *ecdsa_sig
     PREINIT:
+        const BIGNUM *r;
         unsigned char *to;
         STRLEN len;
     CODE:
         to = malloc(sizeof(char) * 128);
-        len = BN_bn2bin(ecdsa_sig->r, to);
+        ECDSA_SIG_get0(ecdsa_sig, &r, NULL);
+        len = BN_bn2bin(r, to);
         RETVAL = newSVpvn((const char*)to, len);
         free(to);
     OUTPUT:
@@ -149,11 +182,13 @@ SV *
 get_s(ecdsa_sig)
         ECDSA_SIG *ecdsa_sig
     PREINIT:
+        const BIGNUM *s;
         unsigned char *to;
         STRLEN len;
     CODE:
         to = malloc(sizeof(char) * 128);
-        len = BN_bn2bin(ecdsa_sig->s, to);
+        ECDSA_SIG_get0(ecdsa_sig, NULL, &s);
+        len = BN_bn2bin(s, to);
         RETVAL = newSVpvn((const char*)to, len);
         free(to);
     OUTPUT:
@@ -164,26 +199,62 @@ set_r(ecdsa_sig, r_SV)
         ECDSA_SIG *ecdsa_sig
         SV * r_SV
     PREINIT:
-	char *s;
+	    char *string;
         STRLEN len;
+        BIGNUM *r;
+        BIGNUM *s;
+        const BIGNUM *old_s;
     CODE:
-        s = SvPV(r_SV, len);
-        if (ecdsa_sig->r)
-            BN_free(ecdsa_sig->r);
-        ecdsa_sig->r = BN_bin2bn((const unsigned char *)s, len, NULL);
+        string = SvPV(r_SV, len);
+        r = BN_bin2bn((const unsigned char *)string, len, NULL);
+        if (NULL == r)
+            croak("Could not convert ECDSA parameter string to big number");
+        ECDSA_SIG_get0(ecdsa_sig, NULL, &old_s);
+        if (NULL == old_s) {
+            s = BN_new();
+        } else {
+            s = BN_dup(old_s);
+        }
+        if (NULL == s) {
+            BN_free(r);
+            croak("Could not duplicate unchanged ECDSA parameter");
+        }
+        if (!ECDSA_SIG_set0(ecdsa_sig, r, s)) {
+            BN_free(r);
+            BN_free(s);
+            croak("Could not store ECDSA parameters");
+        }
 
 void
 set_s(ecdsa_sig, s_SV)
         ECDSA_SIG *ecdsa_sig
         SV * s_SV
     PREINIT:
-	char *s;
+	    char *string;
         STRLEN len;
+        BIGNUM *r;
+        BIGNUM *s;
+        const BIGNUM *old_r;
     CODE:
-        s = SvPV(s_SV, len);
-        if (ecdsa_sig->s)
-            BN_free(ecdsa_sig->s);
-        ecdsa_sig->s = BN_bin2bn((const unsigned char *)s, len, NULL);
+        string = SvPV(s_SV, len);
+        s = BN_bin2bn((const unsigned char *)string, len, NULL);
+        if (NULL == s)
+            croak("Could not convert ECDSA parameter string to big number");
+        ECDSA_SIG_get0(ecdsa_sig, &old_r, NULL);
+        if (NULL == old_r) {
+            r = BN_new();
+        } else {
+            r = BN_dup(old_r);
+        }
+        if (NULL == r) {
+            BN_free(s);
+            croak("Could not duplicate unchanged ECDSA parameter");
+        }
+        if (!ECDSA_SIG_set0(ecdsa_sig, r, s)) {
+            BN_free(r);
+            BN_free(s);
+            croak("Could not store ECDSA parameters");
+        }
 
 
 
diff --git a/t/Crypt-OpenSSL-ECDSA.t b/t/Crypt-OpenSSL-ECDSA.t
index 7ab584f..3c02025 100644
--- a/t/Crypt-OpenSSL-ECDSA.t
+++ b/t/Crypt-OpenSSL-ECDSA.t
@@ -6,7 +6,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 21;
+use Test::More tests => 25;
 BEGIN { use_ok('Crypt::OpenSSL::ECDSA'); use_ok('Crypt::OpenSSL::EC');  };
 
 
@@ -77,6 +77,17 @@ $ret = Crypt::OpenSSL::ECDSA::ECDSA_do_verify($digest, $sig, $key);
 ok($ret);
 undef $sig;
 
+# Test a signature can be built from scratch
+$sig = Crypt::OpenSSL::ECDSA::ECDSA_SIG->new();
+ok($sig, 'Empty Crypt::OpenSSL::ECDSA::ECDSA_SIG object created');
+eval { $sig->set_r($r); };
+ok(!$@, 'R parameter set');
+eval { $sig->set_s($s); };
+ok(!$@, 'S parameter set');
+$ret = Crypt::OpenSSL::ECDSA::ECDSA_do_verify($digest, $sig, $key);
+ok($ret, 'built-from-scratch signature matches');
+undef $sig;
+
 # Testing signing and verifying with the _ex version
 my $dummy = 0;
 $sig = Crypt::OpenSSL::ECDSA::ECDSA_do_sign_ex($digest, \$dummy, \$dummy, $key);
-- 
2.7.4