-
-
Save atc1441/41af75048e4c22af1f5f0d4c1d94bb56 to your computer and use it in GitHub Desktop.
#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);
}
import argparse
from typing import List
def crc16(crc: int, buffer: List[int]) -> int:
"""
Calculates the CRC-16 value for a given buffer.
Args:
crc: The initial CRC value.
buffer: A list of integers representing the data buffer.
Returns:
The calculated CRC-16 value.
"""
for byte in buffer:
crc ^= byte << 8
for _ in range(8):
if crc & 0x8000:
crc = (crc << 1) ^ 0x1021 # CRC-16-CCITT polynomial
else:
crc <<= 1
return crc & 0xFFFF
def compute_password(uid: List[int], nfc_second: bytes) -> str:
"""
Computes the password for a Philips Sonicare NFC device.
Args:
uid: A list of integers representing the NTAG UID.
nfc_second: A bytes object representing the head manufacturing string.
Returns:
A string representing the computed password in hexadecimal format.
"""
# Initial CRC value for NTAG UID
crc_calc = crc16(0x49A3, uid)
# Calculate the MFG CRC and combine with NTAG UID CRC
crc_calc |= crc16(crc_calc, list(nfc_second)) << 16
# Rotate the bytes
crc_calc = ((crc_calc >> 8) & 0x00FF00FF) | ((crc_calc << 8) & 0xFF00FF00)
return f"0x{crc_calc:08X}"
def get_user_input():
"""
Prompts the user for NTAG UID and NFC second string, validates the input,
and returns the parsed UID and NFC second string.
Returns:
A tuple containing the UID as a list of integers and the NFC second
string as a bytes object.
"""
while True:
uid_str = input("Enter the NTAG UID in the format '04:EC:FC:A2:94:10:90': ")
nfc_second_str = input("Enter the Head MFG String (e.g., '221214 12K'): ")
try:
uid = [int(x, 16) for x in uid_str.split(':')]
if len(uid) != 7:
raise ValueError("The UID must have 7 bytes.")
nfc_second = nfc_second_str.encode("utf-8")
return uid, nfc_second
except ValueError as e:
print(f"Invalid input: {e}. Please try again.")
--- Main execution ---
if name == "main":
parser = argparse.ArgumentParser(description="Compute Philips Sonicare NFC password.")
parser.add_argument("--uid", help="NTAG UID in the format '04:EC:FC:A2:94:10:90'")
parser.add_argument("--mfg", help="Head MFG String (e.g., '221214 12K')")
args = parser.parse_args()
if args.uid and args.mfg:
try:
uid = [int(x, 16) for x in args.uid.split(':')]
if len(uid) != 7:
raise ValueError("The UID must have 7 bytes.")
nfc_second = args.mfg.encode("utf-8")
except ValueError as e:
print(f"Invalid input: {e}")
exit(1)
else:
uid, nfc_second = get_user_input()
# Calculate the actual CRC value
crc_actual = compute_password(uid, nfc_second)
# Print the results
print(f"\nby @ATC1441 NFC CRC : {crc_actual}")
Csharp version (LinqPad):