LibLogicalAccess  2.5.0
An Open Source RFID Library
Loading...
Searching...
No Matches
samiso7816commands.hpp
Go to the documentation of this file.
1
7#ifndef LOGICALACCESS_SAMISO7816CARDPROVIDER_HPP
8#define LOGICALACCESS_SAMISO7816CARDPROVIDER_HPP
9
17
18#include <openssl/rand.h>
24
25#include <boost/interprocess/shared_memory_object.hpp>
26#include <boost/interprocess/mapped_region.hpp>
27#include <boost/interprocess/sync/named_mutex.hpp>
28#include <boost/thread/thread_time.hpp>
29
30#include <string>
31#include <vector>
32#include <iostream>
33
35
36#define DEFAULT_SAM_CLA 0x80
37
38namespace logicalaccess
39{
40#define CMD_SAMISO7816 "SAMISO7816"
41
42#ifdef SWIG
43%template(SAMKeyEntrySETAV1Commands) SAMCommands<KeyEntryAV1Information, SETAV1>;
44%template(SAMKeyEntrySETAV2Commands) SAMCommands<KeyEntryAV2Information, SETAV2>;
45#endif
46
50template <typename T, typename S>
51class LLA_READERS_ISO7816_API SAMISO7816Commands : public SAMCommands<T, S>
52{
53 public:
59 {
60 /*
61 # Only one active MIFARE authentication at a time is supported by SAM AV2, so
62 interleaved processing of the commands over differents LCs in parallel is not
63 possible.
64 d_named_mutex.reset(new
65 boost::interprocess::named_mutex(boost::interprocess::open_or_create,
66 "sam_mutex"));
67 bool locked = d_named_mutex->timed_lock(boost::get_system_time() +
68 boost::posix_time::seconds(5));
69
70 boost::interprocess::shared_memory_object
71 shm_obj(boost::interprocess::open_or_create, "sam_memory",
72 boost::interprocess::read_write);
73 boost::interprocess::offset_t size = 0;
74 shm_obj.get_size(size);
75 if (size != 4)
76 shm_obj.truncate(4);
77
78 d_region.reset(new boost::interprocess::mapped_region(shm_obj,
79 boost::interprocess::read_write));
80
81 char *addr = (char*)d_region->get_address();
82
83 if (size != 4 || !locked)
84 std::memset(addr, 0, d_region->get_size());
85
86 unsigned char x = 0;
87 for (; x < d_region->get_size(); ++x)
88 {
89 if (addr[x] == 0)
90 {
91 addr[x] = 1;
92 break;
93 }
94 }
95
96 d_named_mutex->unlock();
97
98 if (x < d_region->get_size())
99 d_cla = DEFAULT_SAM_CLA + x;
100 else
101 THROW_EXCEPTION_WITH_LOG(LibLogicalAccessException, "No channel available.");*/
102 d_cla = DEFAULT_SAM_CLA;
103 d_LastSessionIV.resize(16);
104 }
106 explicit SAMISO7816Commands(std::string ct)
107 : SAMCommands<T, S>(ct)
108 {
109 d_cla = DEFAULT_SAM_CLA;
110 d_LastSessionIV.resize(16);
111 }
112
116 virtual ~SAMISO7816Commands()
117 {
118 /*d_named_mutex->lock();
119 char *addr = (char*)d_region->get_address();
120 addr[d_cla - 0x80] = 0;
121 d_named_mutex->unlock();
122
123 if (!boost::interprocess::shared_memory_object::remove("sam_memory"))
124 LOG(LogLevel::INFOS) << "SAM Shared Memory removed failed. It is probably still
125 open by a process.");*/
126
127 // we do not remove named_mutex - it can still be used by another process
128 }
130 std::shared_ptr<ISO7816ReaderCardAdapter> getISO7816ReaderCardAdapter()
131 {
132 return std::dynamic_pointer_cast<ISO7816ReaderCardAdapter>(
133 this->getReaderCardAdapter());
134 }
136 ByteVector transmit(ByteVector cmd, bool /*first*/ = true,
137 bool /*last*/ = true) override
138 {
139 return getISO7816ReaderCardAdapter()->sendCommand(cmd);
140 }
142 SAMVersion getVersion() override
143 {
144 unsigned char cmd[] = {d_cla, 0x60, 0x00, 0x00, 0x00};
145 ByteVector cmd_vector(cmd, cmd + 5);
146 SAMVersion info;
147 memset(&info, 0x00, sizeof(SAMVersion));
148
149 ByteVector result = transmit(cmd_vector);
150
151 if (result.size() == 33 && result[31] == 0x90 && result[32] == 0x00)
152 memcpy(&info, &result[0], sizeof(info));
153 else
155
156 return info;
157 }
159 ByteVector decipherData(ByteVector data, bool islastdata) override
160 {
161 unsigned char p1 = 0x00;
162 ByteVector datawithlength(3);
163
164 if (!islastdata)
165 p1 = 0xaf;
166 else
167 {
168 datawithlength[0] = (unsigned char)(data.size() & 0xff0000);
169 datawithlength[1] = (unsigned char)(data.size() & 0x00ff00);
170 datawithlength[2] = (unsigned char)(data.size() & 0x0000ff);
171 }
172 datawithlength.insert(datawithlength.end(), data.begin(), data.end());
173
174 unsigned char cmd[] = {
175 d_cla, 0xdd, p1, 0x00, (unsigned char)(datawithlength.size()), 0x00};
176 ByteVector cmd_vector(cmd, cmd + 6);
177 cmd_vector.insert(cmd_vector.end() - 1, datawithlength.begin(),
178 datawithlength.end());
179
180 ByteVector result = transmit(cmd_vector);
181
182 if (result.size() >= 2 && result[result.size() - 2] != 0x90 &&
183 ((p1 == 0x00 && result[result.size() - 1] != 0x00) ||
184 (p1 == 0xaf && result[result.size() - 1] != 0xaf)))
186
187 return ByteVector(result.begin(), result.end() - 2);
188 }
190 ByteVector encipherData(ByteVector data, bool islastdata) override
191 {
192 unsigned char p1 = 0x00;
193
194 if (!islastdata)
195 p1 = 0xaf;
196 unsigned char cmd[] = {d_cla, 0xed, p1, 0x00, (unsigned char)(data.size()), 0x00};
197 ByteVector cmd_vector(cmd, cmd + 6);
198 cmd_vector.insert(cmd_vector.end() - 1, data.begin(), data.end());
199 ByteVector result = transmit(cmd_vector);
200
201 if (result.size() >= 2 && result[result.size() - 2] != 0x90 &&
202 ((p1 == 0x00 && result[result.size() - 1] != 0x00) ||
203 (p1 == 0xaf && result[result.size() - 1] != 0xaf)))
205
206 return ByteVector(result.begin(), result.end() - 2);
207 }
209 void disableKeyEntry(unsigned char keyno) override
210 {
211 unsigned char cmd[] = {d_cla, 0xd8, keyno, 0x00};
212 ByteVector cmd_vector(cmd, cmd + 4);
213
214 ByteVector result = transmit(cmd_vector);
215
216 if (result.size() >= 2 &&
217 (result[result.size() - 2] != 0x90 || result[result.size() - 1] != 0x00))
219 "disableKeyEntry failed.");
220 }
222 ByteVector dumpSessionKey() override
223 {
224 unsigned char cmd[] = {d_cla, 0xd5, 0x00, 0x00, 0x00};
225 ByteVector cmd_vector(cmd, cmd + 5);
226
227 ByteVector result = transmit(cmd_vector);
228
229 if (result.size() >= 2 &&
230 (result[result.size() - 2] != 0x90 || result[result.size() - 1] != 0x00))
231 THROW_EXCEPTION_WITH_LOG(LibLogicalAccessException, "dumpSessionKey failed.");
232
233 return ByteVector(result.begin(), result.end() - 2);
234 }
236 void loadInitVector(ByteVector iv) override
237 {
238 EXCEPTION_ASSERT_WITH_LOG((iv.size() == 0x08 || iv.size() == 0x10),
240 "loadInitVector need a 16 or 8 bytes vector");
241
242 ByteVector loadInitVector = {
243 0x80, 0x71, 0x00, 0x00, static_cast<unsigned char>(iv.size()),
244 };
245 loadInitVector.insert(loadInitVector.end(), iv.begin(), iv.end());
246
247 transmit(loadInitVector);
248 }
250 std::string getSAMTypeFromSAM() override
251 {
252 unsigned char cmd[] = {d_cla, 0x60, 0x00, 0x00, 0x00};
253 ByteVector cmd_vector(cmd, cmd + 5);
254
255 ByteVector result = transmit(cmd_vector);
256
257 if (result.size() > 3)
258 {
259 if (result[result.size() - 3] == 0xA1)
260 return "SAM_AV1";
261 if (result[result.size() - 3] == 0xA2)
262 return "SAM_AV2";
263 if (result[result.size() - 3] == 0xA3)
264 return "SAM_AV3";
265 }
266 return "SAM_NONE";
267 }
269 virtual std::shared_ptr<SAMDESfireCrypto> getCrypto()
270 {
271 return d_crypto;
273 virtual void setCrypto(std::shared_ptr<SAMDESfireCrypto> t)
274 {
275 d_crypto = t;
276 }
278 void lockUnlock(std::shared_ptr<DESFireKey> masterKey, SAMLockUnlock state,
279 unsigned char keyno, unsigned char unlockkeyno,
280 unsigned char unlockkeyversion) override
281 {
282 unsigned char p1_part1 = state;
283 unsigned int le = 2;
284
285 ByteVector maxChainBlocks(3, 0x00); // MaxChainBlocks - unlimited
286
287 ByteVector data_p1(2, 0x00);
288 data_p1[0] = keyno;
289 data_p1[1] = masterKey->getKeyVersion();
290
291 if (state == SwitchAV2Mode)
292 {
293 le += 3;
294 data_p1.insert(data_p1.end(), maxChainBlocks.begin(), maxChainBlocks.end());
295 }
296 else if (state == LockWithSpecifyingKey)
297 {
298 le += 2;
299 data_p1.push_back(unlockkeyno);
300 data_p1.push_back(unlockkeyversion);
301 }
302
303 auto result = this->getISO7816ReaderCardAdapter()->sendAPDUCommand(
304 d_cla, 0x10, p1_part1, 0x00, le, data_p1, 0x00);
305 if (result.getData().size() != 12 || result.getSW1() != 0x90 || result.getSW2() != 0xAF)
306 THROW_EXCEPTION_WITH_LOG(LibLogicalAccessException, "lockUnlock P1 Failed.");
307
308 ByteVector keycipher(masterKey->getData(),
309 masterKey->getData() + masterKey->getLength());
310 std::shared_ptr<openssl::OpenSSLSymmetricCipher> cipher(new openssl::AESCipher());
311 ByteVector emptyIV(16), rnd1;
312
313 /* Create rnd2 for p3 - CMAC: rnd2 | P2 | other data */
314 ByteVector rnd2 = result.getData();
315 rnd2.push_back(p1_part1); // P1_part1
316 rnd2.insert(rnd2.end(), data_p1.begin() + 2, data_p1.end()); // last data
317
318 /* ZeroPad */
319 if (state == LockWithSpecifyingKey)
320 {
321 rnd2.push_back(0x00);
322 }
323 else if (state != SwitchAV2Mode)
324 {
325 rnd2.resize(rnd2.size() + 3);
326 }
327
328 ByteVector macHost =
329 openssl::CMACCrypto::cmac(keycipher, cipher, rnd2, emptyIV, 16);
330 truncateMacBuffer(macHost);
331
332 rnd1.resize(12);
333 if (RAND_bytes(&rnd1[0], static_cast<int>(rnd1.size())) != 1)
334 {
336 "Cannot retrieve cryptographically strong bytes");
337 }
338
339 ByteVector data_p2;
340 data_p2.insert(data_p2.end(), macHost.begin(), macHost.begin() + 8);
341 data_p2.insert(data_p2.end(), rnd1.begin(), rnd1.end());
342
343 result = this->getISO7816ReaderCardAdapter()->sendAPDUCommand(
344 d_cla, 0x10, 0x00, 0x00, 0x14, data_p2, 0x00);
345 if (result.getData().size() != 24 || result.getSW1() != 0x90 ||
346 result.getSW2() != 0xAF)
347 THROW_EXCEPTION_WITH_LOG(LibLogicalAccessException, "lockUnlock P2 Failed.");
348
349 /* Check CMAC - Create rnd1 for p3 - CMAC: rnd1 | P1 | other data */
350 rnd1.insert(rnd1.end(), rnd2.begin() + 12, rnd2.end()); // p2 data without rnd2
351
352 macHost = openssl::CMACCrypto::cmac(keycipher, cipher, rnd1, emptyIV, 16);
353 truncateMacBuffer(macHost);
354
355 for (unsigned char x = 0; x < 8; ++x)
356 {
357 if (macHost[x] != result.getData()[x])
359 "lockUnlock P2 CMAC from SAM is Wrong.");
360 }
361
362 /* Create kxe - d_authkey */
363 generateAuthEncKey(keycipher, rnd1, rnd2);
364
365 // create rndA
366 ByteVector rndA(16);
367 if (RAND_bytes(&rndA[0], static_cast<int>(rndA.size())) != 1)
368 {
370 "Cannot retrieve cryptographically strong bytes");
371 }
372
373 // decipher rndB
374 std::shared_ptr<openssl::SymmetricKey> symkey(new openssl::AESSymmetricKey(
376 std::shared_ptr<openssl::InitializationVector> iv(
379
380 ByteVector encRndB(result.getData().begin() + 8, result.getData().end());
381 ByteVector dencRndB;
382
383 cipher->decipher(encRndB, dencRndB, *symkey.get(), *iv.get(), false);
384
385 // create rndB'
386 ByteVector rndB1;
387 rndB1.insert(rndB1.begin(), dencRndB.begin() + 2,
388 dencRndB.begin() + dencRndB.size());
389 rndB1.push_back(dencRndB[0]);
390 rndB1.push_back(dencRndB[1]);
391
392 ByteVector dataHost, encHost;
393 dataHost.insert(dataHost.end(), rndA.begin(), rndA.end()); // RndA
394 dataHost.insert(dataHost.end(), rndB1.begin(), rndB1.end()); // RndB'
395
398 cipher->cipher(dataHost, encHost, *symkey.get(), *iv.get(), false);
399
400 result = this->getISO7816ReaderCardAdapter()->sendAPDUCommand(
401 d_cla, 0x10, 0x00, 0x00, 0x20, encHost, 0x00);
402 if (result.getData().size() != 16 || result.getSW1() != 0x90 ||
403 result.getSW2() != 0x00)
404 THROW_EXCEPTION_WITH_LOG(LibLogicalAccessException, "lockUnlock P3 Failed.");
405
406 ByteVector SAMrndA;
409 cipher->decipher(result.getData(), SAMrndA, *symkey.get(), *iv.get(), false);
410 SAMrndA.insert(SAMrndA.begin(), SAMrndA.end() - 2, SAMrndA.end());
411
412 if (!equal(SAMrndA.begin(), SAMrndA.begin() + 16, rndA.begin()))
414 "lockUnlock P3 RndA from SAM is invalide.");
415 }
417 void selectApplication(ByteVector aid) override
418 {
419 unsigned char cmd[] = {d_cla, 0x5a, 0x00, 0x00, 0x03};
420 ByteVector cmd_vector(cmd, cmd + 5);
421 cmd_vector.insert(cmd_vector.end(), aid.begin(), aid.end());
422
423 ByteVector result = transmit(cmd_vector);
424
425 if (result.size() >= 2 &&
426 (result[result.size() - 2] != 0x90 || result[result.size() - 1] != 0x00))
428 "selectApplication failed.");
429 }
431 ByteVector changeKeyPICC(const ChangeKeyInfo &info,
432 const ChangeKeyDiversification &diversifycation) override
433 {
434 unsigned char keyCompMeth = info.oldKeyInvolvement;
435
436 unsigned char cfg = info.desfireNumber & 0xf;
437 if (info.isMasterKey)
438 cfg |= 0x10;
439 ByteVector data(4);
440 data[0] = info.currentKeySlotNo;
441 data[1] = info.currentKeySlotV;
442 data[2] = info.newKeySlotNo;
443 data[3] = info.newKeySlotV;
444
445 if (diversifycation.divType != NO_DIV)
446 {
447 if (diversifycation.divType == SAMAV2)
448 keyCompMeth |= 0x20;
449
450 keyCompMeth |= diversifycation.diversifyCurrent == 0x01 ? 0x04 : 0x00;
451 keyCompMeth |= diversifycation.diversifyNew == 0x01 ? 0x02 : 0x00;
452
453 data.insert(data.end(), diversifycation.divInput,
454 diversifycation.divInput + diversifycation.divInputSize);
455 }
456
457 unsigned char cmd[] = {
458 d_cla, 0xc4, keyCompMeth, cfg, (unsigned char)(data.size()), 0x00};
459 ByteVector cmd_vector(cmd, cmd + 6);
460 cmd_vector.insert(cmd_vector.end() - 1, data.begin(), data.end());
461
462 ByteVector result = transmit(cmd_vector);
463
464 if (result.size() >= 2 &&
465 (result[result.size() - 2] != 0x90 || result[result.size() - 1] != 0x00))
466 {
467 char tmp[64];
468 sprintf(tmp, "changeKeyPICC failed (%x %x).", result[result.size() - 2],
469 result[result.size() - 1]);
471 }
472
473 return ByteVector(result.begin(), result.end() - 2);
474 }
475
476 protected:
477 std::shared_ptr<SAMDESfireCrypto> d_crypto;
478
479 // std::shared_ptr<boost::interprocess::mapped_region> d_region;
480
481 // std::shared_ptr<boost::interprocess::named_mutex> d_named_mutex;
483 unsigned char d_cla;
485 ByteVector d_authKey;
487 ByteVector d_sessionKey;
489 ByteVector d_LastSessionIV;
491 static void truncateMacBuffer(ByteVector &data)
492 {
493 unsigned char truncateCount = 0;
494 unsigned char count = 1;
495
496 while (count < data.size())
497 {
498 data[truncateCount] = data[count];
499 count += 2;
500 ++truncateCount;
501 }
502 }
504 void generateAuthEncKey(ByteVector keycipher, ByteVector rnd1, ByteVector rnd2)
505 {
506 ByteVector SV1a(16), emptyIV(16);
507
508 copy(rnd1.begin() + 7, rnd1.begin() + 12, SV1a.begin());
509 copy(rnd2.begin() + 7, rnd2.begin() + 12, SV1a.begin() + 5);
510 copy(rnd1.begin(), rnd1.begin() + 5, SV1a.begin() + 10);
511
512 for (unsigned char x = 0; x <= 4; ++x)
513 {
514 SV1a[x + 10] ^= rnd2[x];
515 }
516
517 SV1a[15] = 0x91; /* AES 128 */
518 /* TODO AES 192 */
519
520 std::shared_ptr<openssl::SymmetricKey> symkey(new openssl::AESSymmetricKey(
522 std::shared_ptr<openssl::InitializationVector> iv(
525 std::shared_ptr<openssl::OpenSSLSymmetricCipher> cipher(new openssl::AESCipher());
526
527 cipher->cipher(SV1a, d_authKey, *symkey.get(), *iv.get(), false);
528 }
529};
530}
531
532#endif /* LOGICALACCESS_SAMAV1ISO7816COMMANDS_HPP */
AES cipher class.
AES initialization vector class.
AES symmetric key class.
A liblogicalaccess exception class.
Definition: myexception.hpp:22
Definition: samcommands.hpp:61
The SAMISO7816 commands class.
Definition: samiso7816commands.hpp:52
SAMISO7816Commands()
Constructor.
Definition: samiso7816commands.hpp:57
AES cipher class.
Definition: aes_cipher.hpp:24
An AES initialization vector.
Definition: aes_initialization_vector.hpp:28
static AESInitializationVector createFromData(const ByteVector &data)
Create an IV from data.
Definition: aes_initialization_vector.cpp:25
An AES symmetric key.
Definition: aes_symmetric_key.hpp:30
static AESSymmetricKey createFromData(const ByteVector &data)
Create a symmetric key from the specified data.
Definition: aes_symmetric_key.cpp:20
static ByteVector cmac(const ByteVector &key, std::string crypto, const ByteVector &data, const ByteVector &iv={}, int padding_size=0)
Definition: cmac.cpp:21
Default ISO7816 reader/card adapter.
std::vector< uint8_t > ByteVector
Definition: lla_fwd.hpp:80
#define THROW_EXCEPTION_WITH_LOG(type, msg,...)
Definition: logs.hpp:237
#define EXCEPTION_ASSERT_WITH_LOG(condition, type, msg,...)
Definition: logs.hpp:246
Definition: asn1.hpp:9
SAMLockUnlock
Definition: samcommands.hpp:49
@ SwitchAV2Mode
Definition: samcommands.hpp:53
@ LockWithSpecifyingKey
Definition: samcommands.hpp:52
@ NO_DIV
Definition: sambasickeyentry.hpp:58
@ SAMAV2
Definition: sambasickeyentry.hpp:58
#define DEFAULT_SAM_CLA
Definition: samav1iso7816commands.hpp:25
SAMDESfireCrypto header.
#define CMD_SAMISO7816
Definition: samiso7816commands.hpp:40
SAMKeyEntry source.
Definition: samcommands.hpp:42
Definition: sambasickeyentry.hpp:61
unsigned char * divInput
Definition: sambasickeyentry.hpp:63
NXPKeyDiversificationType divType
Definition: sambasickeyentry.hpp:62
unsigned char diversifyNew
Definition: sambasickeyentry.hpp:66
unsigned char diversifyCurrent
Definition: sambasickeyentry.hpp:65
unsigned char divInputSize
Definition: sambasickeyentry.hpp:64
Definition: sambasickeyentry.hpp:48
unsigned char newKeySlotV
Definition: sambasickeyentry.hpp:55
unsigned char oldKeyInvolvement
Definition: sambasickeyentry.hpp:51
unsigned char newKeySlotNo
Definition: sambasickeyentry.hpp:54
unsigned char desfireNumber
Definition: sambasickeyentry.hpp:49
unsigned char currentKeySlotV
Definition: sambasickeyentry.hpp:53
unsigned char currentKeySlotNo
Definition: sambasickeyentry.hpp:52
unsigned char isMasterKey
Definition: sambasickeyentry.hpp:50
Symmetric key base class.