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