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