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 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 using Antmicro.Renode.Utilities;
16 using Org.BouncyCastle.Crypto.Digests;
17 using Org.BouncyCastle.Crypto.Macs;
18 using Org.BouncyCastle.Crypto.Parameters;
19 
20 namespace Antmicro.Renode.Peripherals.Miscellaneous
21 {
22     public class OpenTitan_KMAC : IDoubleWordPeripheral, IWordPeripheral, IBytePeripheral, IKnownSize, ISideloadableKey
23     {
OpenTitan_KMAC()24         public OpenTitan_KMAC()
25         {
26             KmacDoneIRQ = new GPIO();
27             FifoEmptyIRQ = new GPIO();
28             KmacErrorIRQ = new GPIO();
29             FatalAlert = new GPIO();
30             RecoverableAlert = new GPIO();
31 
32             keyShare = new byte[NumberOfSecretKeys][];
33             for(var i = 0; i < NumberOfSecretKeys; ++i)
34             {
35                 keyShare[i] = new byte[NumberOfRegistersForSecretKey * 4];
36             }
37             prefix = new byte[NumberOfRegistersForPrefix * 4];
38             state = new byte[StateSize];
39             stateMask = new byte[StateSize];
40             sideloadKey = new byte[ExpectedKeyLength];
41             fifo = new Queue<byte>();
42             registers = new DoubleWordRegisterCollection(this, BuildRegisterMap());
43             previousCommand = Command.Done;
44         }
45 
ReadDoubleWord(long offset)46         public uint ReadDoubleWord(long offset)
47         {
48             if(IsInState(offset))
49             {
50                 return ReadDoubleWordFromState(offset);
51             }
52             if(IsInFifo(offset))
53             {
54                 return ReadDoubleWordFromFifo(offset);
55             }
56             return registers.Read(offset);
57         }
58 
WriteDoubleWord(long offset, uint value)59         public void WriteDoubleWord(long offset, uint value)
60         {
61             if(IsInState(offset))
62             {
63                 WriteToState(offset, value);
64             }
65             else if(IsInFifo(offset))
66             {
67                 WriteToFifo(BitConverter.GetBytes(value));
68             }
69             else
70             {
71                 registers.Write(offset, value);
72             }
73         }
74 
ReadWord(long offset)75         public ushort ReadWord(long offset)
76         {
77             this.Log(LogLevel.Warning, "Tried to read value at offset 0x{0:X}, but word access to this region is not supported", offset);
78             return 0x0;
79         }
80 
WriteWord(long offset, ushort value)81         public void WriteWord(long offset, ushort value)
82         {
83             if(IsInFifo(offset))
84             {
85                 WriteToFifo(BitConverter.GetBytes(value));
86             }
87             else
88             {
89                 this.Log(LogLevel.Warning, "Tried to write value 0x{0:X} at offset 0x{1:X}, but word access to this region is not supported", value, offset);
90             }
91         }
92 
ReadByte(long offset)93         public byte ReadByte(long offset)
94         {
95             this.Log(LogLevel.Warning, "Tried to read value at offset 0x{0:X}, but byte access to this region is not supported", offset);
96             return 0x0;
97         }
98 
WriteByte(long offset, byte value)99         public void WriteByte(long offset, byte value)
100         {
101             if(IsInFifo(offset))
102             {
103                 WriteToFifo(new byte[] { value });
104             }
105             else
106             {
107                 this.Log(LogLevel.Warning, "Tried to write value 0x{0:X} at offset 0x{1:X}, but byte access to this region is not supported", value, offset);
108             }
109         }
110 
Reset()111         public void Reset()
112         {
113             registers.Reset();
114             fifo.Clear();
115             for(var i = 0; i < NumberOfSecretKeys; ++i)
116             {
117                 Array.Clear(keyShare[i], 0, keyShare[i].Length);
118             }
119             Array.Clear(prefix, 0, prefix.Length);
120             Array.Clear(state, 0, state.Length);
121             Array.Clear(stateMask, 0, stateMask.Length);
122             sideloadKey = new byte[ExpectedKeyLength];
123             UpdateInterrupts();
124             FatalAlert.Unset();
125             RecoverableAlert.Unset();
126 
127             previousCommand = Command.Done;
128             stateBuffer = null;
129             ClearHasher();
130         }
131 
132         public long Size => 0x1000;
133 
134         public GPIO KmacDoneIRQ { get; }
135 
136         public GPIO FifoEmptyIRQ { get; }
137 
138         public GPIO KmacErrorIRQ { get; }
139 
140         public GPIO FatalAlert { get; }
141 
142         public GPIO RecoverableAlert { get; }
143 
144         public IEnumerable<byte> SideloadKey
145         {
146             set
147             {
148                 var tempKey = value.ToArray();
149                 if(tempKey.Length != ExpectedKeyLength)
150                 {
151                     throw new RecoverableException($"Key has invalid length {tempKey.Length}, expected {ExpectedKeyLength}, ignoring write");
152                 }
153                 sideloadKey = tempKey;
154             }
155         }
156 
TryDecodeOutputLength(byte[] data, out int length)157         private static bool TryDecodeOutputLength(byte[] data, out int length)
158         {
159             if(data == null || data.Length < 1)
160             {
161                 length = default(int);
162                 return false;
163             }
164             var lengthSize = data[data.Length - 1];
165             if(lengthSize < 1 || lengthSize > 4)
166             {
167                 length = default(int);
168                 return false;
169             }
170 
171             var i = data.Length - lengthSize - 1;
172             if(i < 0)
173             {
174                 length = default(int);
175                 return false;
176             }
177 
178             length = 0;
179             for(; i < data.Length - 1; ++i)
180             {
181                 length = (length << 8) | data[i];
182             }
183             // encoded length is in bits, return length in bytes
184             length /= 8;
185             return true;
186         }
187 
TryLeftDecode(byte[] data, int offset, out byte[] str, out int bytesUsed)188         private static bool TryLeftDecode(byte[] data, int offset, out byte[] str, out int bytesUsed)
189         {
190             // this function assumes max stringLengthSize of 2
191             bytesUsed = 0;
192             if(offset < 0 || offset >= data.Length)
193             {
194                 str = default(byte[]);
195                 return false;
196             }
197 
198             var stringLengthSize = (int)data[offset];
199             if(stringLengthSize < 1 || stringLengthSize > 2 || data.Length - offset < stringLengthSize + 1)
200             {
201                 str = default(byte[]);
202                 return false;
203             }
204 
205             var stringLength = (int)data[offset + 1];
206             if(stringLengthSize == 2)
207             {
208                 stringLength = stringLength << 8 | data[offset + 2];
209             }
210             stringLength /= 8;
211             bytesUsed = 1 + stringLengthSize + stringLength;
212 
213             if(data.Length < offset + bytesUsed)
214             {
215                 bytesUsed = 0;
216                 str = default(byte[]);
217                 return false;
218             }
219 
220             str = data.Skip(offset + 1 + stringLengthSize).Take(stringLength).ToArray();
221             return true;
222         }
223 
224         private static readonly byte[] kmacFunctionName = new byte[] { 0x4B, 0x4D, 0x41, 0x43 }; // KMAC
225 
ReadDoubleWordFromState(long offset)226         private uint ReadDoubleWordFromState(long offset)
227         {
228             if(offset >= (long)Registers.State && offset < (long)Registers.State + StateSize)
229             {
230                 return (uint)BitConverter.ToInt32(state, (int)offset - (int)Registers.State);
231             }
232             else if(offset >= (long)Registers.StateMask && offset < (long)Registers.StateMask + StateSize)
233             {
234                 return (uint)BitConverter.ToInt32(stateMask, (int)offset - (int)Registers.StateMask);
235             }
236             else
237             {
238                 this.Log(LogLevel.Warning, "Unhandled read from state at 0x{0:X}", offset);
239                 return 0x0;
240             }
241         }
242 
WriteToState(long offset, uint value)243         private void WriteToState(long offset, uint value)
244         {
245             if(offset >= (long)Registers.State && offset < (long)Registers.State + StateSize)
246             {
247                 state.SetBytesFromValue(value, (int)offset - (int)Registers.State);
248             }
249             else if(offset >= (long)Registers.StateMask && offset < (long)Registers.StateMask + StateSize)
250             {
251                 stateMask.SetBytesFromValue(value, (int)offset - (int)Registers.StateMask);
252             }
253             else
254             {
255                 this.Log(LogLevel.Warning, "Unhandled write to state at 0x{0:X}, value 0x{1:X}", offset, value);
256             }
257         }
258 
ReadDoubleWordFromFifo(long offset)259         private uint ReadDoubleWordFromFifo(long offset)
260         {
261             this.Log(LogLevel.Warning, "Tried to read from fifo at 0x{0:X}, but this region is write only", offset);
262             return 0x0;
263         }
264 
WriteToFifo(byte[] values)265         private void WriteToFifo(byte[] values)
266         {
267             foreach(var b in values)
268             {
269                 if(sha3Absorb.Value || fifo.Count < FifoMaxCount)
270                 {
271                     fifo.Enqueue(b);
272                 }
273                 else
274                 {
275                     this.Log(LogLevel.Warning, "Attempted write to full fifo, value 0x{0:X}", b);
276                 }
277             }
278         }
279 
BuildRegisterMap()280         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
281         {
282             var registersDictionary = new Dictionary<long, DoubleWordRegister>
283             {
284                 {(long)Registers.InterruptState, new DoubleWordRegister(this)
285                     .WithFlag(0, out interruptKmacDone, FieldMode.Read | FieldMode.WriteOneToClear, name: "kmac_done")
286                     .WithFlag(1, out interruptFifoEmpty, FieldMode.Read | FieldMode.WriteOneToClear, name: "fifo_empty")
287                     .WithFlag(2, out interruptKmacError, FieldMode.Read | FieldMode.WriteOneToClear, name: "kmac_err")
288                     .WithReservedBits(3, 29)
289                     .WithWriteCallback((_, __) => UpdateInterrupts())
290                 },
291                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
292                     .WithFlag(0, out interruptEnableKmacDone, name: "kmac_done")
293                     .WithFlag(1, out interruptEnableFifoEmpty, name: "fifo_empty")
294                     .WithFlag(2, out interruptEnableKmacError, name: "kmac_err")
295                     .WithReservedBits(3, 29)
296                     .WithWriteCallback((_, __) => UpdateInterrupts())
297                 },
298                 {(long)Registers.InterruptTest, new DoubleWordRegister(this)
299                     .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { interruptKmacDone.Value |= val; }, name: "kmac_done")
300                     .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { interruptFifoEmpty.Value |= val; }, name: "fifo_empty")
301                     .WithFlag(2, FieldMode.Write, writeCallback: (_, val) => { interruptKmacError.Value |= val; }, name: "kmac_err")
302                     .WithReservedBits(3, 29)
303                     .WithWriteCallback((_, __) => UpdateInterrupts())
304                 },
305                 {(long)Registers.AlertTest, new DoubleWordRegister(this)
306                     .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) RecoverableAlert.Blink(); }, name: "recov_alert")
307                     .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalAlert.Blink(); }, name: "fatal_fault")
308                     .WithReservedBits(2, 30)
309                 },
310                 {(long)Registers.ConfigurationWriteEnable, new DoubleWordRegister(this)
311                     .WithTaggedFlag("en", 0)
312                     .WithReservedBits(1, 31)
313                 },
314                 {(long)Registers.Configuration, new DoubleWordRegister(this)
315                     .WithFlag(0, out kmacEnable, name: "kmac_en")
316                     .WithEnumField<DoubleWordRegister, HashingStrength>(1, 3, out hashingStrength, name: "kstrength")
317                     .WithEnumField<DoubleWordRegister, HashingMode>(4, 2, out hashingMode, name: "mode")
318                     .WithReservedBits(6, 2)
319                     .WithTaggedFlag("msg_endianness", 8)
320                     .WithTaggedFlag("state_endianness", 9)
321                     .WithReservedBits(10, 2)
322                     .WithFlag(12, out useSideloadedKey, name: "sideload")
323                     .WithReservedBits(13, 3)
324                     .WithTag("entropy_mode", 16, 2)
325                     .WithReservedBits(18, 1)
326                     .WithTaggedFlag("entropy_fast_process", 19)
327                     .WithTaggedFlag("msg_mask", 20)
328                     .WithReservedBits(21, 3)
329                     .WithTaggedFlag("entropy_ready", 24)
330                     .WithTaggedFlag("err_processed", 25)
331                     .WithTaggedFlag("en_unsupported_modestrength", 26)
332                     .WithReservedBits(27, 5)
333                 },
334                 {(long)Registers.Command, new DoubleWordRegister(this)
335                     .WithEnumField<DoubleWordRegister, Command>(0, 6, writeCallback: (_, val) => { RunCommand(val); }, valueProviderCallback: _ => 0x0, name: "cmd")
336                     .WithReservedBits(6, 2)
337                     .WithTaggedFlag("entropy_req", 8)
338                     .WithTaggedFlag("hash_cnt_clr", 9)
339                     .WithReservedBits(10, 22)
340                     .WithWriteCallback((_, __) => UpdateInterrupts())
341                 },
342                 {(long)Registers.Status, new DoubleWordRegister(this, 0x4001)
343                     .WithFlag(0, out sha3Idle, name: "sha3_idle")
344                     .WithFlag(1, out sha3Absorb, name: "sha3_absorb")
345                     .WithFlag(2, out sha3Squeeze, name: "sha3_squeeze")
346                     .WithReservedBits(3, 5)
347                     .WithValueField(8, 5, valueProviderCallback: _ => sha3Absorb.Value ? 0 : (uint)fifo.Count / 8, name: "fifo_depth")
348                     .WithReservedBits(13, 1)
349                     .WithFlag(14, valueProviderCallback: _ => fifo.Count == 0 || sha3Absorb.Value, name: "fifo_empty")
350                     .WithFlag(15, valueProviderCallback: _ => fifo.Count == FifoMaxCount, name: "fifo_full")
351                     .WithReservedBits(16, 16)
352                 },
353                 {(long)Registers.EntropyTimerPeriods, new DoubleWordRegister(this)
354                     .WithTag("prescaler", 0, 10)
355                     .WithReservedBits(10, 6)
356                     .WithTag("wait_timer", 16, 16)
357                 },
358                 {(long)Registers.EntropyRefreshCounter, new DoubleWordRegister(this)
359                     .WithTag("hash_cnt", 0, 10)
360                     .WithReservedBits(10, 22)
361                 },
362                 {(long)Registers.EntropyRefreshThreshold, new DoubleWordRegister(this)
363                     .WithTag("threshold", 0, 10)
364                     .WithReservedBits(10, 22)
365                 },
366                 {(long)Registers.EntropySeed0, new DoubleWordRegister(this)
367                     .WithTag("seed", 0, 32)
368                 },
369                 {(long)Registers.EntropySeed1, new DoubleWordRegister(this)
370                     .WithTag("seed", 0, 32)
371                 },
372                 {(long)Registers.EntropySeed2, new DoubleWordRegister(this)
373                     .WithTag("seed", 0, 32)
374                 },
375                 {(long)Registers.EntropySeed3, new DoubleWordRegister(this)
376                     .WithTag("seed", 0, 32)
377                 },
378                 {(long)Registers.EntropySeed4, new DoubleWordRegister(this)
379                     .WithTag("seed", 0, 32)
380                 },
381                 // KeyShareN_M Registers
382                 {(long)Registers.KeyLength, new DoubleWordRegister(this)
383                 // KeySh(PrefixN, 0, prefix.Length); Registers
384                     .WithEnumField<DoubleWordRegister, KeyLength>(0, 3, out keyLength, name: "len")
385                     .WithReservedBits(3, 29)
386                 },
387                 {(long)Registers.ErrorCode, new DoubleWordRegister(this)
388                     .WithValueField(0, 32, out errorCode, name: "err_code")
389                 },
390             };
391 
392             for(var jj = 0; jj < NumberOfSecretKeys; ++jj)
393             {
394                 var j = jj;
395                 var offset = Registers.KeyShare1_0 - Registers.KeyShare0_0;
396                 for(var ii = 0; ii < NumberOfRegistersForSecretKey; ++ii)
397                 {
398                     var i = ii;
399                     registersDictionary.Add((long)Registers.KeyShare0_0 + i * 4 + offset * j, new DoubleWordRegister(this)
400                         .WithValueField(0, 32,
401                             writeCallback: (_, val) => { keyShare[j].SetBytesFromValue((uint)val, i * 4); },
402                             valueProviderCallback: _ => (uint)BitConverter.ToInt32(keyShare[j], i * 4), name: $"key_{i}")
403                     );
404                 }
405             }
406 
407             for(var ii = 0; ii < NumberOfRegistersForPrefix; ++ii)
408             {
409                 var i = ii;
410                 registersDictionary.Add((long)Registers.Prefix0 + i * 4, new DoubleWordRegister(this)
411                     .WithValueField(0, 32,
412                         writeCallback: (_, val) => { prefix.SetBytesFromValue((uint)val, i * 4); },
413                         valueProviderCallback: _ => (uint)BitConverter.ToInt32(prefix, i * 4), name: $"prefix_{i}")
414                 );
415             }
416 
417             return registersDictionary;
418         }
419 
UpdateInterrupts()420         private void UpdateInterrupts()
421         {
422             KmacDoneIRQ.Set(interruptKmacDone.Value && interruptEnableKmacDone.Value);
423             FifoEmptyIRQ.Set(interruptFifoEmpty.Value && interruptEnableFifoEmpty.Value);
424             KmacErrorIRQ.Set(interruptKmacError.Value && interruptEnableKmacError.Value);
425         }
426 
RunCommand(Command command)427         private void RunCommand(Command command)
428         {
429             CheckComandSequence(command);
430             var written = state.Length;
431             switch(command)
432             {
433                 case Command.None:
434                     return;
435                 case Command.Start:
436                     ClearHasher();
437                     if(!CheckModeAndStrength())
438                     {
439                         errorCode.Value = (uint)ErrorCode.UnexpectedModeStrength;
440                         interruptKmacError.Value = true;
441                         this.Log(LogLevel.Warning, "Failed to run `Start` command, unexpexted mode strength");
442                     }
443                     else
444                     {
445                         InitHasher();
446                     }
447                     sha3Absorb.Value = true;
448                     sha3Squeeze.Value = false;
449                     break;
450                 case Command.Process:
451                     var data = fifo.ToArray();
452                     fifo.Clear();
453                     written = RunFirstHasher(data);
454                     sha3Absorb.Value = false;
455                     sha3Squeeze.Value = true;
456                     break;
457                 case Command.Run:
458                     written = RunHasher();
459                     break;
460                 case Command.Done:
461                     sha3Absorb.Value = false;
462                     sha3Squeeze.Value = false;
463                     ClearHasher();
464                     break;
465                 default:
466                     this.Log(LogLevel.Warning, "Incorrect command 0x{0:X}", command);
467                     return;
468             }
469             Array.Clear(state, written, state.Length - written);
470         }
471 
InitHasher()472         private void InitHasher()
473         {
474             if(kmacEnable.Value)
475             {
476                 if(TryDecodePrefix(out var functionName, out var customization) && functionName.SequenceEqual(kmacFunctionName))
477                 {
478                     kmac = new KMac(HashBitLength, customization);
479                 }
480                 else
481                 {
482                     errorCode.Value = (uint)ErrorCode.IncorrectFunctionName;
483                     interruptKmacError.Value = true;
484                     this.Log(LogLevel.Warning, "Failed to run `Start` command, incorrect function name in KMAC mode");
485                 }
486                 return;
487             }
488 
489             switch(hashingMode.Value)
490             {
491                 case HashingMode.SHA3:
492                     sha3 = new Sha3Digest(HashBitLength);
493                     break;
494                 case HashingMode.SHAKE:
495                     shake = new ShakeDigest(HashBitLength);
496                     break;
497                 case HashingMode.CSHAKE:
498                     if(TryDecodePrefix(out var functionName, out var customization))
499                     {
500                         cshake = new CShakeDigest(HashBitLength, functionName, customization);
501                     }
502                     else
503                     {
504                         this.Log(LogLevel.Warning, "Failed to run `Start` command, unexpexted prefix value for cSHAKE");
505                     }
506                     break;
507                 default:
508                     this.Log(LogLevel.Warning, "Hashing mode is in reserved state");
509                     break;
510             }
511         }
512 
RunFirstHasher(byte[] data)513         private int RunFirstHasher(byte[] data)
514         {
515             if(kmacEnable.Value)
516             {
517                 if(kmac == null)
518                 {
519                     this.Log(LogLevel.Warning, "Attempted to run `Process` command in KMAC mode after failed initialization");
520                     return 0;
521                 }
522 
523                 if(TryDecodeOutputLength(data, out var outputLength))
524                 {
525                     kmac.Init(new KeyParameter(Key.Take(KeyLengthInBytes).ToArray()));
526                     // remove output length bytes
527                     data = data.Take(data.Length - data[data.Length - 1] - 1).ToArray();
528                     kmac.BlockUpdate(data, 0, data.Length);
529 
530                     if(outputLength != 0) // fixed-length output
531                     {
532                         var output = new byte[outputLength];
533                         kmac.DoFinal(output, 0, outputLength);
534                         stateBuffer = new Queue<byte[]>(output.Split(kmac.GetByteLength()));
535                         var buffer = stateBuffer.Dequeue();
536                         Array.Copy(buffer, state, buffer.Length);
537                         kmac = null;
538                         return buffer.Length;
539                     }
540                     else // arbitrary-length output
541                     {
542                         stateBuffer = null;
543                         return kmac.DoOutput(state, 0, kmac.GetByteLength());
544                     }
545                 }
546 
547                 this.Log(LogLevel.Warning, "Unexpexted data in KMAC mode");
548                 return 0;
549             }
550 
551             switch(hashingMode.Value)
552             {
553                 case HashingMode.SHA3:
554                     if(sha3 != null)
555                     {
556                         sha3.BlockUpdate(data, 0, data.Length);
557                         return sha3.DoFinal(state, 0);
558                     }
559                     break;
560                 case HashingMode.SHAKE:
561                     if(shake != null)
562                     {
563                         shake.BlockUpdate(data, 0, data.Length);
564                         return shake.DoOutput(state, 0, shake.GetByteLength());
565                     }
566                     break;
567                 case HashingMode.CSHAKE:
568                     if(cshake != null)
569                     {
570                         cshake.BlockUpdate(data, 0, data.Length);
571                         return cshake.DoOutput(state, 0, cshake.GetByteLength());
572                     }
573                     break;
574                 default:
575                     this.Log(LogLevel.Warning, "Hashing mode is in reserved state");
576                     return 0;
577             }
578 
579             this.Log(LogLevel.Warning, "Attempted to run `Process` command in {0} after failed initialization", hashingMode.Value);
580             return 0;
581         }
582 
RunHasher()583         private int RunHasher()
584         {
585             if(kmacEnable.Value)
586             {
587                 if(stateBuffer != null && stateBuffer.Count > 0)
588                 {
589                     var buffer = stateBuffer.Dequeue();
590                     Array.Copy(buffer, state, buffer.Length);
591                     return buffer.Length;
592                 }
593 
594                 if(kmac != null)
595                 {
596                     return kmac.DoOutput(state, 0, kmac.GetByteLength());
597                 }
598 
599                 this.Log(LogLevel.Warning, "No digest data available for `Run` command in KMAC mode");
600                 return 0;
601             }
602 
603             switch(hashingMode.Value)
604             {
605                 case HashingMode.SHAKE:
606                     if(shake != null)
607                     {
608                         return shake.DoOutput(state, 0, shake.GetByteLength());
609                     }
610                     break;
611                 case HashingMode.CSHAKE:
612                     if(cshake != null)
613                     {
614                         return cshake.DoOutput(state, 0, cshake.GetByteLength());
615                     }
616                     break;
617                 default:
618                     errorCode.Value = (uint)ErrorCode.SwCmdSequence | (uint)Command.Run;
619                     interruptKmacError.Value = true;
620                     this.Log(LogLevel.Warning, "Unexpected hashing mode ({0}) for `Run` command", hashingMode.Value);
621                     return 0;
622             }
623 
624             this.Log(LogLevel.Warning, "Attempted to run `Run` command in {0} after failed initialization", hashingMode.Value);
625             return 0;
626         }
627 
ClearHasher()628         private void ClearHasher()
629         {
630             sha3 = null;
631             shake = null;
632             cshake = null;
633             kmac = null;
634         }
635 
CheckComandSequence(Command command)636         private void CheckComandSequence(Command command)
637         {
638             var error = false;
639             switch(command)
640             {
641                 case Command.None:
642                     return;
643                 case Command.Start:
644                     error = previousCommand != Command.Done;
645                     break;
646                 case Command.Process:
647                     error = previousCommand != Command.Start;
648                     break;
649                 case Command.Run:
650                 case Command.Done:
651                     error = previousCommand != Command.Process && previousCommand != Command.Run;
652                     break;
653                 default:
654                     error = true;
655                     break;
656             }
657             errorCode.Value = (uint)ErrorCode.SwCmdSequence | (uint)command;
658             interruptKmacError.Value = true;
659             previousCommand = command;
660         }
661 
CheckModeAndStrength()662         private bool CheckModeAndStrength()
663         {
664             var mode = kmacEnable.Value ? HashingMode.CSHAKE : hashingMode.Value;
665             switch(mode)
666             {
667                 case HashingMode.SHA3:
668                     switch(HashBitLength)
669                     {
670                         case 224:
671                         case 256:
672                         case 384:
673                         case 512:
674                             return true;
675                         default:
676                             return false;
677                     }
678                 case HashingMode.SHAKE:
679                 case HashingMode.CSHAKE:
680                     switch(HashBitLength)
681                     {
682                         case 128:
683                         case 256:
684                             return true;
685                         default:
686                             return false;
687                     }
688                 default:
689                     return false;
690             }
691         }
692 
TryDecodePrefix(out byte[] functionName, out byte[] customization)693         private bool TryDecodePrefix(out byte[] functionName, out byte[] customization)
694         {
695             if(!TryLeftDecode(prefix, 0, out functionName, out var bytesUsed) || bytesUsed > NumberOfRegistersForPrefix * 4 - 2)
696             {
697                 customization = default(byte[]);
698                 return false;
699             }
700 
701             return TryLeftDecode(prefix, bytesUsed, out customization, out bytesUsed);
702         }
703 
IsInState(long offset)704         private bool IsInState(long offset)
705         {
706             return offset >= (long)Registers.State && offset < (long)Registers.State + StateLength;
707         }
708 
IsInFifo(long offset)709         private bool IsInFifo(long offset)
710         {
711             return offset >= (long)Registers.Fifo && offset < (long)Registers.Fifo + FifoLength;
712         }
713 
714         private IEnumerable<byte> Key
715         {
716             get
717             {
718                 if(useSideloadedKey.Value)
719                 {
720                     return sideloadKey;
721                 }
722                 else
723                 {
724                     return keyShare[0];
725                 }
726             }
727         }
728 
729         private int HashBitLength
730         {
731             get
732             {
733                 switch(hashingStrength.Value)
734                 {
735                     case HashingStrength.L128:
736                         return 128;
737                     case HashingStrength.L224:
738                         return 224;
739                     case HashingStrength.L256:
740                         return 256;
741                     case HashingStrength.L384:
742                         return 384;
743                     case HashingStrength.L512:
744                         return 512;
745                     default:
746                         this.Log(LogLevel.Warning, "Hashing strength set to reserved value, 0x{0:X}", hashingStrength.Value);
747                         return 0;
748                 }
749             }
750         }
751 
752         private int KeyLengthInBytes
753         {
754             get
755             {
756                 switch(keyLength.Value)
757                 {
758                     case KeyLength.Key128:
759                         return 128 / 8;
760                     case KeyLength.Key192:
761                         return 192 / 8;
762                     case KeyLength.Key256:
763                         return 256 / 8;
764                     case KeyLength.Key384:
765                         return 284 / 8;
766                     case KeyLength.Key512:
767                         return 512 / 8;
768                     default:
769                         this.Log(LogLevel.Warning, "Key length set to reserved value, 0x{0:X}", keyLength.Value);
770                         return 0;
771                 }
772             }
773         }
774 
775         private IFlagRegisterField interruptKmacDone;
776         private IFlagRegisterField interruptFifoEmpty;
777         private IFlagRegisterField interruptKmacError;
778         private IFlagRegisterField interruptEnableKmacDone;
779         private IFlagRegisterField interruptEnableFifoEmpty;
780         private IFlagRegisterField interruptEnableKmacError;
781         private IFlagRegisterField kmacEnable;
782         private IFlagRegisterField useSideloadedKey;
783         private IFlagRegisterField sha3Idle;
784         private IFlagRegisterField sha3Absorb;
785         private IFlagRegisterField sha3Squeeze;
786         private IEnumRegisterField<HashingStrength> hashingStrength;
787         private IEnumRegisterField<HashingMode> hashingMode;
788         private IEnumRegisterField<KeyLength> keyLength;
789         private IValueRegisterField errorCode;
790         private Command previousCommand;
791         private byte[] sideloadKey;
792         private Queue<byte[]> stateBuffer;
793         private byte[] stateMask;
794         private Sha3Digest sha3;
795         private ShakeDigest shake;
796         private CShakeDigest cshake;
797         private KMac kmac;
798 
799         private readonly DoubleWordRegisterCollection registers;
800         private readonly byte[][] keyShare;
801         private readonly byte[] prefix;
802         private readonly byte[] state;
803         private readonly Queue<byte> fifo;
804 
805         private const int NumberOfRegistersForSecretKey = 16;
806         private const int NumberOfRegistersForPrefix = 11;
807         private const int NumberOfSecretKeys = 2;
808         private const int StateLength = 0x200;
809         private const int FifoLength = 0x800;
810         private const int ExpectedKeyLength = 64;
811         private const int StateSize = 0xC8;
812         private const int FifoMaxCount = 8 * 9;
813 
814         private enum HashingStrength
815         {
816             L128 = 0,
817             L224 = 1,
818             L256 = 2,
819             L384 = 3,
820             L512 = 4
821         }
822 
823         private enum KeyLength
824         {
825             Key128 = 0,
826             Key192 = 1,
827             Key256 = 2,
828             Key384 = 3,
829             Key512 = 4,
830         }
831 
832         private enum HashingMode
833         {
834             SHA3   = 0,
835             SHAKE  = 2,
836             CSHAKE = 3
837         }
838 
839         private enum Command
840         {
841             None    = 0x0,
842             Start   = 0x1d,
843             Process = 0x2e,
844             Run     = 0x31,
845             Done    = 0x16
846         }
847 
848         private enum ErrorCode
849         {
850             KeyNotValid             = 0x01,
851             SwPushedMsgFifo         = 0x02,
852             SwIssuedCmdInAppActive  = 0x03,
853             WaitTimerExpired        = 0x04,
854             IncorrectEntropyMode    = 0x05,
855             UnexpectedModeStrength  = 0x06,
856             IncorrectFunctionName   = 0x07,
857             SwCmdSequence           = 0x08,
858             Sha3Control             = 0x80,
859         }
860 
861         private enum Registers : long
862         {
863             InterruptState              = 0x00,
864             InterruptEnable             = 0x04,
865             InterruptTest               = 0x08,
866             AlertTest                   = 0x0C,
867             ConfigurationWriteEnable    = 0x10,
868             Configuration               = 0x14,
869             Command                     = 0x18,
870             Status                      = 0x1C,
871             EntropyTimerPeriods         = 0x20,
872             EntropyRefreshCounter       = 0x24,
873             EntropyRefreshThreshold     = 0x28,
874             EntropySeed0                = 0x2C,
875             EntropySeed1                = 0x30,
876             EntropySeed2                = 0x34,
877             EntropySeed3                = 0x38,
878             EntropySeed4                = 0x3C,
879             KeyShare0_0                 = 0x40,
880             KeyShare0_1                 = 0x44,
881             KeyShare0_2                 = 0x48,
882             KeyShare0_3                 = 0x4C,
883             KeyShare0_4                 = 0x50,
884             KeyShare0_5                 = 0x54,
885             KeyShare0_6                 = 0x58,
886             KeyShare0_7                 = 0x5C,
887             KeyShare0_8                 = 0x60,
888             KeyShare0_9                 = 0x64,
889             KeyShare0_10                = 0x68,
890             KeyShare0_11                = 0x6C,
891             KeyShare0_12                = 0x70,
892             KeyShare0_13                = 0x74,
893             KeyShare0_14                = 0x78,
894             KeyShare0_15                = 0x7C,
895             KeyShare1_0                 = 0x80,
896             KeyShare1_1                 = 0x84,
897             KeyShare1_2                 = 0x88,
898             KeyShare1_3                 = 0x8C,
899             KeyShare1_4                 = 0x90,
900             KeyShare1_5                 = 0x94,
901             KeyShare1_6                 = 0x98,
902             KeyShare1_7                 = 0x9C,
903             KeyShare1_8                 = 0xA0,
904             KeyShare1_9                 = 0xA4,
905             KeyShare1_10                = 0xA8,
906             KeyShare1_11                = 0xAC,
907             KeyShare1_12                = 0xB0,
908             KeyShare1_13                = 0xB4,
909             KeyShare1_14                = 0xB8,
910             KeyShare1_15                = 0xBC,
911             KeyLength                   = 0xC0,
912             Prefix0                     = 0xC4,
913             Prefix1                     = 0xC8,
914             Prefix2                     = 0xCC,
915             Prefix3                     = 0xD0,
916             Prefix4                     = 0xD4,
917             Prefix5                     = 0xD8,
918             Prefix6                     = 0xDC,
919             Prefix7                     = 0xE0,
920             Prefix8                     = 0xE4,
921             Prefix9                     = 0xE8,
922             Prefix10                    = 0xEC,
923             ErrorCode                   = 0xF0,
924             State                       = 0x400,
925             StateMask                   = 0x500,
926             Fifo                        = 0x800
927         }
928     }
929 }
930