Upload New File

This commit is contained in:
Joris 2020-10-14 11:05:43 +00:00
parent 72a63c5ea8
commit bd174abd6b

739
src/Ultralight_C.ino Normal file
View File

@ -0,0 +1,739 @@
/*
* --------------------------------------------------------------------------------------------------------------------
* Example sketch/program showing how to read and modify data from a Mifare Ultralight C PICC
* --------------------------------------------------------------------------------------------------------------------
*
* This code enables basic communication with a Mifare Ultralight C PICC.
* It allows reading and writing of data including basic operations for password protection.
*
* This sketch imlements NO measures for page blocking and using OTP and the upwarts counter
*
* This sketch should run stable on a Arduinno Uno with RC522 module.
* But the code itself is very sensitive for modifications due to my bad programming
* or stability issues with the used libraries. If you change / optimize the code, be
* prepared for some weird complications
*
* have fun!
*
* @license Released into the public domain.
*
* use
* - auth 49454D4B41455242214E4143554F5946 for authentication (with standard key)
* - newKey 49454D4B41455242214E4143554F5946 for writing a new key (in this case the standard key) to the PICC.
* - dunp to list the content of the PICC (data might not visible due to read-protection)
* - wchar 10 hello world to write "hello world" starting at page 10
* - whex 10 0123456789ABC to write HEX values starting at page 10
* - protect 25 to protect the page from 25 upward
* - setpbit 1 to set the protection to write-protected
*
*
* Typical pin layout used:
* -----------------------------------------------------------------------------------------
* MFRC522 Arduino Arduino Arduino Arduino Arduino
* Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro
* Signal Pin Pin Pin Pin Pin Pin
* -----------------------------------------------------------------------------------------
* RST/Reset RST 9 5 D9 RESET/ICSP-5 RST
* SPI SS SDA(SS) 10 53 D10 10 10
* SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16
* SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14
* SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15
*/
/*
* IMPORTANT!!!
* in order to be able to read Mifare Ultralight C PICC the typical RC522-module needs to be modified.
* L1 and L2 must be exchanged for better inductors (e.g. FERROCORE CW1008-2200). Otherwise this code will not work!
*/
/*
* The Mifare Ultralight C encrypted authentification process can be found here: https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf
*/
#include <SPI.h>
#include <MFRC522.h>
#include <DES.h>
#define RST_PIN 9 // Configurable, see typical pin layout above
#define SS_PIN 10 // Configurable, see typical pin layout above
DES des;
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
MFRC522::StatusCode status;
//3DES-Key (This is the standard key according to the NPX specification)
byte C_key[] = { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46, 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42};
uint64_t IV=0;
boolean isPresent=0;
boolean isEncrypted=0;
boolean isUlC=0;
char Scmd[28];
char Sdata[64];
// byte page=0;
byte buffer[24];
boolean isLocked[255]; // which Pages are locked to read only
byte startProtect=255; // starting from which page, the data is protectet by the key
boolean access; // how is the data protected: 0=write protection (data visible) 1=read/write protection (data = 0x00)
void setup() {
Serial.begin(115200); // Initialize serial communications with the PC
while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
des.init(C_key,IV);
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522
mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader details
Serial.println(F("Read/Write Mifare Ultralight C"));
}
void loop() {
int i=0;
boolean IsCommand=1;
char x;
if(Serial.available() > 0){
// Serial.println(freeRam());
while(i<40){
while(!Serial.available());
x=Serial.read();
if(IsCommand)
Scmd[i]=x;
else
Sdata[i]=x;
if(x == '\n'){ // newline
if(IsCommand){
Scmd[i]=0x00; // proper Ending
i=41; // exit
}
else{
Sdata[i]=0x00;
i=41;
}
}
else if(x == ' '){
if(IsCommand==1){
IsCommand=0;
Scmd[i]=0x00;
i=0;
}
else
i++;
}
else
i++;
}
if(!isPresent)
Serial.println(F("No PICC available!"));
else{
if(!strcmp(Scmd,"dump")){ // dump data
DumpDataUltralight();
}
else if(!strcmp(Scmd,"auth")){
RequestAuthUltralightC();
}
else if(!strcmp(Scmd,"newKey")){
newKey();
}
else if (!strcmp(Scmd,"wchar")){
writeData(0);
}
else if (!strcmp(Scmd,"whex")){
writeData(1);
}
else if (!strcmp(Scmd,"protect")){
protect(1);
}
else if (!strcmp(Scmd,"setpbit")){
protect(0);
}
}
Serial.flush();
Scmd[0]=0x00;
Sdata[0]=0x00;
}
// Look for new cards
if(!isPresent){
if (!mfrc522.PICC_IsNewCardPresent()) {
return;
}
// Select one of the cards
if (!mfrc522.PICC_ReadCardSerial()) {
return;
}
else{
isPresent=1;
if(mfrc522.uid.sak == 0x00){
if(checkIfUltralight()){
Serial.println (F("Mifare Ultralight C PICC found!"));
getPageInfo();
isUlC=1;
}
else{
Serial.println (F("Other Mifare Ultralight compatible PICC found"));
isUlC=0;
}
Serial.print(F("UID: "));
dumpInfo(mfrc522.uid.uidByte, mfrc522.uid.size);
}
else{
Serial.println (F("Other Card found, not compatible!"));
mfrc522.PICC_HaltA();
isPresent=0;
return;
}
}
}
if(isPresent){ // test read - it it fails, the PICC is most likely gone
byte byteCount = sizeof(buffer);
status = mfrc522.MIFARE_Read(0, buffer, &byteCount);
if(status!=mfrc522.STATUS_OK){
isPresent=0;
isEncrypted=0;
mfrc522.PCD_StopCrypto1();
Serial.println("Card gone...");
}
}
}
/*
* This small routine starts a request for encryption.
* for details see https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf
*/
void RequestAuthUltralightC(void){
int i;
byte AuthBuffer[24] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //
byte AuthLength=24;
byte RndA[8]={0,0,0,0,0,0,0,0};
byte RndB[8]={0,0,0,0,0,0,0,0}; // decrypted RndB
byte rRndB[8]={0,0,0,0,0,0,0,0}; // rotated RndB
byte encRndA[8]={0,0,0,0,0,0,0,0}; // encrypted RndA' from the PICC
byte dRndA[8]={0,0,0,0,0,0,0,0}; // decrypted RndA
byte message[24]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // Message to transfer
byte iv_ar[8]; // this is the starting IV for decryption
byte byteCount=sizeof(buffer);
if(isEncrypted){
Serial.println(F("PICC already authenticated"));
return;
}
// sparse and set the key
if(!setAuthKey()){
Serial.println(F("key could not be set"));
return;
}
// set IV to 0x00
for(i=0 ; i<8 ; i++){
iv_ar[i] = 0x00;
}
memcpy(&IV,iv_ar,8); // set IV
des.set_IV(IV);
// Build command buffer
AuthBuffer[0] = 0x1A; // CMD_3DES_AUTH -> Ultralight C 3DES Authentication.
AuthBuffer[1] = 0x00; //
// Calculate CRC_A
status = mfrc522.PCD_CalculateCRC(AuthBuffer, 2, &AuthBuffer[2]);
if (status != mfrc522.STATUS_OK) {
return;
}
AuthLength=sizeof(AuthBuffer);
// Transmit the buffer and receive the response, validate CRC_A.
status = mfrc522.PCD_TransceiveData(AuthBuffer, 4, AuthBuffer, &AuthLength, NULL, 0, true);
if (status != mfrc522.STATUS_OK) {
Serial.println("Ultralight C Auth failed");
Serial.println(mfrc522.GetStatusCodeName(status));
Serial.print(F("Reply: "));
dumpInfo(AuthBuffer,AuthLength);
return;
}
memcpy(message,AuthBuffer+1,8); // copy the enc(RndB) from the message
memcpy(iv_ar,message,8); // use enc(RndB) as new IV for the next encryption.
des.set_size(8);
des.tdesCbcDecipher(message,RndB); // decrypt enc(RndB) -> now we have RndB
randomSeed(AuthBuffer[1]); // create RndA
int number;
for(int i=0; i<8; i++){
number=random(255);
RndA[i]=number&0xFF; //
}
memcpy(rRndB, RndB ,8); // copy RndB
rol(rRndB,8); // create RndB'
memcpy(message,RndA,8); // copy RndA in the first part of message
memcpy(message+8, rRndB ,8); // adding RndB'
AuthBuffer[0] = 0xAF; // set the PCD-command
memcpy(&IV,iv_ar,8); // set IV = to enk(RndB)
des.set_IV(IV);
des.set_size(16);
des.tdesCbcEncipher(message, &AuthBuffer[1]);
status = mfrc522.PCD_CalculateCRC(AuthBuffer, 17, &AuthBuffer[17]);
if (status != mfrc522.STATUS_OK) {
return;
}
memcpy(&IV,&AuthBuffer[9],8); // set IV to decrypt reply from PICC enc(RndA') -> RndA
des.set_IV(IV);
status = mfrc522.PCD_TransceiveData(AuthBuffer, 19, AuthBuffer, &AuthLength, NULL, 0, true);
if (status != mfrc522.STATUS_OK) {
Serial.print(F("Auth failed failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
Serial.println(F("Reply: "));
dumpInfo(AuthBuffer,AuthLength);
return;
}
else{
if(AuthBuffer[0]==0x00){ // reply from PICC should start with 0x00
memcpy(encRndA, &AuthBuffer[1],8); // copy enc(RndA')
des.set_size(8);
des.tdesCbcDecipher(encRndA,dRndA); // decrypt now we have decrypted RndA'
rol(RndA,8); // rotate orgiginal RndA to RndA'
for(i=0; i<8; i++){ // compare it
if(RndA[i] != dRndA[i]){
i=9;
}
}
if(i==8){
Serial.println(F("Keys are correct :-)"));
isEncrypted=1;
}
else
Serial.println(F("Keys do not match"));
}
else{
Serial.println(F("Wrong answer!!!"));
}
}
return;
}
byte DumpDataUltralight(void){
byte page=0;
for(byte i=0; i<255; i++) // initialize lock bit table
isLocked[i]=0;
byte Count = sizeof(buffer);
Serial.println(F("Page lock auth 0 1 2 3"));
getPageInfo(); // get information about acess
status = mfrc522.MIFARE_Read(page, buffer, &Count);
while((status==mfrc522.STATUS_OK)){ // This loop stops at an NAK-answer
Serial.print(page);
if(page<10)
Serial.print(F(" "));
Serial.print(F(" "));
if(isLocked[page])
Serial.print(F(" x "));
else
Serial.print(F(" "));
if(page>=startProtect){
if(access)
Serial.print(F(" w "));
else
Serial.print(F("r/w "));
}
else
Serial.print(F(" "));
dumpInfo(buffer,4);
page++;
status = mfrc522.MIFARE_Read(page, buffer, &Count);
}
Count = sizeof(buffer);
status=mfrc522.PICC_WakeupA(buffer, &Count); // needed to wake up the card after receiving a NAK-answer
return page;
}
boolean checkIfUltralight(void){
byte Count=sizeof(buffer);
if(mfrc522.MIFARE_Read(43, buffer, &Count)==mfrc522.STATUS_OK){
if(mfrc522.MIFARE_Read(44, buffer, &Count)==mfrc522.STATUS_OK){
return 0;
}
else{
Count = sizeof(buffer);
status=mfrc522.PICC_WakeupA(buffer, &Count); // needed to wake up the card after receiving a NAK-answer
return 1;
}
}
Count = sizeof(buffer);
status=mfrc522.PICC_WakeupA(buffer, &Count); // needed to wake up the card after receiving a NAK-answer
Serial.println(mfrc522.GetStatusCodeName(status));
return 0;
}
boolean getPageInfo(){
int i;
uint32_t mask=0;
byte bsize = sizeof(buffer);
status = mfrc522.MIFARE_Read(2, buffer, &bsize); // read the first lock bits
if(status==mfrc522.STATUS_OK){
mask=buffer[3];
mask=mask<<8;
mask=mask+buffer[2];
isLocked[0]=1;
isLocked[1]=1;
isLocked[2]=1;
if(mask&0x0001){
isLocked[3]=1;
}
mask=mask>>1;
if(mask&0x0001){
for(i=0;i<6;i++)
isLocked[4+i]=1;
}
mask=mask>>1;
if(mask&0x0001){
for(i=0;i<6;i++)
isLocked[10+i]=1;
}
mask=mask>>1;
for(i=0;i<13;i++){
if(mask&0x0001)
isLocked[3+i]=1;
mask=mask>>1;
}
}
else{
bsize = sizeof(buffer);
status=mfrc522.PICC_WakeupA(buffer, &bsize); // needed to wake up the card after receiving a NAK-answer
return 0;
}
status = mfrc522.MIFARE_Read(40, buffer, &bsize);
if(status==mfrc522.STATUS_OK){
mask=buffer[1];
mask=mask<<8;
mask=mask+buffer[0];
int j=16;
for(i=0; i<8; i++){
if((i%4)==0){
if(mask&0x0001)
setBooleanBits(isLocked+j, 12);
}
else{
if(mask&0x0001)
setBooleanBits(isLocked+j, 4);
j=j+4;
}
mask=mask>>1;
}
for(i=0;i<3;i++){
if(mask&0x0001)
isLocked[41+i]=1;
mask=mask>>1;
}
if(mask&0x0001)
setBooleanBits(isLocked+44, 4);
mask=mask>>1;
for(i=0;i<3;i++){
if(mask&0x0001)
isLocked[41+i]=1;
mask=mask>>1;
}
if(mask&0x0001)
setBooleanBits(isLocked+44, 4);
mask=mask>>1;
}
else{
bsize = sizeof(buffer);
status=mfrc522.PICC_WakeupA(buffer, &bsize); // needed to wake up the card after receiving a NAK-answer
return 0;
}
status = mfrc522.MIFARE_Read(42, buffer, &bsize);
if(status==mfrc522.STATUS_OK){
startProtect=buffer[0];
}
else{
bsize = sizeof(buffer);
status=mfrc522.PICC_WakeupA(buffer, &bsize); // needed to wake up the card after receiving a NAK-answer
return 0;
}
status = mfrc522.MIFARE_Read(43, buffer, &bsize);
if(status==mfrc522.STATUS_OK){
access=buffer[0]&0x01;
}
else{
bsize = sizeof(buffer);
status=mfrc522.PICC_WakeupA(buffer, &bsize); // needed to wake up the card after receiving a NAK-answer
return 0;
}
return 1;
}
void setBooleanBits(boolean *ar, int len){
for(int i=0; i<len; i++)
ar[i]=1;
}
void protect(bool mode){
byte value=0;
byte page;
if(mode) // set start page of protection
page=42;
else // set write or read/write protection
page=43;
if(isLocked[page]){
Serial.println(F("PICC is write protected"));
return;
}
if((startProtect<page)&&(!isEncrypted)){
Serial.print(F("page "));
Serial.print(page);
Serial.println(F(" is password protected, autehticate first"));
return;
}
value = atoi(Sdata);
if(mode){
if(value>48)
value=48;
}
else{
if(value>1)
value=1;
}
for(byte i=0;i<4;i++)
buffer[i]=0;
buffer[0]=value;
status = mfrc522.MIFARE_Ultralight_Write(page, buffer, 4);
if(status==mfrc522.STATUS_OK){
if(mode){
Serial.print(F("password protection starts now at page: "));
Serial.println(value);
}
else{
Serial.print(F("protection bit is set to: "));
if(value)
Serial.println(F("write protected"));
else
Serial.println(F("read/write protected"));
}
}
else{
if(mode)
Serial.print(F("ERROR: password protection not set: "));
else
Serial.print(F("ERROR: protection bit not set: "));
Serial.println(mfrc522.GetStatusCodeName(status));
}
}
boolean setAuthKey(void){
int i;
for(i=0;i<32;i++){
if(Sdata[i]==0x00){
Serial.println(F("Key too short! (needs to be 32 digits)"));
return 0;
}
else{
if((i%2==0)){
C_key[i/2]=char2byte(&Sdata[i]);
}
}
}
for(i=0;i<8;i++){
C_key[i+16]=C_key[i];
}
des.change_key(C_key);
return 1;
}
boolean newKey(void){
byte i,j,pos;
byte pos_ar[4]={7,3,15,11};
byte Buffer[4];
byte page=0;
if(!isEncrypted){
Serial.println(F("please authenticate fist"));
return 0;
}
if(setAuthKey()){
for(j=0;j<4;j++){
page=0x2C+j;
pos=pos_ar[j];
for(i=0;i<4;i++){
Buffer[i]=C_key[pos-i];
}
status = mfrc522.MIFARE_Ultralight_Write(page, Buffer, 4);
}
if(status==mfrc522.STATUS_OK){
Serial.println(F("new key is written to the card!"));
}
else{
Serial.print(F("new key could not be written: "));
Serial.println(mfrc522.GetStatusCodeName(status));
// restart the PICC
mfrc522.PICC_IsNewCardPresent(); // is required, as the PICC remains silent after a NAK
mfrc522.PICC_ReadCardSerial();
}
}
else
return 0;
return 1;
}
void writeData(boolean mode){
byte page=0;
byte i=0;
byte Buffer[4]={0,0,0,0};
if(isUlC)
Serial.println(F("Ultralight C"));
else
Serial.println(F("not a Ultralight C PICC!"));
while((Sdata[i]!=' ')&&(i<3)){
page = page * 10;
page = page + Sdata[i]-48;
i++;
}
i++; // to overcome the ' '
if(page<4){
Serial.println(F("no user memory here"));
return;
}
if(isUlC&&(page>39)){
Serial.println(F("no user memory here"));
return;
}
byte j=0;
boolean done=0;
for(; page<40; page++){
for(j=0;j<4;j++){
if(Sdata[i]==0x00){
while(j<4){
Buffer[j]=0;
j++;
}
done=1;
}
else{
if(mode){
Buffer[j]=char2byte(Sdata+i);
i=i+2;
}
else{
Buffer[j]=Sdata[i];
i++;
}
}
}
status = mfrc522.MIFARE_Ultralight_Write(page, Buffer, 4);
Serial.print(F("writing page "));
Serial.print(page);
Serial.println(mfrc522.GetStatusCodeName(status));
if(status != mfrc522.STATUS_OK){
return;
}
if(done)
return;
}
}
// Needed to create RndB' out of RndB
void rol(byte *data, int len){
byte first = data[0];
for (int i = 0; i < len-1; i++) {
data[i] = data[i+1];
}
data[len-1] = first;
}
void dumpInfo(byte *ar, int len){
for(int i=0 ; i<len ; i++){
if(ar[i]<0x10)
Serial.print(F("0"));
Serial.print(ar[i], HEX);
Serial.print(F(" "));
}
Serial.println("");
}
byte char2byte(char *s){
byte x = 0;
for(int i=0; i <2 ; i++) {
char c = *s;
if (c >= '0' && c <= '9') {
x *= 16;
x += c - '0';
}
else if (c >= 'A' && c <= 'F') {
x *= 16;
x += (c - 'A') + 10;
}
else if (c >= 'a' && c <= 'f') {
x *= 16;
x += (c - 'a') + 10;
}
s++;
}
return x;
}
/*
* If the hex key is: "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"
* then you have to write the sequence "07 06 05 04 03 02 01 00 0F 0E 0D 0C 0B 0A 09 08" in 4 pages,
* from page 0x2C (44) to page 0x2F (47).
*
* if you want to change where (which pages) the authentication is required, here is how to do that:
* 0x2A defines the page address from which the authentication is required. E.g. if 0x2A = 0x30 no authentication is needed all as memory goes up to 0x2F.
* 0x2B defines if authentication is required for read/write (0x2B=0) or only for write access (0x2B=1)
On example of Key1 = 0001020304050607h and Key2 = 08090A0B0C0D0E0Fh,
the command sequence needed for key programming with WRITE command is:
A2 2C 07 06 05 04 CRC
A2 2D 03 02 01 00 CRC
A2 2E 0F 0E 0D 0C CRC
A2 2F 0B 0A 09 08 CRC
*/