/// <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);
        }
Ejemplo n.º 5
0
        /// <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);

        }
        /// <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);
        }
Ejemplo n.º 7
0
        /// <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.");
        }
Ejemplo n.º 8
0
        /// <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;
        }
Ejemplo n.º 9
0
        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;
        }
Ejemplo n.º 10
0
        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;
        }