Skip to main content

Code Samples

Construct Tag Data

This examples shows how to construct and write the memory layout of a TigerTag onto an NTAG213-compatible RFID tag.

⚠️ Note: All tag values in this example are hardcoded for demonstration. In a real implementation, you must retrieve the required values from the TigerTag REST API (e.g., /product/filament/get, /material/filament/get, etc.) and map them into the appropriate TigerTag memory layout before writing.


# Python example to construct and write a TigerTag to NTAG213 using RC522
# Requires: pi-rc522 or similar library (SPI-based MFRC522)

from pirc522 import RFID
import RPi.GPIO as GPIO
import struct
import time

# Simulated tag data
TAG_ID = 0x6C223431
PRODUCT_ID = [0x12, 0x34, 0x56, 0x78]
MATERIAL_ID = 0x1665
ASPECT1 = 21
ASPECT2 = 0
TYPE_ID = 142
DIAMETER_ID = 56
BRAND_ID = 1120
COLOR = 0x541D09FF
WEIGHT = 1000
UNIT_ID = 1
TEMP_MIN = 240
TEMP_MAX = 280
DRY_TEMP = 70
DRY_TIME = 8
TIMESTAMP = int(time.time()) - 946684800

# Prepare 144-byte memory (pages 4-39)
tag_data = bytearray(144)

def write(offset, data):
tag_data[offset:offset + len(data)] = data

write(0, struct.pack('<I', TAG_ID))
write(4, bytes(PRODUCT_ID))
write(8, struct.pack('<HBB', MATERIAL_ID, ASPECT1, ASPECT2))
write(12, struct.pack('<BBH', TYPE_ID, DIAMETER_ID, BRAND_ID))
write(16, struct.pack('<I', COLOR))
write(20, struct.pack('<I', WEIGHT | (UNIT_ID << 24)))
write(24, struct.pack('<HH', TEMP_MIN, TEMP_MAX))
write(28, struct.pack('<BBH', DRY_TEMP, DRY_TIME, 0))
write(32, struct.pack('<I', TIMESTAMP))
# Pages 13-15 (reserved) and metadata + signature are already zero

# Initialize RC522 reader
rdr = RFID()
rdr.wait_for_tag()

print("Waiting for tag...")
while True:
rdr.wait_for_tag()
(error, tag_type) = rdr.request()
if not error:
(error, uid) = rdr.anticoll()
if not error:
print("Tag detected UID: {}".format(uid))
rdr.select_tag(uid)
for page in range(4, 40):
offset = (page - 4) * 4
block = list(tag_data[offset:offset+4])
err = rdr.write(page, block)
if err:
print(f"Failed to write page {page}")
break
print("TigerTag written successfully!")
rdr.stop_crypto()
break

GPIO.cleanup()

Verify signature

TigerTag Signature Verification - Introduction for Users

TigerTag is a smart RFID-based tagging system used for identifying and authenticating 3D printer filament spools. To ensure the authenticity of a TigerTag, each tag stores a digital signature that proves it was created by a trusted source.

This document explains the verification process in a simple way:


1. What is a Signature? A digital signature is like a unique stamp made using a private key. Only the original tag maker knows this key, so if the stamp is valid, you can be sure the tag is genuine.

2. What Do We Verify? To check if the tag is authentic, we combine three parts:

  • The tag's unique ID (called UID)
  • The header block (block 4)
  • An extra data block (block 5)

We put these together and calculate a special code called a SHA-256 hash.

3. What is Stored on the Tag?

  • The UID (read-only and unique per tag)
  • Block 4 and Block 5 (standard data for identification)
  • A 64-byte signature (split into two parts: r and s) stored in memory pages starting from page 24

4. How Does Verification Work?

  1. The tag is scanned.
  2. The UID, block 4, and block 5 are read.
  3. The 64-byte signature (r + s) is read.
  4. The software recreates the message: UID + block4 + block5.
  5. This message is hashed (SHA-256).
  6. The public key (freely available) is used to verify the hash against the signature.

If everything matches, the tag is declared authentic.


Why is this Important? Without signature verification, anyone could clone a tag. This process protects your supply chain and ensures you're using trusted materials.

Still Curious?

  • The private key is never shared and only used to sign tags.
  • The public key is embedded in the software to verify signatures.
  • The ECDSA (Elliptic Curve Digital Signature Algorithm) is the method used here.

With this system, you get security, authenticity, and peace of mind for every TigerTag spool.

