File: ip_secure_test.py

package info (click to toggle)
python-xknx 3.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,012 kB
  • sloc: python: 39,710; javascript: 8,556; makefile: 27; sh: 12
file content (92 lines) | stat: -rw-r--r-- 4,297 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""Unit test for IP Secure primitives."""

from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey

from xknx.secure.security_primitives import (
    calculate_message_authentication_code_cbc,
    decrypt_ctr,
    derive_device_authentication_password,
    derive_user_password,
    encrypt_data_ctr,
    generate_ecdh_key_pair,
)


class TestIPSecure:
    """Test class for IP Secure primitives."""

    def test_calculate_message_authentication_code_cbc(self) -> None:
        """Test calculate message authentication code CBC."""
        # SessionResponse from example in KNX specification AN159v06
        assert calculate_message_authentication_code_cbc(
            key=derive_device_authentication_password("trustme"),
            additional_data=bytes.fromhex(
                "06 10 09 52 00 38 00 01 b7 52 be 24 64 59 26 0f"
                "6b 0c 48 01 fb d5 a6 75 99 f8 3b 40 57 b3 ef 1e"
                "79 e4 69 ac 17 23 4e 15"
            ),
        ) == bytes.fromhex("da 3d c6 af 79 89 6a a6 ee 75 73 d6 99 50 c2 83")
        # RoutingIndication from example in KNX specification AN159v06
        assert calculate_message_authentication_code_cbc(
            key=bytes.fromhex("00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"),
            additional_data=bytes.fromhex("06 10 09 50 00 37 00 00"),
            payload=bytes.fromhex("06 10 05 30 00 11 29 00 bc d0 11 59 0a de 01 00 81"),
            block_0=bytes.fromhex("c0 c1 c2 c3 c4 c5 00 fa 12 34 56 78 af fe 00 11"),
        ) == bytes.fromhex("bd 0a 29 4b 95 25 54 b2 35 39 20 4c 22 71 d2 6b")

    def test_encrypt_data_ctr(self) -> None:
        """Test encrypt data with AES-CTR."""
        # RoutingIndication from example in KNX specification AN159v06
        key = bytes.fromhex("00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f")
        counter_0 = bytes.fromhex("c0 c1 c2 c3 c4 c5 00 fa 12 34 56 78 af fe ff 00")
        mac_cbc = bytes.fromhex("bd 0a 29 4b 95 25 54 b2 35 39 20 4c 22 71 d2 6b")
        payload = bytes.fromhex("06 10 05 30 00 11 29 00 bc d0 11 59 0a de 01 00 81")
        encrypted_data, mac = encrypt_data_ctr(
            key=key, counter_0=counter_0, mac_cbc=mac_cbc, payload=payload
        )
        assert encrypted_data == bytes.fromhex(
            "b7 ee 7e 8a 1c 2f 7b ba be c7 75 fd 6e 10 d0 bc 4b"
        )
        assert mac == bytes.fromhex("72 12 a0 3a aa e4 9d a8 56 89 77 4c 1d 2b 4d a4")

    def test_decrypt_ctr(self) -> None:
        """Test decrypt data with AES-CTR."""
        # RoutingIndication from example in KNX specification AN159v06
        key = bytes.fromhex("00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f")
        counter_0 = bytes.fromhex("c0 c1 c2 c3 c4 c5 00 fa 12 34 56 78 af fe ff 00")
        mac = bytes.fromhex("72 12 a0 3a aa e4 9d a8 56 89 77 4c 1d 2b 4d a4")
        payload = bytes.fromhex("b7 ee 7e 8a 1c 2f 7b ba be c7 75 fd 6e 10 d0 bc 4b")
        decrypted_data, mac_tr = decrypt_ctr(
            key=key,
            counter_0=counter_0,
            mac=mac,
            payload=payload,
        )
        assert decrypted_data == bytes.fromhex(
            "06 10 05 30 00 11 29 00 bc d0 11 59 0a de 01 00 81"
        )
        assert mac_tr == bytes.fromhex(
            "bd 0a 29 4b 95 25 54 b2 35 39 20 4c 22 71 d2 6b"
        )

    def test_derive_device_authentication_password(self) -> None:
        """Test derive device authentication password."""
        assert derive_device_authentication_password("trustme") == bytes.fromhex(
            "e1 58 e4 01 20 47 bd 6c c4 1a af bc 5c 04 c1 fc"
        )

    def test_derive_user_password(self) -> None:
        """Test derive user password."""
        assert derive_user_password("secret") == bytes.fromhex(
            "03 fc ed b6 66 60 25 1e c8 1a 1a 71 69 01 69 6a"
        )

    def test_generate_ecdh_key_pair(self) -> None:
        """Test generate ECDH key pair."""
        private_key, public_key_bytes = generate_ecdh_key_pair()
        assert isinstance(private_key, X25519PrivateKey)
        assert isinstance(public_key_bytes, bytes)
        assert len(public_key_bytes) == 32
        private_key_2, public_key_bytes_2 = generate_ecdh_key_pair()
        assert private_key != private_key_2
        assert public_key_bytes != public_key_bytes_2