1 | /* **************************************************************************** |
---|
2 | * |
---|
3 | * PTeID Middleware Project. |
---|
4 | * Copyright(C) 2014 |
---|
5 | * Andre Guerreiro <andre.guerreiro@caixamagica.pt> |
---|
6 | * Card interaction necessary for the Change Address Operation |
---|
7 | * mainly Diffie-Hellman key agreement and mutual authentication with CVC certificates |
---|
8 | **/ |
---|
9 | |
---|
10 | #include "APLCard.h" |
---|
11 | #include "APLCardPteid.h" |
---|
12 | #include "Reader.h" |
---|
13 | #include "SAM.h" |
---|
14 | #include <string> |
---|
15 | #include <cstring> |
---|
16 | #include <iostream> |
---|
17 | #include <openssl/rsa.h> |
---|
18 | #include <openssl/bn.h> |
---|
19 | #include <openssl/pkcs7.h> |
---|
20 | |
---|
21 | namespace eIDMW |
---|
22 | { |
---|
23 | |
---|
24 | SAM::SAM(APL_Card *card) |
---|
25 | { |
---|
26 | m_card = card; |
---|
27 | } |
---|
28 | |
---|
29 | |
---|
30 | void binToHex(const unsigned char *in, size_t in_len, char *out, size_t out_len) |
---|
31 | { |
---|
32 | unsigned int n; |
---|
33 | char *pos; |
---|
34 | |
---|
35 | pos = out; |
---|
36 | for (n = 0; n < in_len; n++) { |
---|
37 | sprintf(pos, "%02x", in[n]); |
---|
38 | pos += 2; |
---|
39 | } |
---|
40 | *pos = '\0'; |
---|
41 | } |
---|
42 | |
---|
43 | char * SAM::_getDH_Param(char specific_byte, unsigned long offset) |
---|
44 | { |
---|
45 | char * hex_param = NULL; |
---|
46 | unsigned char apdu_dh[] = {0x00, 0xCB, 0x00, 0xFF, 0x0A, 0xA6, 0x03, 0x83, 0x01, 0x32, 0x7F, 0x49, 0x02, |
---|
47 | 0x00, 0x00}; |
---|
48 | |
---|
49 | apdu_dh[13] = specific_byte; |
---|
50 | |
---|
51 | CByteArray dh_param_ba = m_card->getCalReader()->SendAPDU(CByteArray(apdu_dh, sizeof(apdu_dh))); |
---|
52 | |
---|
53 | //Parse the DH parameter out of the response APDU excluding also the SW12 bytes |
---|
54 | CByteArray dh_param_final = dh_param_ba.GetBytes(offset); |
---|
55 | dh_param_final.Chop(2); |
---|
56 | |
---|
57 | size_t hex_len = 2 * dh_param_final.Size()+ 1; |
---|
58 | hex_param = (char *)malloc(hex_len); |
---|
59 | //CByteArray::ToString(bool bAddSpace, bool bOneLine, unsigned long ulOffset, unsigned long ulLen) |
---|
60 | |
---|
61 | binToHex(dh_param_final.GetBytes(), dh_param_final.Size(), hex_param, hex_len); |
---|
62 | return hex_param; |
---|
63 | } |
---|
64 | |
---|
65 | |
---|
66 | //Return DER encoding of the pubkey |
---|
67 | char * SAM::_getCVCPublicKey() |
---|
68 | { |
---|
69 | const int offset_mod = 14; |
---|
70 | const int offset_exp = 13; |
---|
71 | unsigned char *rsa_der = NULL; |
---|
72 | char *cvc_key = NULL; |
---|
73 | RSA *key_tmp = RSA_new(); |
---|
74 | EVP_PKEY *evp_key = EVP_PKEY_new(); |
---|
75 | |
---|
76 | unsigned char apdu_cvc_pubkey_mod[] = {0x00, 0xCB, 0x00, |
---|
77 | 0xFF, 0x0A, 0xB6, 0x03, 0x83, 0x01, 0x44, 0x7F, 0x49, 0x02, 0x81, 0x00}; |
---|
78 | |
---|
79 | unsigned char apdu_cvc_pubkey_exponent[] = {0x00, 0xCB, 0x00, 0xFF, 0x0A, 0xB6, 0x03, |
---|
80 | 0x83, 0x01, 0x44, 0x7F, 0x49, 0x02, 0x82, 0x00}; |
---|
81 | |
---|
82 | CByteArray cvc_modulus = m_card->getCalReader()->SendAPDU( |
---|
83 | CByteArray(apdu_cvc_pubkey_mod, sizeof(apdu_cvc_pubkey_mod))); |
---|
84 | |
---|
85 | CByteArray cvc_exponent = m_card->getCalReader()->SendAPDU |
---|
86 | (CByteArray(apdu_cvc_pubkey_exponent, sizeof(apdu_cvc_pubkey_exponent))); |
---|
87 | |
---|
88 | //Remove SW12 bytes from the responses |
---|
89 | cvc_modulus.Chop(2); |
---|
90 | cvc_exponent.Chop(2); |
---|
91 | |
---|
92 | std::cerr << "DEBUG: _getCVCPublicKey(): modulus len=" << cvc_modulus.Size() << std::endl; |
---|
93 | std::cerr << "DEBUG: _getCVCPublicKey(): exp len=" << cvc_exponent.Size() << std::endl; |
---|
94 | |
---|
95 | unsigned char * mod_bytes = cvc_modulus.GetBytes(); |
---|
96 | unsigned char * exp_bytes = cvc_exponent.GetBytes(); |
---|
97 | |
---|
98 | key_tmp->n = BN_bin2bn(mod_bytes+offset_mod, |
---|
99 | 128, key_tmp->n); |
---|
100 | key_tmp->e = BN_bin2bn(exp_bytes+offset_exp, |
---|
101 | 3, key_tmp->e); |
---|
102 | |
---|
103 | EVP_PKEY_assign_RSA(evp_key, key_tmp); |
---|
104 | |
---|
105 | int der_len = i2d_PUBKEY(evp_key, &rsa_der); |
---|
106 | size_t out_len = der_len*2 +1; |
---|
107 | cvc_key = (char *)malloc(out_len); |
---|
108 | |
---|
109 | binToHex(rsa_der, der_len, cvc_key, out_len); |
---|
110 | free(rsa_der); |
---|
111 | |
---|
112 | return cvc_key; |
---|
113 | } |
---|
114 | |
---|
115 | char * SAM::_getCardAuthPublicKey() |
---|
116 | { |
---|
117 | EVP_PKEY *evp_key = EVP_PKEY_new(); |
---|
118 | RSA *key_tmp = RSA_new(); |
---|
119 | char *card_auth_pubkey = NULL; |
---|
120 | unsigned char *rsa_der = NULL; |
---|
121 | |
---|
122 | APL_EIDCard *pcard = dynamic_cast<APL_EIDCard *>(m_card); |
---|
123 | APLPublicKey * key = pcard->getID().getCardAuthKeyObj(); |
---|
124 | |
---|
125 | unsigned char * mod_bytes = key->getModulus()->GetBytes(); |
---|
126 | unsigned char * exp_bytes = key->getExponent()->GetBytes(); |
---|
127 | |
---|
128 | key_tmp->n = BN_bin2bn(mod_bytes, key->getModulus()->Size(), key_tmp->n); |
---|
129 | key_tmp->e = BN_bin2bn(exp_bytes, key->getExponent()->Size(), key_tmp->e); |
---|
130 | EVP_PKEY_assign_RSA(evp_key, key_tmp); |
---|
131 | |
---|
132 | int der_len = i2d_PUBKEY(evp_key, &rsa_der); |
---|
133 | //int der_len = i2d_RSAPublicKey(key_tmp, &rsa_der); |
---|
134 | size_t out_len = der_len*2 +1; |
---|
135 | card_auth_pubkey = (char *)malloc(out_len); |
---|
136 | |
---|
137 | binToHex(rsa_der, der_len, card_auth_pubkey, out_len); |
---|
138 | |
---|
139 | free(rsa_der); |
---|
140 | return card_auth_pubkey; |
---|
141 | } |
---|
142 | |
---|
143 | char * SAM::_getSODCert() |
---|
144 | { |
---|
145 | char *sod_cert = NULL; |
---|
146 | PKCS7 * p7 = NULL; |
---|
147 | CByteArray sod_data = m_card->getRawData(APL_RAWDATA_SOD); |
---|
148 | const unsigned char *temp = sod_data.GetBytes(); |
---|
149 | unsigned char *out = NULL; |
---|
150 | long len = sod_data.Size(); |
---|
151 | |
---|
152 | //Ignoring the first 4 bytes allows us to parse the SOD file as a PKCS7 object |
---|
153 | temp += 4; |
---|
154 | |
---|
155 | p7 = d2i_PKCS7(NULL, (const unsigned char **)&temp, len); |
---|
156 | |
---|
157 | STACK_OF(X509) *pSigners = PKCS7_get0_signers(p7, NULL, 0); |
---|
158 | |
---|
159 | X509 *signer_cert = sk_X509_value(pSigners, 0); |
---|
160 | int der_len = i2d_X509(signer_cert, &out); |
---|
161 | |
---|
162 | size_t hex_len = der_len*2 +1; |
---|
163 | sod_cert = (char *)malloc(hex_len); |
---|
164 | |
---|
165 | binToHex(out, der_len, sod_cert, hex_len); |
---|
166 | |
---|
167 | return sod_cert; |
---|
168 | } |
---|
169 | |
---|
170 | char *SAM::getSerialNumberIAS101() |
---|
171 | { |
---|
172 | const unsigned char apdu_serial[] = {0x00, 0xCA, 0x02, 0x5A, 0x0D}; |
---|
173 | |
---|
174 | CByteArray serial_apdu = CByteArray(apdu_serial, |
---|
175 | sizeof(apdu_serial)); |
---|
176 | |
---|
177 | CByteArray serial_ba = m_card->getCalReader()->SendAPDU(serial_apdu); |
---|
178 | |
---|
179 | serial_ba.Chop(2); |
---|
180 | |
---|
181 | char *serial = (char *)malloc(serial_ba.Size()*2+1); |
---|
182 | |
---|
183 | binToHex(serial_ba.GetBytes(), serial_ba.Size(), serial, serial_ba.Size()*2+1); |
---|
184 | |
---|
185 | return serial; |
---|
186 | } |
---|
187 | |
---|
188 | bool checkResultSW12(CByteArray &result) |
---|
189 | { |
---|
190 | unsigned long ulRespLen = result.Size(); |
---|
191 | unsigned int ulSW12 = (unsigned int)(256 * result.GetByte(ulRespLen - 2) + result.GetByte(ulRespLen - 1)); |
---|
192 | |
---|
193 | return ulSW12 == 0x9000; |
---|
194 | } |
---|
195 | |
---|
196 | |
---|
197 | bool SAM::sendKIFD(char *kifd) |
---|
198 | { |
---|
199 | const unsigned char verify_apdu_key_agreement[] = {0x00, 0x22, 0x41, 0xA6, 0x89, 0x83, 0x01, 0x32, 0x95, 0x01, 0x80, 0x91, 0x81, 0x80}; |
---|
200 | CByteArray sendKIFD_apdu = CByteArray(verify_apdu_key_agreement, |
---|
201 | sizeof(verify_apdu_key_agreement)); |
---|
202 | |
---|
203 | //Create ByteArray from hex-encoded string |
---|
204 | CByteArray kifd_ba(std::string(kifd), true); |
---|
205 | sendKIFD_apdu.Append(kifd_ba); |
---|
206 | |
---|
207 | CByteArray resp = m_card->getCalReader()->SendAPDU(sendKIFD_apdu); |
---|
208 | |
---|
209 | if (!checkResultSW12(resp)) |
---|
210 | { |
---|
211 | fprintf(stderr, "SendKIFD() failed!\n"); |
---|
212 | return false; |
---|
213 | } |
---|
214 | |
---|
215 | return true; |
---|
216 | } |
---|
217 | |
---|
218 | char *SAM::getKICC() |
---|
219 | { |
---|
220 | char *kicc_hex = NULL; |
---|
221 | const int KICC_LEN = 128; |
---|
222 | const int KICC_HEX_LEN = 128*2 +1; |
---|
223 | const int KICC_OFFSET = 6; |
---|
224 | unsigned char apdu_kicc[] = {0x00, 0xCB, 0x00, 0xFF, 0x04, 0xA6, 0x02, 0x91, 0x00}; |
---|
225 | |
---|
226 | CByteArray kicc_ba = m_card->getCalReader()->SendAPDU(CByteArray(apdu_kicc, sizeof(apdu_kicc))); |
---|
227 | if (!checkResultSW12(kicc_ba)) |
---|
228 | { |
---|
229 | fprintf(stderr, "SendKIFD() failed!\n"); |
---|
230 | return NULL; |
---|
231 | } |
---|
232 | kicc_ba.Chop(2); |
---|
233 | |
---|
234 | kicc_hex = (char *)malloc(KICC_HEX_LEN); |
---|
235 | |
---|
236 | binToHex(kicc_ba.GetBytes()+KICC_OFFSET, KICC_LEN, kicc_hex, KICC_HEX_LEN); |
---|
237 | |
---|
238 | return kicc_hex; |
---|
239 | } |
---|
240 | |
---|
241 | |
---|
242 | bool SAM::verifyCert_CV_IFD(char * cv_cert) |
---|
243 | { |
---|
244 | unsigned char ba_apdu_pso_verify[] = {0x00, 0x2A, 0x00, 0xBE, 0xD1}; |
---|
245 | unsigned char apdu_se_verify_cert[] = {0x00, 0x22, 0x41, 0xB6, 0x06, 0x83, 0x01, 0x44, 0x95, 0x01, 0x80}; |
---|
246 | CByteArray apdu_pso_verify(ba_apdu_pso_verify, sizeof(ba_apdu_pso_verify)); |
---|
247 | |
---|
248 | if (cv_cert == NULL || strlen(cv_cert) == 0) |
---|
249 | { |
---|
250 | fprintf(stderr, "Invalid cv_cert in SAM::VerifyCert_CV_IFD()!\n"); |
---|
251 | return false; |
---|
252 | } |
---|
253 | |
---|
254 | CByteArray resp = m_card->getCalReader()->SendAPDU(CByteArray(apdu_se_verify_cert, sizeof(apdu_se_verify_cert))); |
---|
255 | |
---|
256 | if (!checkResultSW12(resp)) |
---|
257 | { |
---|
258 | fprintf(stderr, "VerifyCert_CV_IFD() p1 failed!\n"); |
---|
259 | return false; |
---|
260 | } |
---|
261 | |
---|
262 | CByteArray cvcert_ba(std::string(cv_cert), true); |
---|
263 | apdu_pso_verify.Append(cvcert_ba); |
---|
264 | |
---|
265 | resp = m_card->getCalReader()->SendAPDU(apdu_pso_verify); |
---|
266 | if (!checkResultSW12(resp)) |
---|
267 | { |
---|
268 | fprintf(stderr, "VerifyCert_CV_IFD() p2 failed!\n"); |
---|
269 | return false; |
---|
270 | } |
---|
271 | |
---|
272 | return true; |
---|
273 | } |
---|
274 | |
---|
275 | |
---|
276 | /* |
---|
277 | The parameter external_auth indicates the Security environment that's going to be used in the MSE SET command |
---|
278 | TODO: This should be parameterized to be reused in both cards scenarios |
---|
279 | */ |
---|
280 | char *SAM::generateChallenge() |
---|
281 | { |
---|
282 | char *challenge = NULL; |
---|
283 | //MSE SET External auth or Mutual Auth |
---|
284 | |
---|
285 | // This includes the terminal SN associated with the server CVC cert |
---|
286 | //unsigned char ba1_test[] = {0x00, 0x22, 0x41 ,0xA4 ,0x0D ,0x95 ,0x01 ,0x80 ,0x83 ,0x08 ,0x04 ,0x06 ,0x02 ,0x02 ,0x00 ,0x04 ,0x03 ,0x07}; |
---|
287 | unsigned char ba1_production[] = {0x00, 0x22, 0x41 ,0xA4 ,0x0D ,0x95 ,0x01, 0x80, 0x83, 0x08, 0x02, 0x06, 0x00, 0x07, 0x01, 0x07, 0x08, 0x08}; |
---|
288 | |
---|
289 | //GET CHALLENGE |
---|
290 | unsigned char ba2[] = {0x80, 0x84, 0x00, 0x00, 0x08}; |
---|
291 | |
---|
292 | m_card->getCalReader()->SendAPDU(CByteArray(ba1_production, sizeof(ba1_production))); |
---|
293 | |
---|
294 | CByteArray resp = m_card->getCalReader()->SendAPDU(CByteArray(ba2, sizeof(ba2))); |
---|
295 | |
---|
296 | resp.Chop(2); |
---|
297 | |
---|
298 | challenge = (char *)malloc(resp.Size()*2 +1); |
---|
299 | |
---|
300 | binToHex(resp.GetBytes(), 8, challenge, resp.Size()*2 +1); |
---|
301 | |
---|
302 | return challenge; |
---|
303 | |
---|
304 | } |
---|
305 | |
---|
306 | std::vector<char *> SAM::sendSequenceOfPrebuiltAPDUs(std::vector<char *> &apdu_array) |
---|
307 | { |
---|
308 | int i = 0; |
---|
309 | std::vector <char *> responses; |
---|
310 | while(i != apdu_array.size()) |
---|
311 | { |
---|
312 | char * tmp = sendPrebuiltAPDU(apdu_array.at(i)); |
---|
313 | fprintf(stderr, "APDU %d -> result: %s\n", i, tmp); |
---|
314 | responses.push_back(tmp); |
---|
315 | i++; |
---|
316 | } |
---|
317 | return responses; |
---|
318 | } |
---|
319 | |
---|
320 | char *SAM::sendPrebuiltAPDU(char *apdu_string) |
---|
321 | { |
---|
322 | char *resp_string = NULL; |
---|
323 | |
---|
324 | CByteArray mse_ba(std::string(apdu_string), true); |
---|
325 | CByteArray resp = m_card->getCalReader()->SendAPDU(mse_ba); |
---|
326 | |
---|
327 | resp_string = (char *)malloc(resp.Size()*2 +1); |
---|
328 | |
---|
329 | binToHex(resp.GetBytes(), resp.Size(), resp_string, resp.Size()*2 +1); |
---|
330 | |
---|
331 | return resp_string; |
---|
332 | } |
---|
333 | |
---|
334 | bool SAM::verifySignedChallenge(char *signed_challenge) |
---|
335 | { |
---|
336 | unsigned char external_authenticate[] = {0x80, 0x82, 0x00, 0x00, 0x88}; |
---|
337 | CByteArray ba1(external_authenticate, sizeof(external_authenticate)); |
---|
338 | |
---|
339 | if (signed_challenge == NULL || strlen(signed_challenge) == 0) |
---|
340 | { |
---|
341 | fprintf(stderr, "Invalid signed_challenge in SAM::verifySignedChallenge()!\n"); |
---|
342 | return false; |
---|
343 | } |
---|
344 | |
---|
345 | CByteArray signed_ba(std::string(signed_challenge), true); |
---|
346 | ba1.Append(signed_ba); |
---|
347 | |
---|
348 | CByteArray resp = m_card->getCalReader()->SendAPDU(ba1); |
---|
349 | |
---|
350 | return checkResultSW12(resp); |
---|
351 | } |
---|
352 | |
---|
353 | /* Fetch all the parameters needed from the card */ |
---|
354 | bool SAM::getDHParams(DHParams *dh_struct) |
---|
355 | { |
---|
356 | |
---|
357 | dh_struct->dh_g = _getDH_Param(0x88, 12); |
---|
358 | dh_struct->dh_p = _getDH_Param(0x86, 12); |
---|
359 | dh_struct->dh_q = _getDH_Param(0x87, 10); |
---|
360 | |
---|
361 | dh_struct->cvc_ca_public_key = _getCVCPublicKey(); |
---|
362 | dh_struct->card_auth_public_key = _getCardAuthPublicKey(); |
---|
363 | dh_struct->certificateChain = _getSODCert(); |
---|
364 | dh_struct->version = m_card->getType() == APL_CARDTYPE_PTEID_IAS07 ? 1 : 2; |
---|
365 | |
---|
366 | return true; |
---|
367 | } |
---|
368 | |
---|
369 | |
---|
370 | } |
---|
371 | |
---|
372 | |
---|
373 | |
---|
374 | |
---|