1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 //  This file is licensed under the MIT License.
5 //  Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Utilities;
14 using Org.BouncyCastle.Crypto.Engines;
15 using Org.BouncyCastle.Crypto.Modes;
16 using Org.BouncyCastle.Crypto.Parameters;
17 
18 namespace Antmicro.Renode.Peripherals.Miscellaneous.Crypto
19 {
20     // NOTE: DMA interface is not implemented, supports only AES-GCM mode
21     public class STM32H7_CRYPTO : BasicDoubleWordPeripheral, IKnownSize
22     {
STM32H7_CRYPTO(IMachine machine)23         public STM32H7_CRYPTO(IMachine machine) : base(machine)
24         {
25             DefineRegisters();
26         }
27 
Reset()28         public override void Reset()
29         {
30             inputFIFO.Clear();
31             outputFIFO.Clear();
32             algorithmState = null;
33             // `currentMode` doesn't have to be reset
34 
35             base.Reset();
36             // Needs to be called after the RegisterCollection is reset
37             UpdateInterrupt();
38         }
39 
40         public long Size => 0x400;
41 
42         public GPIO IRQ { get; } = new GPIO();
43 
DefineRegisters()44         private void DefineRegisters()
45         {
46             Registers.Control.Define(this)
47                 .WithReservedBits(0, 2)
48                 .WithFlag(2, out algorithmDirection, name: "Algorithm direction")
49                 .WithValueField(3, 3, out algorithmModeLow, name: "Algorithm mode [0:2]")
50                 .WithEnumField(6, 2, out dataType, name: "Data type selection")
51                 .WithEnumField(8, 2, out keySize, name: "Key size (AES mode only)")
52                 .WithReservedBits(10, 4)
53                 .WithFlag(14, FieldMode.Write,
54                     writeCallback: (_, value) =>
55                     {
56                         if(!value)
57                         {
58                             return;
59                         }
60 
61                         lock(executeLock)
62                         {
63                             inputFIFO.Clear();
64                             outputFIFO.Clear();
65                         }
66                     },
67                     name: "FIFO flush")
68                 .WithFlag(15, out enabled,
69                     writeCallback: (_, __) =>
70                     {
71                         lock(executeLock)
72                         {
73                             EnableOrDisable();
74                             // If there is any pending data in the FIFO, try sending it now
75                             TryFeedPhase();
76                             UpdateInterrupt();
77                         }
78                     },
79                     name: "CRYPTO Enable")
80                 .WithEnumField(16, 2, out phaseGCMOrCCM, name: "GCM or CCM Phase")
81                 .WithReservedBits(18, 1)
82                 .WithValueField(19, 1, out algorithmModeHigh, name: "Algorithm mode [3]")
83                 .WithReservedBits(20, 12);
84 
85             Registers.Status.Define(this)
86                 // The queues don't have fixed lengths for us, but they still report limits, as provided in the docs
87                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => inputFIFO.Count == 0, name: "Input FIFO empty (IFEM)")
88                 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => inputFIFO.Count < MaximumFifoDepth, name: "Input FIFO not full (IFNF)")
89                 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => outputFIFO.Count > 0, name: "Output FIFO not empty (OFNE)")
90                 .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => outputFIFO.Count >= MaximumFifoDepth, name: "Output FIFO full (OFFU)")
91                 .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => false, name: "CRYPTO is Busy") // Always false - operations are instant for us
92                 .WithReservedBits(5, 27);
93 
94             Registers.DataInput.Define(this)
95                 .WithValueField(0, 32,
96                     writeCallback: (_, value) =>
97                     {
98                         lock(executeLock)
99                         {
100                             // Casts to `uint` are safe, since the field is exactly 32 bits wide
101                             inputFIFO.Enqueue(DoByteSwap((uint)value));
102                             TryFeedPhase();
103                             UpdateInterrupt();
104                         }
105                     },
106                     valueProviderCallback: _ =>
107                     {
108                         lock(executeLock)
109                         {
110                             if(enabled.Value)
111                             {
112                                 this.Log(LogLevel.Warning, "DataInput should not be read from, while CRYPTO is enabled");
113                             }
114 
115                             if(!inputFIFO.TryDequeue(out var result))
116                             {
117                                 this.Log(LogLevel.Warning, "Input FIFO is empty, returning 0");
118                                 return 0;
119                             }
120                             UpdateInterrupt();
121                             return result;
122                         }
123                     },
124                     name: "Data input"
125                 );
126 
127             Registers.DataOutput.Define(this)
128                 .WithValueField(0, 32, FieldMode.Read,
129                     valueProviderCallback: _ =>
130                     {
131                         lock(executeLock)
132                         {
133                             if(!outputFIFO.TryDequeue(out var result))
134                             {
135                                 this.Log(LogLevel.Warning, "Output FIFO is empty, returning 0");
136                                 return 0;
137                             }
138                             UpdateInterrupt();
139                             return DoByteSwap(result);
140                         }
141                     },
142                     name: "Data output"
143                 );
144 
145             Registers.InterruptMaskSetClear.Define(this)
146                 .WithFlag(0, out inputFifoIrqMask, name: "Input FIFO service interrupt mask (INIM)")
147                 .WithFlag(1, out outputFifoIrqMask, name: "Output FIFO service interrupt mask (OUTIM)")
148                 .WithReservedBits(2, 30)
149                 .WithWriteCallback((_, __) => UpdateInterrupt());
150 
151             Registers.RawInterruptStatus.Define(this)
152                 .WithFlag(0, out inputFifoIrqRaw, FieldMode.Read, name: "Input FIFO service raw interrupt status (INRIS)")
153                 .WithFlag(1, out outputFifoIrqRaw, FieldMode.Read, name: "Output FIFO service raw interrupt status (OUTRIS)")
154                 .WithReservedBits(2, 30);
155 
156             Registers.MaskedInterruptStatus.Define(this)
157                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => inputFifoIrqRaw.Value && inputFifoIrqMask.Value, name: "Input FIFO service masked interrupt status (INMIS)")
158                 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => outputFifoIrqRaw.Value && outputFifoIrqMask.Value, name: "Output FIFO service masked interrupt status (OUTMIS)")
159                 .WithReservedBits(2, 30);
160 
161             Registers.Key0L.DefineMany(this, 8, (register, registerIndex) =>
162                     register.WithValueField(0, 32, out keys[registerIndex], name: $"Key{registerIndex / 2}{(registerIndex % 2 > 0 ? "R" : "L")}")
163                 );
164 
165             Registers.InitializationVector0L.DefineMany(this, 4, (register, registerIndex) =>
166                     register.WithValueField(0, 32, out initialVectors[registerIndex], name: $"IV{registerIndex / 2}{(registerIndex % 2 > 0 ? "R" : "L")}")
167                 );
168         }
169 
TryFeedPhase()170         private void TryFeedPhase()
171         {
172             if(algorithmState != null)
173             {
174                 while(inputFIFO.TryDequeue(out var result))
175                 {
176                     algorithmState.FeedThePhase(result);
177                 }
178             }
179         }
180 
DoByteSwap(uint value)181         private uint DoByteSwap(uint value)
182         {
183             switch(dataType.Value)
184             {
185                 case DataType.Bit32:
186                     // No byte swapping
187                     return value;
188                 case DataType.Bit16:
189                     return BitHelper.ReverseWords(value);
190                 case DataType.Bit8:
191                     return BitHelper.ReverseBytes(value);
192                 case DataType.Bit1:
193                     return BitHelper.ReverseBits(value);
194                 default:
195                     throw new InvalidOperationException($"Invalid byte swap option selected: {dataType.Value}");
196             }
197         }
198 
UpdateInterrupt()199         private void UpdateInterrupt()
200         {
201             outputFifoIrqRaw.Value = outputFIFO.Any();
202             inputFifoIrqRaw.Value = inputFIFO.Count < MaximumFifoDepth;
203 
204             bool status = (outputFifoIrqRaw.Value && outputFifoIrqMask.Value)
205                           || (inputFifoIrqRaw.Value && inputFifoIrqMask.Value);
206             this.Log(LogLevel.Noisy, "IRQ set to {0}", status);
207             IRQ.Set(status);
208         }
209 
EnableOrDisable()210         private void EnableOrDisable()
211         {
212             if(!enabled.Value)
213             {
214                 // If we are not enabling the CRYPTO, there is no need to update the status.
215                 // In fact, the current status (e.g. execution phase) needs to be preserved, when the peripheral is re-enabled again.
216                 // So just exit immediately on a disabled peripheral.
217                 return;
218             }
219 
220             var algorithmMode = (AlgorithmMode)((algorithmModeHigh.Value << 3) | algorithmModeLow.Value);
221             // Switch algorithm, but only if not executing workaround
222             if((algorithmMode != currentMode || algorithmState == null) && !DetectGCMWorkaround(algorithmMode))
223             {
224                 if(algorithmMode != AlgorithmMode.AES_GCM)
225                 {
226                     this.Log(LogLevel.Error, "This model implements only: {0} mode but tried to configure it to {1}. Ignoring the operation", nameof(AlgorithmMode.AES_GCM), algorithmMode);
227                     return;
228                 }
229                 algorithmState = new RSA_GCM_State(this);
230                 currentMode = algorithmMode;
231             }
232 
233             try
234             {
235                 this.Log(LogLevel.Debug, "Switching to GCM phase {0}", phaseGCMOrCCM.Value);
236                 switch(phaseGCMOrCCM.Value)
237                 {
238                     case GCMOrCCMPhase.Initialization:
239                         algorithmState.InitializeInitializationPhase(
240                             keys.Select(ks => (uint)ks.Value).Skip(keySizeToAesSkip[keySize.Value]).Reverse().SelectMany(e => BitConverter.GetBytes(e)).Reverse().ToArray(),
241                             initialVectors.Select(ks => (uint)ks.Value).Reverse().SelectMany(e => BitConverter.GetBytes(e)).Reverse().ToArray()
242                         );
243                         // According to the docs:
244                         // "This bit is automatically cleared by hardware when the key preparation process ends (ALGOMODE = 0111) or after GCM/GMAC or CCM Initialization phase."
245                         enabled.Value = false;
246                         return;
247                     case GCMOrCCMPhase.Header:
248                         algorithmState.InitializeHeaderPhase();
249                         return;
250                     case GCMOrCCMPhase.Payload:
251                         algorithmState.InitializePayloadPhase();
252                         return;
253                     case GCMOrCCMPhase.Final:
254                         algorithmState.InitializeFinalPhase();
255                         return;
256                     default:
257                         throw new InvalidOperationException($"Invalid GCM phase: {phaseGCMOrCCM.Value}");
258                 }
259             }
260             catch(Exception e)
261             {
262                 this.Log(LogLevel.Error, "Cryptography backend failed with exception: {0}", e);
263                 enabled.Value = false;
264                 algorithmState = null;
265             }
266         }
267 
DetectGCMWorkaround(AlgorithmMode newMode)268         private bool DetectGCMWorkaround(AlgorithmMode newMode)
269         {
270             // This is a workaround for calculating correct MAC for GCM, if the last block size if less than 128 bit
271             // described in 35.4.8 (CRYP stealing and data padding)
272             if(!(algorithmState is RSA_GCM_State s) || newMode != AlgorithmMode.AES_CTR || phaseGCMOrCCM.Value != GCMOrCCMPhase.Payload)
273             {
274                 return false;
275             }
276             if(s.ExecutingWorkaround)
277             {
278                 return true;
279             }
280             this.Log(LogLevel.Debug, "Executing GCM workaround");
281             // Workaround detected - don't switch to newMode, but we need to handle the bytes in a specific way.
282             // BouncyCastle can handle partial (< 128 bit) last block, but in this case, it will emit both the ciphertext and tag, as output from `DoFinal`
283             // the ciphertext from `ProcessBytes` will be invalid and needs to be discarded.
284             // Since the model should return valid data block-for-block we have to run the cipher two times:
285             // first to obtain the ciphertext, and second to obtain the MAC.
286             // This could be optimized, by extracting MAC calculation outside of the lib, if performance becomes critical.
287             s.ExecuteWorkaround();
288             return true;
289         }
290 
291         private readonly Queue<uint> inputFIFO = new Queue<uint>();
292         private readonly Queue<uint> outputFIFO = new Queue<uint>();
293 
294         private IFlagRegisterField outputFifoIrqMask;
295         private IFlagRegisterField inputFifoIrqMask;
296         private IFlagRegisterField outputFifoIrqRaw;
297         private IFlagRegisterField inputFifoIrqRaw;
298 
299         private IFlagRegisterField algorithmDirection;
300         private IFlagRegisterField enabled;
301         private IValueRegisterField algorithmModeLow;
302         private IValueRegisterField algorithmModeHigh;
303         private IEnumRegisterField<DataType> dataType;
304         private IEnumRegisterField<KeySize> keySize;
305         private IEnumRegisterField<GCMOrCCMPhase> phaseGCMOrCCM;
306 
307         // These are expected to be stored in big-endian format
308         private readonly IValueRegisterField[] keys = new IValueRegisterField[8];
309         private readonly IValueRegisterField[] initialVectors = new IValueRegisterField[4];
310 
311         private FourPhaseState algorithmState;
312         private AlgorithmMode currentMode;
313         private readonly object executeLock = new object();
314 
315         private const int MaximumFifoDepth = 8;
316 
317         private readonly Dictionary<KeySize, int> keySizeToAesSkip = new Dictionary<KeySize, int>()
318         {
319             {KeySize.Bit256, 0},
320             {KeySize.Bit192, 2},
321             {KeySize.Bit128, 4},
322         };
323 
324         private abstract class FourPhaseState
325         {
InitializeInitializationPhase(byte[] key, byte[] iv)326             abstract public void InitializeInitializationPhase(byte[] key, byte[] iv);
InitializeHeaderPhase()327             abstract public void InitializeHeaderPhase();
InitializePayloadPhase()328             abstract public void InitializePayloadPhase();
InitializeFinalPhase()329             abstract public void InitializeFinalPhase();
330 
331             // Feed data from input FIFO
FeedThePhase(uint value)332             abstract public void FeedThePhase(uint value);
333 
334             protected STM32H7_CRYPTO parent;
335         }
336 
337         private class RSA_GCM_State : FourPhaseState
338         {
RSA_GCM_State(STM32H7_CRYPTO parent)339             public RSA_GCM_State(STM32H7_CRYPTO parent)
340             {
341                 this.parent = parent;
342             }
343 
344             public bool ExecutingWorkaround { get; private set; }
345 
FeedThePhase(uint value)346             public override void FeedThePhase(uint value)
347             {
348                 var currentPhase = parent.phaseGCMOrCCM.Value;
349                 if(!CheckIfInitialized())
350                 {
351                     return;
352                 }
353                 byte[] bytes = BitConverter.GetBytes(value).Reverse().ToArray();
354 
355                 switch(currentPhase)
356                 {
357                     case GCMOrCCMPhase.Header:
358                         ProcessHeader(bytes);
359                         break;
360                     case GCMOrCCMPhase.Payload:
361                         ProcessPayload(bytes);
362                         break;
363                     case GCMOrCCMPhase.Final:
364                         ProcessFinal(value);
365                         break;
366                 }
367             }
368 
InitializeInitializationPhase(byte[] key, byte[] iv)369             public override void InitializeInitializationPhase(byte[] key, byte[] iv)
370             {
371                 parent.Log(LogLevel.Debug, "Initializing {0} with key: {1}, iv: {2}", nameof(RSA_GCM_State), Misc.PrettyPrintCollectionHex(key), Misc.PrettyPrintCollectionHex(iv));
372 
373                 gcm = new GcmBlockCipher(new AesEngine());
374                 payload = new List<byte>();
375                 aad = new List<byte>();
376                 final = new List<uint>();
377                 ExecutingWorkaround = false;
378                 payloadCounter = 0;
379 
380                 keyParameters = new KeyParameter(key);
381                 this.iv = iv;
382                 isInitialized = true;
383             }
384 
InitializeHeaderPhase()385             public override void InitializeHeaderPhase()
386             {
387                 if(!CheckIfInitialized())
388                 {
389                     return;
390                 }
391                 if(BitHelper.ToUInt32(iv, 12, 4, false) != 0x2)
392                 {
393                     // The docs suggest to put 0x2 here. This is likely a counter block, but the crypto backend doesn't work well when supplying custom counter value
394                     // So this is ignored with the assert, since it should be unlikely, that a different value will be used
395                     parent.Log(LogLevel.Error, "Last block of IV is not equal to 0x2. This will be ignored, and calculation might be unreliable");
396                 }
397                 iv = iv.Take(12).ToArray();
398                 // Associated data will be fed by the input FIFO later
399                 finalParameters = new AeadParameters(keyParameters, MacSizeInBytes * 8, iv);
400                 // It's a symmetric cipher, it's stuck in encryption mode
401                 // for BouncyCastle decryption will try to compare MAC and throw errors, this is not what we want here
402                 gcm.Init(true, finalParameters);
403             }
404 
InitializePayloadPhase()405             public override void InitializePayloadPhase()
406             {
407                 // Intentionally blank - the payload will be fed through input FIFO
408             }
409 
InitializeFinalPhase()410             public override void InitializeFinalPhase()
411             {
412                 // The model is expecting to get the block describing the length of the header and payload
413                 // And only afterwards will it return the computed MAC
414                 // So this method is intentionally blank - will be fed through input FIFO
415             }
416 
ExecuteWorkaround()417             public void ExecuteWorkaround()
418             {
419                 ExecutingWorkaround = true;
420             }
421 
ProcessHeader(byte[] bytes)422             private void ProcessHeader(byte[] bytes)
423             {
424                 aad.AddRange(bytes);
425                 gcm.ProcessAadBytes(bytes, 0, bytes.Length);
426             }
427 
ProcessPayload(byte[] bytes)428             private void ProcessPayload(byte[] bytes)
429             {
430                 byte[] output = new byte[BlockSizeInBytes];
431                 var length = bytes.Length;
432 
433                 if(IsEncryption)
434                 {
435                     payload.AddRange(bytes);
436                 }
437                 gcm.ProcessBytes(bytes, 0, length, output, 0);
438                 if((++payloadCounter % (BlockSizeInBytes / sizeof(uint))) != 0)
439                 {
440                     // Block size is 128 bits in AES-GCM, so we need to first process 4 uints worth of data
441                     // before returning any data from the FIFO
442                     return;
443                 }
444 
445                 if(!IsEncryption)
446                 {
447                     payload.AddRange(output);
448                 }
449                 parent.Log(LogLevel.Debug, "Got cipher block: {0}", Misc.PrettyPrintCollectionHex(output));
450                 parent.outputFIFO.EnqueueRange(BytesToUIntAndSwapEndianness(output));
451             }
452 
ProcessFinal(uint value)453             private void ProcessFinal(uint value)
454             {
455                 final.Add(value);
456 
457                 if(ExecutingWorkaround)
458                 {
459                     if(final.Count != 4)
460                     {
461                         // First 4 uints supplied to Final, when executing workaround, have to be discarded by us
462                         // since they don't contain the header and payload length
463                         // note, that they most likely have some meaning for the silicon, so you shouldn't just write bogus here on real HW
464                         // but not for us, because of how our crypto backend operates
465                         return;
466                     }
467                     final.Clear();
468                     // Return some bogus data - the driver has to discard it, according to the docs
469                     parent.outputFIFO.EnqueueRange(new uint[] { 0xDEADBEEF, 0xBAADBEEF, 0xFEEDC0DE, 0xDEADC0DE });
470                     ExecutingWorkaround = false;
471                     return;
472                 }
473 
474                 if(final.Count != 4)
475                 {
476                     // We don't have the lengths provided just yet
477                     return;
478                 }
479                 // Length is provided in bits, but BouncyCastle needs bytes
480                 uint headerLen = final[1] / 8;
481                 uint payloadLen = final[3] / 8;
482                 final.Clear();
483                 parent.Log(LogLevel.Debug, "Calculating MAC for given final parameters: headerLen={0}, payloadLen={1}", headerLen, payloadLen);
484 
485                 var gcmTag = new GcmBlockCipher(new AesEngine());
486                 gcmTag.Init(true, finalParameters);
487                 gcmTag.ProcessAadBytes(aad.ToArray(), 0, (int)headerLen);
488 
489                 // This is discarded by the model, but the crypto backend needs the space to performs calculations
490                 var output = new byte[payload.Count];
491                 // This will contain both the MAC and the last cipherblock
492                 var mac = new byte[BlockSizeInBytes + MacSizeInBytes];
493 
494                 // Payload is either ciphertext or plaintext, depending on the selected mode
495                 gcmTag.ProcessBytes(payload.ToArray(), 0, (int)payloadLen, output, 0);
496                 gcmTag.DoFinal(mac, 0);
497                 parent.outputFIFO.EnqueueRange(BytesToUIntAndSwapEndianness(gcmTag.GetMac()));
498             }
499 
BytesToUIntAndSwapEndianness(byte[] bytes)500             private IEnumerable<uint> BytesToUIntAndSwapEndianness(byte[] bytes)
501             {
502                 if(bytes.Length % 4 != 0)
503                 {
504                     // This should never happen, since AES blocks are 128 bits, but let's be sure
505                     throw new InvalidOperationException($"{nameof(RSA_GCM_State)} cipher block is not a multiple of 4!");
506                 }
507 
508                 for(int i = 0; i < bytes.Length; i += 4)
509                 {
510                     // Swap endianness here - and since output is "uint" - take 4 bytes
511                     yield return BitHelper.ToUInt32(bytes, i, 4, false);
512                 }
513             }
514 
CheckIfInitialized()515             private bool CheckIfInitialized()
516             {
517                 if(isInitialized == false)
518                 {
519                     parent.Log(LogLevel.Error, "Initialization Phase has not been executed. Aborting");
520                     return false;
521                 }
522                 return true;
523             }
524 
525             private bool IsEncryption => parent.algorithmDirection.Value == false;
526 
527             private const int BlockSizeInBytes = 128 / 8;
528             private const int MacSizeInBytes = BlockSizeInBytes;
529 
530             private bool isInitialized;
531             private int payloadCounter;
532             private GcmBlockCipher gcm;
533             private List<byte> payload;
534             private List<uint> final;
535             private List<byte> aad;
536             private byte[] iv;
537             private KeyParameter keyParameters;
538             private AeadParameters finalParameters;
539         }
540 
541         private enum GCMOrCCMPhase
542         {
543             Initialization = 0b00,
544             Header = 0b01,
545             Payload = 0b10,
546             Final = 0b11
547         }
548 
549         private enum DataType
550         {
551             Bit32 = 0b00,
552             Bit16 = 0b01,
553             Bit8 = 0b10,
554             Bit1 = 0b11,
555         }
556 
557         private enum KeySize
558         {
559             Bit128 = 0b00,
560             Bit192 = 0b01,
561             Bit256 = 0b10,
562             Reserved = 0b11,
563         }
564 
565         private enum AlgorithmMode
566         {
567             TDES_ECB = 0b0000,
568             TDES_CBC = 0b0001,
569             DES_ECB = 0b0010,
570             DES_CBC = 0b0011,
571             AES_ECB = 0b0100,
572             AES_CBC = 0b0101,
573             AES_CTR = 0b0110,
574             AES_key_prepare_EBC_CBC = 0b0111,
575             AES_GCM = 0b1000,
576             AES_CCM = 0b1001,
577         }
578 
579         private enum Registers
580         {
581             Control = 0x0,
582             Status = 0x4,
583             DataInput = 0x8,
584             DataOutput = 0xC,
585             // NOTE: DMA interface is not supported
586             DMAControl = 0x10,
587             InterruptMaskSetClear = 0x14,
588             RawInterruptStatus = 0x18,
589             MaskedInterruptStatus = 0x1C,
590             Key0L = 0x20,
591             Key0R = 0x24,
592             Key1L = 0x28,
593             Key1R = 0x2C,
594             Key2L = 0x30,
595             Key2R = 0x34,
596             Key3L = 0x38,
597             Key3R = 0x3C,
598             InitializationVector0L = 0x40,
599             InitializationVector0R = 0x44,
600             InitializationVector1L = 0x48,
601             InitializationVector1R = 0x4C,
602             // NOTE: Pre-emptive Context Switching is not supported
603             ContentSwapGCM_CCM0 = 0x50,
604             ContentSwapGCM_CCM1 = 0x54,
605             ContentSwapGCM_CCM2 = 0x58,
606             ContentSwapGCM_CCM3 = 0x5C,
607             ContentSwapGCM_CCM4 = 0x60,
608             ContentSwapGCM_CCM5 = 0x64,
609             ContentSwapGCM_CCM6 = 0x68,
610             ContentSwapGCM_CCM7 = 0x6C,
611             ContentSwapGCM0 = 0x70,
612             ContentSwapGCM1 = 0x74,
613             ContentSwapGCM2 = 0x78,
614             ContentSwapGCM3 = 0x7C,
615             ContentSwapGCM4 = 0x80,
616             ContentSwapGCM5 = 0x84,
617             ContentSwapGCM6 = 0x88,
618             ContentSwapGCM7 = 0x8C,
619         }
620     }
621 }
622