+       // CT (cascade tag byte) 0x88 xor SN0 xor SN1 xor SN2 
+       int crc0 = 0x88 ^ data[0] ^ data[1] ^data[2];
+       if ( data[3] == crc0 )
+               PrintAndLog("      BCC0 : %02X, Ok", data[3]);
+       else
+               PrintAndLog("      BCC0 : %02X, crc should be %02X", data[3], crc0);
+
+       int crc1 = data[4] ^ data[5] ^ data[6] ^data[7];
+       if ( data[8] == crc1 )
+               PrintAndLog("      BCC1 : %02X, Ok", data[8]);
+       else
+               PrintAndLog("      BCC1 : %02X, crc should be %02X", data[8], crc1 );
+
+       PrintAndLog("  Internal : %02X, %sdefault", data[9], (data[9]==0x48)?"":"not " );
+
+       PrintAndLog("      Lock : %s - %s",
+                               sprint_hex(data+10, 2),
+                               printBits(2, data+10)
+               );
+
+       PrintAndLog("OneTimePad : %s - %s\n",
+                               sprint_hex(data + 12, 4),
+                               printBits(4, data+12)
+               );
+
+       return 0;
+}
+
+static int ndef_print_CC(uint8_t *data) {
+       // no NDEF message
+       if(data[0] != 0xe1)
+               return -1;
+
+       PrintAndLog("--- NDEF Message");
+       PrintAndLog("Capability Container: %s", sprint_hex(data,4) );
+       PrintAndLog("  %02X : NDEF Magic Number", data[0]); 
+       PrintAndLog("  %02X : version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0f);
+       PrintAndLog("  %02X : Physical Memory Size: %d bytes", data[2], (data[2] + 1) * 8);
+       if ( data[2] == 0x12 )
+               PrintAndLog("  %02X : NDEF Memory Size: %d bytes", data[2], 144);
+       else if ( data[2] == 0x3e )
+               PrintAndLog("  %02X : NDEF Memory Size: %d bytes", data[2], 496);
+       else if ( data[2] == 0x6d )
+               PrintAndLog("  %02X : NDEF Memory Size: %d bytes", data[2], 872);
+
+       PrintAndLog("  %02X : %s / %s", data[3], 
+                               (data[3] & 0xF0) ? "(RFU)" : "Read access granted without any security", 
+                               (data[3] & 0x0F)==0 ? "Write access granted without any security" : (data[3] & 0x0F)==0x0F ? "No write access granted at all" : "(RFU)");
+       return 0;
+}
+
+int ul_print_type(uint32_t tagtype, uint8_t spaces){
+       char spc[11] = "          ";
+       spc[10]=0x00;
+       char *spacer = spc + (10-spaces);
+
+       if ( tagtype & UL )     
+               PrintAndLog("%sTYPE : MIFARE Ultralight (MF0ICU1) %s", spacer, (tagtype & MAGIC) ? "<magic>" : "" );
+       else if ( tagtype & UL_C)
+               PrintAndLog("%sTYPE : MIFARE Ultralight C (MF0ULC) %s", spacer, (tagtype & MAGIC) ? "<magic>" : "" );
+       else if ( tagtype & UL_EV1_48)
+               PrintAndLog("%sTYPE : MIFARE Ultralight EV1 48bytes (MF0UL1101)", spacer); 
+       else if ( tagtype & UL_EV1_128) 
+               PrintAndLog("%sTYPE : MIFARE Ultralight EV1 128bytes (MF0UL2101)", spacer);
+       else if ( tagtype & NTAG )
+               PrintAndLog("%sTYPE : NTAG UNKNOWN", spacer);
+       else if ( tagtype & NTAG_203 )
+               PrintAndLog("%sTYPE : NTAG 203 144bytes (NT2H0301F0DT)", spacer);
+       else if ( tagtype & NTAG_210 )
+               PrintAndLog("%sTYPE : NTAG 210 48bytes (NT2L1011G0DU)", spacer);
+       else if ( tagtype & NTAG_212 )
+               PrintAndLog("%sTYPE : NTAG 212 128bytes (NT2L1211G0DU)", spacer);
+       else if ( tagtype & NTAG_213 )
+               PrintAndLog("%sTYPE : NTAG 213 144bytes (NT2H1311G0DU)", spacer);
+       else if ( tagtype & NTAG_215 )
+               PrintAndLog("%sTYPE : NTAG 215 504bytes (NT2H1511G0DU)", spacer);
+       else if ( tagtype & NTAG_216 )
+               PrintAndLog("%sTYPE : NTAG 216 888bytes (NT2H1611G0DU)", spacer);
+       else if ( tagtype & NTAG_I2C_1K )
+               PrintAndLog("%sTYPE : NTAG I%sC 888bytes (NT3H1101FHK)", spacer, "\xFD");
+       else if ( tagtype & NTAG_I2C_2K )       
+               PrintAndLog("%sTYPE : NTAG I%sC 1904bytes (NT3H1201FHK)", spacer, "\xFD");
+       else if ( tagtype & MY_D )
+               PrintAndLog("%sTYPE : INFINEON my-d\x99 (SLE 66RxxS)", spacer);
+       else if ( tagtype & MY_D_NFC )
+               PrintAndLog("%sTYPE : INFINEON my-d\x99 NFC (SLE 66RxxP)", spacer);
+       else if ( tagtype & MY_D_MOVE )
+               PrintAndLog("%sTYPE : INFINEON my-d\x99 move (SLE 66R01P)", spacer);
+       else if ( tagtype & MY_D_MOVE_NFC )
+               PrintAndLog("%sTYPE : INFINEON my-d\x99 move NFC (SLE 66R01P)", spacer);
+       else if ( tagtype & MY_D_MOVE_LEAN )
+               PrintAndLog("%sTYPE : INFINEON my-d\x99 move lean (SLE 66R01L)", spacer);
+       else if ( tagtype & FUDAN_UL )
+               PrintAndLog("%sTYPE : FUDAN Ultralight Compatible (or other compatible) %s", spacer, (tagtype & MAGIC) ? "<magic>" : "" );
+       else
+               PrintAndLog("%sTYPE : Unknown %06x", spacer, tagtype);
+       return 0;
+}
+
+static int ulc_print_3deskey( uint8_t *data){
+       PrintAndLog("         deskey1 [44/0x2C] : %s [%.4s]", sprint_hex(data   ,4),data);
+       PrintAndLog("         deskey1 [45/0x2D] : %s [%.4s]", sprint_hex(data+4 ,4),data+4);
+       PrintAndLog("         deskey2 [46/0x2E] : %s [%.4s]", sprint_hex(data+8 ,4),data+8);
+       PrintAndLog("         deskey2 [47/0x2F] : %s [%.4s]", sprint_hex(data+12,4),data+12);
+       PrintAndLog("\n 3des key : %s", sprint_hex(SwapEndian64(data, 16, 8), 16));
+       return 0;
+}
+
+static int ulc_print_configuration( uint8_t *data){
+
+       PrintAndLog("--- UL-C Configuration");
+       PrintAndLog(" Higher Lockbits [40/0x28] : %s - %s", sprint_hex(data, 4), printBits(2, data));
+       PrintAndLog("         Counter [41/0x29] : %s - %s", sprint_hex(data+4, 4), printBits(2, data+4));
+
+       bool validAuth = (data[8] >= 0x03 && data[8] <= 0x30);
+       if ( validAuth )
+               PrintAndLog("           Auth0 [42/0x2A] : %s page %d/0x%02X and above need authentication", sprint_hex(data+8, 4), data[8],data[8] );
+       else{
+               if ( data[8] == 0){
+                       PrintAndLog("           Auth0 [42/0x2A] : %s default", sprint_hex(data+8, 4) );
+               } else {
+                       PrintAndLog("           Auth0 [42/0x2A] : %s auth byte is out-of-range", sprint_hex(data+8, 4) );
+               }
+       }
+       PrintAndLog("           Auth1 [43/0x2B] : %s %s",
+                       sprint_hex(data+12, 4),
+                       (data[12] & 1) ? "write access restricted": "read and write access restricted"
+                       );
+       return 0;
+}
+
+static int ulev1_print_configuration( uint8_t *data, uint8_t startPage){
+
+       PrintAndLog("\n--- Tag Configuration");
+
+       bool strg_mod_en = (data[0] & 2);
+       uint8_t authlim = (data[4] & 0x07);
+       bool cfglck = (data[4] & 0x40);
+       bool prot = (data[4] & 0x80);
+       uint8_t vctid = data[5];
+
+       PrintAndLog("  cfg0 [%u/0x%02X] : %s", startPage, startPage, sprint_hex(data, 4));
+       if ( data[3] < 0xff )
+               PrintAndLog("                    - page %d and above need authentication",data[3]);
+       else 
+               PrintAndLog("                    - pages don't need authentication");
+       PrintAndLog("                    - strong modulation mode %s", (strg_mod_en) ? "enabled":"disabled");
+       PrintAndLog("  cfg1 [%u/0x%02X] : %s", startPage + 1, startPage + 1,  sprint_hex(data+4, 4) );
+       if ( authlim == 0)
+               PrintAndLog("                    - Unlimited password attempts");
+       else
+               PrintAndLog("                    - Max number of password attempts is %d", authlim);
+       PrintAndLog("                    - user configuration %s", cfglck ? "permanently locked":"writeable");
+       PrintAndLog("                    - %s access is protected with password", prot ? "read and write":"write");
+       PrintAndLog("                    - %02X, Virtual Card Type Identifier is %s default", vctid, (vctid==0x05)? "":"not");
+       PrintAndLog("  PWD  [%u/0x%02X] : %s- (cannot be read)", startPage + 2, startPage + 2,  sprint_hex(data+8, 4));
+       PrintAndLog("  PACK [%u/0x%02X] : %s      - (cannot be read)", startPage + 3, startPage + 3,  sprint_hex(data+12, 2));
+       PrintAndLog("  RFU  [%u/0x%02X] :       %s- (cannot be read)", startPage + 3, startPage + 3,  sprint_hex(data+12, 2));
+       return 0;
+}
+
+static int ulev1_print_counters(){
+       PrintAndLog("--- Tag Counters");
+       uint8_t tear[1] = {0};
+       uint8_t counter[3] = {0,0,0};
+       uint16_t len = 0;
+       for ( uint8_t i = 0; i<3; ++i) {
+               ulev1_readTearing(i,tear,sizeof(tear));
+               len = ulev1_readCounter(i,counter, sizeof(counter) );
+               if (len == 3) {
+                       PrintAndLog("       [%0d] : %s", i, sprint_hex(counter,3));
+                       PrintAndLog("                    - %02X tearing %s", tear[0], ( tear[0]==0xBD)?"Ok":"failure");
+               }
+       }
+       return len;
+}
+
+static int ulev1_print_signature( uint8_t *data, uint8_t len){
+       PrintAndLog("\n--- Tag Signature");     
+       //PrintAndLog("IC signature public key name  : NXP NTAG21x 2013"); // don't know if there is other NXP public keys.. :(
+       PrintAndLog("IC signature public key value : 04494e1a386d3d3cfe3dc10e5de68a499b1c202db5b132393e89ed19fe5be8bc61");
+       PrintAndLog("    Elliptic curve parameters : secp128r1");
+       PrintAndLog("            Tag ECC Signature : %s", sprint_hex(data, len));
+       //to do:  verify if signature is valid
+       //PrintAndLog("IC signature status: %s valid", (iseccvalid() )?"":"not");
+       return 0;
+}
+
+static int ulev1_print_version(uint8_t *data){
+       PrintAndLog("\n--- Tag Version");
+       PrintAndLog("       Raw bytes : %s",sprint_hex(data, 8) );
+       PrintAndLog("       Vendor ID : %02X, %s", data[1], getTagInfo(data[1]));
+       PrintAndLog("    Product type : %s", getProductTypeStr(data[2]));
+       PrintAndLog(" Product subtype : %02X, %s", data[3], (data[3]==1) ?"17 pF":"50pF");
+       PrintAndLog("   Major version : %02X", data[4]);
+       PrintAndLog("   Minor version : %02X", data[5]);
+       PrintAndLog("            Size : %s", getUlev1CardSizeStr(data[6]));
+       PrintAndLog("   Protocol type : %02X", data[7]);
+       return 0;
+}
+
+/*
+static int ulc_magic_test(){
+       // Magic Ultralight test
+               // Magic UL-C, by observation,
+       // 1) it seems to have a static nonce response to 0x1A command.
+       // 2) the deskey bytes is not-zero:d out on as datasheet states.
+       // 3) UID - changeable, not only, but pages 0-1-2-3.
+       // 4) use the ul_magic_test !  magic tags answers specially!
+       int returnValue = UL_ERROR;
+       iso14a_card_select_t card;
+       uint8_t nonce1[11] = {0x00};
+       uint8_t nonce2[11] = {0x00};
+       int status = ul_select(&card);
+       if ( !status ){
+               return UL_ERROR;
+       }
+       status = ulc_requestAuthentication(nonce1, sizeof(nonce1));
+       if ( status > 0 ) {
+               status = ulc_requestAuthentication(nonce2, sizeof(nonce2));
+               returnValue =  ( !memcmp(nonce1, nonce2, 11) ) ? UL_C_MAGIC : UL_C;
+       } else {
+               returnValue = UL;
+       }       
+       ul_switch_off_field();
+       return returnValue;
+}
+*/
+static int ul_magic_test(){
+
+       // Magic Ultralight tests
+       // 1) take present UID, and try to write it back. OBSOLETE 
+       // 2) make a wrong length write to page0, and see if tag answers with ACK/NACK:
+       iso14a_card_select_t card;
+       if ( !ul_select(&card) ) 
+               return UL_ERROR;
+       int status = ul_comp_write(0, NULL, 0);
+       ul_switch_off_field();
+       if ( status == 0 ) 
+               return MAGIC;
+       return 0;
+}
+
+uint32_t GetHF14AMfU_Type(void){
+
+       TagTypeUL_t tagtype = UNKNOWN;
+       iso14a_card_select_t card;
+       uint8_t version[10] = {0x00};
+       int status = 0;
+       int len;
+
+       if (!ul_select(&card)) return UL_ERROR;
+
+       // Ultralight - ATQA / SAK 
+       if ( card.atqa[1] != 0x00 || card.atqa[0] != 0x44 || card.sak != 0x00 ) {
+               PrintAndLog("Tag is not Ultralight | NTAG | MY-D  [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak);
+               ul_switch_off_field();
+               return UL_ERROR;
+       }
+
+       if ( card.uid[0] != 0x05) {
+
+               len  = ulev1_getVersion(version, sizeof(version));
+               ul_switch_off_field();
+
+               switch (len) {
+                       case 0x0A: {
+
+                               if ( version[2] == 0x03 && version[6] == 0x0B )
+                                       tagtype = UL_EV1_48;
+                               else if ( version[2] == 0x03 && version[6] != 0x0B )
+                                       tagtype = UL_EV1_128;
+                               else if ( version[2] == 0x04 && version[3] == 0x01 && version[6] == 0x0B )
+                                       tagtype = NTAG_210;
+                               else if ( version[2] == 0x04 && version[3] == 0x01 && version[6] == 0x0E )
+                                       tagtype = NTAG_212;
+                               else if ( version[2] == 0x04 && version[3] == 0x02 && version[6] == 0x0F )
+                                       tagtype = NTAG_213;
+                               else if ( version[2] == 0x04 && version[3] == 0x02 && version[6] == 0x11 )
+                                       tagtype = NTAG_215;
+                               else if ( version[2] == 0x04 && version[3] == 0x02 && version[6] == 0x13 )
+                                       tagtype = NTAG_216;
+                               else if ( version[2] == 0x04 && version[3] == 0x05 && version[6] == 0x13 )
+                                       tagtype = NTAG_I2C_1K;
+                               else if ( version[2] == 0x04 && version[3] == 0x05 && version[6] == 0x15 )
+                                       tagtype = NTAG_I2C_2K;
+                               else if ( version[2] == 0x04 )
+                                       tagtype = NTAG;
+
+                               break;
+                       }
+                       case 0x01: tagtype = UL_C; break;
+                       case 0x00: tagtype = UL; break;
+                       case -1  : tagtype = (UL | UL_C | NTAG_203); break;  // could be UL | UL_C magic tags
+                       default  : tagtype = UNKNOWN; break;
+               }
+               // UL vs UL-C vs ntag203 test
+               if (tagtype & (UL | UL_C | NTAG_203)) {
+                       if ( !ul_select(&card) ) return UL_ERROR;
+
+                       // do UL_C check first...
+                       uint8_t nonce[11] = {0x00};
+                       status = ulc_requestAuthentication(nonce, sizeof(nonce));
+                       ul_switch_off_field();
+                       if (status > 1) {
+                               tagtype = UL_C;
+                       } else { 
+                               // need to re-select after authentication error
+                               if ( !ul_select(&card) ) return UL_ERROR;
+
+                               uint8_t data[16] = {0x00};
+                               // read page 0x26-0x29 (last valid ntag203 page)
+                               status = ul_read(0x26, data, sizeof(data));
+                               if ( status <= 1 ) {
+                                       tagtype = UL;
+                               } else {
+                                       // read page 0x30 (should error if it is a ntag203)
+                                       status = ul_read(0x30, data, sizeof(data));
+                                       if ( status <= 1 ){
+                                               tagtype = NTAG_203;
+                                       } else {
+                                               tagtype = UNKNOWN;
+                                       }
+                               }
+                               ul_switch_off_field();
+                       }
+               }
+               if (tagtype & UL) {
+                       tagtype = ul_fudan_check(); 
+                       ul_switch_off_field();
+               }