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