mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -07:00
cryptorf: style
This commit is contained in:
parent
a76596c3fe
commit
b134753a08
9 changed files with 1703 additions and 1498 deletions
|
@ -27,35 +27,32 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CA_ENCRYPT = 0x01,
|
CA_ENCRYPT = 0x01,
|
||||||
CA_DECRYPT = 0x02
|
CA_DECRYPT = 0x02
|
||||||
} CryptoAction;
|
} CryptoAction;
|
||||||
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
|
|
||||||
static uint8_t nibbles_to_byte(nibble b0, nibble b1)
|
static uint8_t nibbles_to_byte(nibble b0, nibble b1) {
|
||||||
{
|
// Combine both nibbles
|
||||||
// Combine both nibbles
|
return ((b0 << 4) | b1);
|
||||||
return ((b0 << 4) | b1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t funny_mod(uint8_t a, uint8_t m)
|
static uint8_t funny_mod(uint8_t a, uint8_t m) {
|
||||||
{
|
// Just return the input when this is less or equal than the modular value
|
||||||
// Just return the input when this is less or equal than the modular value
|
if (a < m) return a;
|
||||||
if (a < m) return a;
|
|
||||||
|
|
||||||
// Compute the modular value
|
// Compute the modular value
|
||||||
a %= m;
|
a %= m;
|
||||||
|
|
||||||
// Return the funny value, when the output was now zero, return the modular value
|
// Return the funny value, when the output was now zero, return the modular value
|
||||||
return (a == 0) ? m : a;
|
return (a == 0) ? m : a;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t bit_rotate_left(uint8_t a, uint8_t n_bits)
|
static uint8_t bit_rotate_left(uint8_t a, uint8_t n_bits) {
|
||||||
{
|
// Rotate value a with the length of n_bits only 1 time
|
||||||
// Rotate value a with the length of n_bits only 1 time
|
uint8_t mask = (1 << n_bits) - 1;
|
||||||
uint8_t mask = (1 << n_bits) - 1;
|
return ((a << 1) | (a >> (n_bits - 1))) & mask;
|
||||||
return ((a << 1) | (a >> (n_bits - 1))) & mask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -92,276 +89,258 @@ static void reconstruct_nibbles(crypto_state s)
|
||||||
s->b1 |= s->b1r & s->b1s;
|
s->b1 |= s->b1r & s->b1s;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
static void next_left(uint8_t in, crypto_state s)
|
static void next_left(uint8_t in, crypto_state s) {
|
||||||
{
|
uint8_t b3, b6, bx;
|
||||||
uint8_t b3, b6, bx;
|
|
||||||
|
|
||||||
// Update the left cipher state with the input byte
|
// Update the left cipher state with the input byte
|
||||||
s->l ^= ((in & 0x1f) << 20);
|
s->l ^= ((in & 0x1f) << 20);
|
||||||
|
|
||||||
// Extract the two (5 bits) values used for modular addtion
|
// Extract the two (5 bits) values used for modular addtion
|
||||||
b3 = (uint8_t)((s->l >> 15) & 0x1f);
|
b3 = (uint8_t)((s->l >> 15) & 0x1f);
|
||||||
b6 = (uint8_t)(s->l & 0x1f);
|
b6 = (uint8_t)(s->l & 0x1f);
|
||||||
|
|
||||||
// Compute the modular addition
|
// Compute the modular addition
|
||||||
bx = funny_mod(b3 + bit_rotate_left(b6, 5), 0x1f);
|
bx = funny_mod(b3 + bit_rotate_left(b6, 5), 0x1f);
|
||||||
|
|
||||||
// Rotate the left cipher state 5 bits
|
// Rotate the left cipher state 5 bits
|
||||||
s->l = ((s->l >> 5) | ((uint64_t)bx << 30));
|
s->l = ((s->l >> 5) | ((uint64_t)bx << 30));
|
||||||
|
|
||||||
// Save the 4 left output bits used for b1
|
// Save the 4 left output bits used for b1
|
||||||
s->b1l = ((bx ^ b3) & 0x0f);
|
s->b1l = ((bx ^ b3) & 0x0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void next_right(uint8_t in, crypto_state s)
|
static void next_right(uint8_t in, crypto_state s) {
|
||||||
{
|
uint8_t b16, b18, bx;
|
||||||
uint8_t b16, b18, bx;
|
|
||||||
|
|
||||||
// Update the right cipher state with the input byte
|
// Update the right cipher state with the input byte
|
||||||
s->r ^= ((in & 0xf8) << 12);
|
s->r ^= ((in & 0xf8) << 12);
|
||||||
|
|
||||||
// Extract the two (5 bits) values used for modular addtion
|
// Extract the two (5 bits) values used for modular addtion
|
||||||
b16 = (uint8_t)((s->r >> 10) & 0x1f);
|
b16 = (uint8_t)((s->r >> 10) & 0x1f);
|
||||||
b18 = (uint8_t)(s->r & 0x1f);
|
b18 = (uint8_t)(s->r & 0x1f);
|
||||||
|
|
||||||
// Compute the modular addition
|
// Compute the modular addition
|
||||||
bx = funny_mod(b18 + b16, 0x1f);
|
bx = funny_mod(b18 + b16, 0x1f);
|
||||||
|
|
||||||
// Rotate the right cipher state 5 bits
|
// Rotate the right cipher state 5 bits
|
||||||
s->r = ((s->r >> 5) | ((uint64_t)bx << 20));
|
s->r = ((s->r >> 5) | ((uint64_t)bx << 20));
|
||||||
|
|
||||||
// Save the 4 right output bits used for b1
|
// Save the 4 right output bits used for b1
|
||||||
s->b1r = ((bx ^ b16) & 0x0f);
|
s->b1r = ((bx ^ b16) & 0x0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void next_middle(uint8_t in, crypto_state s)
|
static void next_middle(uint8_t in, crypto_state s) {
|
||||||
{
|
uint8_t b12, b13, bx;
|
||||||
uint8_t b12, b13, bx;
|
|
||||||
|
|
||||||
// Update the middle cipher state with the input byte
|
// Update the middle cipher state with the input byte
|
||||||
s->m ^= (((((uint64_t)in << 3) & 0x7f) | (in >> 5)) << 14);
|
s->m ^= (((((uint64_t)in << 3) & 0x7f) | (in >> 5)) << 14);
|
||||||
|
|
||||||
// Extract the two (7 bits) values used for modular addtion
|
// Extract the two (7 bits) values used for modular addtion
|
||||||
b12 = (uint8_t)((s->m >> 7) & 0x7f);
|
b12 = (uint8_t)((s->m >> 7) & 0x7f);
|
||||||
b13 = (uint8_t)(s->m & 0x7f);
|
b13 = (uint8_t)(s->m & 0x7f);
|
||||||
|
|
||||||
// Compute the modular addition
|
// Compute the modular addition
|
||||||
bx = (funny_mod(b12 + bit_rotate_left(b13, 7), 0x7f));
|
bx = (funny_mod(b12 + bit_rotate_left(b13, 7), 0x7f));
|
||||||
|
|
||||||
// Rotate the middle cipher state 7 bits
|
// Rotate the middle cipher state 7 bits
|
||||||
s->m = ((s->m >> 7) | ((uint64_t)bx << 42));
|
s->m = ((s->m >> 7) | ((uint64_t)bx << 42));
|
||||||
|
|
||||||
// Save the 4 middle selector bits used for b1
|
// Save the 4 middle selector bits used for b1
|
||||||
s->b1s = bx & 0x0f;
|
s->b1s = bx & 0x0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void next(const bool feedback, uint8_t in, crypto_state s)
|
static void next(const bool feedback, uint8_t in, crypto_state s) {
|
||||||
{
|
// Initialize the (optional) input parameter
|
||||||
// Initialize the (optional) input parameter
|
uint8_t a = in;
|
||||||
uint8_t a = in;
|
|
||||||
|
|
||||||
// Only Cryptomemory uses feedback
|
// Only Cryptomemory uses feedback
|
||||||
if (feedback) {
|
if (feedback) {
|
||||||
// Construct the cipher update 'a' from (input ^ feedback)
|
// Construct the cipher update 'a' from (input ^ feedback)
|
||||||
a = in ^ nibbles_to_byte(s->b0, s->b1);
|
a = in ^ nibbles_to_byte(s->b0, s->b1);
|
||||||
}
|
|
||||||
|
|
||||||
// Shift the cipher state
|
|
||||||
next_left(a, s);
|
|
||||||
next_middle(a, s);
|
|
||||||
next_right(a, s);
|
|
||||||
|
|
||||||
// For active states we can use the available (previous) 'b1' nibble,
|
|
||||||
// otherwise use reconstruct_nibbles() to generate them
|
|
||||||
// reconstruct_nibbles(s)
|
|
||||||
|
|
||||||
// The nible from b1 shifts to b0
|
|
||||||
s->b0 = s->b1;
|
|
||||||
|
|
||||||
// Construct the new value of nible b1
|
|
||||||
s->b1 = s->b1l & ~(s->b1s);
|
|
||||||
s->b1 |= s->b1r & s->b1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void next_n(const bool feedback, size_t n, uint8_t in, crypto_state s)
|
|
||||||
{
|
|
||||||
// While n-rounds left, shift the cipher
|
|
||||||
while (n--) next(feedback, in, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void initialize(const bool feedback, const uint8_t *Gc, const uint8_t *Ci, const uint8_t *Q, const size_t n, crypto_state s)
|
|
||||||
{
|
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
// Reset the cipher state
|
|
||||||
memset(s, 0x00, sizeof(crypto_state_t));
|
|
||||||
|
|
||||||
// Load in the ci (tag-nonce), together with the first half of Q (reader-nonce)
|
|
||||||
for (pos = 0; pos < 4; pos++) {
|
|
||||||
next_n(feedback, n, Ci[2 * pos ], s);
|
|
||||||
next_n(feedback, n, Ci[2 * pos + 1], s);
|
|
||||||
next(feedback, Q[pos], s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load in the diversified key (Gc), together with the second half of Q (reader-nonce)
|
|
||||||
for (pos = 0; pos < 4; pos++) {
|
|
||||||
next_n(feedback, n, Gc[2 * pos ], s);
|
|
||||||
next_n(feedback, n, Gc[2 * pos + 1], s);
|
|
||||||
next(feedback, Q[pos + 4], s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t cm_byte(crypto_state s)
|
|
||||||
{
|
|
||||||
// Construct keystream byte by combining both nibbles
|
|
||||||
return nibbles_to_byte(s->b0, s->b1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t sm_byte(crypto_state s)
|
|
||||||
{
|
|
||||||
uint8_t ks;
|
|
||||||
|
|
||||||
// Construct keystream byte by combining 2 parts from 4 nibbles
|
|
||||||
next_n(false, 2, 0, s);
|
|
||||||
ks = s->b1 << 4;
|
|
||||||
next_n(false, 2, 0, s);
|
|
||||||
ks |= s->b1;
|
|
||||||
|
|
||||||
return ks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_crypto_state(const char *text, crypto_state s)
|
|
||||||
{
|
|
||||||
int pos;
|
|
||||||
|
|
||||||
printf("%s", text);
|
|
||||||
for (pos = 6; pos >= 0; pos--)
|
|
||||||
printf(" %02x", (uint8_t)(s->l >> (pos * 5)) & 0x1f);
|
|
||||||
|
|
||||||
printf(" |");
|
|
||||||
for (pos = 6; pos >= 0; pos--)
|
|
||||||
printf(" %02x", (uint8_t)(s->m >> (pos * 7)) & 0x7f);
|
|
||||||
|
|
||||||
printf(" |");
|
|
||||||
for (pos = 4; pos >= 0; pos--)
|
|
||||||
printf(" %02x", (uint8_t)(s->r >> (pos * 5)) & 0x1f);
|
|
||||||
|
|
||||||
printf(" | %02x", cm_byte(s));
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void sm_auth(const uint8_t *Gc, const uint8_t *Ci, const uint8_t *Q, uint8_t *Ch, uint8_t *Ci_1, crypto_state s)
|
|
||||||
{
|
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
initialize(false, Gc, Ci, Q, 1, s);
|
|
||||||
|
|
||||||
// Generate challange answer for Tag and Reader
|
|
||||||
for (pos = 0; pos < 8; pos++) {
|
|
||||||
Ci_1[pos] = sm_byte(s);
|
|
||||||
Ch[pos] = sm_byte(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void cm_auth(const uint8_t *Gc, const uint8_t *Ci, const uint8_t *Q, uint8_t *Ch, uint8_t *Ci_1, uint8_t *Ci_2, crypto_state s)
|
|
||||||
{
|
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
initialize(true, Gc, Ci, Q, 3, s);
|
|
||||||
|
|
||||||
// Construct the reader-answer (challange)
|
|
||||||
next_n(true, 6, 0, s);
|
|
||||||
Ch[0] = cm_byte(s);
|
|
||||||
for (pos = 1; pos < 8; pos++) {
|
|
||||||
next_n(true, 7, 0, s);
|
|
||||||
Ch [pos] = cm_byte(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the tag-answer (Ci+1 = ff .. .. .. .. .. .. ..)
|
|
||||||
Ci_1[0] = 0xff;
|
|
||||||
for (pos = 1; pos < 8; pos++) {
|
|
||||||
next_n(true, 2, 0, s);
|
|
||||||
Ci_1[pos] = cm_byte(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the session key (Ci+2)
|
|
||||||
for (pos = 0; pos < 8; pos++) {
|
|
||||||
next_n(true, 2, 0, s);
|
|
||||||
Ci_2[pos] = cm_byte(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the cipher for encryption by shifting 3 more times
|
|
||||||
next_n(true, 3, 0, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cm_crypt(const CryptoAction ca, const uint8_t offset, const uint8_t len, const uint8_t *in, uint8_t *out, crypto_state s)
|
|
||||||
{
|
|
||||||
size_t pos;
|
|
||||||
uint8_t bt;
|
|
||||||
|
|
||||||
next_n(true, 5, 0, s);
|
|
||||||
next(true, offset, s);
|
|
||||||
next_n(true, 5, 0, s);
|
|
||||||
next(true, len, s);
|
|
||||||
for (pos = 0; pos < len; pos++) {
|
|
||||||
// Perform the crypto operation
|
|
||||||
bt = in[pos] ^ cm_byte(s);
|
|
||||||
|
|
||||||
// Generate output
|
|
||||||
if (out) out[pos] = bt;
|
|
||||||
|
|
||||||
// Detect where to find the plaintext for loading into cipher state
|
|
||||||
if (ca == CA_DECRYPT) {
|
|
||||||
next(true, bt, s);
|
|
||||||
} else {
|
|
||||||
next(true, in[pos], s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shift the cipher state 5 times
|
// Shift the cipher state
|
||||||
|
next_left(a, s);
|
||||||
|
next_middle(a, s);
|
||||||
|
next_right(a, s);
|
||||||
|
|
||||||
|
// For active states we can use the available (previous) 'b1' nibble,
|
||||||
|
// otherwise use reconstruct_nibbles() to generate them
|
||||||
|
// reconstruct_nibbles(s)
|
||||||
|
|
||||||
|
// The nible from b1 shifts to b0
|
||||||
|
s->b0 = s->b1;
|
||||||
|
|
||||||
|
// Construct the new value of nible b1
|
||||||
|
s->b1 = s->b1l & ~(s->b1s);
|
||||||
|
s->b1 |= s->b1r & s->b1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void next_n(const bool feedback, size_t n, uint8_t in, crypto_state s) {
|
||||||
|
// While n-rounds left, shift the cipher
|
||||||
|
while (n--) next(feedback, in, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize(const bool feedback, const uint8_t *Gc, const uint8_t *Ci, const uint8_t *Q, const size_t n, crypto_state s) {
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
// Reset the cipher state
|
||||||
|
memset(s, 0x00, sizeof(crypto_state_t));
|
||||||
|
|
||||||
|
// Load in the ci (tag-nonce), together with the first half of Q (reader-nonce)
|
||||||
|
for (pos = 0; pos < 4; pos++) {
|
||||||
|
next_n(feedback, n, Ci[2 * pos ], s);
|
||||||
|
next_n(feedback, n, Ci[2 * pos + 1], s);
|
||||||
|
next(feedback, Q[pos], s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load in the diversified key (Gc), together with the second half of Q (reader-nonce)
|
||||||
|
for (pos = 0; pos < 4; pos++) {
|
||||||
|
next_n(feedback, n, Gc[2 * pos ], s);
|
||||||
|
next_n(feedback, n, Gc[2 * pos + 1], s);
|
||||||
|
next(feedback, Q[pos + 4], s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t cm_byte(crypto_state s) {
|
||||||
|
// Construct keystream byte by combining both nibbles
|
||||||
|
return nibbles_to_byte(s->b0, s->b1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t sm_byte(crypto_state s) {
|
||||||
|
uint8_t ks;
|
||||||
|
|
||||||
|
// Construct keystream byte by combining 2 parts from 4 nibbles
|
||||||
|
next_n(false, 2, 0, s);
|
||||||
|
ks = s->b1 << 4;
|
||||||
|
next_n(false, 2, 0, s);
|
||||||
|
ks |= s->b1;
|
||||||
|
|
||||||
|
return ks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_crypto_state(const char *text, crypto_state s) {
|
||||||
|
int pos;
|
||||||
|
|
||||||
|
printf("%s", text);
|
||||||
|
for (pos = 6; pos >= 0; pos--)
|
||||||
|
printf(" %02x", (uint8_t)(s->l >> (pos * 5)) & 0x1f);
|
||||||
|
|
||||||
|
printf(" |");
|
||||||
|
for (pos = 6; pos >= 0; pos--)
|
||||||
|
printf(" %02x", (uint8_t)(s->m >> (pos * 7)) & 0x7f);
|
||||||
|
|
||||||
|
printf(" |");
|
||||||
|
for (pos = 4; pos >= 0; pos--)
|
||||||
|
printf(" %02x", (uint8_t)(s->r >> (pos * 5)) & 0x1f);
|
||||||
|
|
||||||
|
printf(" | %02x", cm_byte(s));
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sm_auth(const uint8_t *Gc, const uint8_t *Ci, const uint8_t *Q, uint8_t *Ch, uint8_t *Ci_1, crypto_state s) {
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
initialize(false, Gc, Ci, Q, 1, s);
|
||||||
|
|
||||||
|
// Generate challange answer for Tag and Reader
|
||||||
|
for (pos = 0; pos < 8; pos++) {
|
||||||
|
Ci_1[pos] = sm_byte(s);
|
||||||
|
Ch[pos] = sm_byte(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_auth(const uint8_t *Gc, const uint8_t *Ci, const uint8_t *Q, uint8_t *Ch, uint8_t *Ci_1, uint8_t *Ci_2, crypto_state s) {
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
initialize(true, Gc, Ci, Q, 3, s);
|
||||||
|
|
||||||
|
// Construct the reader-answer (challange)
|
||||||
|
next_n(true, 6, 0, s);
|
||||||
|
Ch[0] = cm_byte(s);
|
||||||
|
for (pos = 1; pos < 8; pos++) {
|
||||||
|
next_n(true, 7, 0, s);
|
||||||
|
Ch [pos] = cm_byte(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the tag-answer (Ci+1 = ff .. .. .. .. .. .. ..)
|
||||||
|
Ci_1[0] = 0xff;
|
||||||
|
for (pos = 1; pos < 8; pos++) {
|
||||||
|
next_n(true, 2, 0, s);
|
||||||
|
Ci_1[pos] = cm_byte(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the session key (Ci+2)
|
||||||
|
for (pos = 0; pos < 8; pos++) {
|
||||||
|
next_n(true, 2, 0, s);
|
||||||
|
Ci_2[pos] = cm_byte(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the cipher for encryption by shifting 3 more times
|
||||||
|
next_n(true, 3, 0, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cm_crypt(const CryptoAction ca, const uint8_t offset, const uint8_t len, const uint8_t *in, uint8_t *out, crypto_state s) {
|
||||||
|
size_t pos;
|
||||||
|
uint8_t bt;
|
||||||
|
|
||||||
next_n(true, 5, 0, s);
|
next_n(true, 5, 0, s);
|
||||||
}
|
next(true, offset, s);
|
||||||
|
next_n(true, 5, 0, s);
|
||||||
|
next(true, len, s);
|
||||||
|
for (pos = 0; pos < len; pos++) {
|
||||||
|
// Perform the crypto operation
|
||||||
|
bt = in[pos] ^ cm_byte(s);
|
||||||
|
|
||||||
|
// Generate output
|
||||||
|
if (out) out[pos] = bt;
|
||||||
|
|
||||||
|
// Detect where to find the plaintext for loading into cipher state
|
||||||
|
if (ca == CA_DECRYPT) {
|
||||||
|
next(true, bt, s);
|
||||||
|
} else {
|
||||||
|
next(true, in[pos], s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift the cipher state 5 times
|
||||||
|
next_n(true, 5, 0, s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cm_encrypt(const uint8_t offset, const uint8_t len, const uint8_t *ct, uint8_t *pt, crypto_state s)
|
void cm_encrypt(const uint8_t offset, const uint8_t len, const uint8_t *ct, uint8_t *pt, crypto_state s) {
|
||||||
{
|
next_n(true, 5, 0, s);
|
||||||
next_n(true, 5, 0, s);
|
next(true, 0, s);
|
||||||
next(true, 0, s);
|
cm_crypt(CA_ENCRYPT, offset, len, ct, pt, s);
|
||||||
cm_crypt(CA_ENCRYPT, offset, len, ct, pt, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cm_decrypt(const uint8_t offset, const uint8_t len, const uint8_t *ct, uint8_t *pt, crypto_state s)
|
void cm_decrypt(const uint8_t offset, const uint8_t len, const uint8_t *ct, uint8_t *pt, crypto_state s) {
|
||||||
{
|
next_n(true, 5, 0, s);
|
||||||
next_n(true, 5, 0, s);
|
next(true, 0, s);
|
||||||
next(true, 0, s);
|
cm_crypt(CA_DECRYPT, offset, len, ct, pt, s);
|
||||||
cm_crypt(CA_DECRYPT, offset, len, ct, pt, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cm_grind_read_system_zone(const uint8_t offset, const uint8_t len, const uint8_t *pt, crypto_state s)
|
void cm_grind_read_system_zone(const uint8_t offset, const uint8_t len, const uint8_t *pt, crypto_state s) {
|
||||||
{
|
cm_crypt(CA_ENCRYPT, offset, len, pt, NULL, s);
|
||||||
cm_crypt(CA_ENCRYPT, offset, len, pt, NULL, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cm_grind_set_user_zone(const uint8_t zone, crypto_state s)
|
void cm_grind_set_user_zone(const uint8_t zone, crypto_state s) {
|
||||||
{
|
next(true, zone, s);
|
||||||
next(true, zone, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cm_mac(uint8_t *mac, crypto_state s)
|
void cm_mac(uint8_t *mac, crypto_state s) {
|
||||||
{
|
next_n(true, 10, 0, s);
|
||||||
next_n(true, 10, 0, s);
|
if (mac)
|
||||||
if (mac)
|
mac[0] = cm_byte(s);
|
||||||
mac[0] = cm_byte(s);
|
|
||||||
|
|
||||||
next_n(true, 5, 0, s);
|
next_n(true, 5, 0, s);
|
||||||
if (mac)
|
if (mac)
|
||||||
mac[1] = cm_byte(s);
|
mac[1] = cm_byte(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cm_password(const uint8_t *pt, uint8_t *ct, crypto_state s)
|
void cm_password(const uint8_t *pt, uint8_t *ct, crypto_state s) {
|
||||||
{
|
for (size_t pos = 0; pos < 3; pos++) {
|
||||||
for (size_t pos = 0; pos < 3; pos++) {
|
next_n(true, 5, pt[pos], s);
|
||||||
next_n(true, 5, pt[pos], s);
|
ct[pos] = cm_byte(s);
|
||||||
ct[pos] = cm_byte(s);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,14 +33,14 @@ extern "C" {
|
||||||
typedef uint8_t nibble;
|
typedef uint8_t nibble;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t l;
|
uint64_t l;
|
||||||
uint64_t m;
|
uint64_t m;
|
||||||
uint64_t r;
|
uint64_t r;
|
||||||
nibble b0;
|
nibble b0;
|
||||||
nibble b1;
|
nibble b1;
|
||||||
nibble b1l;
|
nibble b1l;
|
||||||
nibble b1r;
|
nibble b1r;
|
||||||
nibble b1s;
|
nibble b1s;
|
||||||
} crypto_state_t;
|
} crypto_state_t;
|
||||||
typedef crypto_state_t *crypto_state;
|
typedef crypto_state_t *crypto_state;
|
||||||
|
|
||||||
|
|
222
common/cryptorf/cryptolib_baloncu.c
Normal file
222
common/cryptorf/cryptolib_baloncu.c
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
#include "cryptolib.h"
|
||||||
|
|
||||||
|
static byte_t rotate5_lut[] = {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x02, 0x02, 0x06, 0x06, 0x0a, 0x0a, 0x0e, 0x0e, 0x12, 0x12, 0x16, 0x16, 0x1a, 0x1a, 0x1e, 0x1e, 0x03, 0x03, 0x07, 0x07, 0x0b, 0x0b, 0x0f, 0x0f, 0x13, 0x13, 0x17, 0x17, 0x1b, 0x1b, 0x1f, 0x1f, 0x04, 0x06, 0x04, 0x06, 0x0c, 0x0e, 0x0c, 0x0e, 0x14, 0x16, 0x14, 0x16, 0x1c, 0x1e, 0x1c, 0x1e, 0x05, 0x07, 0x05, 0x07, 0x0d, 0x0f, 0x0d, 0x0f, 0x15, 0x17, 0x15, 0x17, 0x1d, 0x1f, 0x1d, 0x1f, 0x06, 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x16, 0x16, 0x16, 0x16, 0x1e, 0x1e, 0x1e, 0x1e, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x1f, 0x1f, 0x1f, 0x1f, 0x08, 0x0a, 0x0c, 0x0e, 0x08, 0x0a, 0x0c, 0x0e, 0x18, 0x1a, 0x1c, 0x1e, 0x18, 0x1a, 0x1c, 0x1e, 0x09, 0x0b, 0x0d, 0x0f, 0x09, 0x0b, 0x0d, 0x0f, 0x19, 0x1b, 0x1d, 0x1f, 0x19, 0x1b, 0x1d, 0x1f, 0x0a, 0x0a, 0x0e, 0x0e, 0x0a, 0x0a, 0x0e, 0x0e, 0x1a, 0x1a, 0x1e, 0x1e, 0x1a, 0x1a, 0x1e, 0x1e, 0x0b, 0x0b, 0x0f, 0x0f, 0x0b, 0x0b, 0x0f, 0x0f, 0x1b, 0x1b, 0x1f, 0x1f, 0x1b, 0x1b, 0x1f, 0x1f, 0x0c, 0x0e, 0x0c, 0x0e, 0x0c, 0x0e, 0x0c, 0x0e, 0x1c, 0x1e, 0x1c, 0x1e, 0x1c, 0x1e, 0x1c, 0x1e, 0x0d, 0x0f, 0x0d, 0x0f, 0x0d, 0x0f, 0x0d, 0x0f, 0x1d, 0x1f, 0x1d, 0x1f, 0x1d, 0x1f, 0x1d, 0x1f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f};
|
||||||
|
|
||||||
|
static byte_t rotate7_lut[] = {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x39, 0x3b, 0x3d, 0x3f, 0x41, 0x43, 0x45, 0x47, 0x49, 0x4b, 0x4d, 0x4f, 0x51, 0x53, 0x55, 0x57, 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x65, 0x67, 0x69, 0x6b, 0x6d, 0x6f, 0x71, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d, 0x7f, 0x02, 0x02, 0x06, 0x06, 0x0a, 0x0a, 0x0e, 0x0e, 0x12, 0x12, 0x16, 0x16, 0x1a, 0x1a, 0x1e, 0x1e, 0x22, 0x22, 0x26, 0x26, 0x2a, 0x2a, 0x2e, 0x2e, 0x32, 0x32, 0x36, 0x36, 0x3a, 0x3a, 0x3e, 0x3e, 0x42, 0x42, 0x46, 0x46, 0x4a, 0x4a, 0x4e, 0x4e, 0x52, 0x52, 0x56, 0x56, 0x5a, 0x5a, 0x5e, 0x5e, 0x62, 0x62, 0x66, 0x66, 0x6a, 0x6a, 0x6e, 0x6e, 0x72, 0x72, 0x76, 0x76, 0x7a, 0x7a, 0x7e, 0x7e, 0x03, 0x03, 0x07, 0x07, 0x0b, 0x0b, 0x0f, 0x0f, 0x13, 0x13, 0x17, 0x17, 0x1b, 0x1b, 0x1f, 0x1f, 0x23, 0x23, 0x27, 0x27, 0x2b, 0x2b, 0x2f, 0x2f, 0x33, 0x33, 0x37, 0x37, 0x3b, 0x3b, 0x3f, 0x3f, 0x43, 0x43, 0x47, 0x47, 0x4b, 0x4b, 0x4f, 0x4f, 0x53, 0x53, 0x57, 0x57, 0x5b, 0x5b, 0x5f, 0x5f, 0x63, 0x63, 0x67, 0x67, 0x6b, 0x6b, 0x6f, 0x6f, 0x73, 0x73, 0x77, 0x77, 0x7b, 0x7b, 0x7f, 0x7f};
|
||||||
|
|
||||||
|
static byte_t funny_mod1f_lut[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
|
||||||
|
|
||||||
|
static byte_t funny_mod7f_lut[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x01};
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CA_ENCRYPT = 0x01,
|
||||||
|
CA_DECRYPT = 0x02
|
||||||
|
} CryptoAction;
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
byte_t nibbles_to_byte(nibble b0, nibble b1) {
|
||||||
|
// Combine both nibbles
|
||||||
|
return ((b0 << 4) | b1);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte_t funny_mod(byte_t a, byte_t m) {
|
||||||
|
// Just return the input when this is less or equal than the modular value
|
||||||
|
if (a < m) return a;
|
||||||
|
|
||||||
|
// Compute the modular value
|
||||||
|
a %= m;
|
||||||
|
|
||||||
|
// Return the funny value, when the output was now zero, return the modular value
|
||||||
|
return (a == 0) ? m : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte_t bit_rotate_left(byte_t a, byte_t n_bits) {
|
||||||
|
// Rotate value a with the length of n_bits only 1 time
|
||||||
|
byte_t mask = (1 << n_bits) - 1;
|
||||||
|
return ((a << 1) | (a >> (n_bits - 1))) & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
//meltem changed this function to make it perform faster
|
||||||
|
//to get a better idea of the algorithm check out cryptorf inside the tools directory
|
||||||
|
void RAMFUNC next(size_t repeat, byte_t in, crypto_state s) {
|
||||||
|
size_t i = repeat;
|
||||||
|
byte_t a;
|
||||||
|
byte_t *cipher_p;
|
||||||
|
do {
|
||||||
|
// Construct the cipher update 'a' from (input ^ feedback)
|
||||||
|
a = in ^ ((s->b0 << 4) | s->b1);
|
||||||
|
|
||||||
|
// Shift the cipher state
|
||||||
|
//left
|
||||||
|
cipher_p = (byte_t *) &s->l;
|
||||||
|
cipher_p[5] = cipher_p[5] ^ (a & 0x1f);
|
||||||
|
s->l = (s->l >> 8);
|
||||||
|
cipher_p[7] = funny_mod1f_lut[cipher_p[3] + rotate5_lut[cipher_p[0]]];
|
||||||
|
s->b1l = cipher_p[7] ^ cipher_p[3];
|
||||||
|
|
||||||
|
//middle
|
||||||
|
cipher_p = (byte_t *) &s->m;
|
||||||
|
cipher_p[3] = cipher_p[3] ^ ((a << 3) | (a >> 5));
|
||||||
|
s->m = (s->m >> 8);
|
||||||
|
cipher_p[7] = funny_mod7f_lut[(cipher_p[1] & 0x7f) + rotate7_lut[(cipher_p[0] & 0x7f)]];
|
||||||
|
s->b1s = cipher_p[7] & 0x0f;
|
||||||
|
|
||||||
|
//right
|
||||||
|
cipher_p = (byte_t *) &s->r;
|
||||||
|
cipher_p[6] = cipher_p[6] ^ (a >> 3);
|
||||||
|
s->r = (s->r >> 8);
|
||||||
|
cipher_p[7] = funny_mod1f_lut[cipher_p[4] + cipher_p[2]];
|
||||||
|
s->b1r = cipher_p[7] ^ cipher_p[4];
|
||||||
|
|
||||||
|
|
||||||
|
// The nible from b1 shifts to b0
|
||||||
|
s->b0 = s->b1;
|
||||||
|
|
||||||
|
// Construct the new value of nible b1
|
||||||
|
s->b1 = (s->b1l & 0x0f) & ~(s->b1s);
|
||||||
|
s->b1 |= s->b1r & s->b1s;
|
||||||
|
} while (--i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void next_n(size_t n, byte_t in, crypto_state s) {
|
||||||
|
// While n-rounds left, shift the cipher
|
||||||
|
while (n--) next(1, in, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize(const byte_t *Gc, const byte_t *Ci, const byte_t *Q, const size_t n, crypto_state s) {
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
// Reset the cipher state
|
||||||
|
memset(s, 0x00, sizeof(crypto_state_t));
|
||||||
|
|
||||||
|
// Load in the ci (tag-nonce), together with the first half of Q (reader-nonce)
|
||||||
|
for (pos = 0; pos < 4; pos++) {
|
||||||
|
next(n, Ci[2 * pos ], s);
|
||||||
|
next(n, Ci[2 * pos + 1], s);
|
||||||
|
next(1, Q[pos], s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load in the diversified key (Gc), together with the second half of Q (reader-nonce)
|
||||||
|
for (pos = 0; pos < 4; pos++) {
|
||||||
|
next(n, Gc[2 * pos ], s);
|
||||||
|
next(n, Gc[2 * pos + 1], s);
|
||||||
|
next(1, Q[pos + 4], s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte_t cm_byte(crypto_state s) {
|
||||||
|
// Construct keystream byte by combining both nibbles
|
||||||
|
return nibbles_to_byte(s->b0, s->b1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_crypto_state(const char *text, crypto_state s) {
|
||||||
|
int pos;
|
||||||
|
|
||||||
|
printf("%s", text);
|
||||||
|
for (pos = 6; pos >= 0; pos--)
|
||||||
|
printf(" %02x", (byte_t)(s->l >> (pos * 5)) & 0x1f);
|
||||||
|
printf(" |");
|
||||||
|
for (pos = 6; pos >= 0; pos--)
|
||||||
|
printf(" %02x", (byte_t)(s->m >> (pos * 7)) & 0x7f);
|
||||||
|
printf(" |");
|
||||||
|
for (pos = 4; pos >= 0; pos--)
|
||||||
|
printf(" %02x", (byte_t)(s->r >> (pos * 5)) & 0x1f);
|
||||||
|
|
||||||
|
printf(" | %02x", cm_byte(s));
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_auth(const byte_t *Gc, const byte_t *Ci, const byte_t *Q, byte_t *Ch, byte_t *Ci_1, byte_t *Ci_2, crypto_state s) {
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
initialize(Gc, Ci, Q, 3, s);
|
||||||
|
|
||||||
|
// Construct the reader-answer (challange)
|
||||||
|
next(6, 0, s);
|
||||||
|
Ch[0] = cm_byte(s);
|
||||||
|
for (pos = 1; pos < 8; pos++) {
|
||||||
|
next(7, 0, s);
|
||||||
|
Ch [pos] = cm_byte(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the tag-answer (Ci+1 = ff .. .. .. .. .. .. ..)
|
||||||
|
Ci_1[0] = 0xff;
|
||||||
|
for (pos = 1; pos < 8; pos++) {
|
||||||
|
next(2, 0, s);
|
||||||
|
Ci_1[pos] = cm_byte(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the session key (Ci+2)
|
||||||
|
for (pos = 0; pos < 8; pos++) {
|
||||||
|
next(2, 0, s);
|
||||||
|
Ci_2[pos] = cm_byte(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the cipher for encryption by shifting 3 more times
|
||||||
|
next(3, 0, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_crypt(const CryptoAction ca, const byte_t offset, const byte_t len, const byte_t *in, byte_t *out, crypto_state s) {
|
||||||
|
size_t pos;
|
||||||
|
byte_t bt;
|
||||||
|
|
||||||
|
next(5, 0, s);
|
||||||
|
next(1, offset, s);
|
||||||
|
next(5, 0, s);
|
||||||
|
next(1, len, s);
|
||||||
|
for (pos = 0; pos < len; pos++) {
|
||||||
|
// Perform the crypto operation
|
||||||
|
bt = in[pos] ^ cm_byte(s);
|
||||||
|
|
||||||
|
// Generate output
|
||||||
|
if (out) out[pos] = bt;
|
||||||
|
|
||||||
|
// Detect where to find the plaintext for loading into cipher state
|
||||||
|
if (ca == CA_DECRYPT) {
|
||||||
|
next(1, bt, s);
|
||||||
|
} else {
|
||||||
|
next(1, in[pos], s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift the cipher state 5 times
|
||||||
|
next(5, 0, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_encrypt(const byte_t offset, const byte_t len, const byte_t *ct, byte_t *pt, crypto_state s) {
|
||||||
|
next(5, 0, s);
|
||||||
|
next(1, 0, s);
|
||||||
|
cm_crypt(CA_ENCRYPT, offset, len, ct, pt, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_decrypt(const byte_t offset, const byte_t len, const byte_t *ct, byte_t *pt, crypto_state s) {
|
||||||
|
next(5, 0, s);
|
||||||
|
next(1, 0, s);
|
||||||
|
cm_crypt(CA_DECRYPT, offset, len, ct, pt, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_grind_read_system_zone(const byte_t offset, const byte_t len, const byte_t *pt, crypto_state s) {
|
||||||
|
cm_crypt(CA_ENCRYPT, offset, len, pt, null, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_grind_set_user_zone(const byte_t zone, crypto_state s) {
|
||||||
|
next(1, zone, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_mac(byte_t *mac, crypto_state s) {
|
||||||
|
next(10, 0, s);
|
||||||
|
if (mac) mac[0] = cm_byte(s);
|
||||||
|
next(5, 0, s);
|
||||||
|
if (mac) mac[1] = cm_byte(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_password(const byte_t *pt, byte_t *ct, crypto_state s) {
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
for (pos = 0; pos < 3; pos++) {
|
||||||
|
next(5, pt[pos], s);
|
||||||
|
ct[pos] = cm_byte(s);
|
||||||
|
}
|
||||||
|
}
|
41
common/cryptorf/cryptolib_baloncu.h
Normal file
41
common/cryptorf/cryptolib_baloncu.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef _CRYPTOLIB_H_
|
||||||
|
#define _CRYPTOLIB_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define null 0
|
||||||
|
|
||||||
|
typedef unsigned char byte_t;
|
||||||
|
typedef long long unsigned int ui64;
|
||||||
|
|
||||||
|
// A nibble is actually only 4 bits, but there is no such type ;)
|
||||||
|
typedef byte_t nibble;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t l;
|
||||||
|
uint64_t m;
|
||||||
|
uint64_t r;
|
||||||
|
nibble b0;
|
||||||
|
nibble b1;
|
||||||
|
nibble b1l;
|
||||||
|
nibble b1r;
|
||||||
|
nibble b1s;
|
||||||
|
} crypto_state_t;
|
||||||
|
typedef crypto_state_t *crypto_state;
|
||||||
|
|
||||||
|
void print_crypto_state(const char *text, crypto_state s);
|
||||||
|
void cm_auth(const byte_t *Gc, const byte_t *Ci, const byte_t *Q, byte_t *Ch, byte_t *Ci_1, byte_t *Ci_2, crypto_state s);
|
||||||
|
void cm_encrypt(const byte_t offset, const byte_t len, const byte_t *pt, byte_t *ct, crypto_state s);
|
||||||
|
void cm_decrypt(const byte_t offset, const byte_t len, const byte_t *ct, byte_t *pt, crypto_state s);
|
||||||
|
void cm_grind_read_system_zone(const byte_t offset, const byte_t len, const byte_t *pt, crypto_state s);
|
||||||
|
void cm_grind_set_user_zone(const byte_t zone, crypto_state s);
|
||||||
|
void cm_mac(byte_t *mac, crypto_state s);
|
||||||
|
void cm_password(const byte_t *pt, byte_t *ct, crypto_state s);
|
||||||
|
void RAMFUNC next(size_t repeat, byte_t in, crypto_state s);
|
||||||
|
|
||||||
|
#endif // _CRYPTOLIB_H_
|
|
@ -1,9 +1,9 @@
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* CryptoMemory simulation
|
* CryptoMemory simulation
|
||||||
*
|
*
|
||||||
* Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult
|
* Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult
|
||||||
* and Ronny Wichers Schreur. Radboud University Nijmegen
|
* and Ronny Wichers Schreur. Radboud University Nijmegen
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
@ -26,71 +26,85 @@
|
||||||
#include "cryptolib.h"
|
#include "cryptolib.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
// avoid scanf warnings in Visual Studio
|
// avoid scanf warnings in Visual Studio
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
#define _CRT_SECURE_NO_DEPRECATE
|
#define _CRT_SECURE_NO_DEPRECATE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int main(int argc, const char* argv[])
|
int main(int argc, const char *argv[]) {
|
||||||
{
|
// Cryptomemory state
|
||||||
// Cryptomemory state
|
crypto_state_t s;
|
||||||
crypto_state_t s;
|
|
||||||
|
|
||||||
// Main authentication values
|
// Main authentication values
|
||||||
uint8_t Q[8]; // Reader key-auth random
|
uint8_t Q[8]; // Reader key-auth random
|
||||||
uint8_t Gc[8]; // Secret seed
|
uint8_t Gc[8]; // Secret seed
|
||||||
uint8_t Ci[8]; // Card random (last state)
|
uint8_t Ci[8]; // Card random (last state)
|
||||||
uint8_t Ch[8]; // Reader answer (challenge)
|
uint8_t Ch[8]; // Reader answer (challenge)
|
||||||
uint8_t Ci_1[8]; // Card answer
|
uint8_t Ci_1[8]; // Card answer
|
||||||
uint8_t Ci_2[8]; // Session key
|
uint8_t Ci_2[8]; // Session key
|
||||||
|
|
||||||
// Session authentication values
|
|
||||||
uint8_t Qs[8]; // Reader session-auth random
|
|
||||||
uint8_t Chs[8]; // Reader session-answer (challenge)
|
|
||||||
uint8_t Ci_1s[8]; // Card answer for session
|
|
||||||
uint8_t Ci_2s[8]; // Is this used?
|
|
||||||
|
|
||||||
// Various argument options
|
// Session authentication values
|
||||||
uint64_t nGc; // Card secret
|
uint8_t Qs[8]; // Reader session-auth random
|
||||||
uint64_t nCi; // Card random
|
uint8_t Chs[8]; // Reader session-answer (challenge)
|
||||||
uint64_t nQ; // Reader main-random
|
uint8_t Ci_1s[8]; // Card answer for session
|
||||||
uint64_t nQs; // Reader session-random
|
uint8_t Ci_2s[8]; // Is this used?
|
||||||
|
|
||||||
// Show header and help syntax
|
// Various argument options
|
||||||
printf("CryptoMemory simulator - (c) Radboud University Nijmegen\n");
|
uint64_t nGc; // Card secret
|
||||||
if (argc < 5)
|
uint64_t nCi; // Card random
|
||||||
{
|
uint64_t nQ; // Reader main-random
|
||||||
printf("\nsyntax: cm <Gc> <Ci> <Q> <Q(s)>\n");
|
uint64_t nQs; // Reader session-random
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse arguments
|
// Show header and help syntax
|
||||||
sscanf(argv[1],"%016" SCNx64,&nGc); num_to_bytes(nGc,8,Gc);
|
printf("CryptoMemory simulator - (c) Radboud University Nijmegen\n");
|
||||||
sscanf(argv[2],"%016" SCNx64,&nCi); num_to_bytes(nCi,8,Ci);
|
if (argc < 5) {
|
||||||
sscanf(argv[3],"%016" SCNx64,&nQ); num_to_bytes(nQ,8,Q);
|
printf("\nsyntax: cm <Gc> <Ci> <Q> <Q(s)>\n");
|
||||||
sscanf(argv[4],"%016" SCNx64,&nQs); num_to_bytes(nQs,8,Qs);
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate authentication
|
// Parse arguments
|
||||||
cm_auth(Gc,Ci,Q,Ch,Ci_1,Ci_2,&s);
|
sscanf(argv[1], "%016" SCNx64, &nGc);
|
||||||
|
num_to_bytes(nGc, 8, Gc);
|
||||||
|
sscanf(argv[2], "%016" SCNx64, &nCi);
|
||||||
|
num_to_bytes(nCi, 8, Ci);
|
||||||
|
sscanf(argv[3], "%016" SCNx64, &nQ);
|
||||||
|
num_to_bytes(nQ, 8, Q);
|
||||||
|
sscanf(argv[4], "%016" SCNx64, &nQs);
|
||||||
|
num_to_bytes(nQs, 8, Qs);
|
||||||
|
|
||||||
printf("\nAuthenticate\n");
|
// Calculate authentication
|
||||||
printf(" Gc: "); print_bytes(Gc,8);
|
cm_auth(Gc, Ci, Q, Ch, Ci_1, Ci_2, &s);
|
||||||
printf(" Ci: "); print_bytes(Ci,8);
|
|
||||||
printf(" Q: "); print_bytes(Q,8);
|
|
||||||
printf(" Ch: "); print_bytes(Ch,8);
|
|
||||||
printf(" Ci+1: "); print_bytes(Ci_1,8);
|
|
||||||
printf(" Ci+2: "); print_bytes(Ci_2,8);
|
|
||||||
|
|
||||||
cm_auth(Ci_2,Ci_1,Qs,Chs,Ci_1s,Ci_2s,&s);
|
printf("\nAuthenticate\n");
|
||||||
|
printf(" Gc: ");
|
||||||
|
print_bytes(Gc, 8);
|
||||||
|
printf(" Ci: ");
|
||||||
|
print_bytes(Ci, 8);
|
||||||
|
printf(" Q: ");
|
||||||
|
print_bytes(Q, 8);
|
||||||
|
printf(" Ch: ");
|
||||||
|
print_bytes(Ch, 8);
|
||||||
|
printf(" Ci+1: ");
|
||||||
|
print_bytes(Ci_1, 8);
|
||||||
|
printf(" Ci+2: ");
|
||||||
|
print_bytes(Ci_2, 8);
|
||||||
|
|
||||||
printf("\nVerify Crypto (Session Key)\n");
|
cm_auth(Ci_2, Ci_1, Qs, Chs, Ci_1s, Ci_2s, &s);
|
||||||
printf(" Gc(s): "); print_bytes(Ci_2,8);
|
|
||||||
printf(" Ci(s): "); print_bytes(Ci_1,8);
|
|
||||||
printf(" Q(s): "); print_bytes(Qs,8);
|
|
||||||
printf(" Ch(s): "); print_bytes(Chs,8);
|
|
||||||
printf("Ci+1(s): "); print_bytes(Ci_1s,8);
|
|
||||||
printf("Ci+2(s): "); print_bytes(Ci_2s,8);
|
|
||||||
|
|
||||||
printf("\n");
|
printf("\nVerify Crypto (Session Key)\n");
|
||||||
return 0;
|
printf(" Gc(s): ");
|
||||||
|
print_bytes(Ci_2, 8);
|
||||||
|
printf(" Ci(s): ");
|
||||||
|
print_bytes(Ci_1, 8);
|
||||||
|
printf(" Q(s): ");
|
||||||
|
print_bytes(Qs, 8);
|
||||||
|
printf(" Ch(s): ");
|
||||||
|
print_bytes(Chs, 8);
|
||||||
|
printf("Ci+1(s): ");
|
||||||
|
print_bytes(Ci_1s, 8);
|
||||||
|
printf("Ci+2(s): ");
|
||||||
|
print_bytes(Ci_2s, 8);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* SecureMemory simulation
|
* SecureMemory simulation
|
||||||
*
|
*
|
||||||
* Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult
|
* Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult
|
||||||
* and Ronny Wichers Schreur. Radboud University Nijmegen
|
* and Ronny Wichers Schreur. Radboud University Nijmegen
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
@ -26,58 +26,63 @@
|
||||||
#include "cryptolib.h"
|
#include "cryptolib.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
// avoid scanf warnings in Visual Studio
|
// avoid scanf warnings in Visual Studio
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
#define _CRT_SECURE_NO_DEPRECATE
|
#define _CRT_SECURE_NO_DEPRECATE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int main(int argc, const char* argv[])
|
int main(int argc, const char *argv[]) {
|
||||||
{
|
// Cryptomemory state
|
||||||
// Cryptomemory state
|
crypto_state_t s;
|
||||||
crypto_state_t s;
|
size_t pos;
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
uint8_t Q[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // Reader random
|
uint8_t Q[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // Reader random
|
||||||
uint8_t Gc[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // Secret seed
|
uint8_t Gc[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // Secret seed
|
||||||
uint8_t Ci[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // Card random (last state)
|
uint8_t Ci[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // Card random (last state)
|
||||||
uint8_t Ch[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // Reader answer
|
uint8_t Ch[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // Reader answer
|
||||||
uint8_t Ci_1[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // Card answer
|
uint8_t Ci_1[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // Card answer
|
||||||
|
|
||||||
// Various argument options
|
// Various argument options
|
||||||
uint64_t nGc; // Card secret
|
uint64_t nGc; // Card secret
|
||||||
uint64_t nCi; // Card random
|
uint64_t nCi; // Card random
|
||||||
uint64_t nQ; // Reader main-random
|
uint64_t nQ; // Reader main-random
|
||||||
|
|
||||||
// Show header and help syntax
|
// Show header and help syntax
|
||||||
printf("SecureMemory simulator - (c) Radboud University Nijmegen\n");
|
printf("SecureMemory simulator - (c) Radboud University Nijmegen\n");
|
||||||
if (argc < 4)
|
if (argc < 4) {
|
||||||
{
|
printf("\nsyntax: sm <Gc> <Ci> <Q>\n");
|
||||||
printf("\nsyntax: sm <Gc> <Ci> <Q>\n");
|
return 1;
|
||||||
return 1;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
sscanf(argv[1],"%016" SCNx64,&nGc); num_to_bytes(nGc,8,Gc);
|
sscanf(argv[1], "%016" SCNx64, &nGc);
|
||||||
sscanf(argv[2],"%016" SCNx64,&nCi); num_to_bytes(nCi,8,Ci);
|
num_to_bytes(nGc, 8, Gc);
|
||||||
sscanf(argv[3],"%016" SCNx64,&nQ); num_to_bytes(nQ,8,Q);
|
sscanf(argv[2], "%016" SCNx64, &nCi);
|
||||||
|
num_to_bytes(nCi, 8, Ci);
|
||||||
|
sscanf(argv[3], "%016" SCNx64, &nQ);
|
||||||
|
num_to_bytes(nQ, 8, Q);
|
||||||
|
|
||||||
// Calculate authentication
|
// Calculate authentication
|
||||||
sm_auth(Gc,Ci,Q,Ch,Ci_1,&s);
|
sm_auth(Gc, Ci, Q, Ch, Ci_1, &s);
|
||||||
|
|
||||||
printf("\nAuthentication info\n\n");
|
printf("\nAuthentication info\n\n");
|
||||||
printf(" Gc: "); print_bytes(Gc,8);
|
printf(" Gc: ");
|
||||||
printf(" Ci: "); print_bytes(Ci,8);
|
print_bytes(Gc, 8);
|
||||||
printf(" Q: "); print_bytes(Q,8);
|
printf(" Ci: ");
|
||||||
printf(" Ch: "); print_bytes(Ch,8);
|
print_bytes(Ci, 8);
|
||||||
printf("Ci+1: "); print_bytes(Ci_1,8);
|
printf(" Q: ");
|
||||||
printf("\n");
|
print_bytes(Q, 8);
|
||||||
printf(" Ks: ");
|
printf(" Ch: ");
|
||||||
for (pos=0; pos<8; pos++)
|
print_bytes(Ch, 8);
|
||||||
{
|
printf("Ci+1: ");
|
||||||
printf("%02x ",Ci_1[pos]);
|
print_bytes(Ci_1, 8);
|
||||||
printf("%02x ",Ch[pos]);
|
printf("\n");
|
||||||
}
|
printf(" Ks: ");
|
||||||
printf("\n\n");
|
for (pos = 0; pos < 8; pos++) {
|
||||||
|
printf("%02x ", Ci_1[pos]);
|
||||||
|
printf("%02x ", Ch[pos]);
|
||||||
|
}
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,23 +1,21 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
void num_to_bytes(uint64_t n, size_t len, uint8_t *dst)
|
void num_to_bytes(uint64_t n, size_t len, uint8_t *dst) {
|
||||||
{
|
while (len--) {
|
||||||
while (len--) {
|
dst[len] = (uint8_t)n;
|
||||||
dst[len] = (uint8_t)n;
|
n >>= 8;
|
||||||
n >>= 8;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_bytes(const uint8_t *pbtData, const size_t szLen)
|
void print_bytes(const uint8_t *pbtData, const size_t szLen) {
|
||||||
{
|
size_t uiPos;
|
||||||
size_t uiPos;
|
for (uiPos = 0; uiPos < szLen; uiPos++) {
|
||||||
for (uiPos = 0; uiPos < szLen; uiPos++) {
|
printf("%02x ", pbtData[uiPos]);
|
||||||
printf("%02x ", pbtData[uiPos]);
|
if (uiPos > 20) {
|
||||||
if (uiPos > 20) {
|
printf("...");
|
||||||
printf("...");
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
printf("\n");
|
||||||
printf("\n");
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue