Topic: Nested Authentication

I am having some troubles getting my nested authentication working. This is how I am trying to authenticate:

(1) send an encrypted authentication request
(2) crypto_state = crypto1_create(key)
(3) tag_nonce = swap_endian32(receiveData) ^ crypto1_word(crypto_state, swap_endian32(receiveData), 1)
(4) reader_response = prng_successor(tag_nonce, 64);
(5) encrypt the reader nonce and the reader response like in the normal authentication
(6) send the data
(7) get no answer sad

My transmitted data looks like this:

Auth block 3, key ffffffffffff, nonce: 0
  R: 60  03  6e  49  
  T: 08  23  b3  16  
  R: cd  8c  18  d7  74  86  eb  32  
  T: 69  05  ed  49  
Auth again
  R: 0e  b9  bb  d4  
  T: 4e  d9  ce  a2  
  R: 7f  16  9a  25  50  ae  0d  e7  

Does anybody have an idea what I am doing wrong?

Greetings!

Re: Nested Authentication

For the sake of completeness I have generated the "unsupplied" values wink

UID: 0x154de521
 Nr: 0x00000000

When I simulated your trace I came to the conclusion that your second (reader)authentication is wrong.

Auth block 3, key ffffffffffff, nonce: 0
  R: 60  03  6e  49  
  T: 08  23  b3  16  
  R: cd  8c  18  d7  74  86  eb  32    
  T: 69  05  ed  49  
Auth again (60  03  6e  49)
  R: 0e  b9  bb  d4 
  T: 4e  d9  ce  a2  ==> decrypted-bytes: b1  0e  be  bc
  R: 7f  16  9a  25  50  ae  0d  e7 ==> correct-reader-answer: 68  f0  cd  33  2e  bd  74  dc

I hope this helps! Check out this post for more multi-authentication traces.

ps. Make sure you send the correct parity info. But since the first authentication runs fine I expect you handle them correctly.

Re: Nested Authentication

Thanks a lot for your reply! The unencrypted tag nonce helped me finding my mistake.

(3) tag_nonce = swap_endian32(receiveData) ^ crypto1_word(crypto_state, swap_endian32(receiveData), 1)
should be
(3) tag_nonce = swap_endian32(receiveData) ^ crypto1_word(crypto_state, swap_endian32(receiveData) ^ uid, 1)

Re: Nested Authentication

Hello, I am trying to write implementation for nested authentication attack. I try this:
1) Get the nonce (Nt) from tag, nt = swap_endian32(abtRx);
2) After sending {Nr}{Ar}, obtaining {At} and the successfully authentication, I decrypted {At}, which was Suc3(Nt)

Trying nested auth attack...
uid_tag: fa247164
        Auth:    60  00  f5  7b 
        Nt:    fb  47  c5  94 
        {Ar}:    71! 90! 9d! 28! 0c! 25  48  17 
        {At}:    0d! c7! cf! bd 

Authentication Succesful

        At:    59  51  ae  7d 
        Suc3:    59  51  ae  7d 
        {Auth}:    d0  28  40  76 
        {Nt'}:    6f! 8f  6b! 17!
        Nt':    4d! 38  37! 5c!

3) I am trying to find the distance from first Nt and Nt' this way (nt_first is Nt nt_last is Nt'):
    nt_first = swap_endian32(abtRx);
    nt_last = swap_endian32(abtRx);
    i=0;
    while (nt_first != nt_last) {
        nt_first = prng_successor(nt_first, 32);
        i++;
    }   
    printf("\nDistance: %d\n", i); 

4) After about an hour of running the "while" cycle a didn't get answer. Does anybody have an idea what I am doing wrong? The theorem for counting the distance is from "Wirelessly.Pickpocketing.a.Mifare.Classic.Card-IEEE.2009.pdf"

Thanks very much.

Re: Nested Authentication

sine wrote:

3) I am trying to find the distance from first Nt and Nt' this way (nt_first is Nt nt_last is Nt'):
    nt_first = swap_endian32(abtRx);
    nt_last = swap_endian32(abtRx);
    i=0;
    while (nt_first != nt_last) {
        nt_first = prng_successor(nt_first, 32);
        i++;
    }   
    printf("\nDistance: %d\n", i); 

