"""Tests for HWID activation token -- Sprint 8.""" from __future__ import annotations import hashlib import hmac import os import pytest from arautopilot.core.hwid import ( STUB_SECRET_KEY, TOKEN_BYTES, format_hwid, generate_token, hwid_from_mac_words, verify_token, ) SAMPLE_HWID = "aabbccddeeff" # 12-char lower-case hex class TestHwidFromMacWords: def test_known_bytes(self): # mac01=0xAABB, mac23=0xCCDD, mac45=0xEEFF → "aabbccddeeff" result = hwid_from_mac_words(0xAABB, 0xCCDD, 0xEEFF) assert result == "aabbccddeeff" def test_zero_mac(self): result = hwid_from_mac_words(0, 0, 0) assert result == "000000000000" def test_all_ones(self): result = hwid_from_mac_words(0xFFFF, 0xFFFF, 0xFFFF) assert result == "ffffffffffff" def test_returns_lowercase(self): result = hwid_from_mac_words(0xAABB, 0xCCDD, 0xEEFF) assert result == result.lower() def test_result_is_12_chars(self): result = hwid_from_mac_words(0x0102, 0x0304, 0x0506) assert len(result) == 12 def test_byte_order(self): # 0x1234 → bytes [0x12, 0x34] result = hwid_from_mac_words(0x1234, 0x0000, 0x0000) assert result[:4] == "1234" class TestGenerateToken: def test_returns_32_hex_chars(self): token = generate_token(SAMPLE_HWID) assert len(token) == TOKEN_BYTES * 2 # 32 assert all(c in "0123456789abcdef" for c in token.lower()) def test_deterministic_with_stub_key(self, monkeypatch): monkeypatch.delenv("AR_ACTIVATION_KEY", raising=False) t1 = generate_token(SAMPLE_HWID) t2 = generate_token(SAMPLE_HWID) assert t1 == t2 def test_different_hwid_different_token(self, monkeypatch): monkeypatch.delenv("AR_ACTIVATION_KEY", raising=False) t1 = generate_token("aabbccddeeff") t2 = generate_token("112233445566") assert t1 != t2 def test_uses_env_key_when_set(self, monkeypatch): monkeypatch.setenv("AR_ACTIVATION_KEY", "test-production-key") prod_token = generate_token(SAMPLE_HWID) monkeypatch.delenv("AR_ACTIVATION_KEY") stub_token = generate_token(SAMPLE_HWID) assert prod_token != stub_token def test_case_insensitive_hwid(self, monkeypatch): monkeypatch.delenv("AR_ACTIVATION_KEY", raising=False) t_lower = generate_token("aabbccddeeff") t_upper = generate_token("AABBCCDDEEFF") assert t_lower == t_upper def test_matches_manual_hmac(self, monkeypatch): monkeypatch.delenv("AR_ACTIVATION_KEY", raising=False) expected = hmac.new( STUB_SECRET_KEY, SAMPLE_HWID.encode(), hashlib.sha256 ).hexdigest()[:TOKEN_BYTES * 2] assert generate_token(SAMPLE_HWID) == expected class TestVerifyToken: def test_valid_token_returns_true(self, monkeypatch): monkeypatch.delenv("AR_ACTIVATION_KEY", raising=False) token = generate_token(SAMPLE_HWID) assert verify_token(SAMPLE_HWID, token) is True def test_wrong_token_returns_false(self, monkeypatch): monkeypatch.delenv("AR_ACTIVATION_KEY", raising=False) assert verify_token(SAMPLE_HWID, "a" * 32) is False def test_wrong_hwid_returns_false(self, monkeypatch): monkeypatch.delenv("AR_ACTIVATION_KEY", raising=False) token = generate_token(SAMPLE_HWID) assert verify_token("000000000000", token) is False def test_case_insensitive_token_comparison(self, monkeypatch): monkeypatch.delenv("AR_ACTIVATION_KEY", raising=False) token = generate_token(SAMPLE_HWID) assert verify_token(SAMPLE_HWID, token.upper()) is True def test_constant_time_compare(self, monkeypatch): """verify_token must use hmac.compare_digest (checked by smoke-test, not timing).""" monkeypatch.delenv("AR_ACTIVATION_KEY", raising=False) token = generate_token(SAMPLE_HWID) # Calling with correct and incorrect tokens both return without exception assert verify_token(SAMPLE_HWID, token) is True assert verify_token(SAMPLE_HWID, "x" * 32) is False class TestFormatHwid: def test_formats_correctly(self): result = format_hwid("aabbccddeeff") assert result == "AA:BB:CC:DD:EE:FF" def test_uppercase_output(self): result = format_hwid("aabbccddeeff") assert result == result.upper().replace("X", ":") # colons preserved assert result == "AA:BB:CC:DD:EE:FF" def test_colon_separated_6_groups(self): result = format_hwid("112233445566") parts = result.split(":") assert len(parts) == 6 assert all(len(p) == 2 for p in parts) def test_invalid_length_raises(self): with pytest.raises(ValueError, match="12 hex chars"): format_hwid("aabb") def test_all_zeros(self): assert format_hwid("000000000000") == "00:00:00:00:00:00" def test_all_ff(self): assert format_hwid("ffffffffffff") == "FF:FF:FF:FF:FF:FF"