6
6
Communication with OpenTitan either happens over simpleserial or the uJson
7
7
command interface.
8
8
"""
9
+ import json
10
+ import time
11
+ from typing import Optional
9
12
10
13
11
14
class OTOTBNVERT :
@@ -15,93 +18,236 @@ def __init__(self, target, protocol: str) -> None:
15
18
if protocol == "ujson" :
16
19
self .simple_serial = False
17
20
21
+ def _ujson_otbn_sca_cmd (self ):
22
+ # TODO: without the delay, the device uJSON command handler program
23
+ # does not recognize the commands. Tracked in issue #256.
24
+ time .sleep (0.01 )
25
+ self .target .write (json .dumps ("OtbnSca" ).encode ("ascii" ))
26
+
27
+ def init (self ):
28
+ """ Initializes OTBN on the target.
29
+ """
30
+ if not self .simple_serial :
31
+ # OtbnSca command.
32
+ self ._ujson_otbn_sca_cmd ()
33
+ # Init command.
34
+ self .target .write (json .dumps ("Init" ).encode ("ascii" ))
35
+
18
36
def choose_otbn_app (self , app ):
19
37
""" Select the OTBN application.
20
38
Args:
21
39
app: OTBN application
22
40
"""
41
+ # Select the otbn app on the device (0 -> keygen, 1 -> modinv).
42
+ app_value = 0x00
43
+ if app == 'modinv' :
44
+ app_value = 0x01
23
45
if self .simple_serial :
24
- # Select the otbn app on the device (0 -> keygen, 1 -> modinv).
25
- if app == 'keygen' :
26
- self .target .write (cmd = "a" , data = bytearray ([0x00 ]))
27
- if app == 'modinv' :
28
- self .target .write (cmd = "a" , data = bytearray ([0x01 ]))
29
-
30
- def write_batch_prng_seed (self , seed ):
31
- """ Seed the PRNG.
32
- Args:
33
- seed: The 4-byte seed.
34
- """
35
- if self .simple_serial :
36
- self .target .write (cmd = "s" , data = seed )
46
+ self .target .write (cmd = "a" , data = bytearray ([app_value ]))
47
+ else :
48
+ # OtbnSca command.
49
+ self ._ujson_otbn_sca_cmd ()
50
+ # Ecc256AppSelect command.
51
+ self .target .write (json .dumps ("Ecc256AppSelect" ).encode ("ascii" ))
52
+ # App payload.
53
+ time .sleep (0.01 )
54
+ app_select = {"app" : [app_value ]}
55
+ self .target .write (json .dumps (app_select ).encode ("ascii" ))
56
+ time .sleep (0.01 )
37
57
38
- def write_keygen_seed (self , seed ):
58
+ def write_keygen_seed (self , seed , seed_length : Optional [ int ] = 40 ):
39
59
""" Write the seed used for the keygen app.
40
60
Args:
41
- seed: byte array containing the seed.
61
+ seed: Byte array containing the seed.
62
+ seed_length: The length of the seed.
42
63
"""
43
64
if self .simple_serial :
44
65
self .target .write (cmd = 'x' , data = seed )
66
+ else :
67
+ # OtbnSca command.
68
+ self ._ujson_otbn_sca_cmd ()
69
+ # Ecc256SetSeed command.
70
+ time .sleep (0.01 )
71
+ self .target .write (json .dumps ("Ecc256SetSeed" ).encode ("ascii" ))
72
+ # Seed payload.
73
+ time .sleep (0.01 )
74
+ seed_int = [x for x in seed ]
75
+ seed_data = {"seed" : seed_int }
76
+ self .target .write (json .dumps (seed_data ).encode ("ascii" ))
45
77
46
- def write_keygen_key_constant_redundancy (self , const ):
78
+ def write_keygen_key_constant_redundancy (self , const , const_length : Optional [ int ] = 40 ):
47
79
""" Write the constant redundancy value for the keygen app.
48
80
Args:
49
- seed: byte array containing the redundancy value.
81
+ const: Byte array containing the redundancy value.
82
+ const_length: The length of the constant.
50
83
"""
51
84
if self .simple_serial :
52
85
self .target .write (cmd = "c" , data = const )
86
+ else :
87
+ # OtbnSca command.
88
+ self ._ujson_otbn_sca_cmd ()
89
+ # Ecc256SetC command.
90
+ self .target .write (json .dumps ("Ecc256SetC" ).encode ("ascii" ))
91
+ # Constant payload.
92
+ time .sleep (0.01 )
93
+ const_int = [x for x in const ]
94
+ const_data = {"constant" : const_int , "constant_len" : const_length }
95
+ self .target .write (json .dumps (const_data ).encode ("ascii" ))
53
96
54
97
def config_keygen_masking (self , off ):
55
98
""" Disable/enable masking.
56
99
Args:
57
- off: boolean value.
100
+ off: Boolean value.
58
101
"""
102
+ # Enable/disable masking.
103
+ off_int = 0x01
104
+ if off is True :
105
+ off_int = 0x00
59
106
if self .simple_serial :
60
- # Enable/disable masking.
61
- if off is True :
62
- self .target .write (cmd = "m" , data = bytearray ([0x00 ]))
63
- else :
64
- self .target .write (cmd = "m" , data = bytearray ([0x01 ]))
107
+ self .target .write (cmd = "m" , data = bytearray ([off_int ]))
108
+ else :
109
+ # OtbnSca command.
110
+ self ._ujson_otbn_sca_cmd ()
111
+ # Ecc256EnMasks command.
112
+ self .target .write (json .dumps ("Ecc256EnMasks" ).encode ("ascii" ))
113
+ # Enable/disable masks payload.
114
+ time .sleep (0.01 )
115
+ mask = {"en_masks" : [off_int ]}
116
+ self .target .write (json .dumps (mask ).encode ("ascii" ))
65
117
66
118
def start_keygen (self , mask ):
67
119
""" Write the seed mask and start the keygen app.
68
120
Args:
69
- mask: byte array containing the mask value.
121
+ mask: Byte array containing the mask value.
70
122
"""
71
123
if self .simple_serial :
72
124
# Send the mask and start the keygen operation.
73
- self .target .write ('k' , mask )
125
+ self .target .write (cmd = 'k' , data = mask )
126
+ else :
127
+ # OtbnSca command.
128
+ self ._ujson_otbn_sca_cmd ()
129
+ # Ecc256EcdsaSecretKeygen command.
130
+ self .target .write (json .dumps ("Ecc256EcdsaSecretKeygen" ).encode ("ascii" ))
131
+ # Mask payload.
132
+ time .sleep (0.01 )
133
+ mask_int = [x for x in mask ]
134
+ mask_data = {"mask" : mask_int [0 :20 ]}
135
+ self .target .write (json .dumps (mask_data ).encode ("ascii" ))
136
+ time .sleep (0.01 )
137
+ mask_data = {"mask" : mask_int [20 :41 ]}
138
+ self .target .write (json .dumps (mask_data ).encode ("ascii" ))
74
139
75
- def start_modinv (self , scalar_k0 , scalar_k1 ):
140
+ def start_modinv (self , scalar_k0 , scalar_k1 , k_length : Optional [ int ] = 80 ):
76
141
""" Write the two scalar shares and start the modinv app.
77
142
Args:
78
- scalar_k0: byte array containing the first scalar share.
79
- scalar_k1: byte array containing the second scalar share.
143
+ scalar_k0: Byte array containing the first scalar share.
144
+ scalar_k1: Byte array containing the second scalar share.
145
+ k_length: The length of the scalar shares.
80
146
"""
81
147
if self .simple_serial :
82
148
# Start modinv device computation.
83
- self .target .write ('q' , scalar_k0 + scalar_k1 )
149
+ self .target .write (cmd = 'q' , data = (scalar_k0 + scalar_k1 ))
150
+ else :
151
+ # OtbnSca command.
152
+ self ._ujson_otbn_sca_cmd ()
153
+ # Ecc256Modinv command.
154
+ self .target .write (json .dumps ("Ecc256Modinv" ).encode ("ascii" ))
155
+ # Scalar payload.
156
+ time .sleep (0.01 )
157
+ scalar_int = [x for x in (scalar_k0 + scalar_k1 )]
158
+ scalar_data = {"k" : scalar_int [0 :20 ]}
159
+ self .target .write (json .dumps (scalar_data ).encode ("ascii" ))
160
+ time .sleep (0.01 )
161
+ scalar_data = {"k" : scalar_int [20 :40 ]}
162
+ self .target .write (json .dumps (scalar_data ).encode ("ascii" ))
163
+ time .sleep (0.01 )
164
+ scalar_data = {"k" : scalar_int [40 :60 ]}
165
+ self .target .write (json .dumps (scalar_data ).encode ("ascii" ))
166
+ time .sleep (0.01 )
167
+ scalar_data = {"k" : scalar_int [60 :80 ]}
168
+ self .target .write (json .dumps (scalar_data ).encode ("ascii" ))
84
169
85
170
def start_keygen_batch (self , test_type , num_segments ):
86
171
""" Start the keygen app in batch mode.
87
172
Args:
88
- test_type: string selecting the test type (KEY or SEED).
89
- num_segments: number of keygen executions to perform.
173
+ test_type: String selecting the test type (KEY or SEED).
174
+ num_segments: Number of keygen executions to perform.
90
175
"""
91
- if self . simple_serial :
92
- # Start batch keygen.
93
- if test_type == 'KEY' :
176
+ if test_type == 'KEY' :
177
+ if self . simple_serial :
178
+ # Start batch keygen.
94
179
self .target .write (cmd = "e" , data = num_segments )
95
180
else :
181
+ # OtbnSca command.
182
+ self ._ujson_otbn_sca_cmd ()
183
+ # Ecc256EcdsaKeygenFvsrKeyBatch command.
184
+ self .target .write (json .dumps ("Ecc256EcdsaKeygenFvsrKeyBatch" ).encode ("ascii" ))
185
+ time .sleep (0.01 )
186
+ num_segments_data = {"num_traces" : [x for x in num_segments ]}
187
+ self .target .write (json .dumps (num_segments_data ).encode ("ascii" ))
188
+ else :
189
+ if self .simple_serial :
96
190
self .target .write (cmd = "b" , data = num_segments )
191
+ else :
192
+ # OtbnSca command.
193
+ self ._ujson_otbn_sca_cmd ()
194
+ # Ecc256EcdsaKeygenFvsrSeedBatch command.
195
+ self .target .write (json .dumps ("Ecc256EcdsaKeygenFvsrSeedBatch" ).encode ("ascii" ))
196
+ time .sleep (0.01 )
197
+ num_segments_data = {"num_traces" : [x for x in num_segments ]}
198
+ self .target .write (json .dumps (num_segments_data ).encode ("ascii" ))
97
199
98
- def read_output (self , len_bytes ):
99
- """ Read the output from whichever OTBN app .
200
+ def read_alpha (self , kalpha_inv_length : int , alpha_length : int ):
201
+ """ Read alpha & kalpha_inv from the device .
100
202
Args:
101
- len_bytes: Number of bytes to read.
203
+ kalpha_inv_length: Number of bytes to read for kalpha_inv.
204
+ alpha_length: Number of bytes to read for alpha.
205
+ Returns:
206
+ The received output.
207
+ """
208
+ if self .simple_serial :
209
+ kalpha_inv = self .target .read ("r" , kalpha_inv_length , ack = False )
210
+ alpha = self .target .read ("r" , alpha_length , ack = False )
211
+ return kalpha_inv , alpha
212
+ else :
213
+ while True :
214
+ read_line = str (self .target .readline ())
215
+ if "RESP_OK" in read_line :
216
+ json_string = read_line .split ("RESP_OK:" )[1 ].split (" CRC:" )[0 ]
217
+ try :
218
+ kalpha_inv = json .loads (json_string )["alpha_inv" ]
219
+ alpha = json .loads (json_string )["alpha" ]
220
+ return kalpha_inv , alpha
221
+ except Exception :
222
+ pass # noqa: E302
102
223
224
+ def read_seeds (self , seed_bytes : int ):
225
+ """ Read d0 and d1 from the device.
226
+ Args:
227
+ seed_bytes: Number of bytes to read for kalpha_inv.
228
+ alpha_length: Number of bytes to read for alpha.
103
229
Returns:
104
230
The received output.
105
231
"""
106
232
if self .simple_serial :
107
- return self .target .read ("r" , len_bytes , ack = False )
233
+ share0 = self .target .read ("r" , seed_bytes , ack = False )
234
+ share1 = self .target .read ("r" , seed_bytes , ack = False )
235
+ if share0 is None :
236
+ raise RuntimeError ('Random share0 is none' )
237
+ if share1 is None :
238
+ raise RuntimeError ('Random share1 is none' )
239
+
240
+ return share0 , share1
241
+ else :
242
+ d0 = None
243
+ d1 = None
244
+ while True :
245
+ read_line = str (self .target .readline ())
246
+ if "RESP_OK" in read_line :
247
+ json_string = read_line .split ("RESP_OK:" )[1 ].split (" CRC:" )[0 ]
248
+ if "d0" in json_string :
249
+ d0 = json .loads (json_string )["d0" ]
250
+ elif "d1" in json_string :
251
+ d1 = json .loads (json_string )["d1" ]
252
+ if d0 is not None and d1 is not None :
253
+ return d0 , d1
0 commit comments