4) After about an hour of running the "while" cycle a didn't get answer. Does anybody have an idea what I am doing wrong? The theorem for counting the distance is from "Wirelessly.Pickpocketing.a.Mifare.Classic.Card-IEEE.2009.pdf"

Thanks very much.

If you check out the crapto code there is a usefull function called nonce_distance where it clearly states that
"x,y valid tag nonces, then prng_successor(x, nonce_distance(x, y)) = y"

Re: Nested Authentication

Hello, finally I can recover the key with nonce distances, but I want also to reduce nonces with 3 parity bits.

In Wirelessly.Pickpocketing.a.Mifare.Classic.Card-IEEE.2009.pdf there is Theorem 3.3, and I should compare xor(nt0-8) with {p0}^{nt8}^1, xor(nt8-16) with {p1}^{nt16}^1 and xor(nt16-24) with {p2}^{nt24}^1.

{Nt}:       48! 92! e6! 62 
{P}  00  01  01  00  (this is print_hex(abtRxPar,4) from {Nt} tag challange);

Enc_nt  0x4892e662
Nt         0xb73d815c

01234567890123456789012345678901 ---------
01001000100100101110011001100010 ENC_NT
10110111001111011000000101011100 NT
-------------------------------------------------------
NT ^0-8, ^8-16, ^16-24        {P} ^ {Nt8,16,24} ^ 1   
101101110                = 0  { 0 ^ 1 ^ 1 } = 0
001111011                = 0  { 1 ^ 1 ^ 1 } = 1
100000010                = 0  { 1 ^ 0 ^ 1 } = 0

Please, could you explain me what's wrong? Thanks.

Re: Nested Authentication

More info about the parity can be found in this post.

Maybe it is useful to use the print_hex_par(data,bits) in stead of print_hex(data,byte)

Re: Nested Authentication

Hello
I'm having problems with nested authentication.
Do exactly as blubbel:
(1) send an encrypted authentication request
(2) crypto_state = crypto1_create(key)
(3) tag_nonce = swap_endian32(receiveData) ^ crypto1_word(crypto_state, swap_endian32(receiveData) ^ uid, 1)
(4) reader_response = prng_successor(tag_nonce, 64);
(5) encrypt the reader nonce and the reader response like in the normal authentication
And get the same result calculated by roel here 
But when I'm trying to reproduce more roel's traces from here
I'm getting wrong reader response. Decrypted Nt is OK.

Any ideas what I'm doing wrong?
Thanks!

Re: Nested Authentication

Hey telesfor,

The best way to find your mistake is to compare with working code. The guys from Nethemba released their nested authentication tool. I guess it would be useful to compare their results with your own code.

Let me know if this fixes your problem.

Cheers,

  Roel

Re: Nested Authentication

Hi Roel,

Thanks a lot for your hint but now I'm even more puzzled...
I've compared my code and the one from Nethemba and found no differences.
Just in case I've adopted Nethemba's code to recreate your trace and the result is exactly the same as with my code, which is wrong basically...

Using data from your trace I'm able to recreate the exact values up to step 11:

>   6 | RD  | ok  | AUTH      | 60 00 F5 7B 
>   7 | TAG |  -  | Nt        | 82 A4 16 6C 
>   8 | RD  |  -  | Nr + Nt'  | EF EA 1C DA 8D 65 73 4B 
>   9 | TAG |  -  | Nt"       | 9A 42 7B 20 
>  10 | RD  | ok  | AUTH      | 60 00 F5 7B 
>  11 | TAG |  -  | Nt        | A5 5D 95 0B 
>  12 | RD  |  -  | Nr + Nt'  | EF 60 E2 6F 14 91 FB DB 
>  13 | TAG |  -  | Nt"       | A5 38 5D 38 

But then result of encryption Nr in step 12 is wrong.
According to you it should be 98 d7 6b 77, but what I'm getting is always 00 F5 39 F4.

