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