1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using System.Security.Cryptography;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Utilities;
15 using Antmicro.Renode.Peripherals.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.Miscellaneous
18 {
19     // OpenTitan HMAC AES as per https://docs.opentitan.org/hw/ip/aes/doc/ (16.09.2021)
20     public class OpenTitan_AES : BasicDoubleWordPeripheral, IKnownSize, ISideloadableKey
21     {
OpenTitan_AES(IMachine machine)22         public OpenTitan_AES(IMachine machine) : base(machine)
23         {
24             DefineRegisters();
25             initializationVector = new ByteArrayWithAccessTracking(this, InitializationVectorLengthInBytes / sizeof(uint), sizeof(uint), "IV");
26             initialKeyShare_0 = new ByteArrayWithAccessTracking(this, InitialKeyShareLengthInBytes / sizeof(uint), sizeof(uint), "initialKeyShare_0");
27             initialKeyShare_1 = new ByteArrayWithAccessTracking(this, InitialKeyShareLengthInBytes / sizeof(uint), sizeof(uint), "initialKeyShare_1");
28             inputData = new ByteArrayWithAccessTracking(this, DataLengthInBytes / sizeof(uint), sizeof(uint), "DATA_IN");
29             outputData = new ByteArrayWithAccessTracking(this, DataLengthInBytes / sizeof(uint), sizeof(uint), "DATA_OUT");
30 
31             FatalFaultAlert = new GPIO();
32             UpdateErrorAlert = new GPIO();
33 
34             key = new byte[InitialKeyShareLengthInBytes];
35             random = new PseudorandomNumberGenerator();
36             Reset();
37         }
38 
Reset()39         public override void Reset()
40         {
41             base.Reset();
42             FatalFaultAlert.Unset();
43             UpdateErrorAlert.Unset();
44             readyForInputWrite = true;
45             initializationVector.Reset();
46             initialKeyShare_0.Reset();
47             initialKeyShare_1.Reset();
48             inputData.Reset();
49             outputData.Reset();
50             Array.Clear(key, 0, InitialKeyShareLengthInBytes);
51         }
52 
53         public GPIO FatalFaultAlert { get; }
54         public GPIO UpdateErrorAlert { get; }
55 
56         public long Size => 0x1000;
57 
58         public bool InputDataReady => inputData.AllDataWritten;
59 
60         public IEnumerable<byte> SideloadKey
61         {
62             set
63             {
64                 sideloadKey = value.ToArray();
65             }
66         }
67 
DefineRegisters()68         private void DefineRegisters()
69         {
70             // As the opentitan software often writes 1's to unused register fields they are defined as `IgnoredBits` to avoid flooding the logs
71             Registers.AlertTest.Define(this)
72                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) UpdateErrorAlert.Blink(); }, name: "recov_ctrl_update_err")
73                 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalFaultAlert.Blink(); }, name: "fatal_fault")
74                 .WithIgnoredBits(2, 30);
75 
76             Registers.InitialKeyShare0_0.DefineMany(this, InitialKeyShareLengthInBytes / sizeof(uint), (register, idx) =>
77             {
78                 var part = (uint)idx;
79                 register.WithValueField(0, 32, FieldMode.Write, writeCallback: (_, value) =>
80                 {
81                     if(useSideloadedKey.Value)
82                     {
83                         this.Log(LogLevel.Noisy, "Ignored write to key_share_0_{0}, sideload is set", part);
84                         return;
85                     }
86                     initialKeyShare_0.SetPart(part, (uint)value);
87                     TryPrepareKey();
88                 }, name: $"key_share_0_{part}");
89             });
90 
91             Registers.InitialKeyShare1_0.DefineMany(this, InitialKeyShareLengthInBytes / sizeof(uint), (register, idx) =>
92             {
93                 var part = (uint)idx;
94                 register.WithValueField(0, 32, FieldMode.Write, writeCallback: (_, value) =>
95                 {
96                     if(useSideloadedKey.Value)
97                     {
98                         this.Log(LogLevel.Noisy, "Ignored write to key_share_1_{0}, sideload is set", part);
99                         return;
100                     }
101                     initialKeyShare_1.SetPart(part, (uint)value);
102                     TryPrepareKey();
103                 }, name: $"key_share_1_{part}");
104             });
105 
106             Registers.InitializationVector_0.DefineMany(this, InitializationVectorLengthInBytes / sizeof(uint), (register, idx) =>
107             {
108                 var part = (uint)idx;
109                 register.WithValueField(0, 32, FieldMode.Write, writeCallback: (_, value) => initializationVector.SetPart(part, (uint)value), name: $"iv_{part}");
110             });
111 
112             Registers.InputData_0.DefineMany(this, DataLengthInBytes / sizeof(uint), (register, idx) =>
113             {
114                 var part = (uint)idx;
115                 register.WithValueField(0, 32, FieldMode.Write, writeCallback: (_, value) =>
116                 {
117                     inputData.SetPart(part, (uint)value);
118                     if(inputData.AllDataWritten && !manualOperation.Value)
119                     {
120                         ProcessInput();
121                     }
122                 }, name: $"data_in_{part}");
123             });
124 
125             Registers.OutputData_0.DefineMany(this, DataLengthInBytes / sizeof(uint), (register, idx) =>
126             {
127                 var part = (uint)idx;
128                 register.WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ =>
129                 {
130                     var val = outputData.GetPartAsDoubleWord(part);
131                     if(outputData.AllDataRead)
132                     {
133                         readyForInputWrite = true;
134                         outputValid.Value = false;
135                     }
136                     return val;
137                 }, name: $"data_out_{part}");
138             });
139 
140             Registers.Control.Define(this, 0xc0)
141                 .WithEnumField<DoubleWordRegister, DecryptionMode>(0, 2, out decryptionMode, name: "OPERATION")
142                 .WithEnumField<DoubleWordRegister, OperationMode>(2, 6, out operationMode, name: "MODE")
143                 .WithEnumField<DoubleWordRegister, KeyLength>(8, 3, out keyLength, name: "KEY_LEN")
144                 .WithFlag(11, out useSideloadedKey, name: "SIDELOAD")
145                 .WithTag("PRNG_RESEED_RATE", 12, 3)
146                 .WithFlag(15, out manualOperation, name: "MANUAL_OPERATION")
147                 .WithTaggedFlag("FORCE_ZERO_MASKS", 16)
148                 .WithIgnoredBits(17, 15)
149                 .WithWriteCallback((_, val) =>
150                 {
151                     this.Log(LogLevel.Debug, "New configuration:\n\tOPERATION = {0}\n\tMODE = {1}\n\tKEY_LEN = {2}\n\tSIDELOAD = {3}\n\tMANUAL_OPERATION = {4}",
152                              decryptionMode.Value, operationMode.Value, keyLength.Value, useSideloadedKey.Value, manualOperation.Value);
153                 });
154 
155             Registers.AuxiliaryControl.Define(this, 0x1)
156                 .WithTaggedFlag("KEY_TOUCH_FORCES_RESEED", 0)
157                 .WithReservedBits(1, 31);
158 
159             Registers.AuxiliaryControlWriteEnable.Define(this, 0x1)
160                 .WithTaggedFlag("CTRL_AUX_REGWEN", 0)
161                 .WithReservedBits(1, 31);
162 
163             Registers.Trigger.Define(this, 0xe)
164                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) =>
165                           {
166                               if(val && manualOperation.Value)
167                               {
168                                   ProcessInput();
169                               }
170                               else
171                               {
172                                   this.Log(LogLevel.Warning, "Received the 'START' trigger while not in manual mode - ignoring");
173                               }
174                           }, name: "START")
175                 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) =>
176                     {
177                         if(val)
178                         {
179                             this.Log(LogLevel.Debug, "Randomizing input registers ('IV', 'INPUT_DATA', 'KEY_SHARE_')");
180                             initializationVector.SetArrayTo(GetRandomArrayOfLength(InitializationVectorLengthInBytes), trackAccess: false);
181                             initialKeyShare_0.SetArrayTo(GetRandomArrayOfLength(InitialKeyShareLengthInBytes), trackAccess: false);
182                             initialKeyShare_1.SetArrayTo(GetRandomArrayOfLength(InitialKeyShareLengthInBytes), trackAccess: false);
183                             inputData.SetArrayTo(GetRandomArrayOfLength(DataLengthInBytes), trackAccess: false);
184                         }
185                     }, name: "KEY_IV_DATA_IN_CLEAR")
186                 .WithFlag(2, FieldMode.Write, writeCallback: (_, val) =>
187                     {
188                         if(val)
189                         {
190                             this.Log(LogLevel.Debug, "Randomizing output registers");
191                             outputData.SetArrayTo(GetRandomArrayOfLength(DataLengthInBytes), trackAccess: false);
192                         }
193                     }, name: "DATA_OUT_CLEAR")
194                 .WithTaggedFlag("PRNG_RESEED", 3)
195                 .WithIgnoredBits(4, 28);
196 
197             Registers.Status.Define(this, 0x1)
198                 .WithFlag(0, out statusIdle, FieldMode.Read, name: "IDLE")
199                 .WithTaggedFlag("STALL", 1)
200                 .WithTaggedFlag("OUTPUT_LOST", 2)
201                 .WithFlag(3, out outputValid, FieldMode.Read, name: "OUTPUT_VALID")
202                 .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => readyForInputWrite && !manualOperation.Value, name: "INPUT_READY")
203                 .WithTaggedFlag("ALERT_RECOV_CTRL_UPDATE_ERR", 5)
204                 .WithTaggedFlag("ALERT_FATAL_FAULT", 6)
205                 .WithIgnoredBits(7, 25);
206         }
207 
GetRandomArrayOfLength(int lengthInBytes)208         private byte[] GetRandomArrayOfLength(int lengthInBytes)
209         {
210             var randomized = new byte[lengthInBytes];
211             random.NextBytes(randomized);
212             return randomized;
213         }
214 
TryPrepareKey()215         private void TryPrepareKey()
216         {
217             if(initialKeyShare_0.AllDataWritten && initialKeyShare_1.AllDataWritten)
218             {
219                 var key0 = initialKeyShare_0.RetriveData(trackAccess: false);
220                 var key1 = initialKeyShare_1.RetriveData(trackAccess: false);
221 
222                 for(var i = 0; i < InitialKeyShareLengthInBytes; i++)
223                 {
224                     key[i] = (byte)(((int)key0[i]) ^ ((int)key1[i]));
225                 }
226             }
227         }
228 
ConfigureAES()229         private bool ConfigureAES()
230         {
231             switch(keyLength.Value)
232             {
233                 case KeyLength.Bits128:
234                     aes.KeySize = 128;
235                     break;
236                 case KeyLength.Bits192:
237                     aes.KeySize = 192;
238                     break;
239                 case KeyLength.Bits256:
240                     aes.KeySize = 256;
241                     break;
242                 default:
243                     // This kind of misconfiguration is possible, behaviour is undefined
244                     this.Log(LogLevel.Error, "Invalid KEY_LEN size: '0x{0:x}'. Undefined behavior ahead!", keyLength.Value);
245                     return false;
246             }
247             aes.Key = useSideloadedKey.Value ? sideloadKey : key;
248 #if DEBUG
249             this.Log(LogLevel.Debug, "Generated key: {0}", Misc.Stringify(key, " "));
250 #endif
251             switch(operationMode.Value)
252             {
253                 case OperationMode.ElectronicCodebook:
254                     aes.Mode = CipherMode.ECB;
255                     break;
256                 default:
257                     this.Log(LogLevel.Error, "Encryption Mode {0} is not supported yet", operationMode.Value);
258                     return false;
259             }
260             return true;
261         }
262 
TransformData()263         private bool TransformData()
264         {
265             var output = new byte[DataLengthInBytes];
266             var transformMethod = decryptionMode.Value == DecryptionMode.Decryption ? aes.CreateDecryptor() : aes.CreateEncryptor();
267             var data = inputData.RetriveData(trackAccess: false);
268             int bytesProcessed = 0;
269 
270             if(data.Length == 0)
271             {
272                 this.Log(LogLevel.Error, "Started transforming data before full input was available. No output will be generated");
273                 return false;
274             }
275             // This is a workaround for the problem with zero bytes transformation on first call while decrypting;
276             // doesn't happen while encrypting and found no plausible explanation
277             while(bytesProcessed == 0)
278             {
279                 bytesProcessed = transformMethod.TransformBlock(data, 0, DataLengthInBytes, output, 0);
280             }
281             outputData.SetArrayTo(output, trackAccess: false);
282             this.Log(LogLevel.Debug, "Generated 'DATA_OUT' : {0}", Misc.Stringify(output, " "));
283             return true;
284         }
285 
ProcessInput()286         private void ProcessInput()
287         {
288             var configurationOk = ConfigureAES();
289             if(!configurationOk)
290             {
291                 return;
292             }
293 
294             var transformationSucessfull = TransformData();
295             if(transformationSucessfull)
296             {
297                 outputValid.Value = true;
298             }
299         }
300 
301         private readonly byte[] key;
302         private byte[] sideloadKey;
303         private bool readyForInputWrite;
304         private readonly ByteArrayWithAccessTracking initialKeyShare_0;
305         private readonly ByteArrayWithAccessTracking initialKeyShare_1;
306         private readonly ByteArrayWithAccessTracking initializationVector;
307         private readonly ByteArrayWithAccessTracking inputData;
308         private readonly ByteArrayWithAccessTracking outputData;
309 
310         private IFlagRegisterField manualOperation;
311         private IFlagRegisterField statusIdle;
312         private IFlagRegisterField outputValid;
313         private IFlagRegisterField useSideloadedKey;
314         private IEnumRegisterField<DecryptionMode> decryptionMode;
315         private IEnumRegisterField<OperationMode> operationMode;
316         private IEnumRegisterField<KeyLength> keyLength;
317 
318 // > warning SYSLIB0021: 'AesManaged' is obsolete: 'Derived cryptographic types are obsolete. Use the Create method on the base type instead.'
319 // Replacing with `Aes.Create` isn't straightforward as it breaks serialization on Windows. Let's just hush it.
320 #pragma warning disable SYSLIB0021
321         private readonly AesManaged aes = new AesManaged();
322 #pragma warning restore SYSLIB0021
323 
324         private readonly PseudorandomNumberGenerator random;
325 
326         private const int InitialKeyShareLengthInBytes = 32;
327         private const int InitializationVectorLengthInBytes = 16;
328         private const int DataLengthInBytes = 16;
329 
330         private enum OperationMode
331         {
332             ElectronicCodebook = 0b000001,  // ECB mode
333             CipherBlockChaining = 0b000010, // CBC mode
334             CipherFeedback = 0b000100,      // CFB mode
335             OutputFeedback = 0b001000,      // OFB mode
336             Counter = 0b010000,             // CTR mode
337         }
338 
339         private enum DecryptionMode
340         {
341             Encryption = 0x1,
342             Decryption = 0x2,
343         }
344 
345         private enum KeyLength
346         {
347             Bits128 = 0b001,
348             Bits192 = 0b010,
349             Bits256 = 0b100,
350         }
351 
352         public enum Registers
353         {
354             AlertTest = 0x0,
355             InitialKeyShare0_0 = 0x4,
356             InitialKeyShare0_1 = 0x8,
357             InitialKeyShare0_2 = 0xc,
358             InitialKeyShare0_3 = 0x10,
359             InitialKeyShare0_4 = 0x14,
360             InitialKeyShare0_5 = 0x18,
361             InitialKeyShare0_6 = 0x1c,
362             InitialKeyShare0_7 = 0x20,
363             InitialKeyShare1_0 = 0x24,
364             InitialKeyShare1_1 = 0x28,
365             InitialKeyShare1_2 = 0x2c,
366             InitialKeyShare1_3 = 0x30,
367             InitialKeyShare1_4 = 0x34,
368             InitialKeyShare1_5 = 0x38,
369             InitialKeyShare1_6 = 0x3c,
370             InitialKeyShare1_7 = 0x40,
371             InitializationVector_0 = 0x44,
372             InitializationVector_1 = 0x48,
373             InitializationVector_2 = 0x4c,
374             InitializationVector_3 = 0x50,
375             InputData_0 = 0x54,
376             InputData_1 = 0x58,
377             InputData_2 = 0x5c,
378             InputData_3 = 0x60,
379             OutputData_0 = 0x64,
380             OutputData_1 = 0x68,
381             OutputData_2 = 0x6c,
382             OutputData_3 = 0x70,
383             Control = 0x74,
384             AuxiliaryControl = 0x78,
385             AuxiliaryControlWriteEnable = 0x7C,
386             Trigger = 0x80,
387             Status = 0x84,
388         }
389     }
390 }
391