1 /* -*- mode: c; c-file-style:"stroustrup"; -*- */
2
3 /*
4 * Copyright (c) 2020 Mastercard
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <string.h>
22
23 #include <openssl/ec.h>
24 #include <openssl/ecdsa.h>
25 #include <openssl/err.h>
26 #include <openssl/evp.h>
27
28 #include "pkcs11lib.h"
29 #include "pkcs11_ossl.h"
30
31 typedef struct {
32 pkcs11Context *p11Context;
33 CK_OBJECT_HANDLE hPrivateKey;
34 bool fake;
35 } local_ecdsa_method_st ;
36
37
38 static local_ecdsa_method_st static_st; /* TODO use CTRL API to make reentrant */
39
40
41 static int (*orig_ecdsa_sign) (EVP_PKEY_CTX *ctx,
42 unsigned char *sig, size_t *siglen,
43 const unsigned char *tbs, size_t tbslen) = NULL;
44
45
46
custom_ecdsa_sign(EVP_PKEY_CTX * ctx,unsigned char * sig,size_t * siglen,const unsigned char * tbs,size_t tbslen)47 static int custom_ecdsa_sign( EVP_PKEY_CTX *ctx,
48 unsigned char *sig,
49 size_t *siglen,
50 const unsigned char *tbs,
51 size_t tbslen) {
52
53 static bool entered = false;
54 /* if entered is true, this means we are calling a PKCS#11 implementation */
55 /* that uses also OpenSSL, which is also using the same EVP methods as us */
56 /* In which case, we call the original method (before customization) */
57 /* otherwise we would enter an endless recursion... */
58 /* NOTE: this mechanism is NOT thread-safe */
59
60 if( entered==true ) {
61 return orig_ecdsa_sign(ctx, sig, siglen, tbs, tbslen);
62 } else {
63 entered = true; /* set entered state */
64
65 /* TODO: check if static_st is populated */
66
67 int rc = 0;
68 CK_RV rv;
69 BIGNUM *r = NULL, *s = NULL;
70 ECDSA_SIG *ecdsasig = NULL;
71
72 CK_MECHANISM mechanism = { CKM_ECDSA, NULL_PTR, 0 };
73
74 size_t p11siglen = *siglen;
75 CK_BYTE_PTR p11sig = OPENSSL_zalloc(p11siglen);
76
77 if(!p11sig) {
78 P_ERR();
79 goto err;
80 }
81
82 if(static_st.fake) {
83 /* the buffer that offered to us is in fact oversized, to support DER encoding supplement bytes */
84 /* when invoking C_Sign(), p11siglen gets adjusted to the real value */
85 /* we have to do the same for fake_sign: we must also adjust p11siglen, */
86 /* so we can encapsulate the fake signature accordingly */
87 const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx)); /* TODO error checking */
88 const EC_GROUP *ec_group = EC_KEY_get0_group(ec);
89 const BIGNUM *ec_order = EC_GROUP_get0_order(ec_group);
90 p11siglen = BN_num_bytes(ec_order) * 2;
91 fake_sign(p11sig, p11siglen);
92 } else {
93
94 rv = static_st.p11Context->FunctionList.C_SignInit(static_st.p11Context->Session,
95 &mechanism,
96 static_st.hPrivateKey);
97 if(rv!= CKR_OK) {
98 pkcs11_error(rv,"C_SignInit");
99 goto err;
100 }
101
102 rv = static_st.p11Context->FunctionList.C_Sign(static_st.p11Context->Session,
103 (CK_BYTE_PTR)tbs,
104 tbslen,
105 p11sig,
106 (CK_ULONG_PTR)&p11siglen);
107
108 if(rv!= CKR_OK) {
109 pkcs11_error(rv,"C_Sign");
110 goto err;
111 }
112 }
113
114 /* at this point, we must build a ECDSA_SIG object, using the result of the PKCS#11 computation */
115 ecdsasig = ECDSA_SIG_new();
116 if(!ecdsasig) {
117 P_ERR();
118 goto err;
119 }
120
121 /* making a wild guess here. We are supposed to know the size of our ECDSA signature */
122 /* however, that information can't be inferred from here, unfortunately */
123 /* we will therefore trust the PKCS#11 function, and simply divide by two the signature */
124
125 r = BN_bin2bn( &p11sig[0], p11siglen>>1, NULL);
126 s = BN_bin2bn( &p11sig[p11siglen>>1], p11siglen>>1, NULL);
127
128 if(r==NULL || s==NULL) {
129 P_ERR();
130 goto err;
131 }
132
133 if(!ECDSA_SIG_set0(ecdsasig,r,s)) { /* assign numbers */
134 P_ERR();
135 goto err;
136 }
137 r = s = NULL; /* and forget them */
138
139 int enclen = i2d_ECDSA_SIG(ecdsasig,NULL);
140 if(enclen<0) {
141 P_ERR();
142 goto err;
143 }
144
145 if(*siglen<enclen) {
146 fprintf(stderr,"Error: encoded signature buffer too small!\n");
147 goto err;
148 }
149
150 enclen = i2d_ECDSA_SIG(ecdsasig, &sig);
151 if(enclen<0) {
152 P_ERR();
153 goto err;
154 }
155 *siglen = enclen;
156 rc = 1;
157
158 err:
159 if(ecdsasig) { ECDSA_SIG_free(ecdsasig); }
160 if(r) { BN_free(r); }
161 if(s) { BN_free(s); }
162 if(p11sig) { OPENSSL_free(p11sig); }
163
164 entered = false;
165
166 return rc;
167 }
168 }
169
170
pkcs11_ecdsa_method_setup()171 void pkcs11_ecdsa_method_setup()
172 {
173 static bool initialized = false;
174 const EVP_PKEY_METHOD *orig_ecdsamethod = NULL;
175 EVP_PKEY_METHOD *custom_ecdsamethod = NULL;
176
177 if(initialized) {
178 fprintf(stderr, "Warning: EVP_PKEY_C sign method already customized, skipping setup\n");
179 goto err;
180 }
181
182 /* customizing signing methods */
183 /* we are doing SHA1 / ECDSA signing */
184 orig_ecdsamethod = EVP_PKEY_meth_find(EVP_PKEY_EC);
185 if(orig_ecdsamethod==NULL) {
186 P_ERR();
187 goto err;
188 }
189
190 /* create a new EVP_PKEY_METHOD */
191 custom_ecdsamethod = EVP_PKEY_meth_new( EVP_PKEY_EC, EVP_PKEY_FLAG_AUTOARGLEN);
192 if(custom_ecdsamethod==NULL) {
193 P_ERR();
194 goto err;
195 }
196
197 /* copy all from the EVP_PKEY_METHOD we want to customize */
198 EVP_PKEY_meth_copy( custom_ecdsamethod, orig_ecdsamethod);
199
200 /* For the calls we want to tweak, recover the original fn pointers */
201 int (*orig_ecdsa_sign_init) (EVP_PKEY_CTX *ctx);
202
203 EVP_PKEY_meth_get_sign(orig_ecdsamethod,
204 &orig_ecdsa_sign_init,
205 &orig_ecdsa_sign );
206
207 /* then adapt what we want to, in this case only the sign() fn */
208
209 EVP_PKEY_meth_set_sign(custom_ecdsamethod,
210 orig_ecdsa_sign_init, /* duplicate it, we don't change it */
211 custom_ecdsa_sign ); /* the new, customized method */
212
213 if(!EVP_PKEY_meth_add0(custom_ecdsamethod)) {
214 P_ERR();
215 goto err;
216 }
217
218 custom_ecdsamethod = NULL; /* swallowed by EVP_PKEY_meth_add0 */
219
220 /* now, initialize static member */
221 static_st.p11Context = NULL;
222 static_st.hPrivateKey = NULL_PTR;
223 static_st.fake = false;
224
225 initialized = true;
226
227 err:
228 if(custom_ecdsamethod) { EVP_PKEY_meth_free(custom_ecdsamethod); }
229
230 }
231
232
pkcs11_ecdsa_method_pkcs11_context(pkcs11Context * p11Context,CK_OBJECT_HANDLE hPrivateKey,bool fake)233 void pkcs11_ecdsa_method_pkcs11_context(pkcs11Context * p11Context, CK_OBJECT_HANDLE hPrivateKey, bool fake)
234 {
235 static_st.p11Context = p11Context;
236 static_st.hPrivateKey = hPrivateKey;
237 static_st.fake = fake;
238 }
239