private GPKeySet DeriveSessionKeysSCP03(GPKeySet staticKeys, byte[] host_challenge, byte[] card_challenge) { GPKeySet sessionKeys = new GPKeySet(); byte mac_constant = 0x06; byte enc_constant = 0x04; byte rmac_constant = 0x07; byte[] context = Arrays.Concatenate(host_challenge, card_challenge); // MAC byte[] kdf = SCP03Wrapper.Scp03_kdf(staticKeys.GetKey(KeySessionType.MAC), mac_constant, context, 128); sessionKeys.SetKey(KeySessionType.MAC, new GPKey(kdf, KeyType.AES)); // ENC kdf = SCP03Wrapper.Scp03_kdf(staticKeys.GetKey(KeySessionType.ENC), enc_constant, context, 128); sessionKeys.SetKey(KeySessionType.ENC, new GPKey(kdf, KeyType.AES)); // RMAC kdf = SCP03Wrapper.Scp03_kdf(staticKeys.GetKey(KeySessionType.MAC), rmac_constant, context, 128); sessionKeys.SetKey(KeySessionType.RMAC, new GPKey(kdf, KeyType.AES)); // KEK remains the same sessionKeys.SetKey(KeySessionType.KEK, staticKeys.GetKey(KeySessionType.KEK)); return(sessionKeys); }
public void OpenSecureChannel(GPPlaintextKeys keys, List <APDUMode> securityLevel, byte[] hostChallenge = null, byte[] initUpdateResponse = null, byte[] externalAuthReponse = null) { if (securityLevel.Contains(APDUMode.ENC) && !securityLevel.Contains(APDUMode.MAC)) { securityLevel.Add(APDUMode.MAC); } if (hostChallenge == null) { // Generate host challenge hostChallenge = new byte[8]; SecureRandom sr = new SecureRandom(); sr.NextBytes(hostChallenge); } GPInitializeUpdateReqest initUpdate = new GPInitializeUpdateReqest(keys.GetKeysetVersion(), keys.GetKeysetID(), hostChallenge); //System.Diagnostics.Debug.WriteLine(initUpdate.ToPrintString()); GPInitializeUpdateResponse response; if (initUpdateResponse != null) { response = new GPInitializeUpdateResponse(); response.Deserialize(initUpdateResponse); System.Diagnostics.Debug.WriteLine(response.ToPrintString()); } else { response = (GPInitializeUpdateResponse)SendCommand(initUpdate); } // Detect and report locked cards in a more sensible way. if ((response.SW == (ushort)ISO7816ReturnCodes.SW_SECURITY_STATUS_NOT_SATISFIED) || (response.SW == (ushort)ISO7816ReturnCodes.SW_AUTHENTICATION_METHOD_BLOCKED)) { throw new Exception("INITIALIZE UPDATE failed, Card possibly locked."); } if (response.SW != (ushort)ISO7816ReturnCodes.SW_NO_ERROR) { throw new Exception("INITIALIZE UPDATE failed"); } // Verify response length (SCP01/SCP02 + SCP03 + SCP03 w/ pseudorandom) if (response.ResponseData.Length != 28 && response.ResponseData.Length != 29 && response.ResponseData.Length != 32) { throw new Exception("Invalid INITIALIZE UPDATE response length: " + response.ResponseData.Length); } System.Diagnostics.Debug.WriteLine("Host challenge: " + Formatting.ByteArrayToHexString(hostChallenge)); System.Diagnostics.Debug.WriteLine("Card challenge: " + Formatting.ByteArrayToHexString(response.CardChallenge)); // Verify response // If using explicit key version, it must match. if ((keys.GetKeysetVersion() > 0) && (response.KeyVersionNumber != keys.GetKeysetVersion())) { throw new Exception("Key version mismatch: " + keys.GetKeysetVersion() + " != " + response.KeyVersionNumber); } System.Diagnostics.Debug.WriteLine("Card reports SCP0" + response.SCPId + " with version " + response.KeyVersionNumber + " keys"); if (response.SCPId == 3) { System.Diagnostics.Debug.WriteLine("SCP03 i=" + response.SCPI); } SCPVersions scpVersion; // Derive session keys GPKeySet sessionKeys; if (response.SCPId == 1) { if (securityLevel.Contains(APDUMode.RMAC)) { throw new Exception("SCP01 does not support RMAC"); } scpVersion = SCPVersions.SCP_01_05; sessionKeys = keys.GetSessionKeys(response.SCPId, response.KeyDiversificationData, hostChallenge, response.CardChallenge); } else if (response.SCPId == 2) { scpVersion = SCPVersions.SCP_02_15; sessionKeys = keys.GetSessionKeys(response.SCPId, response.KeyDiversificationData, response.CardChallengeSeq); } else if (response.SCPId == 3) { scpVersion = SCPVersions.SCP_03; sessionKeys = keys.GetSessionKeys(response.SCPId, response.KeyDiversificationData, hostChallenge, response.CardChallenge); } else { throw new Exception("Unsupported scpVersion: " + response.SCPId); } // Verify card cryptogram byte[] my_card_cryptogram = null; byte[] cntx; if (response.SCPId == 2) { cntx = Formatting.ConcatArrays(hostChallenge, response.CardChallengeSeq, response.CardChallenge); } else { cntx = Arrays.Concatenate(hostChallenge, response.CardChallenge); } if (response.SCPId == 1 || response.SCPId == 2) { my_card_cryptogram = SCP0102Wrapper.Mac_3des_nulliv(sessionKeys.GetKey(KeySessionType.ENC), cntx); } else { my_card_cryptogram = SCP03Wrapper.Scp03_kdf(sessionKeys.GetKey(KeySessionType.MAC), (byte)0x00, cntx, 64); } // This is the main check for possible successful authentication. if (!Arrays.AreEqual(response.CardCryptogram, my_card_cryptogram)) { string message = "Card cryptogram invalid." + "\nCard: " + Formatting.ByteArrayToHexString(response.CardCryptogram) + "\nCalculated: " + Formatting.ByteArrayToHexString(my_card_cryptogram) + "\nRetrying the same parameters may disable the card"; System.Diagnostics.Debug.WriteLine(message); throw new Exception(message); } else { System.Diagnostics.Debug.WriteLine("Verified card cryptogram: " + Formatting.ByteArrayToHexString(my_card_cryptogram)); } // Calculate host cryptogram and initialize SCP wrapper byte[] host_cryptogram = null; if (response.SCPId == 1 || response.SCPId == 2) { if (response.SCPId == 2) { host_cryptogram = SCP0102Wrapper.Mac_3des_nulliv(sessionKeys.GetKey(KeySessionType.ENC), Formatting.ConcatArrays(response.CardChallengeSeq, response.CardChallenge, hostChallenge)); } else { host_cryptogram = SCP0102Wrapper.Mac_3des_nulliv(sessionKeys.GetKey(KeySessionType.ENC), Arrays.Concatenate(response.CardChallenge, hostChallenge)); } wrapper = new SCP0102Wrapper(sessionKeys, scpVersion, new List <APDUMode>() { APDUMode.MAC }, null, null, blockSize); } else { host_cryptogram = SCP03Wrapper.Scp03_kdf(sessionKeys.GetKey(KeySessionType.MAC), (byte)0x01, cntx, 64); wrapper = new SCP03Wrapper(sessionKeys, scpVersion, new List <APDUMode>() { APDUMode.MAC }, null, null, blockSize); } System.Diagnostics.Debug.WriteLine("Calculated host cryptogram: " + Formatting.ByteArrayToHexString(host_cryptogram)); GPExternalAuthenticateReqest externalAuthenticate = new GPExternalAuthenticateReqest(GetSetValue(securityLevel), host_cryptogram); GPExternalAuthenticateResponse response2; if (externalAuthReponse != null) { response2 = new GPExternalAuthenticateResponse(); response2.Deserialize(externalAuthReponse); System.Diagnostics.Debug.WriteLine(response2.ToPrintString()); } else { response2 = (GPExternalAuthenticateResponse)SendCommand(externalAuthenticate); } if (response2.SW != (ushort)ISO7816ReturnCodes.SW_NO_ERROR) { throw new Exception("External authenticate failed"); } wrapper.SetSecurityLevel(securityLevel); }