Skip to content

Instantly share code, notes, and snippets.

@klali
Last active May 10, 2022 10:59
Show Gist options
  • Save klali/e390bc095698dc72827fb69b29d4acdd to your computer and use it in GitHub Desktop.
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)
/* 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;
}
@klali
Copy link
Author

klali commented Mar 8, 2021

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