Last active
August 1, 2024 13:50
-
-
Save atc1441/41af75048e4c22af1f5f0d4c1d94bb56 to your computer and use it in GitHub Desktop.
This file contains 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
#include <stdio.h> | |
#include <stdint.h> | |
// Philips Sonicare NFC Head Password calculation by @atc1441 Video manual: https://www.youtube.com/watch?v=EPytrn8i8sc | |
uint16_t CRC16(uint16_t crc, uint8_t *buffer, int len) // Default CRC16 Algo | |
{ | |
while(len--) | |
{ | |
crc ^= *buffer++ << 8; | |
int bits = 0; | |
do | |
{ | |
if ( (crc & 0x8000) != 0 ) | |
crc = (2 * crc) ^ 0x1021; | |
else | |
crc *= 2; | |
} | |
while ( ++bits < 8 ); | |
} | |
return crc; | |
} | |
uint8_t nfctag_uid[] = {0x04,0xEC,0xFC,0xA2,0x94,0x10,0x90}; // NTAG UID | |
uint8_t nfc_second[] = "221214 12K"; // Head MFG String, printed on Head and at memory location 0x23 | |
int main() | |
{ | |
uint32_t crc_calc = CRC16(0x49A3, nfctag_uid, 7); // Calculate the NTAG UID CRC | |
crc_calc = crc_calc | (CRC16(crc_calc, nfc_second, 10) << 16); // Calculate the MFG CRC | |
crc_calc = (crc_calc >> 8) & 0x00FF00FF | (crc_calc << 8) & 0xFF00FF00; // Rotate the uin16_t bytes | |
printf("by @ATC1441 NFC CRC : 0x%08X expected: 0x61F0A50F\r\n", crc_calc);// Print out the calucated password | |
return 0; | |
} |
@older Oh! That might explain some things xD
Updated my answer
Python version (Tested on 3.9):
def crc16(crc: int, buffer: list[int]) -> int:
""" Default CRC16 Algo. """
for byte in buffer:
crc ^= byte << 8
for _ in range(8):
if crc & 0x8000:
crc = (crc << 1) ^ 0x1021
else:
crc <<= 1
crc &= 0xFFFF
return crc
def compute_password(uid: list[int], nfc_second: str) -> str:
crc_calc = crc16(0x49A3, uid) # Calculate the NTAG UID CRC.
nfc_second_bytes = nfc_second.encode("utf-8") # Convert nfc_second to bytes.
crc_calc |= crc16(crc_calc, nfc_second_bytes) << 16 # Calculate the MFG CRC and combine with NTAG UID CRC.
crc_calc = ((crc_calc >> 8) & 0x00FF00FF) | ((crc_calc << 8) & 0xFF00FF00) # Rotate the bytes.
return f"0x{crc_calc:08X}"
uid = [0x04, 0xEC, 0xFC, 0xA2, 0x94, 0x10, 0x90] # NTAG UID.
nfc_second = "221214 12K" # Head MFG String, printed on Head and at memory location 0x23.
crc_expected = "0x61F0A50F"
crc_actual = compute_password(uid, nfc_second)
print(f"by @ATC1441 NFC CRC : {crc_actual} expected: {crc_expected}. Check: {crc_actual == crc_expected}") # Print out the calculated password.
Output:
by @ATC1441 NFC CRC : 0x61F0A50F expected: 0x61F0A50F. Check: True
JavaScript version:
function crc16(crc, buffer) {
for (let byte of buffer) {
crc ^= BigInt(byte) << BigInt(8);
for (let i = 0; i < 8; i++) {
if (crc & BigInt(0x8000)) {
crc = (crc << BigInt(1)) ^ BigInt(0x1021);
} else {
crc <<= BigInt(1);
}
crc &= BigInt(0xFFFF);
}
}
return crc;
}
function generatePassword(uid, mfg) {
let crc = crc16(BigInt(0x49A3), uid); // Calculate the UID CRC
crc = crc | crc16(crc, mfg) << BigInt(16); // Calculate the MFG CRC
crc = ((crc >> BigInt(8)) & BigInt(0x00FF00FF)) | ((crc << BigInt(8)) & BigInt(0xFF00FF00)); // Rotate the bytes
let password = crc.toString(16).toUpperCase().replace(/(..)(..)(..)(..)/g, '$1:$2:$3:$4'); // Format the password
return password;
}
I've implemented it in a little web app.
More beefy python version with built in command generator and ability to set # of sessions left:
https://github.com/filipsworks/SoniKraker
Just for fun - Zig version 🤓⚡
const std = @import("std");
fn parse_uuid(uuid_str: []const u8, uuid: *[7]u8) !void {
var i: usize = 0;
var iter = std.mem.splitScalar(u8, uuid_str, ':');
while (iter.next()) |b| {
uuid[i] = try std.fmt.parseUnsigned(u8, b, 16);
i += 1;
}
}
fn parse_uuid(uuid_str: []const u8, uuid: *[7]u8) !void {
var i: usize = 0;
var iter = std.mem.splitScalar(u8, uuid_str, ':');
while (iter.next()) |b| {
uuid[i] = try std.fmt.parseUnsigned(u8, b, 16);
i += 1;
}
}
fn calc_password(uuid: []const u8, mfg: []const u8) u32 {
var crc_calc: u32 = crc16(0x49A3, uuid);
crc_calc = crc_calc | (@as(u32, crc16(@truncate(crc_calc), mfg)) << 16);
crc_calc = ((crc_calc >> 8) & 0x00FF00FF) | ((crc_calc << 8) & 0xFF00FF00);
return crc_calc;
}
fn crc16(crc: u16, buff: []const u8) u16 {
var crc_c = crc;
var i: usize = 0;
while (i < buff.len) : (i += 1) {
crc_c ^= @truncate(@as(u32, buff[i]) << 8);
for (0..8) |_| {
if (crc_c & 0x8000 != 0) {
crc_c = @truncate((2 * @as(u32, crc_c)) ^ 0x1021);
} else {
crc_c *= 2;
}
}
}
return crc_c;
}
test calc_password {
var uuid: [7]u8 = undefined;
try parse_uuid("04:EC:FC:A2:94:10:90", &uuid);
const pwd = calc_password(&uuid, "221214 12K");
try std.testing.expectEqual(0x61F0A50F, pwd);
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Csharp version (LinqPad):