pairing_handshake
- CSharp
- Java
- Swift
- Python
- Go
/// <summary>Handshakes the with server.</summary>
/// <param name="clientId">The client identifier.</param>
/// <returns>ResponseModel<HandshakeResponse>.</returns>
public ResponseModel<HandshakeResponse> HandshakeWithServer(string clientId)
{
ResponseModel<HandshakeResponse> response =
new ResponseModel<HandshakeResponse>
{ Data = new HandshakeResponse() };
try
{
//--------------------------------
// Create clientId for this client
//--------------------------------
HandshakeModel handshake =
new HandshakeModel { ConversationIdentifier = clientId };
//-------------------------------------------
// Create Eclypses DH containers for handshake
//-------------------------------------------
EclypsesECDH encoderEcdh = new EclypsesECDH();
EclypsesECDH decoderEcdh = new EclypsesECDH();
//-------------------------------------------
// Get the public key to send to other side
//-------------------------------------------
handshake.ClientEncoderPublicKey =
encoderEcdh.GetPublicKey(encoderEcdh.GetTheContainer());
handshake.ClientDecoderPublicKey =
decoderEcdh.GetPublicKey(decoderEcdh.GetTheContainer());
//-------------------
// Perform handshake
//-------------------
string handshakeResponse =
MakeHttpCall($"{Constants.RestAPIName}/api/handshake",
HttpMethod.Post,
handshake.ConversationIdentifier,
Constants.JsonContentType,
JsonSerializer.Serialize(handshake, Constants.JsonOptions)).Result;
//---------------------------------------
// Deserialize the result from handshake
//---------------------------------------
ResponseModel<HandshakeModel> serverResponse =
JsonSerializer.Deserialize<ResponseModel<HandshakeModel>>
(handshakeResponse, Constants.JsonOptions);
//---------------------------------------
// If handshake was not successful, break
//---------------------------------------
if (!serverResponse.Success)
{
response.Success = serverResponse.Success;
response.Message = serverResponse.Message;
response.ResultCode = serverResponse.ResultCode;
Console.WriteLine($"Error making DH handshake for Client " +
"{clientId}: {serverResponse.Message}");
return response;
}
//----------------------
// Create shared secret
//----------------------
var encoderSharedSecretModel =
encoderEcdh.ProcessPartnerPublicKey(serverResponse.Data.ClientEncoderPublicKey);
var decoderSharedSecretModel =
decoderEcdh.ProcessPartnerPublicKey(serverResponse.Data.ClientDecoderPublicKey);
//--------------------------------
// Set MTE settings and get state
//--------------------------------
if (!ulong.TryParse(serverResponse.Data.Timestamp, out ulong nonce))
{
response.Success = false;
response.Message = $"Nonce is not valid ulong: {serverResponse.Data.Timestamp}.";
response.ResultCode = Constants.RC_INVALID_NONCE;
return response;
}
//----------------------------
// Set Encoder and save state
//----------------------------
MteMkeEnc encoder = new MteMkeEnc();
encoder.SetEntropy(encoderSharedSecretModel.SharedSecret);
encoder.SetNonce(nonce);
MteStatus status = encoder.Instantiate(handshake.ConversationIdentifier);
if (status != MteStatus.mte_status_success)
{
response.Success = false;
response.Message = $"Failed to initialize the MTE Encoder engine. Status: " +
"{encoder.GetStatusName(status)} / " +
"{encoder.GetStatusDescription(status)}";
response.ResultCode = Constants.RC_MTE_STATE_CREATION;
return response;
}
response.Data.EncoderState = encoder.SaveStateB64();
//----------------------------
// Set Decoder and save state
//----------------------------
MteMkeDec decoder = new MteMkeDec();
decoder.SetEntropy(decoderSharedSecretModel.SharedSecret);
decoder.SetNonce(nonce);
status = decoder.Instantiate(handshake.ConversationIdentifier);
if (status != MteStatus.mte_status_success)
{
response.Success = false;
response.Message = $"Failed to initialize the MTE Decoder engine. Status: "+
"{decoder.GetStatusName(status)} / " +
"{decoder.GetStatusDescription(status)}";
response.ResultCode = Constants.RC_MTE_STATE_CREATION;
return response;
}
response.Data.DecoderState = decoder.SaveStateB64();
}
catch (Exception ex)
{
response.Message = $"Exception handshaking with server. Ex: {ex.Message}";
response.ResultCode = Constants.RC_HANDSHAKE_EXCEPTION;
response.Success = false;
}
return response;
}
/**
* Handshake with the server and create MTE states
*
* @param clientId --> Current client id
* @param clients --> Client hash map
* @param currentConversation --> current conversation ID
* @return
*/
private static ResponseModel<HandshakeResponse> HandshakeWithServer(String clientId) {
ResponseModel<HandshakeResponse> response = new ResponseModel<HandshakeResponse>();
response.Data = new HandshakeResponse();
try {
System.out.println("Performing Handshake for Client " + clientId);
// --------------------------------
// Create clientId for this client
// --------------------------------
HandshakeModel handshake = new uploadClient.Models.HandshakeModel();
handshake.ConversationIdentifier = clientId;
// -------------------------------------------
// Create Eclypses DH containers for handshake
// -------------------------------------------
EclypsesECDH encoderEcdh = new EclypsesECDH();
EclypsesECDH decoderEcdh = new EclypsesECDH();
// -------------------------------------------
// Get the public key to send to other side
// -------------------------------------------
handshake.ClientEncoderPublicKey = encoderEcdh.getDevicePublicKey();
handshake.ClientDecoderPublicKey = decoderEcdh.getDevicePublicKey();
// -------------------
// Perform handshake
// -------------------
String handshakeString = _gson.toJson(handshake);
String handshakeResponse = MakeHttpCall(Constants.RestAPIName + Constants.HandshakeRoute,
"POST",
handshake.ConversationIdentifier,
Constants.JsonContentType,
handshakeString);
// ---------------------------------------
// Deserialize the result from handshake
// ---------------------------------------
Type handshakeResponseType = new TypeToken<ResponseModel<HandshakeModel>>() {
}.getType();
ResponseModel<HandshakeModel> serverResponse =
_gson.fromJson(handshakeResponse, handshakeResponseType);
// ---------------------------------------
// If handshake was not successful, end
// ---------------------------------------
if (!serverResponse.Success) {
response.Message = serverResponse.Message;
response.Success = serverResponse.Success;
response.ResultCode = serverResponse.ResultCode;
System.out.println("Error making DH handshake for Client "
+ clientId + ": " + serverResponse.Message);
return response;
}
// ----------------------
// Create shared secret
// ----------------------
var encoderSharedSecret =
encoderEcdh.createSharedSecret(serverResponse.Data.ClientEncoderPublicKey);
var decoderSharedSecret =
decoderEcdh.createSharedSecret(serverResponse.Data.ClientDecoderPublicKey);
// ----------------------------------------------------------
// Clear container to ensure key is different for each client
// ----------------------------------------------------------
encoderEcdh = null;
decoderEcdh = null;
// --------------------
// Create MTE Encoder
// --------------------
MteEnc encoder = new MteEnc();
encoder.setEntropy(encoderSharedSecret);
encoder.setNonce(Long.parseLong(serverResponse.Data.Timestamp));
MteStatus status = encoder.instantiate(clientId);
if (status != MteStatus.mte_status_success) {
response.Message = "Error creating Encoder: Status: "
+ encoder.getStatusName(status) + " / "
+ encoder.getStatusDescription(status);
response.ResultCode = Constants.RC_MTE_ENCODE_EXCEPTION;
response.Success = false;
System.out.println(response.Message);
return response;
}
// ------------------------
// Get Encoder State
// ------------------------
response.Data.EncoderState = encoder.saveStateB64();
// --------------------
// Create MTE Decoder
// --------------------
MteDec decoder = new MteDec();
decoder.setEntropy(decoderSharedSecret);
decoder.setNonce(Long.parseLong(serverResponse.Data.Timestamp));
status = decoder.instantiate(clientId);
if (status != MteStatus.mte_status_success) {
response.Message = "Error creating Decoder: Status: "
+ encoder.getStatusName(status) + " / "
+ encoder.getStatusDescription(status);
response.ResultCode = Constants.RC_MTE_DECODE_EXCEPTION;
response.Success = false;
System.out.println(response.Message);
return response;
}
// ------------------------
// Set MTE Decoder state
// ------------------------
response.Data.DecoderState = decoder.saveStateB64();
} catch (Exception ex) {
ex.printStackTrace();
response.Message = "Exception during handshake: " + ex.getMessage();
response.Success = false;
response.ResultCode = Constants.RC_HANDSHAKE_EXCEPTION;
return response;
}
return response;
}
// This Swift Elliptic-Curve Diffie-Hellman implementation uses the EcdhHelper shown below
// for both Encoder and Decoder.
let ecdh = try EcdhHelper(name: "\(pairType.rawValue) Entropy")
let publicKey = try ecdh.getPublicKey()
// The device public key is sent to the server where it is used to create a shared secret.
// The Server then returns its public key that the device uses to create the same shared secret
// In this sample the shared secret itself is used as the entropy
var tempEntropy: [UInt8]()!
try ecdh.createSharedSecret(remotePublicKeyStr: response.publicKey,
entropy: &tempEntropy)
EcdhHelper.swift
// This helper is used by both Encoder and Decoder
import Foundation
import CryptoKit
public class EcdhHelper {
private var sePrivateKey: SecureEnclave.P256.KeyAgreement.PrivateKey!
private var privateKey: P256.KeyAgreement.PrivateKey!
private var remotePublicKey: P256.KeyAgreement.PublicKey!
private var name: String
public init(name: String) throws {
// We use the 'name' param just for debugPrint.
self.name = name
// Create privateKey using Secure Enclave if it's available and we aren't on a simulator
if SecureEnclave.isAvailable && TARGET_OS_SIMULATOR != 1 {
do {
sePrivateKey = try SecureEnclave.P256.KeyAgreement.PrivateKey()
} catch {
debugPrint("Unable to create private Key with Secure Enclave. Error: \(error.localizedDescription)")
throw ECDHErrors.unableToInitializeEcdhHelper
}
} else {
privateKey = P256.KeyAgreement.PrivateKey()
}
debugPrint("EcdhHelper has been instantiated for \(name)")
}
deinit {
// This is included primarily to demonstrate the short lifecycle of this class
debugPrint("EcdhHelper has been destroyed for \(name)")
}
public func getPublicKey() throws -> String {
var publicKeyData = Data()
if SecureEnclave.isAvailable && TARGET_OS_SIMULATOR != 1 {
publicKeyData = sePrivateKey.publicKey.derRepresentation
} else {
publicKeyData = privateKey.publicKey.derRepresentation
}
return publicKeyData.base64EncodedString()
}
public func createSharedSecret(remotePublicKeyStr: String, entropy: inout [UInt8]) throws {
do {
try setRemotePublicKey(keyString: remotePublicKeyStr)
var sharedSecret: SharedSecret
// create the shared secret
if SecureEnclave.isAvailable && TARGET_OS_SIMULATOR != 1 {
sharedSecret = try sePrivateKey.sharedSecretFromKeyAgreement(with: remotePublicKey)
} else {
sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: remotePublicKey)
}
// because C# does it this way, we'll grab the shared secret data and hash it,
// then convert it to a [UInt8] to use as entropy
var sharedSecretData = sharedSecret.withUnsafeBytes {Data(Array($0))}
let sharedSecretDataHash = SHA256.hash(data: sharedSecretData)
entropy = sharedSecretDataHash.withUnsafeBytes {Data(Array($0))}.bytes
// While not strictly necessary because this class is about to be destroyed, we'll zero out the 'sensitive' data.
// entropy byte array will be zeroized immediately after using it.
sharedSecretData.resetBytes(in: 0...sharedSecretData.count-1)
var hashData = sharedSecretDataHash.withUnsafeBytes {Data(Array($0))}
hashData.resetBytes(in: 0...hashData.count-1)
} catch {
debugPrint("Unable to create Shared Secret. Error: \(error.localizedDescription)")
throw ECDHErrors.unableToCreateSharedSecret
}
}
private func setRemotePublicKey(keyString: String) throws {
do {
guard let publicKeyData = Data(base64Encoded: keyString) else {
throw ECDHErrors.unableToCreateRemotePublicKeyData
}
remotePublicKey = try P256.KeyAgreement.PublicKey(derRepresentation: publicKeyData)
} catch {
debugPrint("Unable to create Remote Public Key with Secure Enclave. Error: \(error.localizedDescription)")
throw ECDHErrors.unableToCreateRemotePublicKey
}
}
}
enum ECDHErrors: Error {
case unableToInitializeEcdhHelper
case unableToCreateLocalPublicKey
case unableToCreateRemotePublicKeyData
case unableToCreateRemotePublicKey
case unableToCreateSharedSecret
var resultCode: String {
switch self {
case .unableToInitializeEcdhHelper:
return "Unable to Initialize an Elliptic Curve Diffie-Hellman Helper. Unable to Continue."
case .unableToCreateLocalPublicKey:
return "Unable to Create an Elliptic Curve Diffie-Hellman Public Key with Secure Enclave for this Device. Unable to Continue."
case .unableToCreateRemotePublicKeyData:
return "Unable to convert the provided string to Public Key data. Unable to Continue."
case .unableToCreateRemotePublicKey:
return "Unable to Create an Elliptic Curve Diffie-Hellman Public Key using Secure Enclave with public key from the remote endpoint. Unable to Continue."
case .unableToCreateSharedSecret:
return "Unable to Create a Elliptic Curve Diffie-Hellman Shared Secret. Unable to Continue."
}
}
}
def handshake_with_server(self, client_id: str) -> ResponseModel:
"""
Handshakes with the server.
"""
response = ResponseModel()
# Create client_id for this client.
handshake = HandshakeModel()
handshake.conversation_identifier = client_id
# Create Eclypses DH containers for handshake.
encoder_ecdh = EclypsesECDH()
decoder_ecdh = EclypsesECDH()
# Get the public key to send to other side.
handshake.client_encoder_public_key = encoder_ecdh.get_device_public_key()
handshake.client_decoder_public_key = decoder_ecdh.get_device_public_key()
# Perform handshake.
url = Constants().REST_API_NAME + "/api/handshake"
payload = {
"Timestamp": "null",
"ConversationIdentifier": handshake.conversation_identifier,
"ClientEncoderPublicKey": handshake.client_encoder_public_key.decode(),
"ClientDecoderPublicKey": handshake.client_decoder_public_key.decode()
}
headers = {
"Content-Type": "application/json",
"accept": "*/*",
Constants().CLIENT_ID_HEADER: handshake.conversation_identifier
}
api_handshake_response = requests.post(
url=url, data=json.dumps(payload), headers=headers)
# Deserialize the result from handshake.
server_response = api_handshake_response.json()
# If handshake was not successful, then break.
if server_response['Success'] == False:
print("Error making DH handshake for Client {0}: {1}".format(
client_id, server_response['Message']))
response.data = server_response['Data']
response.message = server_response['Message']
response.success = server_response['Success']
response.result_code = server_response['ResultCode']
response.access_token = server_response['access_token']
response.exception_uid = server_response['ExceptionUid']
return response
# Create shared secret.
encoder_shared_secret_model = encoder_ecdh.create_shared_secret(
bytes(server_response['Data']['ClientEncoderPublicKey'], 'utf-8'))
decoder_shared_secret_model = decoder_ecdh.create_shared_secret(
bytes(server_response['Data']['ClientDecoderPublicKey'], 'utf-8'))
# Set MTE settings and get state.
# Get the nonce from the timestamp.
nonce = int(server_response['Data']['Timestamp'])
response.data = HandshakeResponse()
# Set Encoder and then save state.
encoder = MteMkeEnc.fromdefault()
encoder.set_entropy(base64.b64decode(encoder_shared_secret_model))
encoder.set_nonce(nonce)
status = encoder.instantiate(handshake.conversation_identifier)
if status != MteStatus.mte_status_success:
response.success = False
response.message = "Failed to initialize the MTE Encoder engine. Status {0} / {1}".format(
encoder.get_status_name(status), encoder.get_status_description(status))
response.result_code = Constants().RC_MTE_STATE_CREATION
return response
response.data.encoder_state = encoder.save_state_b64()
# Set Decoder and then save state.
decoder = MteMkeDec.fromdefault()
decoder.set_entropy(base64.b64decode(decoder_shared_secret_model))
decoder.set_nonce(nonce)
status = decoder.instantiate(handshake.conversation_identifier)
if status != MteStatus.mte_status_success:
response.success = False
response.message = "Failed to initialize the MTE Decoder engine. Status {0} / {1}".format(
decoder.get_status_name(status), decoder.get_status_description(status))
response.result_code = Constants().RC_MTE_STATE_CREATION
return response
response.data.decoder_state = decoder.save_state_b64()
return response
/**
* Performs Handshake with Server
* Creates the ECDH public keys and sends them to server
* When the client receives it back generate the shared secret
* Then creates the Encoder and Decoder and saves the states
*
* clientId: clientId string
*
* Returns HandshakeResponse: encoderSharedSecret, decoderSharedSecret
*
*/
func PerformHandshakeWithServer(clientId string) (out int, err error) {
fmt.Println("Performing handshake for client: " + clientId)
//--------------------------------------------
// Set default return and response parameters
var handshakeModel HandshakeModel
handshakeModel.ConversationIdentifier = clientId
//----------------------------------------------
// Create Eclypses ECDH for Encoder and Decoder
encoderEcdh := eclypsesEcdh.New()
decoderEcdh := eclypsesEcdh.New()
//----------------------------
// Get the Encoder public key
clientEncoderPKBytes, err := encoderEcdh.GetPublicKey()
if err != nil {
fmt.Println("Error creating Encoder public key: "
+ err.Error() + " Code: " + strconv.Itoa(errorCreatingPK))
return errorCreatingPK, err
}
//----------------------------
// Get the Decoder public key
clientDecoderPKBytes, err := decoderEcdh.GetPublicKey()
if err != nil {
fmt.Println("Error creating Decoder public key: "
+ err.Error() + " Code: " + strconv.Itoa(errorCreatingPK))
return errorCreatingPK, err
}
//-----------------------------------------
// Base64 encode keys so we can send them
handshakeModel.ClientEncoderPublicKey = base64.StdEncoding.EncodeToString(clientEncoderPKBytes)
handshakeModel.ClientDecoderPublicKey = base64.StdEncoding.EncodeToString(clientDecoderPKBytes)
//----------------------------------
// Json encode our handshake model
handshakeString, err := json.Marshal(handshakeModel)
if err != nil {
fmt.Println("Error marshalling handshakeModel: "
+ err.Error() + " Code: " + strconv.Itoa(errorMarshalJson))
return errorMarshalJson, err
}
//----------------------------------
// Make Http and get return string
hsModelString, errorcode, err := MakeHttpCall(restAPIName+handshakeRoute,
"POST",
clientId,
jsonContent,
string(handshakeString))
if err != nil {
fmt.Println("Error making Http call: "
+ err.Error() + " Code: " + strconv.Itoa(errorcode))
return errorcode, err
}
//-----------------------------
// Marshal json back to class
hrBytes := []byte(hsModelString)
var serverResponse ResponseModel[HandshakeModel]
json.Unmarshal(hrBytes, &serverResponse)
if !serverResponse.Success {
fmt.Println("Error back from server: "
+ serverResponse.Message + " Code: "
+ strconv.Itoa(errorFromServer))
return errorFromServer, errors.New(serverResponse.Message)
}
//--------------------------------------------
// Base64 Decode Encoder public key to []byte
partnerEncoderPublicKeyb64 :=
make([]byte, base64.StdEncoding.DecodedLen(len(serverResponse.Data.ClientEncoderPublicKey)))
n, err := base64.StdEncoding.Decode(partnerEncoderPublicKeyb64,
[]byte(serverResponse.Data.ClientEncoderPublicKey))
if err != nil {
fmt.Println("Error base64 decode encoderPK: "
+ err.Error() + " Code: " + strconv.Itoa(errorDecodingPK))
return errorDecodingPK, err
}
partnerEncoderPublicKeyBytes := partnerEncoderPublicKeyb64[:n]
//--------------------------------------------
// Base64 Decode Decoder public key to []byte
partnerDecoderPublicKeyb64 :=
make([]byte, base64.StdEncoding.DecodedLen(len(serverResponse.Data.ClientDecoderPublicKey)))
n, err = base64.StdEncoding.Decode(partnerDecoderPublicKeyb64,
[]byte(serverResponse.Data.ClientDecoderPublicKey))
if err != nil {
fmt.Println("Error base64 decode decoderPK: "
+ err.Error() + " Code: " + strconv.Itoa(errorDecodingPK))
return errorDecodingPK, err
}
partnerDecoderPublicKeyBytes := partnerDecoderPublicKeyb64[:n]
//-------------------------------
// Create Encoder shared secret
enSSBytes, err := encoderEcdh.CreateSharedSecret(partnerEncoderPublicKeyBytes, nil)
if err != nil {
fmt.Println("Error creating Encoder shared secret: "
+ err.Error() + " Code: " + strconv.Itoa(errorCreatingSS))
return errorCreatingSS, err
}
//-----------------------------
// Create Decoder shared secret
deSSBytes, err := decoderEcdh.CreateSharedSecret(partnerDecoderPublicKeyBytes, nil)
if err != nil {
fmt.Println("Error creating Decoder shared secret: "
+ err.Error() + " Code: " + strconv.Itoa(errorCreatingSS))
return errorCreatingSS, err
}
//-------------------------
// Clear out container
encoderEcdh.ClearContainer()
decoderEcdh.ClearContainer()
//------------------------------------
// Check version and output to screen
//------------------------------------
mteVersion := mte.GetVersion()
fmt.Printf("Using Mte Version %s\n", mteVersion)
//--------------------------------
// Check license -- use constants
// If there is no license,
// These values can be blank
//--------------------------------
if !mte.InitLicense(companyName, companyLicense) {
fmt.Println("There was an error attempting to initialize the MTE License.")
return
}
//---------------------------------
// Create MTE Encoder and Decoder
retcode, err := CreateMteEncoder(serverResponse.Data.TimeStamp,
clientId,
enSSBytes)
if err != nil {
fmt.Println("Error creating Encoder: "
+ err.Error() + " Code: " + strconv.Itoa(errorCreatingEncoder))
return retcode, err
}
retcode, err = CreateMteDecoder(serverResponse.Data.TimeStamp,
clientId,
deSSBytes)
if err != nil {
fmt.Println("Error creating Decoder: "
+ err.Error() + " Code: " + strconv.Itoa(errorCreatingDecoder))
return retcode, err
}
return 0, nil
}