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 using System;
7 using System.Collections.Generic;
8 using System.Security.Cryptography;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Debugging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.Miscellaneous
17 {
18     // OpenTitan HMACi HWIP as per https://docs.opentitan.org/hw/ip/hmac/doc/ (30.06.2021)
19     public class OpenTitan_HMAC: BasicDoubleWordPeripheral, IWordPeripheral, IBytePeripheral, IKnownSize
20     {
OpenTitan_HMAC(IMachine machine)21         public OpenTitan_HMAC(IMachine machine) : base(machine)
22         {
23             key = new byte[SecretKeyLength * 4];
24             digest = new byte[DigestLength * 4];
25 
26             packer = new Packer(this);
27             IRQ = new GPIO();
28             FatalAlert = new GPIO();
29 
30             DefineRegisters();
31         }
32 
Reset()33         public override void Reset()
34         {
35             base.Reset();
36 
37             Array.Clear(key, 0, key.Length);
38             Array.Clear(digest, 0, digest.Length);
39 
40             UpdateInterrupts();
41             FatalAlert.Unset();
42         }
43 
WriteDoubleWord(long offset, uint value)44         public override void WriteDoubleWord(long offset, uint value)
45         {
46             if(IsInFifoWindowRange(offset))
47             {
48                 PushData(value, 4);
49             }
50             else
51             {
52                 base.WriteDoubleWord(offset, value);
53             }
54         }
55 
WriteWord(long offset, ushort value)56         public void WriteWord(long offset, ushort value)
57         {
58             if(IsInFifoWindowRange(offset))
59             {
60                 PushData(value, 2);
61             }
62             else
63             {
64                 this.Log(LogLevel.Warning, "Tried to write value 0x{0:X} at offset 0x{1:X}, but word access to registers is not supported", value, offset);
65             }
66         }
67 
WriteByte(long offset, byte value)68         public void WriteByte(long offset, byte value)
69         {
70             if(IsInFifoWindowRange(offset))
71             {
72                 PushData(value, 1);
73             }
74             else
75             {
76                 this.Log(LogLevel.Warning, "Tried to write value 0x{0:X} at offset 0x{1:X}, but byte access to registers is not supported", value, offset);
77             }
78         }
79 
ReadDoubleWord(long offset)80         public override uint ReadDoubleWord(long offset)
81         {
82             if(IsInFifoWindowRange(offset))
83             {
84                 this.Log(LogLevel.Warning, $"Returning 0");
85                 return 0;
86             }
87             return base.ReadDoubleWord(offset);
88         }
89 
90         //Below methods are required to properly register writes for byte and word accesses
ReadWord(long offset)91         public ushort ReadWord(long offset)
92         {
93             this.Log(LogLevel.Warning, "Tried to read value at offset 0x{0:X}, but word access to registers is not supported", offset);
94             return 0;
95         }
96 
ReadByte(long offset)97         public byte ReadByte(long offset)
98         {
99             this.Log(LogLevel.Warning, "Tried to read value at offset 0x{0:X}, but byte access to registers is not supported", offset);
100             return 0;
101         }
102 
103         public long Size =>  0x1000;
104 
105         public GPIO IRQ { get; }
106         public GPIO FatalAlert { get;  private set;}
107 
HashProcess()108         private void HashProcess()
109         {
110             this.Log(LogLevel.Debug, "Received a 'hash_process' command");
111 
112             var message = packer.DataToArray(endianSwap.Value);
113             byte[] hash;
114 
115             if(hmacEnabled.Value)
116             {
117                 using(var hmac = new HMACSHA256(key))
118                 {
119                     hash = hmac.ComputeHash(message);
120                 }
121             }
122             else
123             {
124                 using(var sha = SHA256.Create())
125                 {
126                     hash = sha.ComputeHash(message);
127                 }
128             }
129 
130             if(digestSwap.Value)
131             {
132                 Misc.EndiannessSwapInPlace(hash, sizeof(uint));
133             }
134 
135             Array.Copy(hash, 0, digest, 0, digest.Length);
136             hmacDoneInterrupt.Value = true;
137             UpdateInterrupts();
138         }
139 
UpdateInterrupts()140         private void UpdateInterrupts()
141         {
142             var hmacDone = hmacDoneInterrupt.Value && hmacDoneInterruptEnable.Value;
143             var fifoEmpty = fifoEmptyInterrupt.Value && fifoEmptyInterruptEnable.Value;
144             var hmacError = hmacErrorInterrupt.Value && hmacErrorInterruptEnable.Value;
145             IRQ.Set(hmacDone || fifoEmpty || hmacError);
146         }
147 
IsInFifoWindowRange(long offset)148         private bool IsInFifoWindowRange(long offset)
149         {
150             return offset >= (long)Registers.FifoWindow && offset < ((long)Registers.FifoWindow + 0x800);
151         }
152 
PushData(uint value, int accessByteCount)153         private void PushData(uint value, int accessByteCount)
154         {
155             this.Log(LogLevel.Noisy, "Pushing {0} bytes from value 0x{1:X}", accessByteCount, value);
156 
157             packer.PushData(value, accessByteCount);
158 
159             // There should be blink on fifoFull bit, but we pretend that we handle hashing fast enough for software not to notice that fifo is ever full
160             // We just raise fifoEmptyInterrupt to make sure it keeps them bytes coming
161             fifoEmptyInterrupt.Value = true;
162             UpdateInterrupts();
163         }
164 
DefineRegisters()165         private void DefineRegisters()
166         {
167             Registers.InterruptStatus.Define(this)
168                 .WithFlag(0, out hmacDoneInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "hmac_done")
169                 .WithFlag(1, out fifoEmptyInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "fifo_empty")
170                 .WithFlag(2, out hmacErrorInterrupt, FieldMode.Read | FieldMode.WriteOneToClear, name: "hmac_err")
171                 .WithIgnoredBits(3, 29)
172                 .WithWriteCallback((_, __) => UpdateInterrupts());
173 
174             Registers.InterruptEnable.Define(this)
175                 .WithFlag(0, out hmacDoneInterruptEnable, name: "hmac_done")
176                 .WithFlag(1, out fifoEmptyInterruptEnable, name: "fifo_empty")
177                 .WithFlag(2, out hmacErrorInterruptEnable, name: "hmac_err")
178                 .WithIgnoredBits(3, 29)
179                 .WithWriteCallback((_, __) => UpdateInterrupts());
180 
181             Registers.InterruptTest.Define(this)
182                 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if(value) hmacDoneInterrupt.Value = true; }, name: "hmac_done")
183                 .WithFlag(1, FieldMode.Write, writeCallback: (_, value) => { if(value) fifoEmptyInterrupt.Value = true; }, name: "fifo_empty")
184                 .WithFlag(2, FieldMode.Write, writeCallback: (_, value) => { if(value) hmacErrorInterrupt.Value = true; }, name: "hmac_err")
185                 .WithIgnoredBits(3, 29)
186                 .WithWriteCallback((_, __) => UpdateInterrupts());
187 
188             Registers.AlertTest.Define(this)
189                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalAlert.Blink(); }, name: "fatal_fault")
190                 .WithIgnoredBits(1, 31);
191 
192             Registers.ConfigurationRegister.Define(this, 0x4)
193                 .WithFlag(0, out hmacEnabled, name: "hmac_en")
194                 .WithFlag(1, out shaEnabled, name: "sha_en")
195                 .WithFlag(2, out endianSwap, name: "endian_swap") //0 - big endian; 1 - little endian
196                 .WithFlag(3, out digestSwap, name: "digest_swap") //1 - big-endian
197                 .WithIgnoredBits(4, 28)
198                 .WithWriteCallback((_, __) =>
199                 {
200                     this.Log(LogLevel.Debug, "Configuration set to hmac_en: {0}, sha_en: {1}, endian_swap: {2}, digest_swap: {3}", hmacEnabled.Value, shaEnabled.Value, endianSwap.Value, digestSwap.Value);
201                 });
202 
203             Registers.Command.Define(this)
204                 .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { }, name: "hash_start") // intentionally do nothing
205                 .WithFlag(1, FieldMode.Write, writeCallback: (_, value) => { if(value) HashProcess(); }, name: "hash_process")
206                 .WithIgnoredBits(2, 30);
207 
208             Registers.Status.Define(this)
209                 .WithFlag(0, FieldMode.Read, name: "fifo_empty", valueProviderCallback: _ => true) // fifo is always empty
210                 .WithFlag(1, FieldMode.Read, name: "fifo_full", valueProviderCallback: _ => false) // fifo is never full
211                 .WithReservedBits(2, 2)
212                 .WithValueField(4, 5, FieldMode.Read, name: "fifo_depth", valueProviderCallback: _ => 0)
213                 .WithIgnoredBits(9, 23);
214 
215             Registers.ErrorCode.Define(this)
216                 .WithTag("err_code", 0, 32);
217 
218             Registers.RandomizationInput.Define(this)
219                 .WithTag("secret", 0, 32);
220 
221             Registers.SecretKey_0.DefineMany(this, SecretKeyLength, (register, idx) =>
222             {
223                 register
224                     .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, value) => SetKeyPart(idx, (uint)value), name: "key");
225             });
226 
227             Registers.Digest_0.DefineMany(this, DigestLength, (register, idx) =>
228             {
229                 register
230                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => GetDigestPart(idx), name: "digest");
231             });
232 
233             Registers.MessageLengthLowerPart.Define(this)
234                 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => (uint)ReceivedLengthInBits, name: "v");
235 
236             Registers.MessageLengthUpperPart.Define(this)
237                 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => (uint)(ReceivedLengthInBits >> 32), name: "v");
238         }
239 
SetKeyPart(int part, uint value)240         private void SetKeyPart(int part, uint value)
241         {
242             DebugHelper.Assert(part >= 0 && part < SecretKeyLength);
243 
244             this.Log(LogLevel.Noisy, "Setting key_{0} to 0x{1:X2}", part, value);
245             var offset = part * 4;
246             for(int i = 3; i >= 0; i--)
247             {
248                 key[i + offset] = (byte)value;
249                 value = value >> 8;
250             }
251         }
252 
GetDigestPart(int part)253         private uint GetDigestPart(int part)
254         {
255             DebugHelper.Assert(part >= 0 && part < DigestLength);
256             return BitHelper.ToUInt32(digest, part * 4, 4, false);
257         }
258 
259         private ulong ReceivedLengthInBits => (ulong)packer.Count * 8;
260 
261         private readonly byte[] digest;
262         private readonly byte[] key;
263         private readonly Packer packer;
264 
265         private IFlagRegisterField hmacDoneInterrupt;
266         private IFlagRegisterField fifoEmptyInterrupt;
267         private IFlagRegisterField hmacErrorInterrupt;
268         private IFlagRegisterField hmacDoneInterruptEnable;
269         private IFlagRegisterField fifoEmptyInterruptEnable;
270         private IFlagRegisterField hmacErrorInterruptEnable;
271         private IFlagRegisterField hmacEnabled;
272         private IFlagRegisterField shaEnabled;
273         private IFlagRegisterField endianSwap;
274         private IFlagRegisterField digestSwap;
275 
276         private const int SecretKeyLength = 8;
277         private const int DigestLength = 8;
278 
279         private class Packer
280         {
Packer(IEmulationElement parent)281             public Packer(IEmulationElement parent)
282             {
283                 this.parent = parent;
284                 byteQueue = new Queue<byte>();
285                 queueLock = new object();
286             }
287 
PushData(uint data, int accessByteCount)288             public void PushData(uint data, int accessByteCount)
289             {
290                 lock(queueLock)
291                 {
292                     for(int i = 0; i < accessByteCount; i++)
293                     {
294                         var b = (byte)(data >> (8 * i));
295                         byteQueue.Enqueue(b);
296                         parent.Log(LogLevel.Noisy, "Pushed byte 0x{0:X2}", b);
297                     }
298                 }
299             }
300 
DataToArray(bool reverse)301             public byte[] DataToArray(bool reverse)
302             {
303                 var byteList = new List<byte>();
304                 lock(queueLock)
305                 {
306                     while(byteQueue.Count > 0)
307                     {
308                         byteList.Add(byteQueue.Dequeue());
309                     }
310                 }
311 
312                 var output = byteList.ToArray();
313                 if(reverse)
314                 {
315                     Array.Reverse(output);
316                 }
317 
318                 return output;
319             }
320 
321             public ulong Count => (ulong)byteQueue.Count;
322 
323             private readonly IEmulationElement parent;
324             private readonly Queue<byte> byteQueue;
325             private readonly object queueLock;
326         }
327 
328         private enum Registers
329         {
330             InterruptStatus        = 0x0,
331             InterruptEnable        = 0x4,
332             InterruptTest          = 0x8,
333             AlertTest              = 0xC,
334             ConfigurationRegister  = 0x10,
335             Command                = 0x14,
336             Status                 = 0x18,
337             ErrorCode              = 0x1C,
338             RandomizationInput     = 0x20,
339             SecretKey_0            = 0x24,
340             SecretKey_1            = 0x28,
341             SecretKey_2            = 0x2C,
342             SecretKey_3            = 0x30,
343             SecretKey_4            = 0x34,
344             SecretKey_5            = 0x38,
345             SecretKey_6            = 0x3C,
346             SecretKey_7            = 0x40,
347             Digest_0               = 0x44,
348             Digest_1               = 0x48,
349             Digest_2               = 0x4C,
350             Digest_3               = 0x50,
351             Digest_4               = 0x54,
352             Digest_5               = 0x58,
353             Digest_6               = 0x5C,
354             Digest_7               = 0x60,
355             MessageLengthLowerPart = 0x64,
356             MessageLengthUpperPart = 0x68,
357             FifoWindow             = 0x800,
358         }
359     }
360 }
361