AIWI
USB Hardware Security Token (AES-256)
คู่มือนักพัฒนา (Developer Manual)
การเชื่อมต่อและพัฒนาโปรแกรมสำหรับ AIWI Hardlock
Document Version 1.0
(สงวนลิขสิทธิ์)

สารบัญ

  1. บทนำ — AIWI คืออะไร
  2. ความต้องการของระบบ
    1. Hardware
    2. Software / Library
  3. โครงสร้างหน่วยความจำ EEPROM
  4. ระบบความปลอดภัย
    1. Anti-crack Counter
    2. Suicide Counter
    3. สัญญาณ LED
  5. Protocol การสื่อสาร
    1. HID Feature Report
    2. รูปแบบคำสั่ง
    3. การเข้ารหัส AES-256 สำหรับคำสั่ง
  6. การ Setup AIWI ครั้งแรก (Initial / Provisioning)
  7. การอ่านและเขียนข้อมูล
    1. อ่านข้อมูล (RDBL)
    2. เขียนข้อมูล (WRBL)
    3. Parameter พิเศษ
  8. Command Reference
  9. ตัวอย่างโค้ด
  10. การจัดการข้อผิดพลาด
  11. ข้อควรระวัง

1. บทนำ — AIWI คืออะไร

AIWI คือ USB Hardware Security Token (Hardlock) ขนาดเล็กกะทัดรัด ราคาประหยัด รองรับได้หลาย OS เช่น mac, Win, Linux ฯลฯ และหลายสถาปัตยกรรม เช่น x86, x64, ARM ฯลฯ โดยใช้การสื่อสารแบบ HID ทำให้ไม่ต้องใช้ driver พิเศษใดๆ ซึ่งเหมาะมากหากคุณต้องการป้องกันระดับพื้นฐาน หรืองานทั่วไป

AIWI ทำหน้าที่เป็น "กล่องเก็บความลับ" ขนาดพกพา — นักพัฒนาสามารถเก็บข้อมูลที่มีความสำคัญ (รหัสผ่าน, key, ข้อมูลใบอนุญาต, ส่วน Header files ฯลฯ) ลงใน EEPROM ภายในของ AIWI ได้ ซึ่งนอกจากจะเข้ารหัสด้วย AES-256 แล้ว ก็ยังมีความสามารถป้องกันการโจมตีจากหลายช่องทาง และระบบทำลายตัวเองอัตโนมัติ หรือตามกำหนดได้อีกด้วย

คุณสมบัติสำคัญ

สำหรับนักพัฒนา คู่มือนี้อธิบาย protocol การสื่อสาร, โครงสร้างหน่วยความจำ, และวิธีพัฒนาโปรแกรม host-side เพื่อเชื่อมต่อกับ AIWI บน platform ต่างๆ

2. ความต้องการของระบบ

2.1 Hardware

รายการรายละเอียด
AIWI deviceAIWI Hardlock v1.04 ขึ้นไป
USB portUSB 1.1 หรือสูงกว่า (รองรับ Low Speed HID)
USB Hubรองรับการเสียบผ่าน Hub (v1.05)

2.2 Software / Library

AIWI ใช้ HID protocol มาตรฐาน จึงรองรับ library เชื่อมต่อ HID ได้ทุกตัว:

ภาษาLibraryวิธีติดตั้ง
C / C++ hidapi sudo apt install libhidapi-dev
Python hid (hidapi-python) pip install hid
Go go-hid go get github.com/sstallion/go-hid
Java hid4java Maven: org.hid4java:hid4java:0.8.0

สำหรับการเข้ารหัส AES-256:

ภาษาLibrary / Module
Pythonpip install pycryptodomefrom Crypto.Cipher import AES
GoStandard library: crypto/aes
JavaStandard library: javax.crypto
COpenSSL หรือ mbedTLS
อื่นๆโปรดติดต่อทีมงานเพื่อขอความช่วยเหลือ

3. โครงสร้างหน่วยความจำ EEPROM

AIWI มีหน่วยความจำ 512 bytes แบ่งเป็น 4 Segment (A–D) แต่ละ Segment ขนาด 128 bytes แต่ละ Segment แบ่งได้อีก 4 block โดยมีขนาด block 32 bytes (โดยพื้นที่ User จะอยู่ที่ Segment B-D เท่านั้น):

0x000–0x01F
(Block 00)
Segment A1 — ข้อมูล System: LockBit, Firmware version, Owner ID, Anti-crack counter, Suicide counter (พื้นที่สงวนต้องเรียกผ่านคำสั่งเท่านั้น)
0x020–0x03F
(Block 01)
Segment A2 — AES-256 Owner Key (32 bytes) — เขียนได้ครั้งเดียวตอน setup
0x040–0x05F
(Block 02)
Segment A3 — USB Descriptor: VID/PID (4 bytes), Vendor Name, Device Name, Serial Number — อ่านตอนเสียบ USB
0x060–0x07F
(Block 03)
Segment A4 — สงวนไว้สำหรับ firmware (ไม่เปิดให้ user เข้าถึง)
0x080–0x0FF
(Block 04–07)
Segment B — User Data (128 bytes = 4 blocks × 32 bytes)
0x100–0x17F
(Block 08–11)
Segment C — User Data (128 bytes = 4 blocks × 32 bytes)
0x180–0x1FF
(Block 12–15)
Segment D — User Data (128 bytes = 4 blocks × 32 bytes)
หมายเหตุ: ขนาดพื้นที่เก็บ (User Data) ขึ้นอยู่กับรุ่นผลิตภัณฑ์

รูปแบบ AES Owner Key ใน Segment A2 (Block 01)

ข้อมูลทั้ง block นี้ คือรหัส AES Owner Key ที่กำหนดโดยผู้ใช้ ยาว 32 bytes (สั่งเขียนได้เฉพาะครั้งแรกตอน initial AIWI เท่านั้น และเปลี่ยนแปลงไม่ได้อีก)

รูปแบบข้อมูล USB Descriptor ใน Segment A3 (Block 02)

ข้อมูล 28 bytes แรกของ block 02 จะมีรูปแบบดังนี้:

[N_vendor][vendor_char_1]...[vendor_char_N]
[N_device][device_char_1]...[device_char_N]
[N_serial][serial_char_1]...[serial_char_N]

โดยที่ byte แรก (N) ของแต่ละกลุ่มคือ ความยาว ของ string ที่ตามมา ตัวอย่าง เช่น:

// Vendor Name = "DuinoThumb" (10 chars)
[0x0A]['D']['u']['i']['n']['o']['T']['h']['u']['m']['b']
// Device Name = "AIWI-PRO" (8 chars)
[0x08]['A']['I']['W']['I']['-']['P']['R']['O']
// Serial = "91D" (3 chars)
[0x03]['9']['1']['D']

พื้นที่ส่วนนี้สั่งเขียนได้เฉพาะครั้งแรก (หลังจากกำหนดรหัส AES) และเขียนได้ครั้งเดียวเท่านั้น กำหนดขนาดสูงสุดไม่เกิน 28 Bytes (ทั้ง Vender_name, Device_name, Serial ใช้พื้นที่ร่วมกัน)

4. ระบบความปลอดภัย

4.1 Anti-crack Counter

AIWI มีระบบป้องกันการ brute-force ด้วยการนับจำนวนครั้งที่ส่งคำสั่งผิด:

เงื่อนไขการทำงานถ้ามีการพยายามสุ่มรหัสเข้ามา ทำให้ AIWI ไม่สามารถถอดรหัสได้ หรือรหัสถูกต้องแต่พยายามอ่าน/เขียนข้อมูลในพื้นที่หวงห้าม (block 01–03 หรือมากกว่า 15) จะทำให้ค่า counter ลดลง เมื่อถึง 0 AIWI จะหยุดทำงาน ต้องส่งกลับมาให้ Owner Reset เท่านั้น

4.2 Suicide Counter

AIWI มีกลไกจำกัดอายุการใช้งานและ self-destruct (ทำลายตัวเอง):

เงื่อนไขการทำงานลดค่าอัตโนมัติทุกครั้งที่มีการอ่านข้อมูลจาก AIWI สำเร็จ — ใช้กำหนดรอบการทำงานของโปรแกรม เช่น สำหรับโปรแกรมทดลองหรือกำหนดช่วงอายุการใช้งาน เมื่อ counter ถึง 0 AIWI จะลบข้อมูลทั้งหมดและหยุดทำงาน ต้องส่งกลับมาให้ Owner Reset เท่านั้น
คำเตือน: Suicide Counter ไม่สามารถยกเลิกได้เมื่อถึง 0 เมื่อ Suicide Counter หมด ข้อมูลใน user blocks ทั้งหมดจะถูกลบและไม่สามารถกู้คืนได้ ควรตั้ง counter ให้เหมาะสมกับ use case และ reset ก่อนหมดเสมอ

4.3 สัญญาณ LED

รูปแบบ LEDความหมาย
กระพริบเร็ว (~250ms on/off)ยังไม่มี owner / ยังไม่ได้ setup
กระพริบปกติ (~250ms on, 3s off)Setup เสร็จแล้ว พร้อมใช้งาน
กระพริบช้ามาก (3s on/3s off)Device locked หรือ Suicide ทำงาน

5. Protocol การสื่อสาร

5.1 HID Feature Report

AIWI ทำงานเป็น USB HID device ใช้ Feature Report สำหรับทั้งการส่งและรับข้อมูล:

OperationHID Requesthidapi functionขนาด
ส่งข้อมูล/คำสั่งSET_REPORThid_send_feature_report()33 bytes
รับข้อมูลGET_REPORThid_get_feature_report()33 bytes
Report ID hidapi กำหนดให้ byte แรก ของ buffer ที่ส่งต้องเป็น Report ID สำหรับ AIWI ใช้ Report ID = 0x00 เสมอ ดังนั้น buffer จริงมีขนาด 33 bytes: [0x00] + [data 32 bytes]

5.2 รูปแบบคำสั่ง (Command Format)

แต่ละ "packet" ที่ส่งไป AIWI มีขนาด 32 bytes เสมอ โดยมี 2 ประเภท:

Command Packet (ส่งคำสั่ง)

Byteเนื้อหาหมายเหตุ
0–3Command string (4 chars)เช่น 'R','D','B','L'
4–5Parameter (2 digits ASCII)เช่น '0','5' = block 05
6–31Padding / Junkค่าอะไรก็ได้ (เติมให้ครบ 32 bytes)

Data Packet (ส่งข้อมูล)

Byteเนื้อหา
0–31ข้อมูล 32 bytes ที่ต้องการเขียน

5.3 การเข้ารหัส AES-256 สำหรับคำสั่ง

เมื่อ AIWI ถูกลงทะเบียนไปแล้ว Command Packet 32 bytes ต้องผ่านการเข้ารหัส AES-256 ECB ก่อนส่งทุกครั้ง:

  1. สร้าง plaintext 16 bytes: command 4 chars + parameter 2 chars + padding 10 bytes (0x00)
  2. เข้ารหัส 16 bytes นั้นด้วย AES-256 ECB โดยใช้ Owner Key 32 bytes → ได้ ciphertext 16 bytes
  3. นำ ciphertext 16 bytes + padding 16 bytes (0x00) ต่อกัน → ได้ Command Packet 32 bytes
  4. เติม Report ID 0x00 ด้านหน้า → รวมส่ง 33 bytes
// ตัวอย่างการสร้าง AES-encrypted command สำหรับ "RDBL05"
plaintext = b"RDBL05" + b"\x00" * 10      # 16 bytes
encrypted_16 = AES256_ECB_Encrypt(plaintext, owner_key_32bytes)
command_packet = encrypted_16 + b"\x00" * 16  # 32 bytes
send_buffer = b"\x00" + command_packet          # 33 bytes (เติม Report ID)
หมายเหตุ : ส่วนของ Data Packet ไม่จำเป็นต้องเข้ารหัส เฉพาะ Command Packet (ชุดแรก) เท่านั้นที่ต้องเข้ารหัส แต่ Data Packet (ชุดที่สอง สำหรับ WRBL) สามารถส่งเป็น plaintext ก็ได้ หรือจะเข้ารหัสอะไรมาก็ได้ เป็นอิสระของผู้พัฒนา

6. การ Setup AIWI ครั้งแรก (Initial / Provisioning)

AIWI ที่ได้รับจากโรงงานจะอยู่ในสถานะ FACTORY_LOCKED ต้องทำการ setup ก่อนจึงจะใช้งาน read/write ได้:

// ขั้นตอนที่ 1: เขียน AES Owner Key ลง Segment A2 (Block 01)
Host →→→ AIWI : SET_REPORT [0x00]["WRBL01" + padding] (33 bytes) ← คำสั่ง WRBL block 01
Host →→→ AIWI : SET_REPORT [0x00][AES Key 32 bytes] ← AES Owner Key

// ขั้นตอนที่ 2: เขียน USB Descriptor ลง Segment A3 (Block 02) → trigger OWNER_LOCKED
Host →→→ AIWI : SET_REPORT [0x00]["WRBL02" + padding] (33 bytes) ← คำสั่ง WRBL block 02
Host →→→ AIWI : SET_REPORT [0x00][USB Descriptor 32 bytes] ← VID/PID + Strings

// หลังจากนี้ AIWI = OWNER_LOCKED ทุกคำสั่งต้องเข้ารหัสด้วย Owner Key

รูปแบบ Command Packet สำหรับ WRBL01

// Command Packet 32 bytes (ส่งแบบ plaintext เพราะยังอยู่ในสถานะ FACTORY_LOCKED และ AIWI ไม่มีตัวถอดรหัส)
buf[0..3]  = 'W','R','B','L'     // command
buf[4..5]  = '0','1'             // parameter = block 01
buf[6..31] = 0x00 * 26           // padding

รูปแบบ USB Descriptor Data Packet (สำหรับ WRBL02)

// Data Packet 32 bytes — ข้อมูล USB Descriptor
buf[0]     = VID_LOW             // Vendor ID low byte  เช่น 0x20
buf[1]     = VID_HIGH            // Vendor ID high byte เช่น 0xA0  (= 0x20A0)
buf[2]     = PID_LOW             // Product ID low byte
buf[3]     = PID_HIGH            // Product ID high byte
// ต่อจากนี้เป็น USB Strings ในรูปแบบ [length][chars...]
buf[4]     = N_vendor            // ความยาว Vendor Name
buf[5..]   = vendor_name_chars
...        = N_device
...        = device_name_chars
...        = N_serial
...        = serial_chars
ข้อควรระวัง: ลำดับขั้นตอนสำคัญมาก

7. การอ่านและเขียนข้อมูล

7.1 อ่านข้อมูล (RDBL)

// อ่านข้อมูลจาก Block xx (xx = 04–15)
Host →→→ AIWI : SET_REPORT [0x00][AES_Encrypt("RDBLxx" + pad)] (33 bytes)
Host ←←← AIWI : GET_REPORT [0x00][data 32 bytes]

ขั้นตอนใน code:

  1. สร้าง Command Packet: เข้ารหัส "RDBL" + "xx" ด้วย AES-256 ECB + Owner Key
  2. เรียก hid_send_feature_report() ส่ง Command Packet 33 bytes
  3. เรียก hid_get_feature_report() รับข้อมูล 33 bytes (ตัด byte แรก = Report ID ออก → ได้ data 32 bytes)

7.2 เขียนข้อมูล (WRBL)

// เขียนข้อมูลลง Block xx (xx = 04–15)
Host →→→ AIWI : SET_REPORT [0x00][AES_Encrypt("WRBLxx" + pad)] (33 bytes) ← Phase 1: คำสั่ง
Host →→→ AIWI : SET_REPORT [0x00][data 32 bytes] ← Phase 2: ข้อมูล

ขั้นตอนใน code:

  1. สร้าง Command Packet: เข้ารหัส "WRBL" + "xx" ด้วย AES-256 ECB + Owner Key
  2. เรียก hid_send_feature_report() ส่ง Command Packet 33 bytes
  3. เรียก hid_send_feature_report() อีกครั้งด้วย Data Packet 33 bytes (data จริง 32 bytes)

7.3 Parameter พิเศษ

CommandParameterผลลัพธ์
RDBL98อ่าน Security Counter (5 bytes: [anti_crack] [suicide 4 bytes])
RDBL99อ่าน Owner ID (8 bytes)
WRBL98รีเซ็ต Anti-crack และ Suicide Counter (5 bytes data)
รูปแบบข้อมูลสำหรับ WRBL98 (รีเซ็ต counter)
data[0]    = ค่า Anti-crack ใหม่ (เช่น 250)
data[1..4] = ค่า Suicide Counter ใหม่ (32-bit, big-endian)
             เช่น 250000 = 0x00 0x03 0xD0 0x90

8. Command Reference

CommandParameter (xx)คำอธิบาย
WRBL 01 เขียน AES Owner Key 32 bytes ลง Segment A2
WRBL 02 เขียน USB Descriptor ลง Segment A3 → (Registered!)
WRBL 04–15 เขียนข้อมูล 32 bytes ลง user block ที่ระบุ (ต้องเข้ารหัสคำสั่ง)
WRBL 98 รีเซ็ต Anti-crack / Suicide counter (ต้องเข้ารหัสคำสั่ง)
RDBL 04–15 อ่านข้อมูล 32 bytes จาก user block ที่ระบุ (ต้องเข้ารหัสคำสั่ง)
RDBL 98 อ่านค่า Security counter 5 bytes (anti_crack + suicide)
RDBL 99 อ่าน Owner ID 8 bytes
Parameter ที่ไม่ถูกต้องถือเป็นการบุกรุก การส่ง block number ที่ไม่อยู่ในช่วงที่อนุญาต หรือการส่ง command ที่ถอดรหัสไม่สำเร็จ จะถูกนับเป็น Anti-crack penalty ทันที

9. ตัวอย่างโค้ด

ดูไฟล์ตัวอย่างในโฟลเดอร์ client-demo/:

ไฟล์ภาษาPlatform
aiwi-linux-embedded.cCLinux (ARM/x86)
aiwi-python.pyPython 3Linux / Windows / macOS
aiwi-golang.goGoLinux / Windows / macOS
aiwi-java.javaJavaLinux / Windows / macOS

ตัวอย่าง Python: การสร้าง AES Command และส่ง Read Request

from Crypto.Cipher import AES
import hid

AIWI_VID = 0x20A0
AIWI_PID = 0x413A

def make_encrypted_cmd(command: str, block_num: int, owner_key: bytes) -> list:
    """สร้าง AES-256 encrypted command packet (32 bytes)"""
    plaintext = command.encode() + f"{block_num:02d}".encode()
    plaintext = plaintext + b"\x00" * (16 - len(plaintext))  # pad to 16 bytes
    cipher = AES.new(owner_key, AES.MODE_ECB)
    encrypted = cipher.encrypt(plaintext)                  # AES-256 ECB
    return list(encrypted) + [0x00] * 16                  # 32 bytes

# เชื่อมต่อ
dev = hid.device()
dev.open(AIWI_VID, AIWI_PID)

owner_key = b"your_32_byte_owner_key_here....."  # 32 bytes

# อ่าน Block 05
cmd = make_encrypted_cmd("RDBL", 5, owner_key)
dev.send_feature_report([0x00] + cmd)
data = dev.get_feature_report(0x00, 33)[1:]   # ตัด Report ID
print(data)

# เขียน Block 04
cmd = make_encrypted_cmd("WRBL", 4, owner_key)
payload = list(b"MySecretData!!") + [0] * 18  # 32 bytes
dev.send_feature_report([0x00] + cmd)
dev.send_feature_report([0x00] + payload)

dev.close()

ตัวอย่าง Go: การเข้ารหัสด้วย crypto/aes

import (
    "crypto/aes"
    "fmt"
    hid "github.com/sstallion/go-hid"
)

func makeEncryptedCmd(command string, blockNum int, ownerKey []byte) []byte {
    plaintext := make([]byte, 16)
    copy(plaintext, []byte(fmt.Sprintf("%s%02d", command, blockNum)))
    block, _ := aes.NewCipher(ownerKey)
    encrypted := make([]byte, 16)
    block.Encrypt(encrypted, plaintext)          // AES-256 ECB
    result := append(encrypted, make([]byte, 16)...)  // 32 bytes
    return result
}

ตัวอย่าง Java: การเข้ารหัสด้วย javax.crypto

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;

static byte[] makeEncryptedCmd(String command, int blockNum, byte[] ownerKey) throws Exception {
    byte[] plaintext = new byte[16];
    byte[] cmdBytes = (command + String.format("%02d", blockNum)).getBytes();
    System.arraycopy(cmdBytes, 0, plaintext, 0, cmdBytes.length);  // pad with 0x00
    SecretKeySpec keySpec = new SecretKeySpec(ownerKey, "AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, keySpec);
    byte[] encrypted = cipher.doFinal(plaintext);                  // AES-256 ECB
    byte[] result = new byte[32];
    System.arraycopy(encrypted, 0, result, 0, 16);                // 32 bytes
    return result;
}

10. การจัดการข้อผิดพลาด

สถานการณ์สาเหตุวิธีแก้ไข
เปิด device ไม่ได้ ไม่ได้เสียบ AIWI / VID:PID ผิด / ไม่มีสิทธิ์ ตรวจสอบ USB connection, ตรวจสอบ VID/PID, บน Linux ต้องเพิ่ม udev rule
ส่งคำสั่งแล้วไม่ได้ข้อมูลกลับ / ได้ 0x00 ทั้งหมด AES key ผิด / command ผิดรูปแบบ / anti-crack ถูกเรียก ตรวจสอบ owner key, ตรวจสอบ block number ว่าอยู่ใน range
Device หยุดตอบสนอง Anti-crack counter หมด ต้อง reset counter ด้วย WRBL98 ด้วย key ที่ถูกต้อง
ข้อมูลหายหมด Suicide counter หมด ไม่สามารถกู้คืนได้ ต้อง reflash และ setup ใหม่
ปัญหา enumerate บน USB Hub เป็นข้อกำหนดห้ามใช้ AIWI v1.04 ผ่าน USB Hub สามารถใช้ได้ใน AIWI v1.05

udev Rule สำหรับ Linux (ไม่ต้องใช้ sudo)

สร้างไฟล์ /etc/udev/rules.d/99-aiwi.rules:

SUBSYSTEM=="usb", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="413a", MODE="0666"
KERNEL=="hidraw*", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="413a", MODE="0666"
sudo udevadm control --reload-rules && sudo udevadm trigger

11. ข้อควรระวัง

Owner Key: เก็บรักษาให้ดีที่สุด Owner Key 32 bytes ที่ตั้งตอน setup คือสิ่งเดียวที่สามารถสั่ง AIWI ได้ ถ้า key หายจะไม่สามารถ read/write ข้อมูลได้เลย และต้องส่ง device มา reflash ที่โรงงานเท่านั้น
ห้ามทดสอบด้วย wrong key ซ้ำๆ ทุกครั้งที่ส่งคำสั่งที่ถอดรหัสไม่ถูก = Anti-crack penalty 1 ครั้ง ถ้า counter หมดจะต้องมี key ถูกต้องเพื่อ reset เท่านั้น
ตรวจสอบ Suicide Counter สม่ำเสมอ ถ้า device ถูกใช้งานมาก ควรอ่าน counter ด้วย RDBL98 เป็นประจำ และ reset ด้วย WRBL98 ก่อนที่จะหมดเพื่อป้องกันการ self-destruct
AES Mode: ECB เท่านั้น AIWI ใช้ AES-256 แบบ ECB (Electronic Code Book) ซึ่งเป็นแบบพื้นฐาน ไม่ได้ใช้ IV หรือ CBC ดังนั้นต้องระบุ mode เป็น AES/ECB/NoPadding หรือ AES.MODE_ECB เสมอเมื่อสร้าง cipher object
VID/PID มาตรฐาน AIWI อนุญาตให้คุณตั้งค่า VID/PID ได้(หากคุณเคยมีการขออนุญาต) ส่วนค่า default คือ VID=0x20A0, PID=0x413A

AIWI Developer Manual — Document v1.0  |  สงวนลิขสิทธิ์