Tryby wysyłania i odbierania DES dla uwierzytelniania DESFire

[2]}próbuję uwierzytelnić kartę DESFire za pomocą mojej aplikacji na Androida. Używam Przykład w tym linku aby rozszyfrować bajty, które otrzymałem z karty. W tym celu wykluczyłem padding w deszyfrowaniu( skomentowałem poniżej), ponieważ dokumentacja DESFire to wskazuje. Ponadto, jeśli tego nie zrobię, deszyfrowanie zwróci 7 bajtów dla wejścia 8 bajtów. Poniżej znajdują się funkcje deszyfrowania DES i TripleDES, których używam:

public static byte[] TripleDES_Decrypt(byte[] data,byte[][] keys)
{
    int i;
    byte[] tmp = new byte[data.length];
    byte[] bloc = new byte[8];

    K = generateSubKeys(keys[0]);
    K1 = generateSubKeys(keys[1]);
    K2 = generateSubKeys(keys[2]);

    for (i = 0; i < data.length; i++) {
        if (i > 0 && i % 8 == 0) {
            bloc = encrypt64Bloc(bloc,K2, true);
            bloc = encrypt64Bloc(bloc,K1, false);
            bloc = encrypt64Bloc(bloc,K, true);
            System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
        }
        if (i < data.length)
            bloc[i % 8] = data[i];
    }
    bloc = encrypt64Bloc(bloc,K2, true);
    bloc = encrypt64Bloc(bloc,K1, false);
    bloc = encrypt64Bloc(bloc,K, true);
    System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);


    //tmp = deletePadding(tmp);

    return tmp;
}

public static byte[] decrypt(byte[] data, byte[] key) {
    int i;
    byte[] tmp = new byte[data.length];
    byte[] bloc = new byte[8];

    K = generateSubKeys(key);

    for (i = 0; i < data.length; i++) {
        if (i > 0 && i % 8 == 0) {
            bloc = encrypt64Bloc(bloc,K, true);
            System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
        }
        if (i < data.length)
            bloc[i % 8] = data[i];
    }
    bloc = encrypt64Bloc(bloc,K, true);
    System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);

    //tmp = deletePadding(tmp);

    return tmp;
}

Zgodnie z dokumentem DesFire, potrzebuję dwóch trybów odszyfrowywanie, wysyłanie i odbieranie. ten wpis na blogu ma na ten temat jakieś wyjaśnienie.

Jednak krypto DESFire różni się nieco od normalnego schematu DES / CBC: PCD używa DES "send mode" podczas wysyłania danych (XOR przed DES), a karta używa DES "recieve mode" podczas odbierania danych (xor po DES). Ale gdy PCD odbiera dane, używa normalnego trybu DES / CBC (XOR po DES), a karta używa normalnego trybu des send podczas wysyłania danych (XOR przed DES).

Oraz po stronie Androida podążam za przykładami i zaleceniami:

// connected to tag and application     

// result = encoded(randB) + af 
byte[] result = idTag.transceive(Utils.wrapMessage((byte)0x0a, new byte[]{(byte)0x0}));

byte[] b0 = new byte[8];
for(int i = 0; i < 8; i++) {
    b0[i] = result[i];
}

