// CMAC Sig check. private int executeKIRKCmd10(ByteBuffer @in, int size) { // Return an error if the crypto engine hasn't been initialized. if (!CryptoEngine.CryptoEngineStatus) { return(PSP_KIRK_NOT_INIT); } int headerOffset = @in.position(); // Read in the CMD10 format header. AES128_CMAC_Header header = new AES128_CMAC_Header(@in); if ((header.mode != PSP_KIRK_CMD_MODE_CMD1) && (header.mode != PSP_KIRK_CMD_MODE_CMD2) && (header.mode != PSP_KIRK_CMD_MODE_CMD3)) { return(PSP_KIRK_INVALID_MODE); // Only valid for modes CMD1, CMD2 and CMD3. } if (header.dataSize == 0) { return(PSP_KIRK_DATA_SIZE_IS_ZERO); } AES128 aes = new AES128("AES/CBC/NoPadding"); // Convert the AES CMD1 key into a real byte array. sbyte[] k = new sbyte[16]; for (int i = 0; i < KeyVault.kirkAESKey0.Length; i++) { k[i] = (sbyte)KeyVault.kirkAESKey0[i]; } // Decrypt and extract the new AES and CMAC keys from the top of the data. sbyte[] encryptedKeys = new sbyte[32]; Array.Copy(header.AES128Key, 0, encryptedKeys, 0, 16); Array.Copy(header.CMACKey, 0, encryptedKeys, 16, 16); sbyte[] decryptedKeys = aes.decrypt(encryptedKeys, k, priv_iv); sbyte[] cmacHeaderHash = new sbyte[16]; sbyte[] cmacDataHash = new sbyte[16]; sbyte[] cmacBuf = new sbyte[16]; Array.Copy(decryptedKeys, 16, cmacBuf, 0, cmacBuf.Length); // Position the buffer at the CMAC keys offset. sbyte[] inBuf = new sbyte[@in.capacity() - 0x60 - headerOffset]; Array.Copy(@in.array(), headerOffset + 0x60, inBuf, 0, inBuf.Length); // Calculate CMAC header hash. aes.doInitCMAC(cmacBuf); aes.doUpdateCMAC(inBuf, 0, 0x30); cmacHeaderHash = aes.doFinalCMAC(); int blockSize = header.dataSize; if ((blockSize % 16) != 0) { blockSize += (16 - (blockSize % 16)); } // Calculate CMAC data hash. aes.doInitCMAC(cmacBuf); aes.doUpdateCMAC(inBuf, 0, 0x30 + blockSize + header.dataOffset); cmacDataHash = aes.doFinalCMAC(); for (int i = 0; i < cmacHeaderHash.Length; i++) { if (cmacHeaderHash[i] != header.CMACHeaderHash[i]) { return(PSP_KIRK_INVALID_HEADER_HASH); } } for (int i = 0; i < cmacDataHash.Length; i++) { if (cmacDataHash[i] != header.CMACDataHash[i]) { return(PSP_KIRK_INVALID_DATA_HASH); } } return(0); }
/* * KIRK commands: main emulated crypto functions. */ // Decrypt with AESCBC128-CMAC header and sig check. private int executeKIRKCmd1(ByteBuffer @out, ByteBuffer @in, int size) { // Return an error if the crypto engine hasn't been initialized. if (!CryptoEngine.CryptoEngineStatus) { return(PSP_KIRK_NOT_INIT); } int outPosition = @out.position(); // Copy the input for sig check. ByteBuffer sigIn = @in.duplicate(); sigIn.order(@in.order()); // duplicate() does not copy the order() int headerSize = AES128_CMAC_Header.SIZEOF(); int headerOffset = @in.position(); // Read in the CMD1 format header. AES128_CMAC_Header header = new AES128_CMAC_Header(@in); if (header.mode != PSP_KIRK_CMD_MODE_CMD1) { return(PSP_KIRK_INVALID_MODE); // Only valid for mode CMD1. } // Start AES128 processing. AES128 aes = new AES128("AES/CBC/NoPadding"); // Convert the AES CMD1 key into a real byte array for SecretKeySpec. sbyte[] k = new sbyte[16]; for (int i = 0; i < KeyVault.kirkAESKey0.Length; i++) { k[i] = (sbyte)KeyVault.kirkAESKey0[i]; } // Decrypt and extract the new AES and CMAC keys from the top of the data. sbyte[] encryptedKeys = new sbyte[32]; Array.Copy(header.AES128Key, 0, encryptedKeys, 0, 16); Array.Copy(header.CMACKey, 0, encryptedKeys, 16, 16); sbyte[] decryptedKeys = aes.decrypt(encryptedKeys, k, priv_iv); // Check for a valid signature. int sigCheck = executeKIRKCmd10(sigIn, size); if (decryptedKeys == null) { // Only return the sig check result if the keys are invalid // to allow skipping the CMAC comparision. // TODO: Trace why the CMAC hashes aren't matching. return(sigCheck); } // Get the newly decrypted AES key and proceed with the // full data decryption. sbyte[] aesBuf = new sbyte[16]; Array.Copy(decryptedKeys, 0, aesBuf, 0, aesBuf.Length); // Extract the sealed override ELF params. int elfDataSize = header.dataSize; int elfDataOffset = header.dataOffset; // Input buffer for decryption must have a Length aligned on 16 bytes int paddedElfDataSize = Utilities.alignUp(elfDataSize, 15); // Decrypt all the ELF data. sbyte[] inBuf = new sbyte[paddedElfDataSize]; Array.Copy(@in.array(), elfDataOffset + headerOffset + headerSize, inBuf, 0, paddedElfDataSize); sbyte[] outBuf = aes.decrypt(inBuf, aesBuf, priv_iv); @out.position(outPosition); @out.put(outBuf); @out.limit(elfDataSize); @in.clear(); return(0); }