Skip to content

Commit 63efb24

Browse files
TinyuZhaolbuque
authored andcommitted
lib/unit: Add Unit ID ECHD & AES function.
Signed-off-by: tinyu <[email protected]>
1 parent c5a5409 commit 63efb24

File tree

3 files changed

+129
-8
lines changed

3 files changed

+129
-8
lines changed

docs/en/units/id.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,40 @@ Methods
222222

223223
|set_certificate_signing_request.png|
224224

225+
.. method:: IDUnit.aes_ecb_encrypt(position, data) -> bytearray
225226

227+
Performs AES-ECB mode encryption on data. The AES key is stored in slot 9 of the chip.
228+
229+
:param int position: Position for encryption operation (used after left shift by 6)
230+
:param bytearray data: Data to be encrypted, must be 16 bytes
231+
232+
- Return: ``bytearray``: Returns 16 bytes of encrypted data
233+
234+
235+
.. method:: IDUnit.aes_ecb_decrypt(position, data) -> bytearray
236+
237+
Performs AES-ECB mode decryption on data. The AES key is stored in slot 9 of the chip.
238+
239+
:param int position: Position for decryption operation (used after left shift by 6)
240+
:param bytearray data: Data to be decrypted, must be 16 bytes
241+
242+
- Return: ``bytearray``: Returns 16 bytes of decrypted data
243+
244+
245+
.. method:: IDUnit.aes_gfm_encrypt(data) -> bytearray
246+
247+
Performs Galois Field Multiplication (GFM) encryption operation in AES-GCM mode.
248+
249+
:param bytearray data: Data to be encrypted, must be 16 bytes
250+
251+
- Return: ``bytearray``: Returns 16 bytes of encrypted data
252+
253+
.. method:: IDUnit.ecdh_stored_key(slot_num, mode, external_public_key=None) -> bytearray
254+
255+
Performs an ECDH key exchange operation using stored keys.
256+
257+
:param int slot_num: The key slot number
258+
:param int mode: ECDH operation mode
259+
:param bytearray external_public_key: External public key, must be 64 bytes. If not provided, a new public key will be generated
260+
261+
- Return: ``bytearray``: Returns 32 bytes shared secret when mode is 0x0C or 0x08;

m5stack/libs/driver/atecc608b_tngtls/atecc.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
#
1414
# This library is distributed in the hope that it will be useful,
1515
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1717
# Lesser General Public License for more details.
1818
#
1919
# You should have received a copy of the GNU Lesser General Public
2020
# License along with this library; if not, write to the Free Software
21-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2222

2323

2424
import time
@@ -409,7 +409,9 @@ def _read(self, zone: int, address: int, buffer: bytearray) -> None:
409409
time.sleep(0.001)
410410
self.idle()
411411

412-
def _send_command(self, opcode: int, param_1: int, param_2: int = 0x00, data="") -> None:
412+
def _send_command(
413+
self, opcode: int, param_1: int, param_2: int = 0x00, data: bytearray = bytearray(b"")
414+
) -> None:
413415
#! Sends a security command packet over i2c.
414416
#! assembling command packet
415417
command_packet = bytearray(8 + len(data))
@@ -451,7 +453,7 @@ def _get_response(self, buf, length: int = None, retries: int = 20) -> int:
451453
raise RuntimeError("Failed to read data from chip")
452454
if self._debug:
453455
print("\tReceived: ", [hex(i) for i in response])
454-
crc = response[-2] | (response[-1] << 8)
456+
crc = (response[-1] << 8) | response[-2]
455457
crc2 = self._at_crc(response[0:-2])
456458
if crc != crc2:
457459
raise RuntimeError("CRC Mismatch")

m5stack/libs/unit/id.py

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
# SPDX-License-Identifier: MIT
44

55
from machine import I2C
6-
from .pahub import PAHUBUnit
7-
from .unit_helper import UnitError
6+
from unit.pahub import PAHUBUnit
7+
from unit.unit_helper import UnitError
88
import binascii
99
import struct
10+
import time
1011

1112
from driver.atecc608b_tngtls.atecc import ATECC
1213
from driver.atecc608b_tngtls.atecc_cert_util import CSR
@@ -24,7 +25,6 @@ def __init__(self, i2c: I2C | PAHUBUnit, address: int = _I2C_ADDR) -> None:
2425
self._i2c_addr = address
2526
if address not in self._i2c.scan():
2627
raise UnitError("ID unit maybe not found in Grove")
27-
2828
self.atecc = ATECC(self._i2c, self._i2c_addr)
2929