This is the relevant fragment of my code:

            // Encrypt Auth command with the current keystream
            for (i = 0; i < 4; i++) {
               AuthEnc[i] = crypto1_byte(pcs,0x00,0) ^ Auth[i];
               // Encrypt the parity bits with the 4 plaintext bytes
               AuthEncPar[i] = filter(pcs->odd) ^ ODD_PARITY(Auth[i]);
            }
            
            // Decrypt the encrypted Nt
            pcs = crypto1_create(KEY_1); //0xffffffffffff
            
            num_to_bytes(0x5a920d85, 4, Rx);  //encrypted Nt (from trace)
            NtLast = bytes_to_num(Rx, 4) ^ crypto1_word(pcs, bytes_to_num(Rx, 4) ^ uid, 1); 
            
            num_to_bytes(0xEF60E26F, 4, Nr);  //reader's Nr (from trace)
            // Again, prepare and send {At}
            for (i = 0; i < 4; i++) {
                ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i];
                ArEncPar[i] = filter(pcs->odd) ^ ODD_PARITY(Nr[i]);
            }

            Nt = prng_successor(NtLast, 32);
            for (i = 4; i < 8; i++) {
                Nt = prng_successor(Nt, 8);
                ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ (Nt&0xFF);
                ArEncPar[i] = filter(pcs->odd) ^ ODD_PARITY(Nt);
            }

Please advice.

Cheers,

telesfor

Re: Nested Authentication

can you post your complete code, so I could test it?

Re: Nested Authentication

Hi Roel.

I've hard coded data from your trace and I feed them into receive buffer (Rx).
Here you are the adopted Nethemba's code:

#define KEY_1 0xffffffffffffULL

#define ODD_PARITY(i) (( (i) ^ (i)>>1 ^ (i)>>2 ^ (i)>>3 ^ \
 (i)>>4 ^ (i)>>5 ^ (i)>>6 ^ (i)>>7 ^ 1) & 0x01)