// key
byte[] key = new byte[] {(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
                 (byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
                 (byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
                 (byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0 };
byte[][] keys = new byte[3][];
keys[0]=key; keys[1]=key; keys[2]=key;

// decrypt encoded(randB)
byte[] r0 = DES.TripleDES_Decrypt(b0, keys);

// generate randA (integer 0-7 for trying) 
byte[] nr = new byte[8];
for(int i = 0; i < 8; i++) {
    nr[i] = Byte.parseByte(Integer.toString(i), 16);
}
// decrypt randA
byte[] b1 = DES.TripleDES_Decrypt(nr, keys);

// shift randB and get randB'
byte[] r1 =new byte[8];
for(int i = 0; i < 7; i++) {
    r1[i] = r0[i + 1];
}
r1[7]=r0[0];

// concat (randA + randB')
byte[] b2 = new byte[16];
for(int i = 0; i < 16; i++)
{
    if(i <= 7) {
    b2[i] = b1[i];
} else {
    b2[i] = r1[i - 8];
}
}

// XOR (randA + randB') with IV
// IV is told to be consisting of 0's, 
// but XOR something with 0 results the same? 
for(int i=0;i<16;i++) {
    b2[i] = (byte) (b2[i] ^ (byte)0x0);
}

// send AF and decrypt(A+B) 
// wrap message adds needed wrapping to message (90 to left, offset bytes etc.)
result = isodepTag.transceive(Utils.wrapMessage((byte)0xaf, DES.TripleDES_Decrypt(b2, keys)));

Otrzymuję pierwszy wynik, zaszyfrowany randB. Jednak drugim "wynikiem" jest zawsze "91ae", czyli błąd uwierzytelniania. Robię coś nie tak, wysyłam złe dane do card.

Czy ktoś może mi powiedzieć co muszę zmienić w kodzie aby działał w tych trybach? Co powinienem zrobić z danymi przed / po TripleDES?

Nie prawdziwe pytanie, ale czytałem, że domyślny "klucz" w DesFire karty jest 16 zero bajtów. Również dokument wskazuje, że muszę użyć TripleDES dla 16 bajtów klucza, DES dla 8 bajtów klucza. Więc używam i muszę używać TripleDES, ponieważ nie zmieniłem domyślnego klucza, prawda?

Dla tych, którzy potrzebują wiedzy o CipherBlockChaining .

EDIT : dowiedziałem się, że muszę zrobić XORing przed i po TripleDES i nie mogę w ogóle dotykać wewnętrznych operacji TripleDES. Będę tego próbował za jakiś czas.

Usunięto wewnętrzne linie trójwymiarowe, tylko mówiąc dla tych, którzy widzą pytanie po raz pierwszy.

Author: İsmet Alkan, 2013-01-02

2 answers

OK, mam rozwiązanie. Mój błąd polegał na tym, że wysyłałem

3DES(randA + randB') 

Ale powinienem wysłać

3DES(randA) + 3DES(randB' XOR 3DES(randA))

Oto kod uwierzytelniający dla Androida/Javy (to takie smutne, że jest to jedyny, który można obecnie znaleźć w sieci!):

Rzeczywisty kod uwierzytelniający:

// send initial authentication request
byte[] result = idTag.transceive(Utils.wrapMessage((byte)0x0a, new byte[]{(byte)0x0}));

// get encrypted(randB) from the response
byte[] b0 = new byte[8];
for(int i = 0; i < 8; i++) {
    b0[i] = result[i];
}

// 16 bytes default key
byte[] key = new byte[] {(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
    (byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
    (byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
    (byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0 };
// keys for TripleDes
byte[][] keys = new byte[3][];
keys[0] = key; keys[1] = key; keys[2] = key;

// decrypt encoded(randB)
byte[] r0 = DES.TripleDES_Decrypt(b0, keys);

// generate randA (integer 0-7 for trying, should randomize for real-life use) 
byte[] nr = new byte[8];
for(int i = 0; i < 8; i++) {
    nr[i] = Byte.parseByte(Integer.toString(i), 16);
}

// decrypt randA, should XOR with IV, but IV is all 0's, not necessary
byte[] b1 = DES.TripleDES_Decrypt(nr, keys);

// shift randB one byte left and get randB'
byte[] r1 =new byte[8];
for(int i = 0; i < 7; i++) {
    r1[i] = r0[i + 1];
}
r1[7]=r0[0];

// xor randB' with randA and decrypt
byte[] b2 = new byte[8];
for(int i = 0; i < 8; i++) {
    b2[i] = (byte) (b1[i] ^ r1[i]);
}
b2 = DES.TripleDES_Decrypt(b2, keys);

// concat (randA + randB')
byte[] b1b2 = new byte[16];

for (int i = 0; i < b1b2.length; i++) {
    if(i <= 7) {
        b1b2[i] = b1[i];
    } else {
        b1b2[i]=b2[i-8];
    }
}

result = idTag.transceive(Utils.wrapMessage((byte)0xaf, b1b2));
TripleDes to ten, o którym mowa. funkcja wrapMessage:
public static byte[] wrapMessage (byte command, byte[] parameters) throws Exception {
    ByteArrayOutputStream stream = new ByteArrayOutputStream();

    stream.write((byte) 0x90);
    stream.write(command);
    stream.write((byte) 0x00);
    stream.write((byte) 0x00);
    if (parameters != null) {
        stream.write((byte) parameters.length);
        stream.write(parameters);
    }
    stream.write((byte) 0x00);

    return stream.toByteArray();
}

EDIT: Dzięki VGe0rge, odkryliśmy powód, dla którego to uwierzytelnianie nie działa od czasu do czas. Zamiast wywoływać funkcję 3DES w pytaniu, po prostu wywołaj:

Cipher.getInstance("DESede/CBC/NoPadding");
 10
Author: İsmet Alkan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-31 23:58:21

Ismat, próbowałeś zapisać dane do plików DESFIRE?

Jak wyjaśniłeś, podczas wysyłania danych na kartę: PCD używa DES "send mode" podczas wysyłania danych (XOR przed DES), a karta używa DES "recieve mode" podczas odbierania danych (xor po DES)

Więc nie mogę uzyskać odpowiedniego kodu, aby zaimplementować TDE z XOR wcześniej..Muszę zrobić wszystkie Crypt-decrypt-crypt i proces jest powolny dla mojej aplikacji:

res = criptoTransformDec.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);
res = criptoTransformEnc.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);
res = criptoTransformDec1.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);

int l_iAux = 0;
while (l_iAux < (datosEscribir.Length - 8))
    {
        criptoTransformDec2 = desDec.CreateDecryptor(claveSes1, tdesInitialVector);
        //desEnc2 = new DESCryptoServiceProvider();
        criptoTransformEnc2 = desEnc.CreateEncryptor(claveSes2, tdesInitialVector);
        //desDec3 = new DESCryptoServiceProvider();
                criptoTransformDec3 = desDec.CreateDecryptor(claveSes1, tdesInitialVector);

        Array.Copy(datosEscribir, 8 + l_iAux, aux1, 0, 8);
        Array.Copy(datosEscribir, l_iAux, aux2, 0, 8);
        DesfireBarik.XorStr(ref aux1, ref aux2, 8);
        res = criptoTransformDec2.TransformBlock(aux1, 0, 8, datosEscribir, 8 + l_iAux);
        res = criptoTransformEnc2.TransformBlock(datosEscribir, 8 + l_iAux, 8, datosEscribir, 8 + l_iAux);
        res = criptoTransformDec3.TransformBlock(datosEscribir, 8 + l_iAux, 8, datosEscribir, 8 + l_iAux);

        l_iAux += 8;
    }

private static void XorStr (ref byte[] str1, ref byte[] str2, int qty )
    {
        int i = 0;
        for (i = 0; i < qty; i++ )
            str1[i] = (byte)(str1[i] ^ str2[i]);
    }
 0
Author: Jose Manuel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-10 12:46:26