void pivacy_cardemu_emulator::process_verify_pin(bytestring& c_apdu, bytestring& r_apdu)
{
	if (c_apdu.size() < 5)
	{
		r_apdu = SW_LENGTH_ERROR;
	}
	else if ((c_apdu[OFS_P1] != 0x00) || ((c_apdu[OFS_P2] != 0x00) && (c_apdu[OFS_P2] != 0x01)))
	{
		ERROR_MSG("Invalid VERIFY PIN request");
		
		r_apdu = SW_DATA_UNKNOWN;
	}
	else
	{
		bytestring supplied_PIN = c_apdu.substr(OFS_CDATA, c_apdu[OFS_LC]);
		
		if (c_apdu[OFS_P2] == 0x00)
		{
			if (supplied_PIN == user_PIN)
			{
				r_apdu = SW_OK;
				
				user_PIN_verified = true;
			}
			else
			{
				r_apdu = SW_PIN_INCORRECT;
				
				user_PIN_verified = false;
			}
		}
		else if (c_apdu[OFS_P2] == 0x01)
		{
			if (supplied_PIN == admin_PIN)
			{
				r_apdu = SW_OK;
				
				admin_PIN_verified = true;
			}
			else
			{
				r_apdu = SW_PIN_INCORRECT;
				
				admin_PIN_verified = false;
			}
		}
	}
}
void pivacy_cardemu_emulator::process_select(bytestring& c_apdu, bytestring& r_apdu)
{
	const bytestring IRMA_AID = "F849524D4163617264";
	
	if (c_apdu.size() < 5)
	{
		r_apdu = SW_LENGTH_ERROR;
	}
	else if (c_apdu.substr(OFS_CDATA, c_apdu[OFS_LC]) != IRMA_AID)
	{
		r_apdu = SW_APPLICATION_UNKNOWN;
	}
	else
	{
		// Version 0.8 + emu
		// 
		// Encoded as ISO7816 FCI record
		//
		// 0x6F YZ: FCI template (length: 0xYZ bytes)
		//   0xA5 YZ: Proprietary information encoded in BER-TLV (length: 0xYZ bytes)
		//     0x10 YZ: Sequence, version information (length: 0xYZ bytes)
		//       0x02 01: Integer, major (length: 0x01 byte)
		//       0x02 01: Integer, minor (length: 0x01 byte)
		//       0x02 01: Integer, maintenance (optional, length: 0x01 byte)
		//       0x02 01: Integer, build (optional, length: 0x01 byte)
		//       0x10 YZ: Sequence, extra information (optional, length: 0xYZ bytes)
		//         0x0C YZ: UTF-8 string, identifier (length: 0xYZ bytes)
		//         0x02 01: Integer, counter (optional, length: 0x01 byte)
		//         0x04 YZ: Octet string, data (optional, length: 0xYZ bytes)

		r_apdu = "6F11";
		  r_apdu += "A50F";
		    r_apdu += "100D";
		      r_apdu += "020100";		// major version = 0
		      r_apdu += "020108";		// minor version = 8
		      r_apdu += "1007";
		        r_apdu += "0C03454D55";	// EMU
		r_apdu += "9000";
	}
}
void pivacy_cardemu_emulator::process_prove_commitment(bytestring& c_apdu, bytestring& r_apdu)
{
	if (!proof_started || !proof_have_context_and_D || (selected_credential == NULL))
	{
		r_apdu = SW_WRONG_STATE;
		reset_proof();
	}
	else if ((c_apdu.size() < 5) || (c_apdu[OFS_LC] != (SYSPAR(l_statzk) / 8)))
	{
		r_apdu = SW_LENGTH_ERROR;
		reset_proof();
	}
	else if ((c_apdu[OFS_P1] != 0x00) || (c_apdu[OFS_P2] != 0x00))
	{
		r_apdu = SW_DATA_UNKNOWN;
		reset_proof();
	}
	else
	{
		// Retrieve the nonce
		bytestring nonce = c_apdu.substr(OFS_CDATA, c_apdu[OFS_LC]);
		
		// Generate the proof
		silvia_prover prover(selected_credential->get_issuer_public_key(), selected_credential->get_silvia_credential());
		
		mpz_class c;
		mpz_class A_prime;
		mpz_class e_hat;
		mpz_class v_prime_hat;
		std::vector<mpz_class> a_i_hat;
		std::vector<silvia_attribute*> a_i;
		
		prover.prove(curproof_D, nonce.mpz_val(), curproof_context.mpz_val(), c, A_prime, e_hat, v_prime_hat, a_i_hat, a_i);
		
		// Save proof output
		std::vector<mpz_class>::iterator a_i_hat_it = a_i_hat.begin();
		std::vector<silvia_attribute*>::iterator a_i_it = a_i.begin();
		
		curproof_A_prime = bytestring(A_prime);
		curproof_e_hat = bytestring(e_hat);
		curproof_v_prime_hat = bytestring(v_prime_hat);
		
		/* Add hidden master secret */
		curproof_attributes.push_back(bytestring(*a_i_hat_it));
		a_i_hat_it++;
		
		for (std::vector<bool>::iterator i = curproof_D.begin(); i != curproof_D.end(); i++)
		{
			if ((*i) == true)
			{
				curproof_attributes.push_back(bytestring((*a_i_it)->rep()));
				a_i_it++;
			}
			else
			{
				curproof_attributes.push_back(bytestring(*a_i_hat_it));
				a_i_hat_it++;
			}
		}
		
		// Return c
		r_apdu = bytestring(c);
		r_apdu += SW_OK;
		
		proof_proved = true;
	}
}
void pivacy_cardemu_emulator::process_prove_credential(bytestring& c_apdu, bytestring& r_apdu)
{
	reset_proof();
	
	if ((c_apdu.size() < 5) || (c_apdu[OFS_LC] != 0x28))
	{
		r_apdu = SW_LENGTH_ERROR;
	}
	else if ((c_apdu[OFS_P1] != 0x00) || (c_apdu[OFS_P2] != 0x00))
	{
		r_apdu = SW_WRONG_STATE;
	}
	else
	{
		unsigned short credential_id = (c_apdu[OFS_CDATA] >> 8) + c_apdu[OFS_CDATA + 1];
		
		/* Check if this credential exists */
		selected_credential = NULL;
		
		for (std::vector<pivacy_credential*>::iterator i = credentials.begin(); i != credentials.end(); i++)
		{
			if ((*i)->get_credential_id() == credential_id)
			{
				selected_credential = *i;
			}
		}
		
		if (selected_credential == NULL)
		{
			ERROR_MSG("Attempt to start proof for non-existent credential 0x%04X", credential_id);
			
			r_apdu = SW_CREDENTIAL_UNKNOWN;
		}
		else
		{
			unsigned short D_val = (c_apdu[OFS_CDATA + 2] << 8) + c_apdu[OFS_CDATA + 2 + 1];
			
			curproof_context = c_apdu.substr(OFS_CDATA + 2 + 2, SYSPAR(l_H) / 8);
			
			time_t timestamp = (c_apdu[OFS_CDATA + 2 + (SYSPAR(l_H) / 8) + 2] << 24) +
			                   (c_apdu[OFS_CDATA + 2 + (SYSPAR(l_H) / 8) + 3] << 16) +
			                   (c_apdu[OFS_CDATA + 2 + (SYSPAR(l_H) / 8) + 4] << 8) +
			                   (c_apdu[OFS_CDATA + 2 + (SYSPAR(l_H) / 8) + 5]);
			                   
			INFO_MSG("Started proof with context = %s, D = 0x%04X, timestamp = %d", curproof_context.hex_str().c_str(), D_val, timestamp);
			
			/* Convert D to vector of booleans, start at "expiry" */
			unsigned short D_mask = 0x0002;
			std::vector<const char*> display_attributes;
			
			for (int i = 0; i < selected_credential->get_silvia_credential()->num_attributes(); i++)
			{
				if (FLAG_SET(D_val, D_mask))
				{
					curproof_D.push_back(true);
					
					INFO_MSG("Revealing attribute %s", selected_credential->get_attribute_names()[i].c_str());
					
					display_attributes.push_back(selected_credential->get_attribute_names()[i].c_str());
				}
				else
				{
					curproof_D.push_back(false);
					
					INFO_MSG("Keeping attribute %s hidden", selected_credential->get_attribute_names()[i].c_str());
				}
				
				D_mask <<= 1;
			}
			
			r_apdu = SW_OK;
				
			proof_started = true;
			proof_have_context_and_D = true;
			
			if (ui_connected)
			{
				int consent_result;
				pivacy_rv rv;
				
				if (((rv = pivacy_ui_consent("This terminal", &display_attributes[0], display_attributes.size(), 0, &consent_result) != PRV_OK) ||
				    ((rv = pivacy_ui_show_status(PIVACY_STATE_PRESENT)) != PRV_OK)) && !ui_optional)
				{
					reset_proof();
					
					r_apdu = SW_UNKNOWN_ERROR;
				}
				
				if (rv == PRV_OK)
				{
					if (consent_result == PIVACY_CONSENT_NO)
					{
						reset_proof();
						
						r_apdu = SW_SECURITY_STATUS_NOT_SATISFIED;
					}
				}
			}
		}
	}
}