/// <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."); } }
/// <summary> /// Constructs a secure channel /// </summary> /// <param name="sessionKeys">Session Keys</param> /// <param name="securityLevel">Security Level</param> /// <param name="scpIdentifier">Secure Channel Identifer: either <see cref="GlobalPlatform.SCP_01"/> or /// <see cref="GlobalPlatform.SCP_02"/>.</param> /// <param name="scpImplementationOption">Secure Channel Implementation Option: See GlobalPlatform.IMPL_OPTION_* </param> /// <param name="icv">Initial Chaining Vector</param> /// <param name="ricv">Response Initial Chaingin Vector</param> public SecureChannel(KeySet sessionKeys, int securityLevel, int scpIdentifier, int scpImplementationOption, byte[] icv, byte[] ricv) { mSessionKeys = sessionKeys; mSecurityLevel = securityLevel; mSCPIdentifier = scpIdentifier; mICV = icv; mRICV = ricv; mFirstCommandInChain = true; ConfigureImplementation(scpImplementationOption); }
private KeySet GenerateSessionKeysSCP01(byte[] cardResponse) { KeySet sessionKeySet = new KeySet(); byte[] derivationData = new byte[16]; System.Array.Copy(cardResponse, 16, derivationData, 0, 4); System.Array.Copy(mHostChallenge, 0, derivationData, 4, 4); System.Array.Copy(cardResponse, 12, derivationData, 8, 4); System.Array.Copy(mHostChallenge, 4, derivationData, 12, 4); sessionKeySet.EncKey = new Key(CryptoUtil.TripleDESECB(new Key(mStaticKeys.EncKey.BuildTripleDesKey()), derivationData, CryptoUtil.MODE_ENCRYPT)); sessionKeySet.MacKey = new Key(CryptoUtil.TripleDESECB(new Key(mStaticKeys.MacKey.BuildTripleDesKey()), derivationData, CryptoUtil.MODE_ENCRYPT)); sessionKeySet.KekKey = new Key(mStaticKeys.KekKey.Value); return(sessionKeySet); }
private KeySet GenerateSessionKeysSCP02(byte[] sequenceCoutner) { KeySet sessionKeySet = new KeySet(); byte[] derivationData = new byte[16]; System.Array.Copy(sequenceCoutner, 0, derivationData, 2, 2); System.Array.Clear(derivationData, 4, 12); // Todo: consider implicit case // Derivate session MAC key System.Array.Copy(CONSTANT_MAC_0101, 0, derivationData, 0, 2); sessionKeySet.MacKey = new Key(CryptoUtil.TripleDESCBC(new Key(mStaticKeys.MacKey.BuildTripleDesKey()), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, derivationData, CryptoUtil.MODE_ENCRYPT)); // Derivate session R-MAC key // To build R-MAC key static MAC key is used. System.Array.Copy(CONSTANT_RMAC_0102, 0, derivationData, 0, 2); sessionKeySet.RmacKey = new Key(CryptoUtil.TripleDESCBC(new Key(mStaticKeys.MacKey.BuildTripleDesKey()), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, derivationData, CryptoUtil.MODE_ENCRYPT)); // Derivate session ENC key System.Array.Copy(CONSTANT_ENC_0182, 0, derivationData, 0, 2); sessionKeySet.EncKey = new Key(CryptoUtil.TripleDESCBC(new Key(mStaticKeys.EncKey.BuildTripleDesKey()), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, derivationData, CryptoUtil.MODE_ENCRYPT)); // Derivate session KEK key System.Array.Copy(CONSTANT_DEK_0181, 0, derivationData, 0, 2); sessionKeySet.KekKey = new Key(CryptoUtil.TripleDESCBC(new Key(mStaticKeys.KekKey.BuildTripleDesKey()), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, derivationData, CryptoUtil.MODE_ENCRYPT)); return(sessionKeySet); }
/// <summary> /// Generates INITIALIZE UPDATE command with specified static key set. /// </summary> /// <param name="staticKeySet">Secure channel static key set</param> /// <param name="securityLevel">Security level. It must be a valid combination of /// security level bit pattern defined in <see cref="SecurityLevel"/>.</param> /// <param name="scpIdentifier">Secure Channel Identifier according to Global Platform 2.1.1 Card Spec section 8.6. /// Currently SCP '01' and SCP '02' is supported. Use <see cref="SCP_ANY"/> if you are not sure.</param> /// <param name="scpImplementationOption">Secure Channel Implementation Option according to /// Global Platform 2.1.1 Card Spec section D.1.1 for SCP '01' or section E.1.1 for SCP '02'. Use <see cref="IMPL_OPTION_ANY"/> /// along with <see cref="SCP_ANY"/> for Secure Channel Identifier, if you are not sure.</param> /// <returns>CommandAPDU for INITIALIZE UPDATE command for specified static key set</returns> public CommandAPDU CreateInitUpdateCommand(KeySet staticKeySet, int securityLevel, int scpIdentifier, int scpImplementationOption) { // Validate Secure Channel Identifier if ((scpIdentifier != SCP_01) && (scpIdentifier != SCP_02) && (scpIdentifier != SCP_ANY)) { throw new Exception( "Invalid secure channel protocol identifier. Currently SCP 01 (0x01) and SCP 02 (0x02) are valid." + " See Global Platform 2.1.1 Card Spec section 8.6."); } //Validate Secure Channel Implementation Option if (scpImplementationOption == IMPL_OPTION_ANY && scpIdentifier != SCP_ANY) { throw new Exception( "Secure Channel Implementation Option IMPL_OPTION_ANY can only be used along with Secure Channel Identifier SCP_ANY."); } if (scpIdentifier == SCP_ANY) { if (scpImplementationOption != IMPL_OPTION_I_05 && scpImplementationOption != IMPL_OPTION_I_15 && scpImplementationOption != IMPL_OPTION_ANY) { throw new Exception( "Invalid implementation option. Only IMPL_OPTION_I_05, IMPL_OPTION_I_15 or IMPL_OPTION_ANY can be used along with SCP_ANY."); } } // Validate Secure Channel Implementation Option for SCP 01 if (scpIdentifier == SCP_01) { if ((scpImplementationOption != IMPL_OPTION_I_05) && (scpImplementationOption != IMPL_OPTION_I_15)) { throw new Exception( "Invalid implementation option for SCP 01. See Global Platform 2.1.1 Card Spec section D.1.1."); } } // Validate Secure Channel Implementation Option for SCP 02 if (scpIdentifier == SCP_02) { if ((scpImplementationOption != IMPL_OPTION_I_04) && (scpImplementationOption != IMPL_OPTION_I_05) && (scpImplementationOption != IMPL_OPTION_I_0A) && (scpImplementationOption != IMPL_OPTION_I_0B) && (scpImplementationOption != IMPL_OPTION_I_14) && (scpImplementationOption != IMPL_OPTION_I_15) && (scpImplementationOption != IMPL_OPTION_I_1A) && (scpImplementationOption != IMPL_OPTION_I_1B)) { throw new Exception( "Invalid implementation option for SCP 02. See Global Platform 2.1.1 Card Spec section E.1.1."); } if ((scpImplementationOption == IMPL_OPTION_I_0A) || (scpImplementationOption == IMPL_OPTION_I_0B) || (scpImplementationOption == IMPL_OPTION_I_1A) || (scpImplementationOption == IMPL_OPTION_I_1B)) { throw new Exception("Implicit secure channel can't be initialized explicitly."); } } mSCPIdentifier = scpIdentifier; mSCPImplementationOption = scpImplementationOption; mStaticKeys = staticKeySet; mSecurityLevel = securityLevel; // C-DECRYPTION allways come with C-MAC if ((securityLevel & SecurityLevel.C_DECRYPTION) != 0) { securityLevel |= SecurityLevel.C_MAC; } // Validate security level if (securityLevel != (SecurityLevel.NO_SECURITY_LEVEL) && securityLevel != (SecurityLevel.C_DECRYPTION | SecurityLevel.C_MAC | SecurityLevel.R_MAC) && securityLevel != (SecurityLevel.C_MAC | SecurityLevel.R_MAC) && securityLevel != (SecurityLevel.R_MAC) && securityLevel != (SecurityLevel.C_DECRYPTION | SecurityLevel.C_MAC) && securityLevel != (SecurityLevel.C_MAC)) { throw new Exception( "Invalid security level. See Global Platform 2.1.1 Card Spec section E.5.2.3 or section D.4.2.3."); } // Host challenge mHostChallenge = new byte[8]; RandomNumberGenerator rng = RandomNumberGenerator.Create(); rng.GetBytes(mHostChallenge); // Build INITIALIZE UPDATE command CommandAPDU initUpdate = new CommandAPDU(CLA_GP, INS_INIT_UPDATE, staticKeySet.KeyVersion, staticKeySet.KeyId, mHostChallenge, 0x00); return(initUpdate); }
/// <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."); }
/// <summary> /// Generates INITIALIZE UPDATE command with specified static key set. /// </summary> /// <param name="staticKeySet">Secure channel static key set</param> /// <param name="securityLevel">Security level. It must be a valid combination of /// security level bit pattern defined in <see cref="SecurityLevel"/>.</param> /// <param name="scpIdentifier">Secure Channel Identifier according to Global Platform 2.1.1 Card Spec section 8.6. /// Currently SCP '01' and SCP '02' is supported. Use <see cref="SCP_ANY"/> if you are not sure.</param> /// <param name="scpImplementationOption">Secure Channel Implementation Option according to /// Global Platform 2.1.1 Card Spec section D.1.1 for SCP '01' or section E.1.1 for SCP '02'. Use <see cref="IMPL_OPTION_ANY"/> /// along with <see cref="SCP_ANY"/> for Secure Channel Identifier, if you are not sure.</param> /// <returns>CommandAPDU for INITIALIZE UPDATE command for specified static key set</returns> public CommandAPDU CreateInitUpdateCommand(KeySet staticKeySet, int securityLevel, int scpIdentifier, int scpImplementationOption) { // Validate Secure Channel Identifier if ((scpIdentifier != SCP_01) && (scpIdentifier != SCP_02) && (scpIdentifier != SCP_ANY)) throw new Exception( "Invalid secure channel protocol identifier. Currently SCP 01 (0x01) and SCP 02 (0x02) are valid." + " See Global Platform 2.1.1 Card Spec section 8.6."); //Validate Secure Channel Implementation Option if (scpImplementationOption == IMPL_OPTION_ANY && scpIdentifier != SCP_ANY) throw new Exception( "Secure Channel Implementation Option IMPL_OPTION_ANY can only be used along with Secure Channel Identifier SCP_ANY."); if (scpIdentifier == SCP_ANY) { if (scpImplementationOption != IMPL_OPTION_I_05 && scpImplementationOption != IMPL_OPTION_I_15 && scpImplementationOption != IMPL_OPTION_ANY) throw new Exception( "Invalid implementation option. Only IMPL_OPTION_I_05, IMPL_OPTION_I_15 or IMPL_OPTION_ANY can be used along with SCP_ANY."); } // Validate Secure Channel Implementation Option for SCP 01 if (scpIdentifier == SCP_01) if ((scpImplementationOption != IMPL_OPTION_I_05) && (scpImplementationOption != IMPL_OPTION_I_15)) throw new Exception( "Invalid implementation option for SCP 01. See Global Platform 2.1.1 Card Spec section D.1.1."); // Validate Secure Channel Implementation Option for SCP 02 if (scpIdentifier == SCP_02) { if ((scpImplementationOption != IMPL_OPTION_I_04) && (scpImplementationOption != IMPL_OPTION_I_05) && (scpImplementationOption != IMPL_OPTION_I_0A) && (scpImplementationOption != IMPL_OPTION_I_0B) && (scpImplementationOption != IMPL_OPTION_I_14) && (scpImplementationOption != IMPL_OPTION_I_15) && (scpImplementationOption != IMPL_OPTION_I_1A) && (scpImplementationOption != IMPL_OPTION_I_1B)) throw new Exception( "Invalid implementation option for SCP 02. See Global Platform 2.1.1 Card Spec section E.1.1."); if ((scpImplementationOption == IMPL_OPTION_I_0A) || (scpImplementationOption == IMPL_OPTION_I_0B) || (scpImplementationOption == IMPL_OPTION_I_1A) || (scpImplementationOption == IMPL_OPTION_I_1B)) throw new Exception("Implicit secure channel can't be initialized explicitly."); } mSCPIdentifier = scpIdentifier; mSCPImplementationOption = scpImplementationOption; mStaticKeys = staticKeySet; mSecurityLevel = securityLevel; // C-DECRYPTION allways come with C-MAC if ((securityLevel & SecurityLevel.C_DECRYPTION) != 0) securityLevel |= SecurityLevel.C_MAC; // Validate security level if (securityLevel != (SecurityLevel.NO_SECURITY_LEVEL) && securityLevel != (SecurityLevel.C_DECRYPTION | SecurityLevel.C_MAC | SecurityLevel.R_MAC) && securityLevel != (SecurityLevel.C_MAC | SecurityLevel.R_MAC) && securityLevel != (SecurityLevel.R_MAC) && securityLevel != (SecurityLevel.C_DECRYPTION | SecurityLevel.C_MAC) && securityLevel != (SecurityLevel.C_MAC)) throw new Exception( "Invalid security level. See Global Platform 2.1.1 Card Spec section E.5.2.3 or section D.4.2.3."); // Host challenge mHostChallenge = new byte[8]; RandomNumberGenerator rng = RandomNumberGenerator.Create(); rng.GetBytes(mHostChallenge); // Build INITIALIZE UPDATE command CommandAPDU initUpdate = new CommandAPDU(CLA_GP, INS_INIT_UPDATE, staticKeySet.KeyVersion, staticKeySet.KeyId, mHostChallenge, 0x00); return initUpdate; }
private KeySet GenerateSessionKeysSCP02(byte[] sequenceCoutner) { KeySet sessionKeySet = new KeySet(); byte[] derivationData = new byte[16]; System.Array.Copy(sequenceCoutner, 0, derivationData, 2, 2); System.Array.Clear(derivationData, 4, 12); // Todo: consider implicit case // Derivate session MAC key System.Array.Copy(CONSTANT_MAC_0101, 0, derivationData, 0, 2); sessionKeySet.MacKey = new Key(CryptoUtil.TripleDESCBC(new Key(mStaticKeys.MacKey.BuildTripleDesKey()), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, derivationData, CryptoUtil.MODE_ENCRYPT)); // Derivate session R-MAC key // To build R-MAC key static MAC key is used. System.Array.Copy(CONSTANT_RMAC_0102, 0, derivationData, 0, 2); sessionKeySet.RmacKey = new Key(CryptoUtil.TripleDESCBC(new Key(mStaticKeys.MacKey.BuildTripleDesKey()), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, derivationData, CryptoUtil.MODE_ENCRYPT)); // Derivate session ENC key System.Array.Copy(CONSTANT_ENC_0182, 0, derivationData, 0, 2); sessionKeySet.EncKey = new Key(CryptoUtil.TripleDESCBC(new Key(mStaticKeys.EncKey.BuildTripleDesKey()), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, derivationData, CryptoUtil.MODE_ENCRYPT)); // Derivate session KEK key System.Array.Copy(CONSTANT_DEK_0181, 0, derivationData, 0, 2); sessionKeySet.KekKey = new Key(CryptoUtil.TripleDESCBC(new Key(mStaticKeys.KekKey.BuildTripleDesKey()), CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, derivationData, CryptoUtil.MODE_ENCRYPT)); return sessionKeySet; }
private KeySet GenerateSessionKeysSCP01(byte[] cardResponse) { KeySet sessionKeySet = new KeySet(); byte[] derivationData = new byte[16]; System.Array.Copy(cardResponse, 16, derivationData, 0, 4); System.Array.Copy(mHostChallenge, 0, derivationData, 4, 4); System.Array.Copy(cardResponse, 12, derivationData, 8, 4); System.Array.Copy(mHostChallenge, 4, derivationData, 12, 4); sessionKeySet.EncKey = new Key(CryptoUtil.TripleDESECB(new Key(mStaticKeys.EncKey.BuildTripleDesKey()), derivationData, CryptoUtil.MODE_ENCRYPT)); sessionKeySet.MacKey = new Key(CryptoUtil.TripleDESECB(new Key(mStaticKeys.MacKey.BuildTripleDesKey()), derivationData, CryptoUtil.MODE_ENCRYPT)); sessionKeySet.KekKey = new Key(mStaticKeys.KekKey.Value); return sessionKeySet; }