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