/// <summary> /// /// </summary> /// <returns></returns> public CommandAPDU CreateExternalAuthCommand() { MemoryStream memStream = new MemoryStream(); memStream.Write(mInitUpdateResponse, 12, 8); memStream.Write(mHostChallenge, 0, mHostChallenge.Length); byte[] hostCryptogram = CryptoUtil.FullTripleDESMAC(mSessionKeys.RetrieveKey(Key.KEY_TYPE_ENC), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, CryptoUtil.DESPad(memStream.ToArray())); int P1 = mSecurityLevel; CommandAPDU externalAuth = new CommandAPDU(CLA_SECURE_GP, INS_EXT_AUTH, P1, 0x00, hostCryptogram); mSecureChannel = new SecureChannel(mSessionKeys, SecurityLevel.C_MAC, mSCPIdentifier, mSCPImplementationOption, CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK); externalAuth = mSecureChannel.wrap(externalAuth); return(externalAuth); }
public ResponseAPDU unwrap(ResponseAPDU response) { if ((mSecurityLevel & net.SecurityLevel.R_MAC) != 0) { if (response.Data.Length < 8) { throw new Exception("Response data length must be at least 8 bytes."); } if (mRMACStream == null) { throw new Exception("No corresponding wrapped command found while R-MAC security level set. Secure channel can only work correctly if " + "for each wrapped command the corresponding response be unwrapped immediately."); } int realResponseLength = response.Data.Length - 8; mRMACStream.WriteByte((byte)realResponseLength); mRMACStream.Write(response.Data, 0, realResponseLength); mRMACStream.WriteByte((byte)response.SW1); mRMACStream.WriteByte((byte)response.SW2); mRICV = CryptoUtil.SingleDESFullTripleDESMAC(mSessionKeys.RmacKey, mRICV, CryptoUtil.DESPad(mRMACStream.ToArray())); byte[] realMac = new byte[8]; System.Array.Copy(response.Data, realResponseLength, realMac, 0, 8); if (Enumerable.SequenceEqual(realMac, mRICV)) { throw new Exception("Invalid R-MAC."); } mRMACStream = null; response = new ResponseAPDU(response.SW1, response.SW2, CryptoUtil.SubArray(response.Data, 0, realResponseLength)); } return(response); }
/// <summary> /// /// </summary> /// <param name="response"></param> public void ProcessInitUpdateResponse(ResponseAPDU response) { checkResponse(response.SW1, response.SW2, "INITIALIZE UPDATE command failed."); // Check response length it should always be 28 bytes if (response.Data.Length != 28) { throw new Exception("Wrong INIT UPDATE response length."); } System.Array.Copy(response.Data, mInitUpdateResponse, 28); if (mSCPIdentifier == SCP_ANY) { mSCPIdentifier = mInitUpdateResponse[11] == SCP_02 ? SCP_02 : SCP_01; if (mSCPImplementationOption == IMPL_OPTION_ANY) { mSCPImplementationOption = mSCPIdentifier == SCP_02 ? IMPL_OPTION_I_15 : IMPL_OPTION_I_05; } } if (mSCPIdentifier != mInitUpdateResponse[11]) { throw new Exception("Secure channel identifier specified does not match to card"); } // If we use SPC '01' then clear R_MAC bit if (mSCPIdentifier == SCP_01) { mSecurityLevel &= ~SecurityLevel.R_MAC; } // derivate session keys if (mSCPIdentifier == SCP_01) { mSessionKeys = GenerateSessionKeysSCP01(mInitUpdateResponse); } else if (mSCPIdentifier == SCP_02) { byte[] sequenceCoutner = new byte[2]; System.Array.Copy(mInitUpdateResponse, 12, sequenceCoutner, 0, 2); mSessionKeys = GenerateSessionKeysSCP02(sequenceCoutner); } MemoryStream memStream = new MemoryStream(); memStream.Write(mHostChallenge, 0, mHostChallenge.Length); memStream.Write(mInitUpdateResponse, 12, 8); byte[] calculatedCryptogram = CryptoUtil.FullTripleDESMAC(mSessionKeys.RetrieveKey(Key.KEY_TYPE_ENC), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, CryptoUtil.DESPad(memStream.ToArray())); byte[] cardCryptogram = new byte[8]; System.Array.Copy(mInitUpdateResponse, 20, cardCryptogram, 0, 8); if (!Enumerable.SequenceEqual(cardCryptogram, calculatedCryptogram)) { throw new Exception("Invalid cryptogram."); } }
public CommandAPDU wrap(CommandAPDU command) { // Apply R-MAC if ((mSecurityLevel & net.SecurityLevel.R_MAC) != 0) { if (mRMACStream != null) { throw new Exception("There exists an unwrapped response while R-MAC security level set. Secure channel can only work correctly if " + "for each wrapped command the corresponding response be unwrapped immediately."); } mRMACStream = new MemoryStream(); //Clear 3 LSB of CLA mRMACStream.WriteByte((byte)(command.CLA & ~0x07)); mRMACStream.WriteByte((byte)command.INS); mRMACStream.WriteByte((byte)command.P1); mRMACStream.WriteByte((byte)command.P2); if (command.LC > 0) { mRMACStream.WriteByte((byte)command.LC); mRMACStream.Write(command.Data, 0, command.Data.Length); } } if ((mSecurityLevel & (net.SecurityLevel.C_MAC | net.SecurityLevel.C_DECRYPTION)) == 0) { return(command); } int secureCLA = command.CLA; byte[] wrappedData = null; int wrappedDataSize = command.LC; MemoryStream commandStream = new MemoryStream(); int maxCommandSize = 255; if ((mSecurityLevel & net.SecurityLevel.C_MAC) != 0) { maxCommandSize -= 8; } if ((mSecurityLevel & net.SecurityLevel.C_DECRYPTION) != 0) { maxCommandSize -= 8; } if (command.LC > maxCommandSize) { throw new Exception("APDU command too large. Max command length = 255 - 8(for C-MAC if present) - 8(for C-DECRYTPION padding if present)."); } if ((mSecurityLevel & net.SecurityLevel.C_MAC) != 0) { if (mFirstCommandInChain) { mFirstCommandInChain = false; } else if (mICVEncryption) { if (mSCPIdentifier == GlobalPlatform.SCP_01) { mICV = CryptoUtil.TripleDESECB(new Key(mSessionKeys.MacKey.BuildTripleDesKey()), mICV, CryptoUtil.MODE_ENCRYPT); } else { mICV = CryptoUtil.DESECB(new Key(mSessionKeys.MacKey.BuildDesKey()), mICV, CryptoUtil.MODE_ENCRYPT); } } // If ICV Encryption if (mApplyToModifiedAPDU) { secureCLA = command.CLA | 0x04; wrappedDataSize += 8; } commandStream.WriteByte((byte)secureCLA); commandStream.WriteByte((byte)command.INS); commandStream.WriteByte((byte)command.P1); commandStream.WriteByte((byte)command.P2); commandStream.WriteByte((byte)wrappedDataSize); commandStream.Write(command.Data, 0, command.Data.Length); if (mSCPIdentifier == GlobalPlatform.SCP_01) { mICV = CryptoUtil.FullTripleDESMAC(mSessionKeys.MacKey, mICV, CryptoUtil.DESPad(commandStream.ToArray())); } else { mICV = CryptoUtil.SingleDESFullTripleDESMAC(mSessionKeys.MacKey, mICV, CryptoUtil.DESPad(commandStream.ToArray())); } if (!mApplyToModifiedAPDU) { secureCLA = command.CLA | 0x04; wrappedDataSize += 8; } wrappedData = command.Data; commandStream = new MemoryStream(); } // If C-MAC if (((mSecurityLevel & net.SecurityLevel.C_DECRYPTION) != 0) && command.LC > 0) { if (mSCPIdentifier == GlobalPlatform.SCP_01) { if ((command.LC + 1) % 8 != 0) { commandStream.WriteByte((byte)command.LC); commandStream.Write(command.Data, 0, command.Data.Length); byte[] paddedData = CryptoUtil.DESPad(commandStream.ToArray()); commandStream = new MemoryStream(); commandStream.Write(paddedData, 0, paddedData.Length); } else { commandStream.WriteByte((byte)command.LC); commandStream.Write(command.Data, 0, command.Data.Length); } } // If SCP '01' else { byte[] paddedData = CryptoUtil.DESPad(command.Data); commandStream.Write(paddedData, 0, paddedData.Length); } wrappedDataSize += (int)(commandStream.Length - command.Data.Length); wrappedData = CryptoUtil.TripleDESCBC(new Key(mSessionKeys.EncKey.BuildTripleDesKey()), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, commandStream.ToArray(), CryptoUtil.MODE_ENCRYPT); commandStream = new MemoryStream(); } // If C-DECRYPTION commandStream.WriteByte((byte)secureCLA); commandStream.WriteByte((byte)command.INS); commandStream.WriteByte((byte)command.P1); commandStream.WriteByte((byte)command.P2); if (wrappedDataSize > 0) { commandStream.WriteByte((byte)wrappedDataSize); commandStream.Write(wrappedData, 0, wrappedData.Length); } if ((mSecurityLevel & net.SecurityLevel.C_MAC) != 0) { commandStream.Write(mICV, 0, mICV.Length); } if (command.LE > 0) { commandStream.WriteByte((byte)command.LE); } return(new CommandAPDU(commandStream.ToArray())); }