#include <mbedtls/ecdsa.h>
#include <mbedtls/ecp.h>
#include <mbedtls/sha256.h>
#include <mbedtls/asn1write.h>
#include <mbedtls/pk.h>
#include <mbedtls/x509_crt.h>
#include <mbedtls/bignum.h>
#include <string.h>
#include <stdio.h>

const char *uid_hex = "04AABBCCDDEE1122";
uint8_t block4[4] = {0x5B, 0xF5, 0x92, 0x64};
uint8_t block5[4] = {0x00, 0x00, 0x00, 0x00};
uint8_t signature_raw[64];
char public_key_pem[300];

bool encodeDssSignature(uint8_t *out_der, size_t *sig_len, const uint8_t *r, const uint8_t *s) {
mbedtls_mpi mpi_r, mpi_s;
mbedtls_mpi_init(&mpi_r);
mbedtls_mpi_init(&mpi_s);
mbedtls_mpi_read_binary(&mpi_r, r, 32);
mbedtls_mpi_read_binary(&mpi_s, s, 32);

uint8_t buf[80];
uint8_t *p = buf + sizeof(buf);
int ret;

ret = mbedtls_asn1_write_mpi(&p, buf, &mpi_s);
if (ret < 0) return false;
ret = mbedtls_asn1_write_mpi(&p, buf, &mpi_r);
if (ret < 0) return false;
ret = mbedtls_asn1_write_len(&p, buf, buf + sizeof(buf) - p);
if (ret < 0) return false;
ret = mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret < 0) return false;

*sig_len = buf + sizeof(buf) - p;
memcpy(out_der, p, *sig_len);
mbedtls_mpi_free(&mpi_r);
mbedtls_mpi_free(&mpi_s);
return true;
}

bool generateSignatureAndKey() {
mbedtls_pk_context key;
mbedtls_pk_init(&key);
mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(key), mbedtls_ctr_drbg_random, nullptr);

uint8_t uid_bytes[16] = {0};
for (int i = 0; i < 8; ++i)
sscanf(uid_hex + 2 * i, "%2hhx", &uid_bytes[i]);

uint8_t message[16] = {0};
memcpy(message, uid_bytes, 8);
memcpy(message + 8, block4, 4);
memcpy(message + 12, block5, 4);

uint8_t hash[32];
mbedtls_sha256(message, 16, hash, 0);

unsigned char der_signature[80];
size_t der_len;
mbedtls_ecdsa_write_signature(mbedtls_pk_ec(key), MBEDTLS_MD_SHA256,
hash, sizeof(hash), der_signature, &der_len, mbedtls_ctr_drbg_random, nullptr);

mbedtls_ecdsa_context *ecdsa = mbedtls_pk_ec(key);
memcpy(signature_raw, ecdsa->r.p + (ecdsa->r.n - 32), 32);
memcpy(signature_raw + 32, ecdsa->s.p + (ecdsa->s.n - 32), 32);

mbedtls_pk_write_pubkey_pem(&key, (unsigned char *)public_key_pem, sizeof(public_key_pem));
mbedtls_pk_free(&key);
return true;
}

bool verifySignature() {
uint8_t uid_bytes[16] = {0};
for (int i = 0; i < 8; ++i)
sscanf(uid_hex + 2 * i, "%2hhx", &uid_bytes[i]);

uint8_t message[16];
memcpy(message, uid_bytes, 8);
memcpy(message + 8, block4, 4);
memcpy(message + 12, block5, 4);

uint8_t hash[32];
mbedtls_sha256(message, 16, hash, 0);

uint8_t signature_der[80];
size_t sig_len = 0;
if (!encodeDssSignature(signature_der, &sig_len, signature_raw, signature_raw + 32)) {
printf("Failed to encode signature\n");
return false;
}

mbedtls_pk_context pk;
mbedtls_pk_init(&pk);
if (mbedtls_pk_parse_public_key(&pk, (const unsigned char *)public_key_pem, strlen(public_key_pem) + 1) != 0) {
printf("Failed to parse public key\n");
return false;
}

int ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA256, hash, sizeof(hash), signature_der, sig_len);
mbedtls_pk_free(&pk);

if (ret == 0) {
printf("✅ Signature is VALID\n");
return true;
} else {
printf("❌ Signature is INVALID. Error code: %d\n", ret);
return false;
}
}

int main() {
if (generateSignatureAndKey()) {
verifySignature();
} else {
printf("Key/signature generation failed\n");
}
return 0;
}