Last active
May 10, 2022 10:59
-
-
Save klali/e390bc095698dc72827fb69b29d4acdd to your computer and use it in GitHub Desktop.
Convert a 7 byte Mifare UID to access control system formats (RCO, ESMI, RFIDeas and OnGuard)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright (c) 2020 Yubico AB. */ | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <ctype.h> | |
/* This small program takes in a 7 byte NFC UID and converts to format | |
* needed for access control systems. This is implemented for the esmi, | |
* RCO, RFIDeas and OnGuard systems */ | |
// RCO needs a 9 character decimal code, constructed from the 4 low bytes of the uid | |
// It's put together by two little-endian 16-bit ints with the final high digit removed | |
// So high-part is uid[3] | uid[2], low part is uid[1] | uid[0], | |
// then each converted to 5 digit decimal and the first character removed. | |
static void convert_rco(const uint8_t *uid, char *str) { | |
uint16_t hp = (uid[3] << 8) | uid[2]; | |
uint16_t lp = (uid[1] << 8) | uid[0]; | |
sprintf(str, "%04d%05d", hp % 10000, lp); | |
} | |
// esmi needs an 8 character decimal code, constructed from the 4 low bytes of the uid | |
// It's put together by two little-endian 16-bit ints with the two final high digits removed | |
// The first step of this is done by a circular shift 4 bits to the left, essentially moving the first nibble last. | |
// After that step the two 16-bit ints are constructed by uid[3] | uid[2] and uid[1] | uid[0], | |
// then each converted to 5 digit decimal and the first two characters removed. | |
static void convert_esmi(const uint8_t *uid, char *str) { | |
uint16_t hp = ((uid[0] & 0x0f) << 12) | (uid[3] << 4) | ((uid[2] & 0xf0) >> 4); | |
uint16_t lp = ((uid[2] & 0x0f) << 12) | (uid[1] << 4) | ((uid[0] & 0xf0) >> 4); | |
sprintf(str, "%03d%05d", hp % 1000, lp); | |
} | |
// rfideas and onguard needs an 18 digit decimal code, constructed from the full uid | |
// It's simply constructed from the reverse uid interpreted as a 56 bit int. | |
static void convert_onguard(const uint8_t *uid, char *str) { | |
uint64_t part = ((uint64_t)uid[6] << 48) | ((uint64_t)uid[5] << 40) | ((uint64_t)uid[4] << 32) | ((uint64_t)uid[3] << 24) | (uid[2] << 16) | (uid[1] << 8) | uid[0]; | |
sprintf(str, "%lu", part); | |
} | |
static const char trans[] = "0123456789abcdef"; | |
int main(int argc, char **argv) { | |
uint8_t uid[7]; | |
char str[64]; | |
if(argc != 3) { | |
printf("only 1 arg!\n"); | |
return 1; | |
} | |
if(strlen(argv[2]) != 14) { | |
printf("only input concatenated hex uid, 14 characters!\n"); | |
return 1; | |
} | |
for(int i = 0; i < strlen(argv[2]); i += 2) { | |
char *p1 = strchr(trans, tolower(argv[2][i])); | |
char *p2 = strchr(trans, tolower(argv[2][i+1])); | |
if(p1 == NULL || p2 == NULL) { | |
printf("non hex discovered.\n"); | |
return 1; | |
} | |
uid[i/2] = (p1 - trans) << 4; | |
uid[i/2] |= (p2 - trans); | |
} | |
if(strcmp(argv[1], "rco") == 0) { | |
convert_rco(uid, str); | |
} else if(strcmp(argv[1], "esmi") == 0) { | |
convert_esmi(uid, str); | |
} else if(strcmp(argv[1], "onguard") == 0 || strcmp(argv[1], "rfideas") == 0) { | |
convert_onguard(uid, str); | |
} else { | |
printf("unknown format \"%s\"\n", argv[1]); | |
return 1; | |
} | |
printf("num: %s\n", str); | |
return 0; | |
} |
Trying to figure out how to reverse the RCO number calculation. 9-digit RCO to 4-byte UID hex.
Trying to figure out how to reverse the RCO number calculation. 9-digit RCO to 4-byte UID hex.
That's not possible, information is removed when truncating it to 9 digits, there's 10 possible 4-byte UIDs for each RCO code.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Klas! Thanks for sharing!