3030
def get_revision_number(self) -> int:
@@ -55,14 +55,34 @@ def uniform(self, min: float = 0, max: float = 0) -> float:
5555
min, max = (max, min) if min > max else (min, max)
5656
return min + (max - min) * self.random()
5757

58-
def get_generate_key(self, slot_num: int, private_key: bool = False) -> bytearray:
58+
def get_generate_key(self, slot_num: int, private_key: bool = False):
5959
#! Generates a private or public key.
6060
assert 0 <= slot_num <= 4, "Provided slot must be between 0 and 4."
6161
# Create a new key
6262
key = bytearray(64)
6363
self.atecc.gen_key(key, slot_num, private_key)
6464
return key
6565

66+
def ecdh_stored_key(self, slot_num: int, mode: int, external_public_key: bytearray = None):
67+
if external_public_key is None:
68+
external_public_key = bytearray(64)
69+
self.atecc.gen_key(external_public_key, 0, private_key=False)
70+
71+
if len(external_public_key) != 64:
72+
raise ValueError("External public key must be exactly 64 bytes.")
73+
74+
self.atecc._send_command(
75+
opcode=0x43, param_1=mode, param_2=slot_num, data=external_public_key
76+
)
77+
78+
if mode in (0x0C, 0x08):
79+
time.sleep(1)
80+
res = bytearray(32)
81+
self.atecc._get_response(res)
82+
return res
83+
else:
84+
return None
85+
6686
def get_ecdsa_sign(self, slot: int, message: str | list | bytearray) -> bytearray | None:
6787
#! Generates and returns a signature using the ECDSA algorithm.
6888
if len(message) == 32:
@@ -120,3 +140,66 @@ def set_certificate_signing_request(
120140
)
121141
with open(file_path, "w+") as pem_file:
122142
pem_file.write(pem_content)
143+
144+
# ref: ATECC608A-TFLXTLS 4.4.1 Table 4-5
145+
def read_data_zone(self, data_zone: int):
146+
self.atecc._send_command(
147+
opcode=0x02,
148+
param_1=0x00,
149+
param_2=data_zone,
150+
)
151+
time.sleep(0.1)
152+
temp = bytearray(4)
153+
self.atecc._get_response(temp)
154+
return temp
155+
156+
def write_data_zone(self, data_zone: int, data: bytearray):
157+
self.atecc._send_command(
158+
opcode=0x12,
159+
param_1=0x02,
160+
param_2=data_zone,
161+
data=data,
162+
)
163+
164+
def aes_ecb_encrypt(self, position: int, data: bytearray):
165+
if len(data) != 16:
166+
raise ValueError("Data must be 16 bytes")
167+
self.atecc._send_command(
168+
opcode=0x51,
169+
param_1=(position << 6),
170+
param_2=0x09, # ATECC608B AES Key only stored in slot 9
171+
data=data,
172+
)
173+
time.sleep(1)
174+
res = bytearray(16)
175+
self.atecc._get_response(res)
176+
return res
177+
178+
def aes_ecb_decrypt(self, position: int, data: bytearray):
179+
if len(data) != 16:
180+
raise ValueError("Data must be 16 bytes")
181+
self.atecc._send_command(
182+
opcode=0x51,
183+
param_1=(position << 6) + 1,
184+
param_2=0x09, # ATECC608B AES Key only stored in slot 9
185+
data=data,
186+
)
187+
time.sleep(1)
188+
res = bytearray(16)
189+
self.atecc._get_response(res)
190+
return res
191+
192+
def get_h_field(self):
193+
zero_block = bytearray([0x00] * 16)
194+
return self.aes_ecb_encrypt(0x00, zero_block)
195+
196+
def aes_gfm_encrypt(self, data: bytearray):
197+
if len(data) != 16:
198+
raise ValueError("Data must be 16 bytes")
199+
data = self.get_h_field() + data
200+
time.sleep(1)
201+
self.atecc._send_command(opcode=0x51, param_1=0x03, param_2=0x00, data=data)
202+
time.sleep(1)
203+
res = bytearray(16)
204+
self.atecc._get_response(res)
205+
return res

0 commit comments

Comments
 (0)