Skip to content

Instantly share code, notes, and snippets.

@atc1441
Last active August 17, 2025 02:02
Show Gist options
  • Save atc1441/41af75048e4c22af1f5f0d4c1d94bb56 to your computer and use it in GitHub Desktop.
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;
}
@samascience
Copy link

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}")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment