Tài liệu này mô tả chi tiết toàn bộ quy trình mã hóa của Digital Inheritance. Thiết kế ưu tiên tính minh bạch — mọi quyết định kỹ thuật đều có thể được kiểm chứng độc lập.
1. Tổng quan luồng mã hóa
1
Master password + salt_128bit → Argon2id → AES key 256-bit (key không rời thiết bị)
2
Plaintext payload + nonce_96bit + AAD = user_id → AES-256-GCM → ciphertext + auth_tag_128bit
3
AES key 256-bit → Shamir SSS trên GF(M521) → n SharePacket (mỗi người thừa kế giữ 1)
4
Server lưu: salt · nonce · ciphertext · auth_tag · share_index — không có key, không có password, không có share_value
2. Key Derivation — Argon2id
| Tham số | Giá trị | Ghi chú |
| Algorithm | Argon2id | Hybrid mode, kháng side-channel |
| Memory | 65536 KiB (64 MiB) | OWASP High-Security preset |
| Iterations | 3 | time_cost |
| Parallelism | 1 | lanes |
| Salt | 128 bits (16 bytes) | os.urandom(16), lưu server |
| Output | 256 bits (32 bytes) | Dùng làm AES key trực tiếp |
| Library | argon2-cffi ≥ 23.1 | Python binding của argon2 C ref |
# Pseudocode — key derivation
salt = os.urandom(16)
aes_key = argon2.hash(
password = master_password.encode("utf-8"),
salt = salt,
time_cost = 3,
memory_cost= 65536,
parallelism= 1,
hash_len = 32,
type = Argon2Type.ID,
)
3. Symmetric Encryption — AES-256-GCM
| Tham số | Giá trị | Ghi chú |
| Algorithm | AES-256-GCM | NIST SP 800-38D |
| Key size | 256 bits | Output từ Argon2id |
| Nonce (IV) | 96 bits (12 bytes) | os.urandom(12), lưu server |
| Auth tag | 128 bits (16 bytes) | Lưu kèm ciphertext |
| AAD | user_id (bytes) | Chống relocation attack |
| Library | cryptography ≥ 42.0 | PyCA cryptography, FIPS-140 backend |
# Pseudocode — encryption
nonce = os.urandom(12)
aesgcm = AESGCM(aes_key)
ct = aesgcm.encrypt(nonce, plaintext, aad=user_id.encode())
# ct = ciphertext || auth_tag (16 bytes cuối)
# Decryption (xác thực trước khi giải mã)
plaintext = aesgcm.decrypt(nonce, ct, aad=user_id.encode())
# Raise InvalidTag nếu auth_tag không khớp
4. Secret Sharing — Shamir SSS
| Tham số | Giá trị | Ghi chú |
| Trường | GF(M521) | Prime p = 2^521 − 1 (Mersenne prime) |
| Secret | AES key 256-bit | Nhúng vào GF(M521) như một số nguyên |
| Tổng mảnh (n) | 1 – 10 | Do người dùng cấu hình |
| Ngưỡng (k) | 1 – n | Cần đúng k mảnh để khôi phục |
| Khôi phục | Lagrange interpolation | Tính f(0) từ k điểm |
| Bảo mật | Information-theoretic | k-1 mảnh = zero information về secret |
# Pseudocode — share generation
M521 = 2**521 - 1
secret = int.from_bytes(aes_key, "big")
# Polynomial: f(x) = secret + a1*x + ... + a(k-1)*x^(k-1) mod M521
coeffs = [secret] + [randbelow(M521) for _ in range(k-1)]
shares = [(i, poly_eval(coeffs, i, M521)) for i in range(1, n+1)]
# share_value không bao giờ lưu trên server
5. Ghi chú triển khai
- Tất cả random generation dùng
secrets module (CSPRNG) hoặc os.urandom()
- Không dùng
random module tiêu chuẩn cho bất kỳ giá trị bảo mật nào
- AES key và share_value chỉ tồn tại trong RAM, không được ghi vào disk hay log
- Constant-time comparison cho auth_tag verification (do thư viện
cryptography xử lý)
- Toàn bộ crypto được thực hiện phía server — kế hoạch tương lai: migrate sang client-side WASM
6. Tài liệu tham khảo
- NIST SP 800-38D — Recommendation for Block Cipher Modes: GCM
- RFC 9106 — Argon2 Memory-Hard Function
- Shamir, A. (1979) — "How to Share a Secret", Communications of the ACM
- OWASP Password Storage Cheat Sheet