xref: /aosp_15_r20/external/openthread/tests/scripts/thread-cert/net_crypto.py (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
1*cfb92d14SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*cfb92d14SAndroid Build Coastguard Worker#
3*cfb92d14SAndroid Build Coastguard Worker#  Copyright (c) 2016, The OpenThread Authors.
4*cfb92d14SAndroid Build Coastguard Worker#  All rights reserved.
5*cfb92d14SAndroid Build Coastguard Worker#
6*cfb92d14SAndroid Build Coastguard Worker#  Redistribution and use in source and binary forms, with or without
7*cfb92d14SAndroid Build Coastguard Worker#  modification, are permitted provided that the following conditions are met:
8*cfb92d14SAndroid Build Coastguard Worker#  1. Redistributions of source code must retain the above copyright
9*cfb92d14SAndroid Build Coastguard Worker#     notice, this list of conditions and the following disclaimer.
10*cfb92d14SAndroid Build Coastguard Worker#  2. Redistributions in binary form must reproduce the above copyright
11*cfb92d14SAndroid Build Coastguard Worker#     notice, this list of conditions and the following disclaimer in the
12*cfb92d14SAndroid Build Coastguard Worker#     documentation and/or other materials provided with the distribution.
13*cfb92d14SAndroid Build Coastguard Worker#  3. Neither the name of the copyright holder nor the
14*cfb92d14SAndroid Build Coastguard Worker#     names of its contributors may be used to endorse or promote products
15*cfb92d14SAndroid Build Coastguard Worker#     derived from this software without specific prior written permission.
16*cfb92d14SAndroid Build Coastguard Worker#
17*cfb92d14SAndroid Build Coastguard Worker#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18*cfb92d14SAndroid Build Coastguard Worker#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*cfb92d14SAndroid Build Coastguard Worker#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*cfb92d14SAndroid Build Coastguard Worker#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21*cfb92d14SAndroid Build Coastguard Worker#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22*cfb92d14SAndroid Build Coastguard Worker#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23*cfb92d14SAndroid Build Coastguard Worker#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*cfb92d14SAndroid Build Coastguard Worker#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25*cfb92d14SAndroid Build Coastguard Worker#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26*cfb92d14SAndroid Build Coastguard Worker#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27*cfb92d14SAndroid Build Coastguard Worker#  POSSIBILITY OF SUCH DAMAGE.
28*cfb92d14SAndroid Build Coastguard Worker#
29*cfb92d14SAndroid Build Coastguard Worker
30*cfb92d14SAndroid Build Coastguard Workerimport hmac
31*cfb92d14SAndroid Build Coastguard Workerimport hashlib
32*cfb92d14SAndroid Build Coastguard Workerimport struct
33*cfb92d14SAndroid Build Coastguard Worker
34*cfb92d14SAndroid Build Coastguard Workerfrom binascii import hexlify
35*cfb92d14SAndroid Build Coastguard Worker
36*cfb92d14SAndroid Build Coastguard Worker
37*cfb92d14SAndroid Build Coastguard Workerclass CryptoEngine:
38*cfb92d14SAndroid Build Coastguard Worker    """ Class responsible for encryption and decryption of data. """
39*cfb92d14SAndroid Build Coastguard Worker
40*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, crypto_material_creator):
41*cfb92d14SAndroid Build Coastguard Worker        """
42*cfb92d14SAndroid Build Coastguard Worker        Args:
43*cfb92d14SAndroid Build Coastguard Worker            network_key (bytearray)
44*cfb92d14SAndroid Build Coastguard Worker
45*cfb92d14SAndroid Build Coastguard Worker        """
46*cfb92d14SAndroid Build Coastguard Worker        self._crypto_material_creator = crypto_material_creator
47*cfb92d14SAndroid Build Coastguard Worker
48*cfb92d14SAndroid Build Coastguard Worker    @property
49*cfb92d14SAndroid Build Coastguard Worker    def mic_length(self):
50*cfb92d14SAndroid Build Coastguard Worker        return self._crypto_material_creator.mic_length
51*cfb92d14SAndroid Build Coastguard Worker
52*cfb92d14SAndroid Build Coastguard Worker    def encrypt(self, data, message_info):
53*cfb92d14SAndroid Build Coastguard Worker        """ Encrypt message.
54*cfb92d14SAndroid Build Coastguard Worker
55*cfb92d14SAndroid Build Coastguard Worker        Args:
56*cfb92d14SAndroid Build Coastguard Worker            data (bytearray)
57*cfb92d14SAndroid Build Coastguard Worker            message_info (MessageInfo)
58*cfb92d14SAndroid Build Coastguard Worker
59*cfb92d14SAndroid Build Coastguard Worker        Returns:
60*cfb92d14SAndroid Build Coastguard Worker            tuple: Encrypted message (bytearray), MIC (bytearray)
61*cfb92d14SAndroid Build Coastguard Worker
62*cfb92d14SAndroid Build Coastguard Worker        """
63*cfb92d14SAndroid Build Coastguard Worker        key, nonce, auth_data = self._crypto_material_creator.create_key_and_nonce_and_authenticated_data(message_info)
64*cfb92d14SAndroid Build Coastguard Worker
65*cfb92d14SAndroid Build Coastguard Worker        from Crypto.Cipher import AES
66*cfb92d14SAndroid Build Coastguard Worker
67*cfb92d14SAndroid Build Coastguard Worker        cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=self.mic_length)
68*cfb92d14SAndroid Build Coastguard Worker        cipher.update(auth_data)
69*cfb92d14SAndroid Build Coastguard Worker
70*cfb92d14SAndroid Build Coastguard Worker        return cipher.encrypt_and_digest(bytes(data))
71*cfb92d14SAndroid Build Coastguard Worker
72*cfb92d14SAndroid Build Coastguard Worker    def decrypt(self, enc_data, mic, message_info):
73*cfb92d14SAndroid Build Coastguard Worker        """ Decrypt MLE message.
74*cfb92d14SAndroid Build Coastguard Worker
75*cfb92d14SAndroid Build Coastguard Worker        Args:
76*cfb92d14SAndroid Build Coastguard Worker            enc_data (bytearray)
77*cfb92d14SAndroid Build Coastguard Worker            mic (bytearray)
78*cfb92d14SAndroid Build Coastguard Worker            message_info (MessageInfo)
79*cfb92d14SAndroid Build Coastguard Worker
80*cfb92d14SAndroid Build Coastguard Worker        Returns:
81*cfb92d14SAndroid Build Coastguard Worker            bytearray: Decrypted message.
82*cfb92d14SAndroid Build Coastguard Worker
83*cfb92d14SAndroid Build Coastguard Worker        """
84*cfb92d14SAndroid Build Coastguard Worker        key, nonce, auth_data = self._crypto_material_creator.create_key_and_nonce_and_authenticated_data(message_info)
85*cfb92d14SAndroid Build Coastguard Worker
86*cfb92d14SAndroid Build Coastguard Worker        from Crypto.Cipher import AES
87*cfb92d14SAndroid Build Coastguard Worker
88*cfb92d14SAndroid Build Coastguard Worker        cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=self.mic_length)
89*cfb92d14SAndroid Build Coastguard Worker        cipher.update(auth_data)
90*cfb92d14SAndroid Build Coastguard Worker
91*cfb92d14SAndroid Build Coastguard Worker        dec_data = cipher.decrypt_and_verify(bytes(enc_data), bytes(mic))
92*cfb92d14SAndroid Build Coastguard Worker        return bytearray(dec_data)
93*cfb92d14SAndroid Build Coastguard Worker
94*cfb92d14SAndroid Build Coastguard Worker
95*cfb92d14SAndroid Build Coastguard Workerclass CryptoMaterialCreator(object):
96*cfb92d14SAndroid Build Coastguard Worker
97*cfb92d14SAndroid Build Coastguard Worker    _salt = b'Thread'
98*cfb92d14SAndroid Build Coastguard Worker
99*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, network_key):
100*cfb92d14SAndroid Build Coastguard Worker        """
101*cfb92d14SAndroid Build Coastguard Worker        Args:
102*cfb92d14SAndroid Build Coastguard Worker            network_key (bytearray)
103*cfb92d14SAndroid Build Coastguard Worker
104*cfb92d14SAndroid Build Coastguard Worker        """
105*cfb92d14SAndroid Build Coastguard Worker        self.network_key = network_key
106*cfb92d14SAndroid Build Coastguard Worker
107*cfb92d14SAndroid Build Coastguard Worker    def _generate_keys(self, sequence_counter):
108*cfb92d14SAndroid Build Coastguard Worker        """ Generate MLE and MAC keys.
109*cfb92d14SAndroid Build Coastguard Worker
110*cfb92d14SAndroid Build Coastguard Worker        Read more: 7.1.4. Key Generation - Thread v1.1 Specification Final
111*cfb92d14SAndroid Build Coastguard Worker
112*cfb92d14SAndroid Build Coastguard Worker        Args:
113*cfb92d14SAndroid Build Coastguard Worker            sequence_counter (int)
114*cfb92d14SAndroid Build Coastguard Worker
115*cfb92d14SAndroid Build Coastguard Worker        Returns:
116*cfb92d14SAndroid Build Coastguard Worker            tuple: MLE and MAC as bytes
117*cfb92d14SAndroid Build Coastguard Worker
118*cfb92d14SAndroid Build Coastguard Worker        """
119*cfb92d14SAndroid Build Coastguard Worker        k = self.network_key
120*cfb92d14SAndroid Build Coastguard Worker        s = struct.pack(">L", sequence_counter) + self._salt
121*cfb92d14SAndroid Build Coastguard Worker        d = hmac.new(k, s, digestmod=hashlib.sha256).digest()
122*cfb92d14SAndroid Build Coastguard Worker
123*cfb92d14SAndroid Build Coastguard Worker        mle = d[:16]
124*cfb92d14SAndroid Build Coastguard Worker        mac = d[16:]
125*cfb92d14SAndroid Build Coastguard Worker        return mle, mac
126*cfb92d14SAndroid Build Coastguard Worker
127*cfb92d14SAndroid Build Coastguard Worker    def create_key_and_nonce_and_authenticated_data(self, message_info):
128*cfb92d14SAndroid Build Coastguard Worker        raise NotImplementedError
129*cfb92d14SAndroid Build Coastguard Worker
130*cfb92d14SAndroid Build Coastguard Worker    @property
131*cfb92d14SAndroid Build Coastguard Worker    def mic_length(self):
132*cfb92d14SAndroid Build Coastguard Worker        raise NotImplementedError
133*cfb92d14SAndroid Build Coastguard Worker
134*cfb92d14SAndroid Build Coastguard Worker
135*cfb92d14SAndroid Build Coastguard Workerclass MacCryptoMaterialCreator(CryptoMaterialCreator):
136*cfb92d14SAndroid Build Coastguard Worker
137*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, network_key):
138*cfb92d14SAndroid Build Coastguard Worker        """
139*cfb92d14SAndroid Build Coastguard Worker        Args:
140*cfb92d14SAndroid Build Coastguard Worker            network_key (bytearray)
141*cfb92d14SAndroid Build Coastguard Worker
142*cfb92d14SAndroid Build Coastguard Worker        """
143*cfb92d14SAndroid Build Coastguard Worker        super(MacCryptoMaterialCreator, self).__init__(network_key)
144*cfb92d14SAndroid Build Coastguard Worker
145*cfb92d14SAndroid Build Coastguard Worker    def _create_nonce(self, eui64, frame_counter, security_level):
146*cfb92d14SAndroid Build Coastguard Worker        """ Create CCM Nonce required by AES-128 CCM for encryption and decryption.
147*cfb92d14SAndroid Build Coastguard Worker
148*cfb92d14SAndroid Build Coastguard Worker        Read more: 7.6.3.2 CCM Nonce - Std 802.15.4-2006
149*cfb92d14SAndroid Build Coastguard Worker
150*cfb92d14SAndroid Build Coastguard Worker        Args:
151*cfb92d14SAndroid Build Coastguard Worker            eui64 (bytes)
152*cfb92d14SAndroid Build Coastguard Worker            frame_counter (int)
153*cfb92d14SAndroid Build Coastguard Worker            security_level (int)
154*cfb92d14SAndroid Build Coastguard Worker
155*cfb92d14SAndroid Build Coastguard Worker        Returns:
156*cfb92d14SAndroid Build Coastguard Worker            bytes: created Nonce
157*cfb92d14SAndroid Build Coastguard Worker
158*cfb92d14SAndroid Build Coastguard Worker        """
159*cfb92d14SAndroid Build Coastguard Worker        return bytes(eui64 + struct.pack(">LB", frame_counter, security_level))
160*cfb92d14SAndroid Build Coastguard Worker
161*cfb92d14SAndroid Build Coastguard Worker    def _create_authenticated_data(self, mhr, auxiliary_security_header, extra_open_fields):
162*cfb92d14SAndroid Build Coastguard Worker        """ Create Authenticated Data
163*cfb92d14SAndroid Build Coastguard Worker
164*cfb92d14SAndroid Build Coastguard Worker        Read more: 7.6.3.3 CCM prerequisites - Std 802.15.4-2006
165*cfb92d14SAndroid Build Coastguard Worker
166*cfb92d14SAndroid Build Coastguard Worker        Args:
167*cfb92d14SAndroid Build Coastguard Worker            mhr (bytes)
168*cfb92d14SAndroid Build Coastguard Worker            auxiliary_security_header (bytes)
169*cfb92d14SAndroid Build Coastguard Worker            extra_open_fields (bytes)
170*cfb92d14SAndroid Build Coastguard Worker
171*cfb92d14SAndroid Build Coastguard Worker        Returns:
172*cfb92d14SAndroid Build Coastguard Worker            bytes: Authenticated Data
173*cfb92d14SAndroid Build Coastguard Worker
174*cfb92d14SAndroid Build Coastguard Worker        """
175*cfb92d14SAndroid Build Coastguard Worker        return bytes(mhr + auxiliary_security_header + extra_open_fields)
176*cfb92d14SAndroid Build Coastguard Worker
177*cfb92d14SAndroid Build Coastguard Worker    def create_key_and_nonce_and_authenticated_data(self, message_info):
178*cfb92d14SAndroid Build Coastguard Worker        _, mac_key = self._generate_keys(message_info.aux_sec_hdr.sequence_counter)
179*cfb92d14SAndroid Build Coastguard Worker
180*cfb92d14SAndroid Build Coastguard Worker        nonce = self._create_nonce(
181*cfb92d14SAndroid Build Coastguard Worker            message_info.source_mac_address,
182*cfb92d14SAndroid Build Coastguard Worker            message_info.aux_sec_hdr.frame_counter,
183*cfb92d14SAndroid Build Coastguard Worker            message_info.aux_sec_hdr.security_level,
184*cfb92d14SAndroid Build Coastguard Worker        )
185*cfb92d14SAndroid Build Coastguard Worker
186*cfb92d14SAndroid Build Coastguard Worker        auth_data = self._create_authenticated_data(
187*cfb92d14SAndroid Build Coastguard Worker            message_info.mhr_bytes,
188*cfb92d14SAndroid Build Coastguard Worker            message_info.aux_sec_hdr_bytes,
189*cfb92d14SAndroid Build Coastguard Worker            message_info.extra_open_fields,
190*cfb92d14SAndroid Build Coastguard Worker        )
191*cfb92d14SAndroid Build Coastguard Worker
192*cfb92d14SAndroid Build Coastguard Worker        return mac_key, nonce, auth_data
193*cfb92d14SAndroid Build Coastguard Worker
194*cfb92d14SAndroid Build Coastguard Worker    @property
195*cfb92d14SAndroid Build Coastguard Worker    def mic_length(self):
196*cfb92d14SAndroid Build Coastguard Worker        return 4
197*cfb92d14SAndroid Build Coastguard Worker
198*cfb92d14SAndroid Build Coastguard Worker
199*cfb92d14SAndroid Build Coastguard Workerclass MleCryptoMaterialCreator(CryptoMaterialCreator):
200*cfb92d14SAndroid Build Coastguard Worker
201*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, network_key):
202*cfb92d14SAndroid Build Coastguard Worker        """
203*cfb92d14SAndroid Build Coastguard Worker        Args:
204*cfb92d14SAndroid Build Coastguard Worker            network_key (bytearray)
205*cfb92d14SAndroid Build Coastguard Worker
206*cfb92d14SAndroid Build Coastguard Worker        """
207*cfb92d14SAndroid Build Coastguard Worker        super(MleCryptoMaterialCreator, self).__init__(network_key)
208*cfb92d14SAndroid Build Coastguard Worker
209*cfb92d14SAndroid Build Coastguard Worker    def _create_nonce(self, source_eui64, frame_counter, security_level):
210*cfb92d14SAndroid Build Coastguard Worker        """ Create CCM Nonce required by AES-128 CCM for encryption and decryption.
211*cfb92d14SAndroid Build Coastguard Worker
212*cfb92d14SAndroid Build Coastguard Worker        Read more: 7.6.3.2 CCM Nonce - Std 802.15.4-2006
213*cfb92d14SAndroid Build Coastguard Worker
214*cfb92d14SAndroid Build Coastguard Worker        Args:
215*cfb92d14SAndroid Build Coastguard Worker            eui64 (bytearray)
216*cfb92d14SAndroid Build Coastguard Worker            frame_counter (int)
217*cfb92d14SAndroid Build Coastguard Worker            security_level (int)
218*cfb92d14SAndroid Build Coastguard Worker
219*cfb92d14SAndroid Build Coastguard Worker        Returns:
220*cfb92d14SAndroid Build Coastguard Worker            bytes: created Nonce
221*cfb92d14SAndroid Build Coastguard Worker
222*cfb92d14SAndroid Build Coastguard Worker        """
223*cfb92d14SAndroid Build Coastguard Worker        return bytes(source_eui64[:8] + struct.pack(">LB", frame_counter, security_level))
224*cfb92d14SAndroid Build Coastguard Worker
225*cfb92d14SAndroid Build Coastguard Worker    def _create_authenticated_data(self, source_address, destination_address, auxiliary_security_header):
226*cfb92d14SAndroid Build Coastguard Worker        """ Create Authenticated Data
227*cfb92d14SAndroid Build Coastguard Worker
228*cfb92d14SAndroid Build Coastguard Worker        Read more: 4.8 - Thread v1.0 Specification
229*cfb92d14SAndroid Build Coastguard Worker
230*cfb92d14SAndroid Build Coastguard Worker        Args:
231*cfb92d14SAndroid Build Coastguard Worker            source_address (ip_address)
232*cfb92d14SAndroid Build Coastguard Worker            destination_address (ip_address)
233*cfb92d14SAndroid Build Coastguard Worker            auxiliary_security_header (bytearray)
234*cfb92d14SAndroid Build Coastguard Worker
235*cfb92d14SAndroid Build Coastguard Worker        Returns:
236*cfb92d14SAndroid Build Coastguard Worker            bytes: Authenticated Data
237*cfb92d14SAndroid Build Coastguard Worker
238*cfb92d14SAndroid Build Coastguard Worker        """
239*cfb92d14SAndroid Build Coastguard Worker        return bytes(source_address.packed + destination_address.packed + auxiliary_security_header)
240*cfb92d14SAndroid Build Coastguard Worker
241*cfb92d14SAndroid Build Coastguard Worker    def create_key_and_nonce_and_authenticated_data(self, message_info):
242*cfb92d14SAndroid Build Coastguard Worker        mle_key, _ = self._generate_keys(message_info.aux_sec_hdr.sequence_counter)
243*cfb92d14SAndroid Build Coastguard Worker
244*cfb92d14SAndroid Build Coastguard Worker        nonce = self._create_nonce(
245*cfb92d14SAndroid Build Coastguard Worker            message_info.source_mac_address.mac_address,
246*cfb92d14SAndroid Build Coastguard Worker            message_info.aux_sec_hdr.frame_counter,
247*cfb92d14SAndroid Build Coastguard Worker            message_info.aux_sec_hdr.security_level,
248*cfb92d14SAndroid Build Coastguard Worker        )
249*cfb92d14SAndroid Build Coastguard Worker
250*cfb92d14SAndroid Build Coastguard Worker        auth_data = self._create_authenticated_data(
251*cfb92d14SAndroid Build Coastguard Worker            message_info.source_ipv6,
252*cfb92d14SAndroid Build Coastguard Worker            message_info.destination_ipv6,
253*cfb92d14SAndroid Build Coastguard Worker            message_info.aux_sec_hdr_bytes,
254*cfb92d14SAndroid Build Coastguard Worker        )
255*cfb92d14SAndroid Build Coastguard Worker
256*cfb92d14SAndroid Build Coastguard Worker        return mle_key, nonce, auth_data
257*cfb92d14SAndroid Build Coastguard Worker
258*cfb92d14SAndroid Build Coastguard Worker    @property
259*cfb92d14SAndroid Build Coastguard Worker    def mic_length(self):
260*cfb92d14SAndroid Build Coastguard Worker        return 4
261*cfb92d14SAndroid Build Coastguard Worker
262*cfb92d14SAndroid Build Coastguard Worker
263*cfb92d14SAndroid Build Coastguard Workerclass AuxiliarySecurityHeader:
264*cfb92d14SAndroid Build Coastguard Worker
265*cfb92d14SAndroid Build Coastguard Worker    def __init__(
266*cfb92d14SAndroid Build Coastguard Worker        self,
267*cfb92d14SAndroid Build Coastguard Worker        key_id_mode,
268*cfb92d14SAndroid Build Coastguard Worker        security_level,
269*cfb92d14SAndroid Build Coastguard Worker        frame_counter,
270*cfb92d14SAndroid Build Coastguard Worker        key_id,
271*cfb92d14SAndroid Build Coastguard Worker        big_endian=True,
272*cfb92d14SAndroid Build Coastguard Worker    ):
273*cfb92d14SAndroid Build Coastguard Worker        """
274*cfb92d14SAndroid Build Coastguard Worker        Args:
275*cfb92d14SAndroid Build Coastguard Worker            key_id_mode (int)
276*cfb92d14SAndroid Build Coastguard Worker            security_level (int)
277*cfb92d14SAndroid Build Coastguard Worker            frame_counter (int)
278*cfb92d14SAndroid Build Coastguard Worker            key_id (bytearray)
279*cfb92d14SAndroid Build Coastguard Worker        """
280*cfb92d14SAndroid Build Coastguard Worker        self._key_id_mode = key_id_mode
281*cfb92d14SAndroid Build Coastguard Worker        self._security_level = security_level
282*cfb92d14SAndroid Build Coastguard Worker        self._frame_counter = frame_counter
283*cfb92d14SAndroid Build Coastguard Worker        self._key_id = key_id
284*cfb92d14SAndroid Build Coastguard Worker        self._big_endian = big_endian
285*cfb92d14SAndroid Build Coastguard Worker
286*cfb92d14SAndroid Build Coastguard Worker    @property
287*cfb92d14SAndroid Build Coastguard Worker    def sequence_counter(self):
288*cfb92d14SAndroid Build Coastguard Worker        """ Compute or extract sequence counter based on currently set Key Index Mode. """
289*cfb92d14SAndroid Build Coastguard Worker
290*cfb92d14SAndroid Build Coastguard Worker        if self.key_id_mode == 0:
291*cfb92d14SAndroid Build Coastguard Worker            key_source = self.key_id[:8]
292*cfb92d14SAndroid Build Coastguard Worker            format = ">Q" if self._big_endian else "<Q"
293*cfb92d14SAndroid Build Coastguard Worker        elif self.key_id_mode == 1:
294*cfb92d14SAndroid Build Coastguard Worker            # Try to guess valid Key Sequence Counter based on Key Index. This
295*cfb92d14SAndroid Build Coastguard Worker            # one should work for now.
296*cfb92d14SAndroid Build Coastguard Worker            return self.key_index - 1
297*cfb92d14SAndroid Build Coastguard Worker        elif self.key_id_mode == 2:
298*cfb92d14SAndroid Build Coastguard Worker            # In this mode sequence counter is stored on the first four bytes
299*cfb92d14SAndroid Build Coastguard Worker            # of Key ID.
300*cfb92d14SAndroid Build Coastguard Worker            key_source = self.key_id[:4]
301*cfb92d14SAndroid Build Coastguard Worker            format = ">I" if self._big_endian else "<I"
302*cfb92d14SAndroid Build Coastguard Worker        else:
303*cfb92d14SAndroid Build Coastguard Worker            raise ValueError("Unsupported Key Index Mode: {}".format(self.key_id_mode))
304*cfb92d14SAndroid Build Coastguard Worker
305*cfb92d14SAndroid Build Coastguard Worker        return struct.unpack(format, key_source)[0]
306*cfb92d14SAndroid Build Coastguard Worker
307*cfb92d14SAndroid Build Coastguard Worker    @property
308*cfb92d14SAndroid Build Coastguard Worker    def key_index(self):
309*cfb92d14SAndroid Build Coastguard Worker        return struct.unpack(">B", self.key_id[-1:])[0]
310*cfb92d14SAndroid Build Coastguard Worker
311*cfb92d14SAndroid Build Coastguard Worker    @property
312*cfb92d14SAndroid Build Coastguard Worker    def key_id_mode(self):
313*cfb92d14SAndroid Build Coastguard Worker        return self._key_id_mode
314*cfb92d14SAndroid Build Coastguard Worker
315*cfb92d14SAndroid Build Coastguard Worker    @property
316*cfb92d14SAndroid Build Coastguard Worker    def security_level(self):
317*cfb92d14SAndroid Build Coastguard Worker        return self._security_level
318*cfb92d14SAndroid Build Coastguard Worker
319*cfb92d14SAndroid Build Coastguard Worker    @property
320*cfb92d14SAndroid Build Coastguard Worker    def frame_counter(self):
321*cfb92d14SAndroid Build Coastguard Worker        return self._frame_counter
322*cfb92d14SAndroid Build Coastguard Worker
323*cfb92d14SAndroid Build Coastguard Worker    @property
324*cfb92d14SAndroid Build Coastguard Worker    def key_id(self):
325*cfb92d14SAndroid Build Coastguard Worker        return self._key_id
326*cfb92d14SAndroid Build Coastguard Worker
327*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
328*cfb92d14SAndroid Build Coastguard Worker        return "AuxiliarySecurityHeader(key_id_mode={}, security_level={}, frame_counter={}, key_id={})".format(
329*cfb92d14SAndroid Build Coastguard Worker            self.key_id_mode,
330*cfb92d14SAndroid Build Coastguard Worker            self.security_level,
331*cfb92d14SAndroid Build Coastguard Worker            self.frame_counter,
332*cfb92d14SAndroid Build Coastguard Worker            hexlify(self.key_id),
333*cfb92d14SAndroid Build Coastguard Worker        )
334*cfb92d14SAndroid Build Coastguard Worker
335*cfb92d14SAndroid Build Coastguard Worker
336*cfb92d14SAndroid Build Coastguard Workerclass AuxiliarySecurityHeaderFactory:
337*cfb92d14SAndroid Build Coastguard Worker
338*cfb92d14SAndroid Build Coastguard Worker    _SECURITY_CONTROL_LENGTH = 1
339*cfb92d14SAndroid Build Coastguard Worker    _FRAME_COUNTER_LENGTH = 4
340*cfb92d14SAndroid Build Coastguard Worker
341*cfb92d14SAndroid Build Coastguard Worker    _KEY_ID_LENGTH_KEY_ID_0 = 0
342*cfb92d14SAndroid Build Coastguard Worker    _KEY_ID_LENGTH_KEY_ID_1 = 1
343*cfb92d14SAndroid Build Coastguard Worker    _KEY_ID_LENGTH_KEY_ID_2 = 5
344*cfb92d14SAndroid Build Coastguard Worker    _KEY_ID_LENGTH_KEY_ID_3 = 9
345*cfb92d14SAndroid Build Coastguard Worker
346*cfb92d14SAndroid Build Coastguard Worker    _key_id_lengths = {
347*cfb92d14SAndroid Build Coastguard Worker        0: _KEY_ID_LENGTH_KEY_ID_0,
348*cfb92d14SAndroid Build Coastguard Worker        1: _KEY_ID_LENGTH_KEY_ID_1,
349*cfb92d14SAndroid Build Coastguard Worker        2: _KEY_ID_LENGTH_KEY_ID_2,
350*cfb92d14SAndroid Build Coastguard Worker        3: _KEY_ID_LENGTH_KEY_ID_3,
351*cfb92d14SAndroid Build Coastguard Worker    }
352*cfb92d14SAndroid Build Coastguard Worker
353*cfb92d14SAndroid Build Coastguard Worker    def _parse_security_control(self, security_control_byte):
354*cfb92d14SAndroid Build Coastguard Worker        security_level = security_control_byte & 0x07
355*cfb92d14SAndroid Build Coastguard Worker        key_id_mode = (security_control_byte >> 3) & 0x03
356*cfb92d14SAndroid Build Coastguard Worker
357*cfb92d14SAndroid Build Coastguard Worker        return security_level, key_id_mode
358*cfb92d14SAndroid Build Coastguard Worker
359*cfb92d14SAndroid Build Coastguard Worker    def _parse_frame_counter(self, frame_counter_bytes):
360*cfb92d14SAndroid Build Coastguard Worker        return struct.unpack("<I", frame_counter_bytes)[0]
361*cfb92d14SAndroid Build Coastguard Worker
362*cfb92d14SAndroid Build Coastguard Worker    def _key_id_length(self, key_id_mode):
363*cfb92d14SAndroid Build Coastguard Worker        return self._key_id_lengths[key_id_mode]
364*cfb92d14SAndroid Build Coastguard Worker
365*cfb92d14SAndroid Build Coastguard Worker    def parse(self, data, message_info):
366*cfb92d14SAndroid Build Coastguard Worker        security_control_bytes = bytearray(data.read(self._SECURITY_CONTROL_LENGTH))
367*cfb92d14SAndroid Build Coastguard Worker        frame_counter_bytes = bytearray(data.read(self._FRAME_COUNTER_LENGTH))
368*cfb92d14SAndroid Build Coastguard Worker
369*cfb92d14SAndroid Build Coastguard Worker        security_level, key_id_mode = self._parse_security_control(security_control_bytes[0])
370*cfb92d14SAndroid Build Coastguard Worker        frame_counter = self._parse_frame_counter(frame_counter_bytes)
371*cfb92d14SAndroid Build Coastguard Worker
372*cfb92d14SAndroid Build Coastguard Worker        key_id_length = self._key_id_length(key_id_mode)
373*cfb92d14SAndroid Build Coastguard Worker        key_id_bytes = bytearray(data.read(key_id_length))
374*cfb92d14SAndroid Build Coastguard Worker
375*cfb92d14SAndroid Build Coastguard Worker        aux_sec_hdr = AuxiliarySecurityHeader(key_id_mode, security_level, frame_counter, key_id_bytes)
376*cfb92d14SAndroid Build Coastguard Worker
377*cfb92d14SAndroid Build Coastguard Worker        message_info.aux_sec_hdr_bytes = (security_control_bytes + frame_counter_bytes + key_id_bytes)
378*cfb92d14SAndroid Build Coastguard Worker        message_info.aux_sec_hdr = aux_sec_hdr
379*cfb92d14SAndroid Build Coastguard Worker
380*cfb92d14SAndroid Build Coastguard Worker        return aux_sec_hdr
381