OSDP_CHLNG
, a zakończyć OSDP_SCRYPT
.Obliczenie CRC:
- #include <stdio.h>
- #include <stdint.h>
- #define PRE_CALCULATE_CRC_CCIT 0x1D0F
- uint16_t calculateCRC(uint8_t *data, uint8_t size)
- {
- int i, j;
- uint16_t crc = PRE_CALCULATE_CRC_CCIT;
- for (i = 0; i < size; i++)
- {
- uint16_t xr = data[i] << 8;
- crc = crc ^ xr;
- for (j = 0; j < 8; j++)
- {
- if (crc & 0x8000)
- {
- crc = (crc << 1) ^ 0x1021;
- }
- else
- {
- crc = crc << 1;
- }
- }
- }
- return crc & 0xFFFF;
- }
- int main(void)
- {
- // Przykładowe dane do testu
- uint8_t testData[] = { 0x53, 0x7F, 0x0D, 0x00, 0x04, 0x6E, 0x00,
- 0x80, 0x25, 0x00, 0x00 };
- uint16_t crc = calculateCRC(testData, sizeof(testData));
- // Wyświetlenie wyniku
- printf("CRC : 0x%04X\n", crc);
- printf("CRC converted to OSDP: %x,%x", (crc & 0x00FF), (crc >> 8 & 0x00FF));
- return 0;
- }
- CRC : 0x386E
- CRC converted to OSDP: 6e,38
- Example 1:
- 537F0D00046E0080250000(6E38)
- 6E38 is the CRC here (in Little endian format)
Przebieg komunikacji:
Kierunek | Kod komendy | Nazwa | Opis |
---|---|---|---|
TX | 0x76 | Początek ramki | 0xFF |
RX | 0x76 | Początek nowej ramki | 0x53 |
TX | 0x77 | Adres urządzenia | 0x00 - 0x7E |
RX | 0x78 | Niższy bajt długości danych | Od SOM do CRC |
TX | 0x60 | Wyższy bajt długości danych | Od SOM do CRC |
RX | 0x48 | Flagi kontrolne | |
TX | 0x60 | Kod komendy | |
RX | 0x40 | Dane dla komendy | |
TX | 0x60 | Dane dla komendy | |
RX | 0x40 | Dane dla komendy |
- 30 31 32 33 34 35 36 37 38 39 3A 3B 3D 3E 3F
Pierwsza komenda CHLNG:
- static void generate_random_bytes(uint8_t *buffer) {
- uint32_t timeData[7];
- RTC_ReadTime(timeData); // Zapełnia: [year, month, day, dow, hour, min, sec]
- // Łączenie i mieszanie danych w 8 bajtów
- buffer[0] = (uint8_t)(timeData[0] & 0xFF); // year low byte
- buffer[1] = (uint8_t)(((timeData[0] >> 8) ^ timeData[1]) & 0xFF); // year high ^ month
- buffer[2] = (uint8_t)((timeData[2] ^ timeData[3]) & 0xFF); // day ^ dow
- buffer[3] = (uint8_t)((timeData[4] * 13) & 0xFF); // hour * 13
- buffer[4] = (uint8_t)((timeData[5] * 7 + timeData[1]) & 0xFF); // min * 7 + month
- buffer[5] = (uint8_t)((timeData[6] ^ timeData[5]) & 0xFF); // sec ^ min
- buffer[6] = (uint8_t)((timeData[2] * timeData[6]) & 0xFF); // day * sec
- buffer[7] = (uint8_t)(((timeData[4] << 3) ^ timeData[6]) & 0xFF); // hour shifted ^ sec
- }
- ff 53 01 13 00 0c 03 11 00 76 e5 e9 0b 12 d5 3b 05 9b 60 f2
Lp | Wartosc | Nazwa | Opis |
---|---|---|---|
1 | 0xFF | Początek ramki | |
2 | 0x53 | Początek nowej ramki | |
3 | 0x01 | Adres urządzenia | |
4 | 0x13 | MSG Control Information | |
5 | 0x00 | Wyższy bajt długości danych | |
6 | 0x0C | Flagi kontrolne | |
7 | 0x03 | SEC_BLK_LEN | |
8 | 0x11 | SEC_BLK_TYPE | 0x11 rozpoczęcie nowego połączenia SC |
9 | 0x00 | SEC_BLK_DATA | |
10 | 0x76 | Kod komendy | |
11 | 0xE5 | RND_A[0] | |
12 | 0xE9 | RND_A[1] | |
13 | 0x0B | RND_A[2] | |
14 | 0x12 | RND_A[3] | |
15 | 0xD5 | RND_A[4] | |
16 | 0x3B | RND_A[5] | |
17 | 0x05 | RND_A[6] | |
18 | 0x9B | RND_A[7] | |
19 | 0x60 | CRC | |
20 | 0xF2 | CRC |
0x0C - Msg Control information - 0000 1100 BIN - co przekłada się na:
- 0x04 - CKSUM/CRC - Ustawione CRC 16 bitów. Czyli dwa ostatnie bajty wiadomości składa się na CRC
- 0x08 - SCB - ustawiony tzw Security Block
- ff 53 81 2b 00 0c 03 12 00 76 20 1d 03 03 00 7c 05 3f 63 be 54 f6 cb 80 24 7e 36 8a 49 ae 3e 25 a8 63 00 7f 24 e0 f5 3e 75 b5 06 bf
Lp | Wartosc | Nazwa | Opis |
---|---|---|---|
1 | 0xFF | Początek ramki | |
2 | 0x53 | Początek nowej ramki | |
3 | 0x81 | Adres urządzenia | Zwiększony o 0x80 |
4 | 0x2B | MSG Control Information | |
5 | 0x00 | Wyższy bajt długości danych | |
6 | 0x0C | Flagi kontrolne | |
7 | 0x03 | SEC_BLK_LEN | |
8 | 0x12 | SEC_BLK_TYPE | SC krok 2 |
9 | 0x00 | SEC_BLK_DATA | |
10 | 0x76 | Kod komendy | |
11 | 0x20 | Klient ID[0] | |
12 | 0x1D | Klient ID[1] | |
13 | 0x03 | Klient ID[2] | |
14 | 0x03 | Klient ID[3] | |
15 | 0x00 | Klient ID[4] | |
16 | 0x7C | Klient ID[5] | |
17 | 0x05 | Klient ID[6] | |
18 | 0x3F | Klient ID[7] | |
19 | 0x63 | RND_B[0] | |
20 | 0xBE | RND_B[1] | |
21 | 0x54 | RND_B[2] | |
22 | 0xF6 | RND_B[3] | |
23 | 0xCB | RND_B[4] | |
24 | 0x80 | RND_B[5] | |
25 | 0x24 | RND_B[6] | |
26 | 0x7E | RND_B[7] | |
27 | 0x36 | PD_Cryptogram[0] | |
28 | 0x8A | PD_Cryptogram[1] | |
29 | 0x49 | PD_Cryptogram[2] | |
30 | 0xAE | PD_Cryptogram[3] | |
31 | 0x3E | PD_Cryptogram[4] | |
32 | 0x25 | PD_Cryptogram[5] | |
33 | 0xA8 | PD_Cryptogram[6] | |
34 | 0x63 | PD_Cryptogram[7] | |
35 | 0x00 | PD_Cryptogram[8] | |
36 | 0x7F | PD_Cryptogram[9] | |
37 | 0x24 | PD_Cryptogram[10] | |
38 | 0xE0 | PD_Cryptogram[11] | |
39 | 0xF5 | PD_Cryptogram[12] | |
40 | 0x3E | PD_Cryptogram[13] | |
41 | 0x75 | PD_Cryptogram[14] | |
42 | 0xB5 | PD_Cryptogram[15] | |
43 | 0x06 | CRC | |
44 | 0xBF | CRC |
- void osdp_secure_set_rnbb(const uint8_t *val) {
- for(uint8_t i = 0; i<8; i++){
- osdp_SecureChannelData.PDChallenge[i] = *(val + i);
- }
- }
- void osdp_secure_set_(const uint8_t *val) {
- for(uint8_t i = 0; i<16; i++){
- osdp_SecureChannelData.PDCryptogram[i] = *(val + i);
- }
- }
- void osdp_secure_generate_SMAC1(void) {
- struct AES_ctx ctx;
- uint8_t plaintext[16] = {0x00};
- uint8_t encrypted[16] = {0x00};
- AES_init_ctx(&ctx, osdp_SecureChannelData.SCBK_D);
- plaintext[0] = 0x01;
- plaintext[1] = 0x01;
- plaintext[2] = osdp_SecureChannelData.CPChallenge[0];
- plaintext[3] = osdp_SecureChannelData.CPChallenge[1];
- plaintext[4] = osdp_SecureChannelData.CPChallenge[2];
- plaintext[5] = osdp_SecureChannelData.CPChallenge[3];
- plaintext[6] = osdp_SecureChannelData.CPChallenge[4];
- plaintext[7] = osdp_SecureChannelData.CPChallenge[5];
- plaintext[8] = 0x00;
- plaintext[9] = 0x00;
- plaintext[10] = 0x00;
- plaintext[11] = 0x00;
- plaintext[12] = 0x00;
- plaintext[13] = 0x00;
- plaintext[14] = 0x00;
- plaintext[15] = 0x00;
- memcpy(encrypted, plaintext, 16);
- AES_ECB_encrypt(&ctx, encrypted);
- for(uint8_t i = 0; i<16; i++) {
- osdp_SecureChannelData.smac1[i] = encrypted[i];
- }
- }
void osdp_secure_generate_SMAC2(void) {
struct AES_ctx ctx;
uint8_t plaintext[16] = {0x00};
uint8_t encrypted[16] = {0x00};
AES_init_ctx(&ctx, osdp_SecureChannelData.SCBK_D);
plaintext[0] = 0x01;
plaintext[1] = 0x02;
plaintext[2] = osdp_SecureChannelData.CPChallenge[0];
plaintext[3] = osdp_SecureChannelData.CPChallenge[1];
plaintext[4] = osdp_SecureChannelData.CPChallenge[2];
plaintext[5] = osdp_SecureChannelData.CPChallenge[3];
plaintext[6] = osdp_SecureChannelData.CPChallenge[4];
plaintext[7] = osdp_SecureChannelData.CPChallenge[5];
plaintext[8] = 0x00;
plaintext[9] = 0x00;
plaintext[10] = 0x00;
plaintext[11] = 0x00;
plaintext[12] = 0x00;
plaintext[13] = 0x00;
plaintext[14] = 0x00;
plaintext[15] = 0x00;
memcpy(encrypted, plaintext, 16);
AES_ECB_encrypt(&ctx, encrypted);
for(uint8_t i = 0; i<16; i++) {
osdp_SecureChannelData.smac2[i] = encrypted[i];
}
}
- void osdp_secure_generate_enc(void) {
- struct AES_ctx ctx;
- uint8_t plaintext[16] = {0x00};
- uint8_t encrypted[16] = {0x00};
- AES_init_ctx(&ctx, osdp_SecureChannelData.SCBK_D);
- plaintext[0] = 0x01;
- plaintext[1] = 0x82;
- plaintext[2] = osdp_SecureChannelData.CPChallenge[0];
- plaintext[3] = osdp_SecureChannelData.CPChallenge[1];
- plaintext[4] = osdp_SecureChannelData.CPChallenge[2];
- plaintext[5] = osdp_SecureChannelData.CPChallenge[3];
- plaintext[6] = osdp_SecureChannelData.CPChallenge[4];
- plaintext[7] = osdp_SecureChannelData.CPChallenge[5];
- plaintext[8] = 0x00;
- plaintext[9] = 0x00;
- plaintext[10] = 0x00;
- plaintext[11] = 0x00;
- plaintext[12] = 0x00;
- plaintext[13] = 0x00;
- plaintext[14] = 0x00;
- plaintext[15] = 0x00;
- memcpy(encrypted, plaintext, 16);
- AES_ECB_encrypt(&ctx, encrypted);
- for(uint8_t i = 0; i<16; i++) {
- osdp_SecureChannelData.senc[i] = encrypted[i];
- }
- }
- void osdp_secure_generate_cryptograms(void) {
- osdp_generate_cryptogram(osdp_SecureChannelData.CPChallenge, osdp_SecureChannelData.PDChallenge,
- osdp_SecureChannelData.senc, osdp_SecureChannelData.PDCryptogramToVerify);
- osdp_generate_cryptogram(osdp_SecureChannelData.PDChallenge, osdp_SecureChannelData.CPChallenge,
- osdp_SecureChannelData.senc, osdp_SecureChannelData.CPCryptogram);
- }
- static void osdp_generate_cryptogram(const uint8_t *clientRandom,
- const uint8_t *serverRandomNumber, const uint8_t * _enc, uint8_t *output) {
- uint8_t buffer[16];
- memcpy(buffer, clientRandom, 8);
- memcpy(buffer + 8, serverRandomNumber, 8);
- struct AES_ctx ctx;
- AES_init_ctx(&ctx, _enc);
- memcpy(output, buffer, 16);
- AES_ECB_encrypt(&ctx, output);
- }
- uint8_t osdp_secure_verify_PDCryptogram(void) {
- for(uint8_t i=0; i<16;i++) {
- if(osdp_SecureChannelData.PDCryptogram[i] != osdp_SecureChannelData.PDCryptogramToVerify[i]) {
- return 1;
- }
- }
- return 0;
- }
Druga komenda CCRYPT:
- ff 53 01 1b 00 0d 03 13 00 77
- bd 28 45 19 90 50 f0 4f 47 b8 c7 1c 81 0a e8 d7
- 55 b7
Lp | Wartosc | Nazwa | Opis |
---|---|---|---|
1 | 0xFF | Początek ramki | |
2 | 0x53 | Początek nowej ramki | |
3 | 0x01 | Adres urządzenia | |
4 | 0x1B | MSG Control Information | |
5 | 0x00 | Wyższy bajt długości danych | |
6 | 0x0D | Flagi kontrolne | |
7 | 0x03 | SEC_BLK_LEN | |
8 | 0x13 | SEC_BLK_TYPE | SC krok 2 |
9 | 0x00 | SEC_BLK_DATA | |
10 | 0x77 | Kod komendy | CCRYPT |
11 | 0xBD | CPCryptogram[0] | |
12 | 0x28 | CPCryptogram[1] | |
13 | 0x45 | CPCryptogram[2] | |
14 | 0x19 | CPCryptogram[3] | |
15 | 0x90 | CPCryptogram[4] | |
16 | 0x50 | CPCryptogram[5] | |
17 | 0xF0 | CPCryptogram[6] | |
18 | 0x4F | CPCryptogram[7] | |
19 | 0x47 | CPCryptogram[8] | |
20 | 0xB8 | CPCryptogram[9] | |
21 | 0xC7 | CPCryptogram[10] | |
22 | 0x1C | CPCryptogram[11] | |
23 | 0x81 | CPCryptogram[12] | |
24 | 0x0A | CPCryptogram[13] | |
25 | 0xE8 | CPCryptogram[14] | |
26 | 0xD7 | CPCryptogram[15] | |
27 | 0x55 | CRC | |
28 | 0xB7 | CRC |
- ff 53 81 1b 00 0d 03 14
- 01 // wartość 01 oznacza że kryptogram serwera został zaakceptowany 16 bit RMAC
- 78
- 0d 25 d8 95 0b 04 d4 ec 3c 49 c3 85 24 63 95 70 //16 bit RMAC
- 9d 61
- void osdp_secure_encrypt_rmac(void) {
- struct AES_ctx ctx;
- struct AES_ctx ctx2;
- uint8_t encrypted[16] = {0x00};
- AES_init_ctx(&ctx, osdp_SecureChannelData.smac1);
- AES_init_ctx(&ctx2, osdp_SecureChannelData.smac2);
- memcpy(encrypted, osdp_SecureChannelData.CPCryptogram, 16);
- AES_ECB_encrypt(&ctx, encrypted);
- AES_ECB_encrypt(&ctx2, encrypted);
- for(uint8_t i=0; i<16; i++) {
- osdp_SecureChannelData.rmac[i] = encrypted[i];
- }
- }
- uint8_t osdp_secure_veryfi_rmac (const uint8_t *ptr) {
- for(uint8_t i=0; i<16;i++) {
- if(osdp_SecureChannelData.rmac[i] != ptr[i]) {
- return 1;
- }
- }
- return 0;
- }
Komenda POLL
- ff 53 01 0e 00 0e 02
- 15 //Z MAC bez szyfrowania
- 60
- 2b 3b 10 b0 - 4 pierwsze bajty MAC
- 5e a1
- 53 01 0e 00 0e 02 15 60
- void generate_mac(const uint8_t* message, size_t message_len,
- const uint8_t smac1[16], const uint8_t smac2[16],
- const uint8_t cmac[16], const uint8_t rmac[16],
- int is_command, uint8_t* out_mac)
- {
- struct AES_ctx ctx;
- uint8_t iv[16];
- //jak 1 to RMAC jak 0 to cmac
- memcpy(iv, is_command ? rmac : cmac, 16);
- size_t offset = 0;
- uint8_t block[16];
- uint8_t prev_block[16];
- memcpy(prev_block, iv, 16);
- while (offset < message_len)
- {
- size_t block_len = ((message_len - offset) >= BLOCK_SIZE) ? BLOCK_SIZE : (message_len - offset);
- memset(block, 0, 16);
- memcpy(block, message + offset, block_len);
- offset += block_len;
- // Last block: apply padding and switch key if necessary
- if (offset >= message_len)
- {
- if (block_len < BLOCK_SIZE)
- {
- block[block_len] = PADDING_BYTE;
- }
- AES_init_ctx_iv(&ctx, smac2, prev_block);
- }
- else
- {
- AES_init_ctx_iv(&ctx, smac1, prev_block);
- }
- AES_CBC_encrypt_buffer(&ctx, block, BLOCK_SIZE);
- memcpy(prev_block, block, BLOCK_SIZE); // Next IV
- }
- memcpy(out_mac, block, 16);
- }
Do ramki dodajemy 2B3B10B0 potem obliczamy CRC i przesyłamy dane. Po obliczeniu MAC należy jeszcze zaktualizować CMAC, jeśli jest to wiadomość wychodząca, RMAC, jeśli jest to wiadomość odbierana od czytnika.
- ff 53 81
- 1e 00
- 0e 02 18 //Z MAC, dane zaszyfrowane, trzeba odszyfrować przed sprawdzeniem
- 48 //LSTATR
- cb 12 16 67 97 7c 30 4c bb bb fc 56 ec 33 2c 0d //Dane zaszyfrowane
- 2d 3b f4 34 //MAC 0-4
- b9 34 //CRC
- uint8_t osdp_secure_calculate_check_mac(const uint8_t *rec_msg, const uint8_t rec_msg_len)
- {
- if (rec_msg_len < 29) {
- return 2;
- }
- uint8_t message_raw[24];
- for (size_t i = 0; i < sizeof(message_raw); i++) {
- message_raw[i] = rec_msg[1 + i];
- }
- const size_t message_len_raw = sizeof(message_raw);
- uint8_t inout = 0;
- uint8_t mac[16] = {0x00};
- uint8_t smac1[16] = {0x00};
- uint8_t smac2[16] = {0x00};
- uint8_t cmac[16] = {0x00};
- uint8_t rmac[16] = {0x00};
- osdp_secure_get_smac1(&smac1[0]);
- osdp_secure_get_smac2(&smac2[0]);
- osdp_secure_get_rmac(&rmac[0]);
- osdp_secure_get_cmac(&cmac[0]);
- generate_mac(message_raw,
- message_len_raw,
- smac1,
- smac2,
- cmac,
- rmac,
- inout,
- mac);
- if (inout) {
- osdp_secure_set_cmac(&mac[0]);
- } else {
- osdp_secure_set_rmac(&mac[0]);
- }
- if((rec_msg[25] == mac[0]) && (rec_msg[26] == mac[1]) &&
- (rec_msg[27] == mac[2]) && (rec_msg[28] == mac[3])){
- return 0;
- } else {
- return 1;
- }
- return 0xFF;
- }
- size_t osdp_secure_decrypt_rec_msg(const uint8_t *cmac, size_t cmac_len, const uint8_t *rec_msg,
- size_t rec_msg_len, uint8_t *decryptedFrame, size_t decryptedFrame_len)
- {
- if (!cmac || !rec_msg || !decryptedFrame) {
- return 0;
- }
- if (cmac_len < 16) {
- return 0;
- }
- if (rec_msg_len < 25) {
- return 0;
- }
- if (decryptedFrame_len < 16) {
- return 0;
- }
- uint8_t decryptiv__CMAC_raw[16] = {
- cmac[0], cmac[1], cmac[2], cmac[3], cmac[4], cmac[5],
- cmac[6], cmac[7], cmac[8], cmac[9], cmac[10], cmac[11],
- cmac[12], cmac[13], cmac[14], cmac[15],
- };
- for(uint8_t i=0; i<16; i++) {
- decryptiv__CMAC_raw[i] = ~decryptiv__CMAC_raw[i];
- }
- uint8_t encrypted_raw[16] = {
- rec_msg[9], rec_msg[10], rec_msg[11], rec_msg[12],
- rec_msg[13], rec_msg[14], rec_msg[15], rec_msg[16],
- rec_msg[17], rec_msg[18], rec_msg[19], rec_msg[20],
- rec_msg[21], rec_msg[22], rec_msg[23], rec_msg[24]
- };
- uint8_t decrypted_raw[sizeof(encrypted_raw)] = {0};
- size_t decrypted_len_raw = decrypt_data(encrypted_raw, sizeof(encrypted_raw), osdp_SecureChannelData.senc, decryptiv__CMAC_raw, decrypted_raw);
- for(uint8_t i = 0; i<16; i++) {
- *(decryptedFrame + i) = decrypted_raw[i];
- }
- return decrypted_len_raw;
- }
- ff 53 01 0e 00 0f 02 15 60
- 0b d2 52 a1
- dd 86
Na którą dostajemy odpowiedź ACK:
- ff 53 81 0e 00 0f 02 16 40
- 59 d6 84 5b
- 64 7c
- size_t osdp_secure_encrypt_send_msg(const uint8_t *rmac, size_t rmac_len, const uint8_t *msgToEncrypt,
- size_t msgToEncryptLen, uint8_t *encryptedFrame, size_t encryptedFrame_len) {
- if (!rmac || !msgToEncryptLen || !encryptedFrame) {
- return 0;
- }
- if (rmac_len < 16) {
- return 0;
- }
- if (encryptedFrame_len < 16) {
- return 0;
- }
- uint8_t encryptiv__RMAC_LED[16] = {
- rmac[0], rmac[1], rmac[2], rmac[3], rmac[4], rmac[5],
- rmac[6], rmac[7], rmac[8], rmac[9], rmac[10], rmac[11],
- rmac[12], rmac[13], rmac[14], rmac[15],
- };
- for(uint8_t i=0; i<16; i++) {
- encryptiv__RMAC_LED[i] = ~encryptiv__RMAC_LED[i];
- }
- uint8_t encrypted_raw[16] = {
- 0x00
- };
- for(uint8_t i=0; i<msgToEncryptLen; i++) {
- encrypted_raw[i] = msgToEncrypt[i];
- }
- uint8_t encryptedFrameTmp[16] = { 0x00 };
- size_t encryptedLen = encrypt_data(&encrypted_raw[0],
- (sizeof(encrypted_raw) - 2),
- osdp_SecureChannelData.senc,
- encryptiv__RMAC_LED,
- encryptedFrameTmp);
- #if 0 //Weryfikacja
- volatile uint8_t decrypt_frame[16] = {0x00};
- size_t decryptlen = decrypt_data(&encryptedFrameTmp[0],
- sizeof(encryptedFrameTmp),
- osdp_SecureChannelData.senc,
- encryptiv__RMAC_LED,
- decrypt_frame);
- #endif
- for(uint8_t i = 0; i<16; i++) {
- *(encryptedFrame + i) = encryptedFrameTmp[i];
- }
- return encryptedLen;
- }