Skip to main content

console_application

//-------------------------------------------------------------------------
// The file structure for the CSharp project should include the following:
//-------------------------------------------------------------------------
// - Models (folder)
// - Constants.cs
// - HandshakeModel.cs
// - HandshakeResponse.cs
// - ResponseModel.cs
// - UploadResponse.cs
// - Add NuGet dependencies
// - Eclypses.MTE.Core(2.1.x)
// - PackageCSharpECDH(1.0.1)
// - mte.dll or libmte.so --> mte library
// - Program.cs
// - UploadFiles.cs
//-------------------------------------------------------------------------
// Below only pertinent MTE file code is included
// Links are included at the bottom of this page to download entire project
//-------------------------------------------------------------------------
Program.cs
using MteConsoleUploadTest.Models;
using System;
using System.Windows.Forms;

namespace MteConsoleUploadTest
{
class Program
{
//------------------
// set if using MTE
//------------------
private static bool useMte = true;


[STAThread]
static void Main()
{
//--------------------------
// Create Upload File class
//--------------------------
UploadFile uploadFile = new UploadFile();

//-------------------------------------------------
// Initialize encoder and decoder and set clientId
//-------------------------------------------------
HandshakeResponse handshake = new HandshakeResponse { EncoderState = String.Empty, DecoderState = String.Empty };
string clientId = Guid.NewGuid().ToString();

//---------------------------------------------------------
// Handshake with server and create MTE if "useMte == true"
//---------------------------------------------------------
if (useMte)
{
ResponseModel<HandshakeResponse> handshakeResponse = uploadFile.HandshakeWithServer(clientId);
if (!handshakeResponse.Success)
{
throw new ApplicationException($"Error trying to handshake with server: {handshakeResponse.Message}");
}

//-------------------------------
// Set decoder and encoder state
//-------------------------------
handshake.DecoderState = handshakeResponse.Data.DecoderState;
handshake.EncoderState = handshakeResponse.Data.EncoderState;
}

while (true)
{
//--------------------------------
// Prompt user for file to upload
//--------------------------------
string path = string.Empty;
while (string.IsNullOrWhiteSpace(path))
{
OpenFileDialog dialog = new OpenFileDialog();
if (DialogResult.OK == dialog.ShowDialog())
{
path = dialog.FileName;
}
}

//---------------------
// Send file to server
//---------------------
ResponseModel<UploadResponse> uploadResponse = uploadFile.Send(path, useMte, handshake.EncoderState, handshake.DecoderState, clientId);

if (!uploadResponse.Success)
{
throw new ApplicationException($"Error uploading file: {uploadResponse.Message}");
}
Console.WriteLine(uploadResponse.Data.ServerResponse);

//--------------------------------------------------------
// Update Encoder and Decoder states to be latest version
//--------------------------------------------------------
handshake.EncoderState = uploadResponse.Data.EncoderState;
handshake.DecoderState = uploadResponse.Data.DecoderState;

//--------------------------------------
// Promot to upload another file or not
//--------------------------------------
Console.WriteLine($"Would you like to upload an additional file? (y/n)");
string sendAdditional = Console.ReadLine();
if (sendAdditional != null && sendAdditional.Equals("n", StringComparison.InvariantCultureIgnoreCase))
{
break;
}
}
}
}
}
UploadFile.cs
using Eclypses.MTE;
using MteConsoleUploadTest.Models;
using PackageCSharpECDH;
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace MteConsoleUploadTest
{
public class UploadFile
{
#region UploadFile Constructor
//------------------
// Set Rest API URL
//------------------
private static readonly string _restAPIName = "http://localhost:52603";

//---------------------------
// Set upload File constants
//---------------------------
private const int _maxChunkSize = 1024;
private HttpWebRequest _webRequest = null;
private FileStream _fileReader = null;
private Stream _requestStream = null;
private static JsonSerializerOptions _jsonOptions;
private static readonly string _jsonContentType = "application/json";


/// <summary>Initializes a new instance of the <see cref="T:MteConsoleUploadTest.UploadFile" /> class.</summary>
public UploadFile()
{
//-------------------------------------
// Set up Json to be not case sensative
//-------------------------------------
_jsonOptions = new JsonSerializerOptions();
_jsonOptions.PropertyNameCaseInsensitive = true;
_jsonOptions.Converters.Add(new JsonStringEnumConverter());
}
#endregion

#region Send
/// <summary>
/// Sends the file up to the server
/// </summary>
/// <param name="path">The file path.</param>
/// <param name="useMte">Whether or not to use MTE when sending file.</param>
/// <param name="encoderState">The current encoder state.</param>
/// <param name="decoderState">The current decoder state.</param>
/// <param name="clientid">The client id.</param>
/// <returns>System.Int32.</returns>
public ResponseModel<UploadResponse> Send(string path, bool useMte, string encoderState, string decoderState, string clientid)
{
ResponseModel<UploadResponse> uploadResponse = new()
{
Data = new UploadResponse
{
EncoderState = encoderState,
DecoderState = decoderState
}
};
try
{
//------------------------
// Create default encoder
//------------------------
MteMkeEnc encoder = new MteMkeEnc();

//------------------------------
// Get file info and create url
//------------------------------
FileInfo file = new FileInfo(path);
string urlType = (useMte) ? "mte" : "nomte";
string fileUrl = Path.Combine($"{_restAPIName}/FileUpload/", urlType + "?name=" + file.Name);

//-----------------------------------
// Create file stream and webRequest
//-----------------------------------
_fileReader = new FileStream(path, FileMode.Open, FileAccess.Read);
_webRequest = (HttpWebRequest)WebRequest.Create(fileUrl);
_webRequest.Method = "POST";
if (useMte)
{
//--------------------------------------------------
// If we are using the MTE adjust the content length
//--------------------------------------------------
int additionalBytes = encoder.EncryptFinishBytes();
long finalLength = (long)(_fileReader.Length + additionalBytes);
_webRequest.ContentLength = finalLength;
}
else
{
//-------------------------------------------------------------
// Regular request will have the file length as content length
//-------------------------------------------------------------
_webRequest.ContentLength = _fileReader.Length;
}
_webRequest.Timeout = 600000;

//-------------------------
// Add client id to header if not null
//-------------------------
if (!string.IsNullOrWhiteSpace(clientid))
{
_webRequest.Headers.Add(Constants.ClientIdHeader, clientid);
}

_webRequest.Credentials = CredentialCache.DefaultCredentials;
_webRequest.AllowWriteStreamBuffering = false;
_requestStream = _webRequest.GetRequestStream();

long fileSize = _fileReader.Length;
long remainingBytes = fileSize;
int numberOfBytesRead = 0, done = 0;


if (useMte)
{
//----------------------------
// Restore encoder from state
//----------------------------
MteStatus status = encoder.RestoreStateB64(encoderState);
if (status != MteStatus.mte_status_success)
{
uploadResponse.Success = false;
uploadResponse.ResultCode = Constants.RC_MTE_STATE_RETRIEVAL;
uploadResponse.Message = $"Failed to restore MTE encoder engine. Status: {encoder.GetStatusName(status)} / {encoder.GetStatusDescription(status)}";
return uploadResponse;
}

//----------------------------
// start the chunking session
//----------------------------
status = encoder.StartEncrypt();
if (status != MteStatus.mte_status_success)
{
uploadResponse.Success = false;
uploadResponse.ResultCode = Constants.RC_MTE_ENCODE_EXCEPTION;
uploadResponse.Message = "Failed to start encode chunk. Status: "
+ encoder.GetStatusName(status) + " / "
+ encoder.GetStatusDescription(status);
return uploadResponse;
}
}

//------------------------
// Break up files to send
//------------------------
while (numberOfBytesRead < fileSize)
{
byte[] fileData;
SetByteArray(out fileData, remainingBytes);
done = _fileReader.Read(fileData, 0, fileData.Length);
if (useMte)
{
//------------------------------------------------------------
// Encode the data in place - encoded data put back in buffer
//------------------------------------------------------------
MteStatus chunkStatus = encoder.EncryptChunk(fileData, 0, fileData.Length);
if (chunkStatus != MteStatus.mte_status_success)
{
uploadResponse.Success = false;
uploadResponse.ResultCode = Constants.RC_MTE_ENCODE_EXCEPTION;
uploadResponse.Message = "Failed to encode chunk. Status: "
+ encoder.GetStatusName(chunkStatus) + " / "
+ encoder.GetStatusDescription(chunkStatus);
return uploadResponse;
}
}
//-----------------------------
// Write the data to the stream
//-----------------------------
_requestStream.Write(fileData, 0, fileData.Length);
numberOfBytesRead += done;
remainingBytes -= done;
}

if (useMte)
{
//----------------------------
// Finish the chunking session
//----------------------------
byte[] finalEncodedChunk = encoder.FinishEncrypt(out MteStatus finishStatus);
if (finishStatus != MteStatus.mte_status_success)
{
uploadResponse.Success = false;
uploadResponse.ResultCode = Constants.RC_MTE_ENCODE_EXCEPTION;
uploadResponse.Message = "Failed to finish encode chunk. Status: "
+ encoder.GetStatusName(finishStatus) + " / "
+ encoder.GetStatusDescription(finishStatus);
return uploadResponse;
}

//------------------------------------
// Append the final data to the stream
//------------------------------------
_requestStream.Write(finalEncodedChunk, 0, finalEncodedChunk.Length);

//-----------------------
// Save the encoderState
//-----------------------
uploadResponse.Data.EncoderState = encoder.SaveStateB64();

}

//------------------
// Get the response.
//------------------
WebResponse response = _webRequest.GetResponse();

//---------------------
// get the return text
//---------------------
using Stream data = response.GetResponseStream();
using var reader = new StreamReader(data);
string text = reader.ReadToEnd();

ResponseModel<byte[]> textResponse =
JsonSerializer.Deserialize<ResponseModel<byte[]>>(text, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

if (!textResponse.Success)
{
//-----------------------------------
// Check if we need to "re-handshake"
//-----------------------------------
if (textResponse.ResultCode.Equals(Constants.RC_MTE_STATE_NOT_FOUND,
StringComparison.InvariantCultureIgnoreCase))
{
//-------------------------------------------------------------------------
// the server does not have this client's state - we should "re-handshake"
//-------------------------------------------------------------------------
var handshakeRessponse = HandshakeWithServer(clientid);

//-----------------------------------------------------------
// return response, if successful give message to try again.
//-----------------------------------------------------------
uploadResponse.Success = handshakeRessponse.Success;
uploadResponse.Message = (uploadResponse.Success)
? "Server lost MTE state, client needed to handshake again, handshake successful, please try again."
: handshakeRessponse.Message;
uploadResponse.ResultCode = handshakeRessponse.ResultCode;
return uploadResponse;
}
}

if (useMte)
{
//------------------
// Restroe decoder
//------------------
MteMkeDec decoder = new MteMkeDec();
MteStatus status = decoder.RestoreStateB64(decoderState);
if (status != MteStatus.mte_status_success)
{
uploadResponse.Success = false;
uploadResponse.ResultCode = Constants.RC_MTE_STATE_RETRIEVAL;
uploadResponse.Message = $"Failed to restore the MTE decoder engine. Status: {decoder.GetStatusName(status)} / {decoder.GetStatusDescription(status)}";
return uploadResponse;
}

//----------------------------
// start the chunking session
//----------------------------
status = decoder.StartDecrypt();
if (status != MteStatus.mte_status_success)
{
throw new Exception("Failed to start decode chunk. Status: "
+ decoder.GetStatusName(status) + " / "
+ decoder.GetStatusDescription(status));
}
//-----------------
// Decode the data
//-----------------
byte[] decodedChunk = decoder.DecryptChunk(textResponse.Data);
var clearBytes = decoder.FinishDecrypt(out MteStatus finalStatus);
if (clearBytes == null) { clearBytes = new byte[0]; }
if (finalStatus != MteStatus.mte_status_success)
{
throw new Exception("Failed to finish decode. Status: "
+ decoder.GetStatusName(finalStatus) + " / "
+ decoder.GetStatusDescription(finalStatus));
}
//---------------------
// Set decoded message
//---------------------
byte[] decodedMessage = new byte[decodedChunk.Length + clearBytes.Length];
Buffer.BlockCopy(decodedChunk, 0, decodedMessage, 0, decodedChunk.Length);
Buffer.BlockCopy(clearBytes, 0, decodedMessage, decodedChunk.Length, clearBytes.Length);

//----------------------------
// Return the server response
//----------------------------
uploadResponse.Data.ServerResponse = Encoding.UTF8.GetString(decodedMessage);

//------------------------
// Save the decoder state
//------------------------
uploadResponse.Data.DecoderState = decoder.SaveStateB64();
}
else
{
//----------------------------
// Return the server response
//----------------------------
uploadResponse.Data.ServerResponse = Encoding.UTF8.GetString(textResponse.Data);
}

}
catch (Exception e)
{
uploadResponse.Message = $"Exception uploading file to server. Ex: {e.Message}";
uploadResponse.ResultCode = Constants.RC_UPLOAD_EXCEPTION;
uploadResponse.Success = false;
}
return uploadResponse;
}
#endregion

#region SetByteArray
/// <summary>
/// Sets the byte array.
/// </summary>
/// <param name="fileData">The file data.</param>
/// <param name="bytesLeft">The bytes left.</param>
private void SetByteArray(out byte[] fileData, long bytesLeft)
{
fileData = bytesLeft < _maxChunkSize ? new byte[bytesLeft] : new byte[_maxChunkSize];
}
#endregion

#region HandshakeWithServer
/// <summary>Handshakes the with server.</summary>
/// <param name="clientId">The client identifier.</param>
/// <returns>ResponseModel&lt;HandshakeResponse&gt;.</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($"{_restAPIName}/api/handshake", HttpMethod.Post, handshake.ConversationIdentifier,
_jsonContentType, JsonSerializer.Serialize(handshake, _jsonOptions)).Result;

//---------------------------------------
// Deserialize the result from handshake
//---------------------------------------
ResponseModel<HandshakeModel> serverResponse =
JsonSerializer.Deserialize<ResponseModel<HandshakeModel>>(handshakeResponse, _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;
}
#endregion

#region MakeHttpCall (Async)
/// <summary>
/// Makes the HTTP call.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="method">The method.</param>
/// <param name="clientId">The client identifier.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="payload">The payload.</param>
/// <returns>System.String.</returns>
private async Task<string> MakeHttpCall(string url, HttpMethod method, string clientId, string contentType, string payload = null)
{
//----------------------------------------------
// Declare return payload string and initialize
//----------------------------------------------
string returnPayload = string.Empty;
try
{
//-----------------------------------------
// Set URI and other default Http settings
//-----------------------------------------
Uri uri = new Uri($"{url}");
HttpResponseMessage responseMessage = null;
var handler = new HttpClientHandler() { };
using var client = new HttpClient(handler) { BaseAddress = uri };

//-------------------------
// Add client id to header
//-------------------------
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add(Constants.ClientIdHeader, clientId);

//---------------------------------------------------
// Check if we have a payload or not and send request
//---------------------------------------------------
if (string.IsNullOrWhiteSpace(payload))
{
//------------------------------------------------------------------------
// The only two methods that will not have any content are delete and get
//------------------------------------------------------------------------
responseMessage = method switch
{
HttpMethod m when m == HttpMethod.Delete => await client.DeleteAsync(uri),
HttpMethod m when m == HttpMethod.Get => await client.GetAsync(uri),
_ => await client.GetAsync(uri)
};
}
else
{
//Console.WriteLine($"Sending: '{payload}'");

//-----------------------------------------------
// Set byte payload and content type for request
//-----------------------------------------------
byte[] bytePayload = Encoding.ASCII.GetBytes(payload);
ByteArrayContent byteContent = new ByteArrayContent(bytePayload, 0, bytePayload.Length);
byteContent.Headers.Add("Content-Type", contentType);

//---------------------------------------------
// Create httpRequest with given byte payload
//---------------------------------------------
var httpRequest = new HttpRequestMessage
{
Method = method,
RequestUri = uri,
Content = byteContent
};

//------------------------
// Send out client request
//------------------------
responseMessage = await client.SendAsync(httpRequest).ConfigureAwait(false);
}

//------------------------------------------------------
// If the response is successful get the return payload
//------------------------------------------------------
if (responseMessage.IsSuccessStatusCode)
{
//------------------------------------------
// Use read as string if other content type
//------------------------------------------
returnPayload = await responseMessage.Content.ReadAsStringAsync();

}
else
{
//-----------------------------------------------------------------
// If the response is NOT successful return error in ResponseModel
//-----------------------------------------------------------------
ResponseModel<object> errorResponse = new ResponseModel<object>();
errorResponse.Success = false;
errorResponse.Message = $"HttpResponse status was not okay, Message: {responseMessage.ReasonPhrase} -- Code: {responseMessage.StatusCode}";
errorResponse.ResultCode = Constants.RC_HTTP_ERROR;
errorResponse.Data = null;
returnPayload = JsonSerializer.Serialize(errorResponse, _jsonOptions);
}
}
catch (Exception ex)
{
ResponseModel<object> errorResponse = new ResponseModel<object>();
errorResponse.Success = false;
errorResponse.Message = $"Exception sending Message: {ex.Message}";
errorResponse.ResultCode = Constants.RC_HTTP_ERROR;
errorResponse.Data = null;
returnPayload = JsonSerializer.Serialize(errorResponse, _jsonOptions);
}
return returnPayload;
}
#endregion
}
}