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 Antmicro.Renode.Logging; 8 using Antmicro.Renode.Peripherals.Bus; 9 using Antmicro.Renode.Utilities; 10 using Org.BouncyCastle.Crypto.Engines; 11 using Org.BouncyCastle.Crypto.Modes; 12 using Org.BouncyCastle.Crypto.Parameters; 13 using System; 14 using System.IO; 15 using System.Linq; 16 using System.Security.Cryptography; 17 18 namespace Antmicro.Renode.Peripherals.Miscellaneous.Crypto 19 { 20 public class MessageAuthenticationServiceProvider 21 { MessageAuthenticationServiceProvider(InternalMemoryManager manager, IBusController bus)22 public MessageAuthenticationServiceProvider(InternalMemoryManager manager, IBusController bus) 23 { 24 this.manager = manager; 25 this.bus = bus; 26 } 27 PerformSHA()28 public void PerformSHA() 29 { 30 // Message length is hardcoded to the same value as in the software because it is not written to the internal memories 31 manager.TryReadBytes((long)MsgAuthRegisters.HashInput, SHAMsgLen, out var hashInput); 32 manager.TryWriteBytes((long)MsgAuthRegisters.HashResult, GetHashedBytes(SHAMsgLen, hashInput)); 33 } 34 PerformSHADMA()35 public void PerformSHADMA() 36 { 37 manager.TryReadDoubleWord((long)MsgAuthRegisters.SHADataBytesToProcess, out var hashInputLength); 38 manager.TryReadDoubleWord((long)MsgAuthRegisters.SHAExternalDataLocation, out var hashInputAddr); 39 var bytes = bus.ReadBytes(hashInputAddr, (int)hashInputLength); 40 41 manager.TryReadDoubleWord((long)MsgAuthRegisters.SHAExternalDataResultLocation, out var hashResultLocation); 42 bus.WriteBytes(GetHashedBytes((int)hashInputLength, bytes), hashResultLocation); 43 } 44 PerformHMACSHA()45 public void PerformHMACSHA() 46 { 47 RunAlgorithm(MsgAuthRegisters.SHADMAChannelConfig, NonDMAHMACSHA, DMAHMACSHA); 48 } 49 PerformGCMMessageAuthentication()50 public void PerformGCMMessageAuthentication() 51 { 52 RunAlgorithm(MsgAuthRegisters.DMAChannelConfig, NonDMAGCM, DMAGCM); 53 } 54 Reset()55 public void Reset() 56 { 57 manager.ResetMemories(); 58 } 59 RunAlgorithm(MsgAuthRegisters config, Action nonDMAVersion, Action dmaVersion)60 private void RunAlgorithm(MsgAuthRegisters config, Action nonDMAVersion, Action dmaVersion) 61 { 62 manager.TryReadDoubleWord((long)config, out var dmaConfig); 63 switch((WriteType)dmaConfig) 64 { 65 case WriteType.Direct: 66 nonDMAVersion(); 67 break; 68 case WriteType.DMA: 69 dmaVersion(); 70 break; 71 default: 72 Logger.Log(LogLevel.Warning, "Encountered unexpected DMA configuration: 0x{0:X}", dmaConfig); 73 break; 74 } 75 } 76 GetHashedBytes(int count, byte[] input)77 private byte[] GetHashedBytes(int count, byte[] input) 78 { 79 using(SHA256 sha256Hash = SHA256.Create()) 80 { 81 return sha256Hash.ComputeHash(input, 0, count); 82 } 83 } 84 NonDMAHMACSHA()85 private void NonDMAHMACSHA() 86 { 87 manager.TryReadDoubleWord((long)MsgAuthRegisters.HMACSHAKeyByteCount, out var keyLength); 88 manager.TryReadBytes((long)MsgAuthRegisters.HashMACKey, (int)keyLength, out var hashKey); 89 90 // Message length is hardcoded to the same value as in the software because it is not written to the internal memories 91 // Additionally we add 2 padding bytes to be able to reverse bytes in doublewords 92 manager.TryReadBytes((long)MsgAuthRegisters.HashMACInput, HMACSHAMsgLen + 2, out var msgBytes); 93 94 var myhmacsha256 = new HMACSHA256(hashKey); 95 using(var stream = new MemoryStream(msgBytes, 0, HMACSHAMsgLen)) 96 { 97 var result = myhmacsha256.ComputeHash(stream); 98 manager.TryWriteBytes((long)MsgAuthRegisters.HashResult, result); 99 } 100 } 101 DMAHMACSHA()102 private void DMAHMACSHA() 103 { 104 manager.TryReadDoubleWord((long)MsgAuthRegisters.HMACSHAKeyByteCount, out var keyLength); 105 manager.TryReadBytes((long)MsgAuthRegisters.HashMACKey, (int)keyLength, out var hashKey); 106 107 manager.TryReadDoubleWord((long)MsgAuthRegisters.SHADataBytesToProcess, out var hashInputLength); 108 manager.TryReadDoubleWord((long)MsgAuthRegisters.SHAExternalDataLocation, out var hashInputAddr); 109 var inputBytes = bus.ReadBytes(hashInputAddr, (int)hashInputLength); 110 111 var myhmacsha256 = new HMACSHA256(hashKey); 112 using(var stream = new MemoryStream(inputBytes)) 113 { 114 var result = myhmacsha256.ComputeHash(stream); 115 manager.TryReadDoubleWord((long)MsgAuthRegisters.SHAExternalDataResultLocation, out var hashResultLocation); 116 bus.WriteBytes(result, hashResultLocation); 117 } 118 } 119 NonDMAGCM()120 private void NonDMAGCM() 121 { 122 manager.TryReadDoubleWord((long)MsgAuthRegisters.AuthDataByteCount, out var authBytesLength); 123 manager.TryReadBytes((long)MsgAuthRegisters.AuthData, (int)authBytesLength, out var authBytes); 124 125 manager.TryReadDoubleWord((long)MsgAuthRegisters.InputDataByteCount, out var msgBytesLength); 126 var msgBytesAddend = (msgBytesLength % 4); 127 if(msgBytesAddend != 0) 128 { 129 msgBytesAddend = 4 - msgBytesAddend; 130 msgBytesLength += msgBytesAddend; 131 } 132 133 manager.TryReadBytes((long)MsgAuthRegisters.Message, (int)msgBytesLength, out var msgBytes); 134 135 if(msgBytesAddend != 0) 136 { 137 msgBytes = msgBytes.Take((int)(msgBytesLength - msgBytesAddend)).ToArray(); 138 } 139 140 CalculateGCM(msgBytes, authBytes, MsgAuthRegisters.InitVector, out var ciphertext, out var tag); 141 142 manager.TryWriteBytes((long)MsgAuthRegisters.Message, ciphertext); 143 // We clear the next 4 bytes after the ciphertext to remove any unwanted data written by 144 // previous steps of the algorithm. 145 manager.TryWriteBytes((long)MsgAuthRegisters.Message + ciphertext.Length, new byte[] { 0, 0, 0, 0 }); 146 manager.TryWriteBytes((long)MsgAuthRegisters.Tag, tag); 147 } 148 DMAGCM()149 private void DMAGCM() 150 { 151 manager.TryReadDoubleWord((long)MsgAuthRegisters.KeyWordCount, out var msgByteCount); 152 manager.TryReadDoubleWord((long)MsgAuthRegisters.PointerToExternalData, out var inputDataAddr); 153 var msgBytes = bus.ReadBytes(inputDataAddr, (int)msgByteCount); 154 155 manager.TryReadDoubleWord((long)MsgAuthRegisters.AuthDataByteCount, out var authByteCount); 156 manager.TryReadDoubleWord((long)MsgAuthRegisters.PointerToExternalAuthData, out var authDataAddr); 157 var authBytes = bus.ReadBytes(authDataAddr, (int)authByteCount); 158 159 CalculateGCM(msgBytes, authBytes, MsgAuthRegisters.InitVectorDMA, out var ciphertext, out var tag); 160 161 manager.TryReadDoubleWord((long)MsgAuthRegisters.PointerToExternalResultLocation, out var resultAddr); 162 bus.WriteBytes(ciphertext, resultAddr); 163 manager.TryReadDoubleWord((long)MsgAuthRegisters.PointerToExternalMACLocation, out var macAddr); 164 bus.WriteBytes(tag, macAddr); 165 } 166 CalculateGCM(byte[] msgBytesSwapped, byte[] authBytesSwapped, MsgAuthRegisters initVector, out byte[] ciphertext, out byte[] tag)167 private void CalculateGCM(byte[] msgBytesSwapped, byte[] authBytesSwapped, MsgAuthRegisters initVector, out byte[] ciphertext, out byte[] tag) 168 { 169 var initVectorSize = GCMInitVectorSize128; 170 171 manager.TryReadBytes((long)MsgAuthRegisters.Key, KeyLen, out var keyBytesSwapped); 172 173 manager.TryReadBytes((long)initVector, initVectorSize, out var ivBytesSwapped); 174 // If the user has entered an initialization vector ending with [0x0, 0x0, 0x0, 0x1] (1 being the youngest byte) 175 // it means that it is in fact a 96bit key that was padded with these special bytes, to be 128bit. 176 // Unfortunately, we have to manually trim the padding here because BouncyCastle is expecting an unpadded 177 // initialization vector. 178 // See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf, p.15 for further details. 179 if((ivBytesSwapped[12] == 0) && (ivBytesSwapped[13] == 0) && (ivBytesSwapped[14] == 0) && (ivBytesSwapped[15] == 1)) 180 { 181 ivBytesSwapped = ivBytesSwapped.Take(12).ToArray(); 182 initVectorSize = GCMInitVectorSize96; 183 } 184 185 var cipher = new GcmBlockCipher(new AesEngine()); 186 var parameters = new AeadParameters(new KeyParameter(keyBytesSwapped), (initVectorSize * 8), ivBytesSwapped, authBytesSwapped); 187 cipher.Init(true, parameters); 188 189 var encryptedBytes = new byte[msgBytesSwapped.Length + initVectorSize]; 190 191 var retLen = cipher.ProcessBytes(msgBytesSwapped, 0, msgBytesSwapped.Length, encryptedBytes, 0); 192 cipher.DoFinal(encryptedBytes, retLen); 193 194 ciphertext = new byte[msgBytesSwapped.Length]; 195 tag = new byte[initVectorSize]; 196 197 Buffer.BlockCopy(encryptedBytes, 0, ciphertext, 0, msgBytesSwapped.Length); 198 Buffer.BlockCopy(encryptedBytes, msgBytesSwapped.Length, tag, 0, initVectorSize); 199 } 200 201 private const int GCMInitVectorSize128 = 16; 202 private const int GCMInitVectorSize96 = 12; 203 private const int HMACSHAMsgLen = 34; 204 private const int SHAMsgLen = 32; 205 private const int KeyLen = 32; 206 207 private readonly IBusController bus; 208 private readonly InternalMemoryManager manager; 209 210 private enum WriteType 211 { 212 Direct = 0x0, 213 DMA = 0x8 214 } 215 216 private enum MsgAuthRegisters 217 { 218 KeyWordCount = 0x8, 219 InputDataByteCount = 0x8, 220 HMACSHAKeyByteCount = 0x18, 221 SHADMAChannelConfig = 0x2C, 222 AuthDataByteCount = 0x30, 223 SHADataBytesToProcess = 0x44, 224 SHAExternalDataLocation = 0x48, 225 SHAExternalDataResultLocation = 0x4C, 226 DMAChannelConfig = 0x58, 227 PointerToExternalData = 0x60, 228 PointerToExternalAuthData = 0x64, 229 PointerToExternalResultLocation = 0x68, 230 PointerToExternalMACLocation = 0x6C, 231 OperationAuthBlock = 0x7C, 232 TagWordCount = 0x1054, 233 HashResult = 0x8064, 234 InitVector = 0x807C, 235 InitVectorDMA = 0x8080, 236 HashMACKey = 0x80A4, 237 HashInput = 0x80A8, 238 AuthData = 0x80CC, 239 Message = 0x80DC, 240 Tag = 0x811C, 241 HashMACInput = 0x81A4, 242 Key = 0x9000 243 } 244 } 245 } 246