int mf_enhanced_auth(void) {
    struct Crypto1State* pcs;
    struct Crypto1State* revstate;
    struct Crypto1State* revstate_start;

    uint64_t lfsr;
        
    uint8_t Nr[4] = { 0x00,0x00,0x00,0x00 }; // Reader nonce
    uint8_t Auth[4] = { 0x00, 0x00, 0x00, 0x00 };
    uint8_t AuthEnc[4] = { 0x00, 0x00, 0x00, 0x00 };
    uint8_t AuthEncPar[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
    
    uint8_t ArEnc[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
    uint8_t ArEncPar[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
    
    uint8_t Rx[25]; // Tag response
    uint8_t RxPar[25]; // Tag response
    uint32_t RxLen;
    
    uint32_t Nt, NtLast, NtProbe, NtEnc, Ks1;

    int i, m;
    
    // values from Roel's trace
    uint32_t uid = 0x9c599b32;
    uint32_t card_challenge = 0x82a4166c;
    uint32_t reader_challenge = 0xEFEA1CDA;
    uint32_t card_response = 0x5cadf439;
    
    // Prepare AUTH command
    Auth[0] = 0x60;
    ComputeCrc((char*)Auth, 2, &Auth[2], &Auth[3]);

    // Request plain tag-nonce
    num_to_bytes(card_challenge, 4, Rx);
    
    // Save the tag nonce (Nt)
    Nt = card_challenge;
    
    // Init the cipher with key {0..47} bits
    pcs = crypto1_create(KEY_1);
    
    // Load (plain) uid^nt into the cipher {48..79} bits
    crypto1_word(pcs, bytes_to_num(Rx, 4) ^ uid, 0);
    
    num_to_bytes(reader_challenge, 4, Nr);
    // Generate (encrypted) nr+parity by loading it into the cipher
    for (i = 0; i < 4; i++) {
        // Load in, and encrypt the reader nonce (Nr)
        ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i];
        ArEncPar[i] = filter(pcs->odd) ^ ODD_PARITY(Nr[i]);
    }
    // Skip 32 bits in the pseudo random generator
    Nt = prng_successor(Nt, 32);
    // Generate reader-answer from tag-nonce
    for (i = 4; i < 8; i++) {
        // Get the next random byte
        Nt = prng_successor(Nt, 8);
        // Encrypt the reader-answer (Nt' = suc2(Nt))
        ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ (Nt&0xff);
        ArEncPar[i] = filter(pcs->odd) ^ ODD_PARITY(Nt);
    }
    
    num_to_bytes(card_response, 4, Rx);

    // Decrypt the tag answer and verify that suc3(Nt) is At
    Nt = prng_successor(Nt, 32);
    if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num(Rx, 4)) == (Nt&0xFFFFFFFF))) {
        fprintf(stderr, "[At] is not Suc3(Nt), something is wrong, exiting..\n");
        exit(1);
    }

    // If we are in "Get Distances" mode
    {
        {
            // Encrypt Auth command with the current keystream
            for (i = 0; i < 4; i++) {
               AuthEnc[i] = crypto1_byte(pcs,0x00,0) ^ Auth[i];
               // Encrypt the parity bits with the 4 plaintext bytes
               AuthEncPar[i] = filter(pcs->odd) ^ ODD_PARITY(Auth[i]);
            }

            // Sending the encrypted Auth command
            
            // Decrypt the encrypted auth 
            pcs = crypto1_create(KEY_1); //0xffffffffffff
            
            num_to_bytes(0x5a920d85, 4, Rx);  //encrypted Nt (from trace)
            NtLast = bytes_to_num(Rx, 4) ^ crypto1_word(pcs, bytes_to_num(Rx, 4) ^ uid, 1); 
            
            num_to_bytes(0xEF60E26F, 4, Nr);  //reader's Nr (from trace)
            // Again, prepare and send {At}
            for (i = 0; i < 4; i++) {
                ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i];
                ArEncPar[i] = filter(pcs->odd) ^ ODD_PARITY(Nr[i]);
            }
            Nt = prng_successor(NtLast, 32);
            for (i = 4; i < 8; i++) {
                Nt = prng_successor(Nt, 8);
                ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ (Nt&0xFF);
                ArEncPar[i] = filter(pcs->odd) ^ ODD_PARITY(Nt);
            }
            
            num_to_bytes(0xca7e0b63, 4, Rx);  // Nt'' from trace
            Nt = prng_successor(Nt, 32);
            if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num(Rx, 4)) == (Nt&0xFFFFFFFF))) {
                fprintf(stderr, "[At] is not Suc3(Nt), something is wrong, exiting..\n");
                exit(1);
            }
        } // Next auth probe
        
    } // The end of Get Distances mode
    
    
    crypto1_destroy(pcs);
    return 0;
}

void num_to_bytes(uint64_t n, uint32_t len, uint8_t* dest) {
    while (len--) {
        dest[len] = (uint8_t) n;
        n >>= 8;
    }
}

uint64_t bytes_to_num(uint8_t* src, uint32_t len) {
    uint64_t num = 0;
    while (len--)
    {
        num = (num << 8) | (*src);
        src++;
    }
    return num;
}

unsigned short UpdateCrc(unsigned char ch, unsigned short *lpwCrc)
{
    ch = (ch^(unsigned char)((*lpwCrc) & 0x00FF));
    ch = (ch^(ch<<4));
    *lpwCrc = (*lpwCrc >> 8)^((unsigned short)ch << 8)^((unsigned short)ch<<3)^((unsigned short)ch>>4);
    return(*lpwCrc);
}

void ComputeCrc(char *Data, int Length, BYTE *TransmitFirst, BYTE *TransmitSecond)
{
    unsigned char chBlock;
    unsigned short wCrc;

    wCrc = 0x6363; /* ITU-V.41 */

    do {
    chBlock = *Data++;
    UpdateCrc(chBlock, &wCrc);
    } while (--Length);
    
    *TransmitFirst = (BYTE) (wCrc & 0xFF);
    *TransmitSecond = (BYTE) ((wCrc >> 8) & 0xFF);
    return;
}

Thanks!