1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System; 9 using System.Collections.Generic; 10 using System.Linq; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Exceptions; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Utilities; 16 using Org.BouncyCastle.Crypto; 17 using Org.BouncyCastle.Crypto.Engines; 18 using Org.BouncyCastle.Crypto.Prng.Drbg; 19 20 namespace Antmicro.Renode.Peripherals.Miscellaneous 21 { 22 public class OpenTitan_CSRNG: BasicDoubleWordPeripheral, IKnownSize 23 { OpenTitan_CSRNG(IMachine machine, OpenTitan_EntropySource entropySource)24 public OpenTitan_CSRNG(IMachine machine, OpenTitan_EntropySource entropySource) : base(machine) 25 { 26 this.entropySource = entropySource; 27 28 DefineRegisters(); 29 30 RequestCompletedIRQ = new GPIO(); 31 EntropyeRequestedIRQ = new GPIO(); 32 HardwareInstanceIRQ = new GPIO(); 33 FatalErrorIRQ = new GPIO(); 34 RecoverableAlert = new GPIO(); 35 FatalAlert = new GPIO(); 36 37 WorkingMode = RandomType.HardwareCompliant; 38 generatedBitsFifo = new Queue<uint>(); 39 40 seed = new uint[12]; 41 fakeEntropy = new FakeEntropy(DefaultSeedSizeInBytes); 42 internalStateReadFifo = new Queue<uint>(); 43 generatedBitsFifo = new Queue<uint>(); 44 appendedData = new List<uint>(); 45 Reset(); 46 } 47 RequestData(out uint result)48 public bool RequestData(out uint result) 49 { 50 if(generatedBitsFifo.TryDequeue(out result)) 51 { 52 return generatedBitsFifo.Count != 0; 53 } 54 else 55 { 56 this.Log(LogLevel.Warning, "Trying to read from empty FIFO"); 57 return false; 58 } 59 } 60 EdnSoftwareCommandRequestWrite(uint writeValue)61 public void EdnSoftwareCommandRequestWrite(uint writeValue) 62 { 63 HandleCommandRequestWrite(writeValue); 64 } 65 Reset()66 public override void Reset() 67 { 68 appendedDataCount = 0; 69 appendedData.Clear(); 70 generatedBitsFifo.Clear(); 71 internalStateReadFifo.Clear(); 72 base.Reset(); 73 RecoverableAlert.Unset(); 74 FatalAlert.Unset(); 75 76 InstantiateRandom(); 77 78 readyFlag.Value = true; 79 } 80 ReinstantiateInternal()81 private void ReinstantiateInternal() 82 { 83 drbgEngine = new CtrSP800Drbg(new AesEngine(), keySizeInBits: 256, securityStrength: 256, entropySource: fakeEntropy, 84 personalizationString: null, nonce: null, withDerivationFuction: false); 85 } 86 DefineRegisters()87 private void DefineRegisters() 88 { 89 Registers.InterruptState.Define(this) 90 .WithFlag(0, out requestCompletedInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "cs_cmd_req_done") 91 .WithFlag(1, out entropyRequestInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "cs_entropy_req") 92 .WithFlag(2, out hardwareInstanceInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "cs_hw_inst_exc") 93 .WithFlag(3, out fatalErrorInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "cs_fatal_err") 94 .WithReservedBits(4, 32 - 4) 95 .WithWriteCallback((_, __) => UpdateInterrupts()); 96 Registers.InterruptEnable.Define(this) 97 .WithFlag(0, out requestCompletedInterruptEnabled, FieldMode.Read | FieldMode.WriteOneToClear, name: "cs_cmd_req_done") 98 .WithFlag(1, out entropyRequestInterruptEnabled, FieldMode.Read | FieldMode.WriteOneToClear, name: "cs_entropy_req") 99 .WithFlag(2, out hardwareInstanceInterruptEnabled, FieldMode.Read | FieldMode.WriteOneToClear, name: "cs_hw_inst_exc") 100 .WithFlag(3, out fatalErrorInterruptEnabled, FieldMode.Read | FieldMode.WriteOneToClear, name: "cs_fatal_err") 101 .WithReservedBits(4, 32 - 4) 102 .WithWriteCallback((_, __) => UpdateInterrupts()); 103 Registers.InterruptTest.Define(this) 104 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) requestCompletedInterrupt.Value = true; }, name: "cs_cmd_req_done") 105 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { if(val) entropyRequestInterrupt.Value = true; }, name: "cs_entropy_req") 106 .WithFlag(2, FieldMode.Write, writeCallback: (_, val) => { if(val) hardwareInstanceInterrupt.Value = true; }, name: "cs_hw_inst_exc") 107 .WithFlag(3, FieldMode.Write, writeCallback: (_, val) => { if(val) fatalErrorInterrupt.Value = true; }, name: "cs_fatal_err") 108 .WithReservedBits(4, 32 - 4) 109 .WithWriteCallback((_, val) => { if(val != 0) UpdateInterrupts(); }); 110 Registers.AlertTest.Define(this) 111 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) RecoverableAlert.Blink(); }, name: "recov_alert") 112 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalAlert.Blink(); }, name: "fatal_alert") 113 .WithReservedBits(2, 30); 114 Registers.RegisterWriteEnable.Define(this, 0x1) 115 .WithTaggedFlag("REGWEN", 0) 116 .WithReservedBits(1, 31); 117 Registers.Control.Define(this, 0x999) 118 .WithEnumField<DoubleWordRegister, MultiBitBool4>(0, 4, out enabled, name: "ENABLE") 119 .WithEnumField<DoubleWordRegister, MultiBitBool4>(4, 4, out genbitsReadEnabled, name: "SW_APP_ENABLE") 120 .WithTag("READ_INT_STATE", 8, 4) 121 .WithReservedBits(12, 20); 122 Registers.CommandRequest.Define(this) 123 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, val) => HandleCommandRequestWrite((uint)val), name: "CMD_REQ"); 124 Registers.CommandStatus.Define(this, 0x1) 125 .WithFlag(0, out readyFlag, FieldMode.Read, name: "CMD_RDY") 126 .WithFlag(1, out requestFailedFlag, FieldMode.Read, name: "CMD_STS") 127 .WithReservedBits(2, 30); 128 Registers.GenerateBitsValid.Define(this) 129 .WithFlag(0, out generatedValidFlag, FieldMode.Read, name: "GENBITS_VLD") 130 .WithFlag(1, out fipsCompliant, FieldMode.Read, name: "GENBITS_FIPS") 131 .WithReservedBits(2, 30); 132 Registers.GenerateBits.Define(this) 133 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: (_) => 134 { 135 if(genbitsReadEnabled.Value == MultiBitBool4.False) 136 { 137 this.Log(LogLevel.Error, "Trying to read the generatedBitsFifo when 'SW_APP_ENABLE' is not set to true"); 138 return 0; 139 } 140 141 if(generatedBitsFifo.TryDequeue(out var value)) 142 { 143 return value; 144 } 145 this.Log(LogLevel.Warning, "Trying to get an value when the fifo with generated bits is empty. Generate lenght mismatch?"); 146 return 0; 147 }, name: "GENBITS"); 148 Registers.InternalStateNumber.Define(this) 149 .WithValueField(0, 4, out internalStateSelection, writeCallback: (_, val) => 150 { 151 if(val != InternalStateSoftwareStateSelection) 152 { 153 this.Log(LogLevel.Error, "This internal state is not being tracked. The only internal state implemented is the SoftwareIdState ({})", InternalStateSoftwareStateSelection); 154 155 } 156 internalStateReadFifo.Clear(); 157 FillFifoWithInternalState(); 158 }, name: "INT_STATE_NUM") 159 .WithReservedBits(5, 32 - 5); 160 Registers.InternalStateReadAccess.Define(this) 161 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: (_) => 162 { 163 return internalStateReadFifo.Count > 0 ? internalStateReadFifo.Dequeue() : 0u; 164 }, name: "INT_STATE_VAL"); 165 Registers.HardwareExceptionStatus.Define(this) 166 .WithTag("HW_EXC_STS", 0, 15) 167 .WithReservedBits(16, 16); 168 Registers.RecoverableAlertStatus.Define(this) 169 .WithTaggedFlag("ENABLE_FIELD_ALERT", 0) 170 .WithTaggedFlag("SW_APP_ENABLE_FIELD_ALERT", 1) 171 .WithTaggedFlag("READ_INT_STATE_FIELD_ALERT", 2) 172 .WithReservedBits(3, 9) 173 .WithTaggedFlag("CS_BUS_CMP_ALERT", 12) 174 .WithReservedBits(13, 32 - 13); 175 Registers.ErrorCode.Define(this) 176 .WithFlag(0, out commandError, FieldMode.Read, name: "SFIFO_CMD_ERR") 177 .WithTaggedFlag("SFIFO_GENBITS_ERR", 1) 178 .WithTaggedFlag("SFIFO_CMDREQ_ERR", 2) 179 .WithTaggedFlag("SFIFO_RCSTAGE_ERR", 3) 180 .WithTaggedFlag("SFIFO_KEYVRC_ERR", 4) 181 .WithTaggedFlag("SFIFO_UPDREQ_ERR", 5) 182 .WithTaggedFlag("SFIFO_BENCREQ_ERR", 6) 183 .WithTaggedFlag("SFIFO_BENCACK_ERR", 7) 184 .WithTaggedFlag("SFIFO_PDATA_ERR", 8) 185 .WithTaggedFlag("SFIFO_FINAL_ERR", 9) 186 .WithTaggedFlag("SFIFO_GBENCACK_ERR", 10) 187 .WithTaggedFlag("SFIFO_GRCSTAGE_ERR", 11) 188 .WithTaggedFlag("SFIFO_GGENREQ_ERR", 12) 189 .WithTaggedFlag("SFIFO_GADSTAGE_ERR", 13) 190 .WithTaggedFlag("SFIFO_GGENBITS_ERR", 14) 191 .WithTaggedFlag("SFIFO_BLKENC_ERR", 15) 192 .WithReservedBits(16, 4) 193 .WithTaggedFlag("CMD_STAGE_SM_ERR", 20) 194 .WithTaggedFlag("MAIN_SM_ERR", 21) 195 .WithTaggedFlag("DRBG_GEN_SM_ERR", 22) 196 .WithTaggedFlag("DRBG_UPDBE_SM_ERR", 23) 197 .WithTaggedFlag("DRBG_UPDOB_SM_ERR", 24) 198 .WithTaggedFlag("AES_CIPHER_SM_ERR", 25) 199 .WithTaggedFlag("CMD_GEN_CNT_ERR", 26) 200 .WithReservedBits(27, 1) 201 .WithTaggedFlag("FIFO_WRITE_ERR", 28) 202 .WithTaggedFlag("FIFO_READ_ERR", 29) 203 .WithTaggedFlag("FIFO_STATE_ERR", 30) 204 .WithReservedBits(31, 1); 205 Registers.ErrorCodeTest.Define(this) 206 .WithTag("ERR_CODE_TEST", 0, 5) 207 .WithReservedBits(5, 32 - 5); 208 Registers.StateMachineState.Define(this, 0x4e) 209 .WithTag("MAIN_SM_STATE", 0, 8) 210 .WithReservedBits(8, 24); 211 } 212 213 public long Size => 0x1000; 214 215 public GPIO RequestCompletedIRQ { get; } 216 public GPIO EntropyeRequestedIRQ { get; } 217 public GPIO HardwareInstanceIRQ { get; } 218 public GPIO FatalErrorIRQ { get; } 219 220 public GPIO RecoverableAlert { get; } 221 public GPIO FatalAlert { get; } 222 223 public uint ReseedCount => (uint)(drbgEngine?.InternalReseedCount ?? 0u); 224 public uint[] InternalV 225 { 226 get 227 { 228 if(drbgEngine == null) 229 { 230 return new uint[0]; 231 } 232 return ByteArrayToRegisterOrderedUIntArray(drbgEngine.InternalV); 233 } 234 } 235 236 public uint[] InternalKey 237 { 238 get 239 { 240 if(drbgEngine == null) 241 { 242 return new uint[0]; 243 } 244 return ByteArrayToRegisterOrderedUIntArray(drbgEngine.InternalKey); 245 } 246 } 247 248 public RandomType WorkingMode 249 { 250 get 251 { 252 return workingMode; 253 } 254 set 255 { 256 workingMode = value; 257 if(workingMode != RandomType.HardwareCompliant) 258 { 259 fipsCompliant.Value = true; 260 InstantiateRandom(); 261 } 262 } 263 } 264 265 public string FixedData 266 { 267 set 268 { 269 if(value.Length % 8 != 0) 270 { 271 throw new RecoverableException("The data must be aligned to a 4 bytes (double word)"); 272 } 273 fixedData = Misc.HexStringToByteArray(value); 274 Misc.EndiannessSwapInPlace(fixedData, sizeof(uint)); 275 InstantiateRandom(); 276 } 277 } 278 InstantiateRandom()279 private void InstantiateRandom() 280 { 281 if(WorkingMode == RandomType.PseudoRandomFixedSeed) 282 { 283 randomSource = new Random((int)Misc.ByteArrayRead(0, fixedData)); 284 return; 285 } 286 randomSource = new Random(); 287 } 288 UpdateInterrupts()289 private void UpdateInterrupts() 290 { 291 EntropyeRequestedIRQ.Set(entropyRequestInterrupt.Value && entropyRequestInterruptEnabled.Value); 292 FatalErrorIRQ.Set(fatalErrorInterrupt.Value && fatalErrorInterruptEnabled.Value); 293 HardwareInstanceIRQ.Set(hardwareInstanceInterrupt.Value && hardwareInstanceInterruptEnabled.Value); 294 RequestCompletedIRQ.Set(requestCompletedInterrupt.Value && requestCompletedInterruptEnabled.Value); 295 } 296 HandleCommandRequestWrite(uint writeValue)297 private void HandleCommandRequestWrite(uint writeValue) 298 { 299 if(enabled.Value == MultiBitBool4.False) 300 { 301 this.Log(LogLevel.Error, "Peripheral disabled - will not execute command"); 302 return; 303 } 304 if(!NoMoreDataToConsume && TryConsumeAppendedData(writeValue)) 305 { 306 return; 307 } 308 309 if(TryExtractCommandParameters(writeValue, out var command, out var commandLength, out var flags, out var generateLength)) 310 { 311 this.Log(LogLevel.Debug, "Got command {0}, with commandLength {1}, and generateLength {2}", command, commandLength, generateLength); 312 ExecuteCommand(command, commandLength, flags, generateLength); 313 } 314 else 315 { 316 RaiseCommandError(); 317 } 318 } 319 TryConsumeAppendedData(uint data)320 private bool TryConsumeAppendedData(uint data) 321 { 322 if(NoMoreDataToConsume) 323 { 324 return false; 325 } 326 appendedDataCount--; 327 if(WorkingMode == RandomType.HardwareCompliant) 328 { 329 appendedData.Add(Misc.EndiannessSwap(data)); 330 if(NoMoreDataToConsume) 331 { 332 this.Log(LogLevel.Warning, "Finished completing additional command data"); 333 var dataArray = appendedData.ToArray(); 334 appendedDataAction(dataArray); 335 appendedData.Clear(); 336 } 337 } 338 return true; 339 } 340 TryExtractCommandParameters(uint commandHeader, out CommandName command, out uint commandLength, out bool[] flags, out uint generateLength)341 private bool TryExtractCommandParameters(uint commandHeader, out CommandName command, out uint commandLength, out bool[] flags, out uint generateLength) 342 { 343 command = default(CommandName); 344 commandLength = 0; 345 generateLength = 0; 346 flags = new bool[8]; 347 348 var rawCommand = (int)BitHelper.GetValue(commandHeader, 0, 4); 349 if(!Enum.IsDefined(typeof(CommandName), rawCommand)) 350 { 351 return false; 352 } 353 354 command = (CommandName)rawCommand; 355 commandLength = BitHelper.GetValue(commandHeader, 4, 4); 356 flags = BitHelper.GetBits(BitHelper.GetValue(commandHeader, 8, 4)); 357 generateLength = BitHelper.GetValue(commandHeader, 12, 13); 358 359 return true; 360 } 361 RaiseCommandError()362 private void RaiseCommandError() 363 { 364 commandError.Value = true; 365 fatalErrorInterrupt.Value = true; 366 UpdateInterrupts(); 367 } 368 ExecuteCommand(CommandName command, uint commandLength, bool[] flags, uint generateLength)369 private void ExecuteCommand(CommandName command, uint commandLength, bool[] flags, uint generateLength) 370 { 371 var useEntropy = !flags[0]; 372 switch(command) 373 { 374 case CommandName.Instantiate: 375 ExecuteInstantiate(commandLength, useEntropy); 376 break; 377 case CommandName.Generate: 378 ExecuteGenerate(generateLength); 379 break; 380 case CommandName.Uninstantiate: 381 if(commandLength != 0) 382 { 383 this.Log(LogLevel.Warning, "The 'Uninstantiate' command can be used only with a zero 'clen' value."); 384 } 385 ExecuteUninstantiate(); 386 break; 387 case CommandName.Reseed: 388 ExecuteReseed(commandLength, useEntropy); 389 break; 390 case CommandName.Update: 391 ExecuteUpdate(commandLength); 392 break; 393 default: 394 this.Log(LogLevel.Error, "Got an illegal application command. Ignoring"); 395 return; 396 } 397 requestCompletedInterrupt.Value = true; 398 UpdateInterrupts(); 399 } 400 ExecuteGenerate(uint generateLength)401 private void ExecuteGenerate(uint generateLength) 402 { 403 var lengthInBytes = BytesPerEntropyUnit * (int)generateLength; 404 FillFifoWithGeneratedBits(lengthInBytes); 405 } 406 FillFifoWithGeneratedBits(int bytesToGenerate)407 private void FillFifoWithGeneratedBits(int bytesToGenerate) 408 { 409 var generatedBytes = new byte[bytesToGenerate]; 410 switch(WorkingMode) 411 { 412 case RandomType.PseudoRandom: 413 case RandomType.PseudoRandomFixedSeed: 414 randomSource.NextBytes(generatedBytes); 415 break; 416 case RandomType.FixedData: 417 Misc.FillByteArrayWithArray(generatedBytes, fixedData); 418 break; 419 case RandomType.HardwareCompliant: 420 if(drbgEngine.Generate(generatedBytes, additionalInput: null, predictionResistant: false) == -1) 421 { 422 requestFailedFlag.Value = true; 423 generatedValidFlag.Value = false; 424 fatalErrorInterrupt.Value = true; 425 UpdateInterrupts(); 426 return; 427 }; 428 // The CtrSP800Drbg returns bytes in reversed order 429 Array.Reverse(generatedBytes); 430 // Peripheral expects the entropy units to be in a reversed order 431 ReorderEntropyUnits(ref generatedBytes); 432 break; 433 default: 434 throw new ArgumentException("Unknown type of simulation mode"); 435 } 436 var generatedDoubleWords = new uint[bytesToGenerate / sizeof(uint)]; 437 Buffer.BlockCopy(generatedBytes, 0, generatedDoubleWords, 0, bytesToGenerate); 438 439 requestFailedFlag.Value = false; 440 generatedValidFlag.Value = true; 441 foreach(var doubleWord in generatedDoubleWords) 442 { 443 generatedBitsFifo.Enqueue(doubleWord); 444 } 445 } 446 ReorderEntropyUnits(ref byte[] inputData)447 private void ReorderEntropyUnits(ref byte[] inputData) 448 { 449 if(inputData.Length % BytesPerEntropyUnit != 0) 450 { 451 throw new ArgumentException($"Input data must bu aligned to the size of entropy unit ({BytesPerEntropyUnit} bytes)"); 452 } 453 var temp = new byte[BytesPerEntropyUnit]; 454 var unitsCount = inputData.Length / BytesPerEntropyUnit; 455 int insertOffset = (unitsCount - 1) * BytesPerEntropyUnit; 456 for(var unit = 0; unit <= (unitsCount -1)/2; unit++) 457 { 458 var sourceOffset = unit * BytesPerEntropyUnit; 459 Buffer.BlockCopy(inputData, sourceOffset, temp, 0, BytesPerEntropyUnit); 460 Buffer.BlockCopy(inputData, insertOffset, inputData, sourceOffset, BytesPerEntropyUnit); 461 Buffer.BlockCopy(temp, 0, inputData, insertOffset, BytesPerEntropyUnit); 462 insertOffset -= BytesPerEntropyUnit; 463 } 464 } 465 ExecuteInstantiate(uint commandLength, bool useEntropy)466 private void ExecuteInstantiate(uint commandLength, bool useEntropy) 467 { 468 if(WorkingMode == RandomType.PseudoRandomFixedSeed) 469 { 470 InstantiateRandom(); 471 } 472 else if(WorkingMode == RandomType.HardwareCompliant) 473 { 474 fipsCompliant.Value = useEntropy; 475 if(commandLength != 0) 476 { 477 appendedDataCount = commandLength; 478 appendedDataAction = (dataArray) => 479 { 480 var data = ProcessAppendedData(dataArray, useEntropy); 481 EngineReinitWithSeed(data); 482 }; 483 } 484 else 485 { 486 ReseedZeroLength(useEntropy); 487 } 488 } 489 } 490 ProcessAppendedData(uint[] dataArray, bool useEntropy)491 private uint[] ProcessAppendedData(uint[] dataArray, bool useEntropy) 492 { 493 if(useEntropy) 494 { 495 this.DebugLog("Reseed with seed XOR'ed with received data"); 496 for(var index = 0; index < dataArray.Length; index++) 497 { 498 seed[index] |= dataArray[index]; 499 } 500 } 501 else 502 { 503 this.DebugLog("Reseed with received data"); 504 seed = dataArray; 505 } 506 return seed.Reverse().ToArray(); 507 } 508 EngineReinitWithSeed(uint[] seed)509 private void EngineReinitWithSeed(uint[] seed) 510 { 511 fakeEntropy.SetEntropySizeInBytes(seed.Length * sizeof(uint)); 512 fakeEntropy.SetEntropySource(() => 513 { 514 return Misc.AsBytes(seed); 515 }); 516 ReinstantiateInternal(); 517 appendedData.Clear(); 518 } 519 ReseedZeroLength(bool useEntropy)520 private void ReseedZeroLength(bool useEntropy) 521 { 522 if(useEntropy) 523 { 524 fakeEntropy.SetEntropySizeInBytes(DefaultSeedSizeInBytes); 525 fakeEntropy.SetEntropySource(entropySource.RequestEntropySourceData); 526 } 527 else 528 { 529 // Seed should be all zeroes 530 Array.Clear(seed, 0, seed.Length); 531 EngineReseed(seed); 532 } 533 } 534 ExecuteReseed(uint commandLength, bool useEntropy)535 private void ExecuteReseed(uint commandLength, bool useEntropy) 536 { 537 if(WorkingMode == RandomType.HardwareCompliant) 538 { 539 if(commandLength != 0) 540 { 541 appendedDataCount = commandLength; 542 appendedDataAction = (dataArray) => 543 { 544 var data = ProcessAppendedData(dataArray, useEntropy); 545 EngineReseed(data); 546 }; 547 } 548 else 549 { 550 ReseedZeroLength(useEntropy); 551 } 552 } 553 } 554 EngineReseed(uint[] data)555 private void EngineReseed(uint[] data) 556 { 557 if(drbgEngine == null) 558 { 559 ReinstantiateInternal(); 560 } 561 var dataAsBytes = Misc.AsBytes(data); 562 drbgEngine.Reseed(dataAsBytes); 563 } 564 ExecuteUpdate(uint commandLength)565 private void ExecuteUpdate(uint commandLength) 566 { 567 appendedDataCount = commandLength; 568 appendedDataAction = (dataArray) => 569 { 570 var data = dataArray.Reverse().ToArray(); 571 EngineUpdate(data); 572 }; 573 } 574 EngineUpdate(uint[] data)575 private void EngineUpdate(uint[] data) 576 { 577 var dataAsBytes = Misc.AsBytes(data); 578 drbgEngine.Update(dataAsBytes); 579 } 580 ExecuteUninstantiate()581 private void ExecuteUninstantiate() 582 { 583 drbgEngine = null; 584 ClearState(); 585 } 586 ClearState()587 private void ClearState() 588 { 589 fatalErrorInterrupt.Value = false; 590 entropyRequestInterrupt.Value = false; 591 hardwareInstanceInterrupt.Value = false; 592 requestCompletedInterrupt.Value = false; 593 fipsCompliant.Value = false; 594 generatedValidFlag.Value = false; 595 commandError.Value = false; 596 requestFailedFlag.Value = false; 597 } 598 FillFifoWithInternalState()599 private void FillFifoWithInternalState() 600 { 601 foreach(var doubleWord in GenerateInternalStateArray()) 602 { 603 internalStateReadFifo.Enqueue(doubleWord); 604 } 605 } 606 GenerateInternalStateArray()607 private uint[] GenerateInternalStateArray() 608 { 609 var list = new List<uint>(); 610 list.Add(ReseedCount); 611 list.AddRange(InternalV); 612 list.AddRange(InternalKey); 613 var statusBit = drbgEngine != null ? 1u : 0u; 614 var complianceBit = (fipsCompliant.Value ? 1u : 0u) << 1; 615 list.Add(statusBit | complianceBit); 616 return list.ToArray(); 617 } 618 ByteArrayToRegisterOrderedUIntArray(byte[] inputArray)619 private uint[] ByteArrayToRegisterOrderedUIntArray(byte[] inputArray) 620 { 621 uint[] doubleWordRepresentation = new uint[inputArray.Length / sizeof(uint)]; 622 Buffer.BlockCopy(inputArray, 0, doubleWordRepresentation, 0, inputArray.Length); 623 return doubleWordRepresentation.Select(x => Misc.EndiannessSwap(x)).Reverse().ToArray(); 624 } 625 626 private bool NoMoreDataToConsume => appendedDataCount == 0; 627 628 private IFlagRegisterField requestCompletedInterrupt; 629 private IFlagRegisterField entropyRequestInterrupt; 630 private IFlagRegisterField hardwareInstanceInterrupt; 631 private IFlagRegisterField fatalErrorInterrupt; 632 private IFlagRegisterField requestCompletedInterruptEnabled; 633 private IFlagRegisterField entropyRequestInterruptEnabled; 634 private IFlagRegisterField hardwareInstanceInterruptEnabled; 635 private IFlagRegisterField fatalErrorInterruptEnabled; 636 637 private IFlagRegisterField fipsCompliant; 638 private IFlagRegisterField readyFlag; 639 private IFlagRegisterField commandError; 640 private IFlagRegisterField generatedValidFlag; 641 private IFlagRegisterField requestFailedFlag; 642 private IValueRegisterField internalStateSelection; 643 644 private IEnumRegisterField<MultiBitBool4> enabled; 645 private IEnumRegisterField<MultiBitBool4> genbitsReadEnabled; 646 private uint appendedDataCount; 647 private byte[] fixedData; 648 private uint[] seed; 649 private Action<uint[]> appendedDataAction; 650 private Random randomSource; 651 private RandomType workingMode; 652 private readonly Queue<uint> generatedBitsFifo; 653 private readonly FakeEntropy fakeEntropy; 654 private readonly List<uint> appendedData; 655 private readonly Queue<uint> internalStateReadFifo; 656 657 private CtrSP800Drbg drbgEngine; 658 659 private const int BytesPerEntropyUnit = 16; 660 private const int DefaultSeedSizeInBytes = 48; 661 private const int InternalStateSoftwareStateSelection = 2; 662 663 private readonly OpenTitan_EntropySource entropySource; 664 665 #pragma warning disable format 666 public enum RandomType 667 { 668 PseudoRandom = 0x0, // Generated using the System.Random 669 FixedData = 0x1, // Fixed data supplied using the property 670 PseudoRandomFixedSeed = 0x2, // Generated using the System.Random using fixed seed - produces the same sequence of bytes every time 671 HardwareCompliant = 0x3, // Per OpenTitan_CSRNG specification 672 } 673 674 private enum CommandName 675 { 676 Instantiate = 0x1, 677 Reseed = 0x2, 678 Generate = 0x3, 679 Update = 0x4, 680 Uninstantiate = 0x5, 681 } 682 683 private enum Registers 684 { 685 InterruptState = 0x0, 686 InterruptEnable = 0x4, 687 InterruptTest = 0x8, 688 AlertTest = 0xC, 689 RegisterWriteEnable = 0x10, 690 Control = 0x14, 691 CommandRequest = 0x18, 692 CommandStatus = 0x1C, 693 GenerateBitsValid = 0x20, 694 GenerateBits = 0x24, 695 InternalStateNumber = 0x28, 696 InternalStateReadAccess = 0x2C, 697 HardwareExceptionStatus = 0x30, 698 RecoverableAlertStatus = 0x34, 699 ErrorCode = 0x38, 700 ErrorCodeTest = 0x3C, 701 StateMachineState = 0x40, 702 } 703 #pragma warning restore format 704 705 class FakeEntropy: IEntropySource 706 { FakeEntropy(int defaultLengthInBytes, bool predictionResistant = false)707 public FakeEntropy(int defaultLengthInBytes, bool predictionResistant = false) 708 { 709 this.entropySizeInBytes = defaultLengthInBytes; 710 this.predictionResistant = predictionResistant; 711 } 712 713 public bool IsPredictionResistant => predictionResistant; 714 715 // Entropy size in bits 716 public int EntropySize => entropySizeInBytes * 8; 717 SetEntropySizeInBytes(int size)718 public void SetEntropySizeInBytes(int size) 719 { 720 entropySizeInBytes = size; 721 } SetEntropySource(Func<byte[]> function)722 public void SetEntropySource(Func<byte[]> function) 723 { 724 this.function = function; 725 } 726 GetEntropy()727 public byte[] GetEntropy() 728 { 729 if(function == null) 730 { 731 return new byte[entropySizeInBytes]; 732 } 733 return function(); 734 } 735 736 private Func<byte[]> function; 737 private int entropySizeInBytes; 738 private bool predictionResistant; 739 } 740 } 741 } 742