05 May 2011

AES CMAC as used in DESFire AV2

AES CMAC
nb may need to chain the IV


import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import java.security.NoSuchAlgorithmException;
import javax.crypto.spec.SecretKeySpec;


/**
 *
 * @author Chris Skinner
 */
public class AES {


    static public byte[] K = new byte [16]; // 128 bit key
    static public byte[] K1 = new byte [16]; // 128 bit sub key
    static public byte[] K2 = new byte [16]; // 128 bit sub key
    static final public byte[] Z16  = new byte [16]; // 128 bit zero
    static Cipher cipher = null;


//♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧
    static void print (String s) {
        System.out.print(s);
    }
//♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧


    static byte[] shl (byte[] bin)  // << 16 byte array
    {
        byte[] bout = new byte[16];
        for (short j = 0; j < 15; j++)  // java b[0] is the highorder
        {
            int sot = ((bin[j+1] & 0x80) >> 7);
            int sef = ( bin[j] << 1 ) | sot;
            bout[j] = ( byte)sef;
        }
        bout[15] = (byte)(bin[15] << 1);
        return bout;
    }
//♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧
    static SecretKeySpec skeySpec = null;


    public static void subKeys(byte[] key) // make K1 K2 from key
    {
    /*
         +                    Algorithm Generate_Subkey                      +
   +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   +                                                                   +
   +   Input    : K (128-bit key)                                      +
   +   Output   : K1 (128-bit first subkey)                            +
   +              K2 (128-bit second subkey)                           +
   +-------------------------------------------------------------------+
   +                                                                   +
   +   Constants: const_Zero is 0x00000000000000000000000000000000     +
   +              const_Rb   is 0x00000000000000000000000000000087
    binary 10000111    +
   +   Variables: L          for output of AES-128 applied to 0^128    +
   +                                                                   +
   +   Step 1.  L := AES-128(K, const_Zero);                           +
   +   Step 2.  if MSB(L) is equal to 0                                +
   +            then    K1 := L << 1;                                  +
   +            else    K1 := (L << 1) XOR const_Rb;                   +
   +   Step 3.  if MSB(K1) is equal to 0                               +
   +            then    K2 := K1 << 1;                                 +
   +            else    K2 := (K1 << 1) XOR const_Rb;                  +
   +   Step 4.  return K1, K2;                                         +
   +                                                                   +
         */
    byte bRb = (byte)0x87; // Rb for AES128
    // key must be 16 bytes
    try
        {
        skeySpec = new SecretKeySpec(key, "AES");
        if (cipher == null)
            cipher = Cipher.getInstance("AES/ECB/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        K1 =  cipher.doFinal(Z16);
        }
        catch (Exception ex)  // nosuchalgorithm, invalidkey,nosuchpadding...
        {
             print("\n error 400 AES   " + ex.getMessage());
        }
        boolean highL = ((K1[0] & 0x80) != 0);
        K1 = shl(K1);
        if (highL)
            K1[15] = (byte)(K1[15] ^ bRb);
        highL =  ((K1[0] & 0x80) != 0);
        K2 = shl(K1);
        if (highL)
            K2[15] = (byte)(K2[15] ^ bRb);
    }
//♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧
    static byte[] xor16(byte[] ba, byte[] bb)
    {
        byte[] bout = new byte[ba.length];
        for (short j = 0; j < ba.length; j++)
            bout[j] = (byte)(ba[j] ^ bb[j]);
        return bout;
    }


//♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧


    static public byte[] CMAC(byte[] key, byte[] mesg)
    {
        /*
         +   Input    : K    ( 128-bit key )                                 +
   +            : M    ( message to be authenticated )                 +
   +            : len  ( length of the message in octets )             +
   +   Output   : T    ( message authentication code )                 +
   +
         +              const_Bsize is 16                                    +
   +                                                                   +
   +   Variables: K1, K2 for 128-bit subkeys                           +
   +              M_i is the i-th block (i=1..ceil(len/const_Bsize))   +
   +              M_last is the last block xor-ed with K1 or K2        +
   +              n      for number of blocks to be processed          +
   +              r      for number of octets of last block            +
   +              flag   for denoting if last block is complete or not +
   +                                                                   +
   +   Step 1.  (K1,K2) := Generate_Subkey(K);                         +
   +   Step 2.  n := ceil(len/const_Bsize);
         The smallest integer no smaller than x.
          ceil(3.5) is 4.  ceil(5) is 5.+
   +   Step 3.  if n = 0                                               +
   +            then                                                   +
   +                 n := 1;                                           +
   +                 flag := false;                                    +
   +            else                                                   +
   +                 if len mod const_Bsize is 0                       +
   +                 then flag := true;  no overflow                              +
   +                 else flag := false;                               +
   +                                                                   +
   +   Step 4.  if flag is true                                        +
   +            then M_last := M_n XOR K1;                             +
   +            else M_last := padding(M_n) XOR K2;                    +
   +   Step 5.  X := const_Zero;                                       +
   +   Step 6.  for i := 1 to n-1 do                                   +
   +                begin                                              +
   +                  Y := X XOR M_i;                                  +
   +                  X := AES-128(K,Y);                               +
   +                end                                                +
   +            Y := M_last XOR X;                                     +
   +            T := AES-128(K,Y);                                     +
   +   Step 7.  return T;                                              +
   +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


         */
    subKeys(key);  // set K1,K2
    int mz = mesg.length ;
    print("\n\n message " + Uti.asString(mesg));  //✦✦✦✦✦✦✦✦✦✦✦✦✦✦✦
    int n = mz / 16;  // number of 16byte chunks.. if overflow > 0 add 1 to this
    print("\n mz " + mz + "   first n " + n);  //✦✦✦✦✦✦✦✦✦✦✦✦✦✦✦
    int m = n * 16;
    int v = mz - m; // overflow bytes 0..15
    print("       m " + m + "   v " + v);//✦✦✦✦✦✦✦✦✦✦✦✦✦✦✦
    if (v > 0)
        n++;  // now the "actual" number of chunks  ie ceiling
    print("    new n " + n);//✦✦✦✦✦✦✦✦✦✦✦✦✦✦✦


    byte[] MLast = new byte[16];
    int lastn = ( n-1 ) * 16;  //byte address of lastchunk within mesg
    int lastz = mz -lastn;  // number of bytes to copy to lastchunk
    System.arraycopy(mesg,lastn, MLast,0,lastz);
    print("\n MLast " + Uti.asString(MLast));  //✦✦✦✦✦✦✦✦✦✦✦✦✦✦✦
    if (v == 0)  // no overflow
       MLast = xor16(MLast,K1);
    else
    {
        MLast[lastz] = (byte)0x80;  // this does the padding
        print("\n v pre  MLast " + Uti.asString(MLast)); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        MLast = xor16(MLast,K2);
        print("\n v post MLast " + Uti.asString(MLast)); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<




    }
    //todo: put MLast backk into mesg and do a normal CBC....
    // we should be able to use CBC...    // todo replace with CBC


    byte[] X     = new byte[16];  // zeros by default;
    //BUT updated IV has to be used in next CMAC or encryption
    byte[] plain = new byte[16];
    try
    {
        if (cipher == null)
            cipher = Cipher.getInstance("AES/ECB/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);   // spec set in subkeys???


        for (short j = 0 ; j < n-1; j++)
        {
            int jx = j * 16;  // dont do this kind of thing...
            System.arraycopy(mesg,jx, plain,0,16);
            plain = xor16(plain,X);
            print("\n plain aaa   " + Uti.asString(plain)); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            X =  cipher.doFinal(plain);
        }
        plain = xor16(MLast,X);
        print("\n plain zzz   " + Uti.asString(plain)); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        X =  cipher.doFinal(plain);
        print("\n cipherzzz   " + Uti.asString(X)); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    }
    catch (Exception ex)  // nosuchalgorithm, invalidkey,nosuchpadding...
    {
             print("\n error 400 AES   " + ex.getMessage());
    }
    return X;
    }
}//♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧ fin ♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧♧

8 comments:

  1. This CMAC works with DESFire cards ("EV1")
    - Chaining required, ie previous MAC is XORed with X at start.
    BUT it does not work with DESFire SAM, when attempting to "Activate" (convert from AV1 to AV2)
    - Can it be possible that DESFire use different CMACs for the Card and the SAM?
    What a hideous possibility!

    ReplyDelete
  2. I know I can do a "standard" CMAC on a 16byte input

    byte[] M2 = Uti.asHex("6bc1bee2 2e409f96 93d7e11 7393172a");
    T = AES.CMAC(K,M2);
    print("\n\n Ex 2 " + Uti.asString(T));
    print( "\n expect 070a16b4 6b4d4144 f79bdd9d 04a287c");
    So DESFire "Activate" - version of Lock/UnLock seems to use a non-standard. ZUT ALORS!
    OR: they are unable to describe accurately the field to be MACed - (Isnt the entire art of computer programming the understanding of inadequate specifications? If specifications were adequate, there would be no programmers.)

    ReplyDelete
  3. Forgot the Key

    these from NIST
    http://csrc.nist.gov/publications/nistpubs/800-38B/Updated_CMAC_Examples.pdf


    byte[] K = Uti.asHex("2b7e1516 28aed2a6 abf71588 09cf4f3c");

    ReplyDelete
  4. maybe its a problem with AES(0,0)
    - How I wish that Sun Java Forums was still extant

    Oracle took it over and killed it... a great learning resource gone

    ReplyDelete
  5. // This May be how the 16 byte AES CMAc is truncated to 8 bytes
    // .. Not NIST Special publication 800-38B standard...

    static public byte[] truncCMAC(byte[] key, byte[] mesg) // 8 byte truncated MAC
    {

    byte[] cmac = CMAC(key,mesg); // 800-38B standard
    byte[] tmac = new byte[8];
    // assuming Java big-endian
    tmac[0] = cmac[1];
    tmac[1] = cmac[3];
    tmac[2] = cmac[5];
    tmac[3] = cmac[7];
    tmac[4] = cmac[9];
    tmac[5] = cmac[11];
    tmac[6] = cmac[13];
    tmac[7] = cmac[15];
    return tmac;
    }

    ReplyDelete
  6. Hi,

    I'm looking for the way how to generate a CMAC in Java to use for the communication with an EV1 card. In detail I try to read a fully enciphered communication with AES. I've got a example with example values but did not find a cmac method which generates these values yet.
    I'm working with the appnotes from NXT. They use AES as cipher and work with IV.
    I also read your blog entry about CRC32 (with invert and read it from end to begin etc.) so maybe it's a problem like that. So that i have the right value but not "right displayed".
    I'm from Germany and very sorry for my english. Hope you understand most of my problem.

    S.P.

    ReplyDelete
  7. Hi,

    i successfully generated a cmac like in the appnotes from NXP.
    I did some extensions:
    Cipher AES with IV

    and after some problems with padding it works.

    Stefan

    ReplyDelete
  8. Hi Stefan,
    could you put your source code with some "extensions", becouse i can't generate CMAC according to NXP documents.

    ReplyDelete