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 System.Security.Cryptography;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 using Antmicro.Renode.Utilities;
16 using Antmicro.Renode.Utilities.Crypto;
17 
18 namespace Antmicro.Renode.Peripherals.Miscellaneous
19 {
20     public sealed class CC2538_Cryptoprocessor : IDoubleWordPeripheral, IKnownSize
21     {
CC2538_Cryptoprocessor(IMachine machine)22         public CC2538_Cryptoprocessor(IMachine machine)
23         {
24             sysbus = machine.GetSystemBus(this);
25             Interrupt = new GPIO();
26 
27             var keyStoreWrittenRegister = new DoubleWordRegister(this);
28             var keyStoreWriteAreaRegister = new DoubleWordRegister(this);
29             for(var i = 0; i < NumberOfKeys; i++)
30             {
31                 var j = i;
32                 keyStoreWrittenRegister.DefineFlagField(i, writeCallback: (_, value) => { if(value) keys[j] = null; }, valueProviderCallback: _ => keys[j] != null, name: "RAM_AREA_WRITTEN" + i);
33                 keyStoreWriteAreaRegister.DefineFlagField(i, writeCallback: (_, value) => keyStoreWriteArea[j] = value, valueProviderCallback: _ => keyStoreWriteArea[j], name: "RAM_AREA" + i);
34             }
35 
36             var registersMap = new Dictionary<long, DoubleWordRegister>
37             {
38                 {(long)Registers.DmaChannel0Control, new DoubleWordRegister(this)
39                     .WithFlag(0, out dmaInputChannelEnabled, name: "EN")
40                     .WithFlag(1, name: "PRIO") // priority is not handled
41                 },
42                 {(long)Registers.DmaChannel0ExternalAddress, new DoubleWordRegister(this)
43                     .WithValueField(0, 32, out dmaInputAddress)
44                 },
45                 {(long)Registers.DmaChannel0Length, new DoubleWordRegister(this)
46                     .WithValueField(0, 15, writeCallback: (_, value) => DoInputTransfer((int)value), valueProviderCallback: _ => 0)
47                 },
48                 {(long)Registers.DmaChannel1Control, new DoubleWordRegister(this)
49                     .WithFlag(0, out dmaOutputChannelEnabled, name: "EN")
50                     .WithFlag(1, name: "PRIO") // priority is not handled
51                 },
52                 {(long)Registers.DmaChannel1ExternalAddress, new DoubleWordRegister(this)
53                     .WithValueField(0, 32, out dmaOutputAddress)
54                 },
55                 {(long)Registers.DmaChannel1Length, new DoubleWordRegister(this)
56                     .WithValueField(0, 15, writeCallback: (_, value) => DoOutputTransfer((int)value), valueProviderCallback: _ => 0)
57                 },
58                 {(long)Registers.KeyStoreWriteArea, keyStoreWriteAreaRegister},
59                 {(long)Registers.KeyStoreWrittenArea, keyStoreWrittenRegister},
60                 {(long)Registers.KeyStoreSize, new DoubleWordRegister(this)
61                     .WithEnumField(0, 2, out keySize)
62                 },
63                 {(long)Registers.KeyStoreReadArea, new DoubleWordRegister(this)
64                     .WithValueField(0, 4, out selectedKey)
65                     .WithFlag(31, FieldMode.Read, name: "BUSY", valueProviderCallback: _ => false)
66                 },
67                 {(long)Registers.AesControl, new DoubleWordRegister(this)
68                     .WithEnumField(2, 1, out direction)
69                     .WithFlag(5, out cbcEnabled)
70                     .WithFlag(6, out ctrEnabled)
71                     .WithEnumField(7, 2, out counterWidth)
72                     .WithFlag(15, out cbcMacEnabled)
73                     .WithValueField(16, 2, out gcmEnabled, name: "GCM")
74                     .WithFlag(18, out ccmEnabled)
75                     .WithValueField(19, 3, out ccmLengthField, name: "CCM_L")
76                     .WithValueField(22, 3, out ccmLengthOfAuthenticationField, name: "CCM_M")
77                     .WithFlag(29, out saveContext)
78                     .WithFlag(30, out savedContextReady, mode: FieldMode.Read | FieldMode.WriteOneToClear)
79                 },
80                 {(long)Registers.AesCryptoLength0, new DoubleWordRegister(this)
81                     .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, value) => aesOperationLength = checked((int)value))
82                 },
83                 {(long)Registers.AesCryptoLength1, new DoubleWordRegister(this)
84                     .WithValueField(0, 29, FieldMode.Write, writeCallback: (_, value) => { if(value != 0) this.Log(LogLevel.Error, "Unsupported crypto length that spans more than one register."); })
85                 },
86                 {(long)Registers.AlgorithmSelection, new DoubleWordRegister(this)
87                     .WithEnumField(0, 3, out dmaDestination, name: "KEY-STORE AES HASH")
88                     .WithFlag(31, name: "TAG")
89                 },
90                 {(long)Registers.InterruptConfiguration, new DoubleWordRegister(this)
91                     .WithFlag(0, out interruptIsLevel)
92                 },
93                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
94                     .WithFlag(0, out resultInterruptEnabled)
95                     .WithFlag(1, out dmaDoneInterruptEnabled)
96                 },
97                 {(long)Registers.InterruptClear, new DoubleWordRegister(this)
98                     .WithFlag(0, writeCallback: (_, value) => { if(value) { resultInterrupt = false; RefreshInterrupts(); } }, valueProviderCallback: _ => false )
99                     .WithFlag(1, writeCallback: (_, value) => { if(value) { dmaDoneInterrupt = false; RefreshInterrupts(); } }, valueProviderCallback: _ => false )
100                     .WithFlag(29, FieldMode.Read, name: "KEY_ST_RD_ERR")
101                     .WithFlag(30, writeCallback: (_, value) => { if(value) { keyStoreWriteErrorInterrupt = false; RefreshInterrupts(); } }, valueProviderCallback: _ => false, name: "KEY_ST_WR_ERR")
102                     .WithFlag(31, FieldMode.Read, name: "DMA_BUS_ERR")
103                 },
104                 {(long)Registers.InterruptStatus, new DoubleWordRegister(this)
105                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => resultInterrupt, name: "RESULT_AV")
106                     .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => dmaDoneInterrupt, name: "DMA_IN_DONE")
107                     .WithFlag(30, FieldMode.Read, valueProviderCallback: _ => keyStoreWriteErrorInterrupt, name: "KEY_ST_WR_ERR")
108                 },
109                 {(long)Registers.AesAuthLength, new DoubleWordRegister(this)
110                     .WithValueField(0, 32, out aesAuthLength)
111                 }
112             };
113 
114             for(var i = 0; i < 4; i++)
115             {
116                 var j = i;
117                 var ivRegister = new DoubleWordRegister(this)
118                     .WithValueField(0, 32, writeCallback: (_, value) => BitConverter.GetBytes((uint)value).CopyTo(inputVector, j * 4),
119                                             valueProviderCallback: _ => BitConverter.ToUInt32(inputVector, j * 4));
120                 registersMap.Add((long)Registers.AesInputVector + 4 * i, ivRegister);
121 
122                 var tagRegister = new DoubleWordRegister(this)
123                     .WithValueField(0, 32, writeCallback: (_, value) => BitConverter.GetBytes((uint)value).CopyTo(tag, j * 4),
124                                             valueProviderCallback: _ => BitConverter.ToUInt32(tag, j * 4));
125                 registersMap.Add((long)Registers.AesTagOut + 4 * i, tagRegister);
126             }
127 
128             registers = new DoubleWordRegisterCollection(this, registersMap);
129             Reset();
130         }
131 
Reset()132         public void Reset()
133         {
134             registers.Reset();
135 
136             aesOperationLength = 0;
137 
138             keyStoreWriteErrorInterrupt = false;
139             dmaDoneInterrupt = false;
140             resultInterrupt = false;
141             RefreshInterrupts();
142 
143             keys = new byte[NumberOfKeys][];
144             keyStoreWriteArea = new bool[NumberOfKeys];
145             inputVector = new byte[AesBlockSizeInBytes];
146             tag = new byte[AesBlockSizeInBytes];
147         }
148 
ReadDoubleWord(long offset)149         public uint ReadDoubleWord(long offset)
150         {
151             return registers.Read(offset);
152         }
153 
WriteDoubleWord(long offset, uint value)154         public void WriteDoubleWord(long offset, uint value)
155         {
156             registers.Write(offset, value);
157         }
158 
159         public long Size
160         {
161             get
162             {
163                 return 0x800;
164             }
165         }
166 
167         public GPIO Interrupt { get; private set; }
168 
RefreshInterrupts()169         private void RefreshInterrupts()
170         {
171             var value = (resultInterruptEnabled.Value && resultInterrupt) || (dmaDoneInterruptEnabled.Value && dmaDoneInterrupt) || keyStoreWriteErrorInterrupt;
172             this.Log(LogLevel.Debug, "Setting Interrupt to {0}.", value);
173             Interrupt.Set(value);
174             if(!interruptIsLevel.Value)
175             {
176                 keyStoreWriteErrorInterrupt = false;
177                 dmaDoneInterrupt = false;
178                 resultInterrupt = false;
179                 Interrupt.Unset();
180             }
181         }
182 
ProcessDataInMemory(uint inputAddress, uint? outputAddress, int length, Action<Block> processor, Block data = null)183         private void ProcessDataInMemory(uint inputAddress, uint? outputAddress, int length, Action<Block> processor, Block data = null)
184         {
185             SysbusWriter writer = null;
186             var reader = new SysbusReader(sysbus, inputAddress, length);
187             if(outputAddress.HasValue)
188             {
189                 writer = new SysbusWriter(sysbus, outputAddress.Value, length);
190             }
191 
192             if(data == null)
193             {
194                 data = Block.OfSize(AesBlockSizeInBytes);
195             }
196             while(!reader.IsFinished)
197             {
198                 reader.Read(data);
199                 data.PadSpaceLeft(0);
200                 processor(data);
201                 if(writer != null)
202                 {
203                     writer.Write(data.Buffer);
204                 }
205                 data.Index = 0;
206             }
207         }
208 
DoInputTransfer(int length)209         private void DoInputTransfer(int length)
210         {
211             if(!dmaInputChannelEnabled.Value)
212             {
213                 this.Log(LogLevel.Warning, "DMA input transfer detected, but input channel is not enabled. Ignoring it.");
214                 return;
215             }
216             if(length == 0)
217             {
218                 this.Log(LogLevel.Warning, "DMA input transfer of length 0 detected. Ignoring it.");
219                 return;
220             }
221 
222             switch(dmaDestination.Value)
223             {
224             case DmaDestination.KeyStore:
225                 HandleKeyTransfer(length);
226                 break;
227             case DmaDestination.HashEngine:
228                 this.Log(LogLevel.Error, "Hash engine is not supported.");
229                 return;
230             case DmaDestination.Aes:
231                 if(!HandleInputAesTransfer(length))
232                 {
233                     return;
234                 }
235                 break;
236             default:
237                 var isKeyStoreSelected = (((int)dmaDestination.Value) & (int)DmaDestination.KeyStore) != 0;
238                 var isAesSelected = (((int)dmaDestination.Value) & (int)DmaDestination.Aes) != 0;
239                 var isHashSelected = (((int)dmaDestination.Value) & (int)DmaDestination.HashEngine) != 0;
240                 throw new InvalidOperationException(string.Format("Invalid combination of algorithm selection flags: [KEY STORE: {0}, AES: {1}, HASH: {2}]",
241                     isKeyStoreSelected, isAesSelected, isHashSelected));
242             }
243 
244             resultInterrupt = true;
245             dmaDoneInterrupt = true;
246             RefreshInterrupts();
247         }
248 
DoOutputTransfer(int length)249         private void DoOutputTransfer(int length)
250         {
251             if(!dmaOutputChannelEnabled.Value)
252             {
253                 this.Log(LogLevel.Warning, "DMA output transfer detected, but output channel is not enabled. Ignoring it.");
254                 return;
255             }
256             if(dmaDestination.Value != DmaDestination.Aes)
257             {
258                 this.Log(LogLevel.Error, "Output transfer is implemented for AES destination only, but not for {0}. Ignoring the transfer.", dmaDestination.Value);
259                 return;
260             }
261             if(length != aesOperationLength)
262             {
263                 this.Log(LogLevel.Error, "AES operation in which dma length is different than aes length is not supported. Ignoring the transfer.");
264                 return;
265             }
266 
267             // here the real cipher operation begins
268             if(cbcEnabled.Value)
269             {
270                 HandleCbc(length);
271             }
272             else if(ccmEnabled.Value)
273             {
274                 if(direction.Value == Direction.Encryption)
275                 {
276                     HandleCcmEncryption(length);
277                 }
278                 else
279                 {
280                     HandleCcmDecryption(length);
281                     if(saveContext.Value)
282                     {
283                         savedContextReady.Value = true;
284                     }
285                 }
286             }
287             else if(ctrEnabled.Value)
288             {
289                 HandleCtr(length);
290             }
291             else if(gcmEnabled.Value != 0)
292             {
293                 this.Log(LogLevel.Error, "GCM mode is not supported");
294             }
295             else if(!cbcEnabled.Value && !ctrEnabled.Value && (counterWidth.Value == 0) && !cbcMacEnabled.Value && (gcmEnabled.Value == 0) && !ccmEnabled.Value && (ccmLengthField.Value == 0) && (ccmLengthOfAuthenticationField.Value == 0))
296             {
297                 // ECB mode is selected only if bits [28:5] in AesControl register are set to 0.
298                 HandleEcb(length);
299             }
300             else
301             {
302                 this.Log(LogLevel.Error, "No supported cipher mode selected: CCM, CBC-MAC, CBC, CTR, ECB");
303             }
304 
305             dmaDoneInterrupt = true;
306             resultInterrupt = true;
307             RefreshInterrupts();
308         }
309 
HandleInputAesTransfer(int length)310         private bool HandleInputAesTransfer(int length)
311         {
312             if(ccmEnabled.Value)
313             {
314                 HandleCcmAuthentication(length);
315             }
316             else if(cbcMacEnabled.Value)
317             {
318                 HandleCbcMac(length);
319             }
320             else if(cbcEnabled.Value || ctrEnabled.Value)
321             {
322                 return false; // the real crypto operation will start on output transfer
323             }
324             else if(!cbcEnabled.Value && !ctrEnabled.Value && (counterWidth.Value == 0) && !cbcMacEnabled.Value && (gcmEnabled.Value == 0) && !ccmEnabled.Value && (ccmLengthField.Value == 0) && (ccmLengthOfAuthenticationField.Value == 0))
325 
326             {
327                 // ECB mode is selected only if bits [28:5] in AesControl register are set to 0.
328                 return false;
329             }
330             else
331             {
332                 this.Log(LogLevel.Error, "No supported cipher mode selected: CCM, CBC-MAC, CBC, CTR, ECB");
333             }
334 
335             if(saveContext.Value)
336             {
337                 savedContextReady.Value = true;
338             }
339             return true;
340         }
341 
HandleKeyTransfer(int length)342         private void HandleKeyTransfer(int length)
343         {
344             var totalNumberOfActivatedSlots = keyStoreWriteArea.Sum(x => x ? 1 : 0);
345             var keyWriteSlotIndex = keyStoreWriteArea.IndexOf(x => true);
346             var numberOfConsecutiveSlots = keyStoreWriteArea.Skip(keyWriteSlotIndex).TakeWhile(x => x == true).Count();
347 
348             if(totalNumberOfActivatedSlots != numberOfConsecutiveSlots)
349             {
350                 this.Log(LogLevel.Warning, "Bits in key store write area are not set consecutively: {0}, ignoring transfer.", BitHelper.GetSetBitsPretty(BitHelper.GetValueFromBitsArray(keyStoreWriteArea)));
351                 keyStoreWriteErrorInterrupt = true;
352                 RefreshInterrupts();
353                 return;
354             }
355 
356             if(length != numberOfConsecutiveSlots * KeyEntrySizeInBytes)
357             {
358                 this.Log(LogLevel.Warning, "Transfer length {0}B is not consistent with the number selected slots: {1} (each of size {2}B). Ignoring transfer", length, numberOfConsecutiveSlots, KeyEntrySizeInBytes);
359                 keyStoreWriteErrorInterrupt = true;
360                 RefreshInterrupts();
361                 return;
362             }
363 
364             var nonEmptyKeyStoreSlots = keys.Skip(keyWriteSlotIndex).Take(numberOfConsecutiveSlots).Select((v, i) => new { v, i }).Where(x => x.v != null).Select(x => x.i.ToString()).ToList();
365             if(nonEmptyKeyStoreSlots.Count > 0)
366             {
367                 this.Log(LogLevel.Warning, "Trying to write a key to a non empty key store: {0}, ignoring transfer.", string.Join(", ", nonEmptyKeyStoreSlots));
368                 keyStoreWriteErrorInterrupt = true;
369                 RefreshInterrupts();
370                 return;
371             }
372 
373             for(var i = keyWriteSlotIndex; i < numberOfConsecutiveSlots; i++)
374             {
375                 keys[i] = sysbus.ReadBytes(dmaInputAddress.Value, KeyEntrySizeInBytes);
376                 dmaInputAddress.Value += KeyEntrySizeInBytes;
377             }
378         }
379 
HandleCbcMac(int length)380         private void HandleCbcMac(int length)
381         {
382             if(inputVector.Any(x => x != 0))
383             {
384                 this.Log(LogLevel.Warning, "Input vector in CBC-MAC mode should be set to all zeros!. Ignoring this transfer");
385                 return;
386             }
387 
388             using(var aes = AesProvider.GetCbcMacProvider(GetSelectedKey()))
389             {
390                 ProcessDataInMemory((uint)dmaInputAddress.Value, null, length, aes.EncryptBlockInSitu);
391                 aes.LastBlock.CopyTo(tag);
392             }
393         }
394 
IncrementCounter(byte[] buffer, int counterWidth)395         private static void IncrementCounter(byte[] buffer, int counterWidth)
396         {
397             // This is just a manual increment of integer value stored in `counterWidth` LSB bytes of a buffer.
398             // It must be ensured that in case of an overflow the rest of a buffer is not modified.
399             for(int i = 0; i < (counterWidth + 1) * 4; i++)
400             {
401                 if(unchecked(++buffer[buffer.Length - i - 1]) != 0)
402                 {
403                     break;
404                 }
405             }
406         }
407 
HandleCtr(int length)408         private void HandleCtr(int length)
409         {
410             var ivBlock = Block.UsingBytes(inputVector);
411             var encryptedNonceCounterBlock = Block.OfSize(AesBlockSizeInBytes);
412             using(var aes = AesProvider.GetEcbProvider(GetSelectedKey()))
413             {
414                 ProcessDataInMemory((uint)dmaInputAddress.Value, (uint)dmaOutputAddress.Value, length, b =>
415                 {
416                     aes.EncryptBlock(ivBlock, encryptedNonceCounterBlock);
417                     b.XorWith(encryptedNonceCounterBlock);
418                     IncrementCounter(ivBlock.Buffer, (int)counterWidth.Value);
419                 });
420             }
421         }
422 
HandleEcb(int length)423         private void HandleEcb(int length)
424         {
425             using(var aes = AesProvider.GetEcbProvider(GetSelectedKey()))
426             {
427                 var processor = direction.Value == Direction.Encryption
428                     ? (Action<Block>)aes.EncryptBlockInSitu
429                     : aes.DecryptBlockInSitu;
430 
431                 ProcessDataInMemory((uint)dmaInputAddress.Value, (uint)dmaOutputAddress.Value, length, processor);
432             }
433         }
434 
HandleCbc(int length)435         private void HandleCbc(int length)
436         {
437             using(var aes = AesProvider.GetCbcProvider(GetSelectedKey(), inputVector))
438             {
439                 var processor = direction.Value == Direction.Encryption
440                     ? (Action<Block>)aes.EncryptBlockInSitu
441                     : aes.DecryptBlockInSitu;
442 
443                 ProcessDataInMemory((uint)dmaInputAddress.Value, (uint)dmaOutputAddress.Value, length, processor);
444             }
445         }
446 
HandleCcmAuthentication(int length)447         private void HandleCcmAuthentication(int length)
448         {
449             if(ccmLengthOfAuthenticationField.Value == 0 && aesOperationLength == 0)
450             {
451                 // no authentication header is calculated
452                 return;
453             }
454 
455             var adataPresent = false;
456             if(ccmCbcMacAesProvider == null)
457             {
458                 // this is a first ccm dma transfer;
459                 // if it uses adata there will be a second one;
460 
461                 // CCM mode uses CBC-MAC for authentication;
462                 ccmCbcMacAesProvider = AesProvider.GetCbcMacProvider(GetSelectedKey());
463 
464                 ccmCbcMacAesProvider.EncryptBlockInSitu(GenerateB0Block());
465                 var adataBlock = GenerateFirstAdataBlock();
466                 if(adataBlock != null)
467                 {
468                     adataPresent = true;
469                     // there is adata
470                     ProcessDataInMemory((uint)dmaInputAddress.Value, null, length, ccmCbcMacAesProvider.EncryptBlockInSitu, adataBlock);
471                     if(aesOperationLength > 0)
472                     {
473                         // message data will be sent in a second dma transfer
474                         return;
475                     }
476                 }
477             }
478 
479             if(!adataPresent)
480             {
481                 if(aesOperationLength != length)
482                 {
483                     this.Log(LogLevel.Warning, "Message data detected, but aes operation length ({0}) is different than this transfer length ({1}). Aborting the transfer.", aesOperationLength, length);
484                     ccmCbcMacAesProvider.Dispose();
485                     ccmCbcMacAesProvider = null;
486                     return;
487                 }
488 
489                 if(direction.Value == Direction.Decryption)
490                 {
491                     // we must first decrypt the message data before calculating the tag
492                     return;
493                 }
494 
495                 // this is the second transfer with message data
496                 ProcessDataInMemory((uint)dmaInputAddress.Value, null, length, ccmCbcMacAesProvider.EncryptBlockInSitu);
497             }
498 
499             // calculate tag
500             GenerateS0Block().XorWith(ccmCbcMacAesProvider.LastBlock).CopyTo(tag);
501 
502             ccmCbcMacAesProvider.Dispose();
503             ccmCbcMacAesProvider = null;
504         }
505 
HandleCcmEncryption(int length)506         private void HandleCcmEncryption(int length)
507         {
508             // first, we increment a counter
509             IncrementCounter(inputVector, (int)counterWidth.Value);
510             HandleCtr(length);
511 
512             if(ccmCbcMacAesProvider != null)
513             {
514                 ccmCbcMacAesProvider.Dispose();
515                 ccmCbcMacAesProvider = null;
516             }
517         }
518 
HandleCcmDecryption(int length)519         private void HandleCcmDecryption(int length)
520         {
521             // calculate s0 block before changing the counter (and input vector)
522             var s0Block = GenerateS0Block();
523             // first, increment a counter
524             IncrementCounter(inputVector, (int)counterWidth.Value);
525             // decrypt in CTR mode
526             HandleCtr(length);
527 
528             if(ccmCbcMacAesProvider != null)
529             {
530                 // calculate authentication from decrypted data
531                 ProcessDataInMemory((uint)dmaInputAddress.Value, null, length, ccmCbcMacAesProvider.EncryptBlockInSitu);
532                 // calculate tag
533                 s0Block.XorWith(ccmCbcMacAesProvider.LastBlock).CopyTo(tag);
534 
535                 ccmCbcMacAesProvider.Dispose();
536                 ccmCbcMacAesProvider = null;
537             }
538         }
539 
GenerateB0Block()540         private Block GenerateB0Block()
541         {
542             const int aesAuthLengthOffset = 6;
543             const int ccmLengthOfAuthenticationFieldOffset = 3;
544 
545             var result = Block.OfSize(AesBlockSizeInBytes);
546             // flags
547             var flags = (byte)(((aesAuthLength.Value > 0 ? 1u : 0u) << aesAuthLengthOffset)
548                 + ((uint)ccmLengthOfAuthenticationField.Value << ccmLengthOfAuthenticationFieldOffset)
549                 + (uint)ccmLengthField.Value);
550             result.UpdateByte(flags);
551             // nonce
552             var nonceLength = 15 - (int)(ccmLengthField.Value + 1);
553             result.UpdateBytes(inputVector, 1, nonceLength);
554             // l(m) - fill LSB with aes operation length
555             while(result.SpaceLeft > 0)
556             {
557                 result.UpdateByte(result.SpaceLeft > 4
558                     ? (byte)0
559                     : (byte)((aesOperationLength >> ((result.SpaceLeft - 1) * 8)) & 0xff));
560             }
561             return result;
562         }
563 
GenerateFirstAdataBlock()564         private Block GenerateFirstAdataBlock()
565         {
566             const int twoOctetsThreshold = (1 << 16) - (1 << 8);
567 
568             var adataLength = (int)aesAuthLength.Value;
569             if(adataLength == 0)
570             {
571                 return null;
572             }
573 
574             var result = Block.OfSize(AesBlockSizeInBytes);
575 
576             // encode a length
577             if(aesAuthLength.Value < twoOctetsThreshold)
578             {
579                 // use two LSB of adataLength
580                 result.UpdateByte((byte)(adataLength >> 8));
581                 result.UpdateByte((byte)adataLength);
582             }
583             else
584             {
585                 // those are just magic numbers required by RFC 3610
586                 result.UpdateByte(0xff);
587                 result.UpdateByte(0xfe);
588 
589                 // use four LSB of adataLength
590                 result.UpdateByte((byte)(adataLength >> 24));
591                 result.UpdateByte((byte)(adataLength >> 16));
592                 result.UpdateByte((byte)(adataLength >> 8));
593                 result.UpdateByte((byte)adataLength);
594             }
595             // standard allows for longer Adata fields, but we cannot express
596             // them using 32-bit register architecture
597 
598             return result;
599         }
600 
GenerateS0Block()601         private Block GenerateS0Block()
602         {
603             var resultBlock = Block.WithCopiedBytes(inputVector);
604             using(var aesEcb = new AesProvider(CipherMode.ECB, PaddingMode.None, GetSelectedKey()))
605             {
606                 aesEcb.EncryptBlockInSitu(resultBlock);
607             }
608             return resultBlock;
609         }
610 
GetSelectedKey()611         private byte[] GetSelectedKey()
612         {
613             byte[] result;
614 
615             switch(keySize.Value)
616             {
617             case KeySize.Bits128:
618                 return keys[selectedKey.Value];
619             case KeySize.Bits192:
620                 result = new byte[24];
621                 Array.Copy(keys[selectedKey.Value + 1], 0, result, 16, 8);
622                 break;
623             case KeySize.Bits256:
624                 result = new byte[32];
625                 keys[selectedKey.Value + 1].CopyTo(result, 16);
626                 break;
627             default:
628                 this.Log(LogLevel.Error, "Reserved key size value ({0}) used instead of the proper one. Using key consiting of 16 zeroed bytes.", keySize.Value);
629                 return new byte[16];
630             }
631             Array.Copy(keys[selectedKey.Value], result, 16);
632             return result;
633         }
634 
635         private AesProvider ccmCbcMacAesProvider;
636         private bool dmaDoneInterrupt;
637         private bool resultInterrupt;
638         private bool keyStoreWriteErrorInterrupt;
639         private int aesOperationLength;
640         private byte[] inputVector;
641         private byte[] tag;
642         private bool[] keyStoreWriteArea;
643         private byte[][] keys;
644         private readonly IFlagRegisterField saveContext;
645         private readonly IFlagRegisterField savedContextReady;
646         private readonly IFlagRegisterField cbcEnabled;
647         private readonly IFlagRegisterField ctrEnabled;
648         private readonly IEnumRegisterField<CounterWidth> counterWidth;
649         private readonly IFlagRegisterField cbcMacEnabled;
650         private readonly IValueRegisterField gcmEnabled;
651         private readonly IFlagRegisterField ccmEnabled;
652         private readonly IValueRegisterField ccmLengthField;
653         private readonly IValueRegisterField ccmLengthOfAuthenticationField;
654         private readonly IEnumRegisterField<Direction> direction;
655         private readonly IValueRegisterField aesAuthLength;
656         private readonly IValueRegisterField dmaInputAddress;
657         private readonly IValueRegisterField dmaOutputAddress;
658         private readonly IValueRegisterField selectedKey;
659         private readonly IFlagRegisterField dmaInputChannelEnabled;
660         private readonly IFlagRegisterField dmaOutputChannelEnabled;
661         private readonly IEnumRegisterField<KeySize> keySize;
662         private readonly IEnumRegisterField<DmaDestination> dmaDestination;
663         private readonly IFlagRegisterField interruptIsLevel;
664         private readonly IFlagRegisterField resultInterruptEnabled;
665         private readonly IFlagRegisterField dmaDoneInterruptEnabled;
666         private readonly DoubleWordRegisterCollection registers;
667         private readonly IBusController sysbus;
668 
669         private const int NumberOfKeys = 8;
670         private const int KeyEntrySizeInBytes = 16;
671         private const int AesBlockSizeInBytes = 16;
672 
673         private enum Registers : uint
674         {
675             DmaChannel0Control = 0x0, // DMAC_CH0_CTRL
676             DmaChannel0ExternalAddress = 0x4, // DMAC_CH0_EXTADDR
677             DmaChannel0Length = 0xC, // DMAC_CH0_DMALENGTH
678             DmaChannel1Control = 0x20, // DMAC_CH1_CTRL
679             DmaChannel1ExternalAddress = 0x24, // DMAC_CH1_EXTADDR
680             DmaChannel1Length = 0x2C, // DMAC_CH1_DMALENGTH
681             KeyStoreWriteArea = 0x400, // AES_KEY_STORE_WRITE_AREA
682             KeyStoreWrittenArea = 0x404, // AES_KEY_STORE_WRITTEN_AREA
683             KeyStoreSize = 0x408, // AES_KEY_STORE_SIZE
684             KeyStoreReadArea = 0x40C, // AES_KEY_STORE_READ_AREA
685             AesInputVector = 0x540, // AES_AES_IV_0
686             AesControl = 0x550, // AES_AES_CTRL
687             AesCryptoLength0 = 0x554, // AES_AES_C_LENGTH_0
688             AesCryptoLength1 = 0x558, // AES_AES_C_LENGTH_1
689             AesAuthLength = 0x55C, // AES_AES_AUTH_LENGTH
690             AesTagOut = 0x570, // AES_TAG_OUT_0
691             AlgorithmSelection = 0x700, // AES_CTRL_ALG_SEL
692             InterruptConfiguration = 0x780, // AES_CTRL_INT_CFG
693             InterruptEnable = 0x784, // AES_CTRL_INT_EN
694             InterruptClear = 0x788, // AES_CTRL_INT_CLR
695             InterruptStatus = 0x790, // AES_CTRL_INT_STAT
696         }
697 
698         private enum DmaDestination
699         {
700             KeyStore = 1,
701             Aes = 2,
702             HashEngine = 4
703         }
704 
705         private enum KeySize
706         {
707             Bits128 = 1,
708             Bits192 = 2,
709             Bits256 = 3
710         }
711 
712         private enum Direction
713         {
714             Decryption,
715             Encryption
716         }
717 
718         private enum CounterWidth
719         {
720             Bits32,
721             Bits64,
722             Bits96,
723             Bits128
724         }
725 
726         private abstract class SysbusReaderWriterBase
727         {
SysbusReaderWriterBase(IBusController bus, ulong startAddress, int length)728             protected SysbusReaderWriterBase(IBusController bus, ulong startAddress, int length)
729             {
730                 this.bus = bus;
731                 currentAddress = startAddress;
732                 bytesLeft = length;
733             }
734 
735             public bool IsFinished { get { return bytesLeft == 0; } }
736 
737             protected ulong currentAddress;
738             protected int bytesLeft;
739             protected readonly IBusController bus;
740         }
741 
742         private class SysbusReader : SysbusReaderWriterBase
743         {
SysbusReader(IBusController bus, ulong startAddress, int length)744             public SysbusReader(IBusController bus, ulong startAddress, int length) : base(bus, startAddress, length)
745             {
746             }
747 
Read(Block destination)748             public int Read(Block destination)
749             {
750                 var bytesToRead = Math.Min(bytesLeft, destination.SpaceLeft);
751                 bus.ReadBytes(currentAddress, bytesToRead, destination.Buffer, destination.Index);
752                 destination.Index += bytesToRead;
753                 currentAddress += (ulong)bytesToRead;
754                 bytesLeft -= bytesToRead;
755                 return bytesToRead;
756             }
757         }
758 
759         private class SysbusWriter : SysbusReaderWriterBase
760         {
SysbusWriter(IBusController bus, ulong startAddress, int length)761             public SysbusWriter(IBusController bus, ulong startAddress, int length) : base(bus, startAddress, length)
762             {
763             }
764 
Write(byte[] bytes)765             public void Write(byte[] bytes)
766             {
767                 var length = Math.Min(bytesLeft, bytes.Length);
768                 bus.WriteBytes(bytes, currentAddress, length);
769                 currentAddress += (ulong)length;
770                 bytesLeft -= length;
771             }
772         }
773     }
774 }
775