Baseline: Ausgangszustand vor Modularisierung

Erster Commit des bestehenden monolithischen WinForms-Copytraders,
inklusive der Alt-Backups (*.bak), damit diese dauerhaft in der
Historie rekonstruierbar bleiben. Threema-Lib unter libs/ wurde
vendored (nested .git entfernt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
bergm
2026-07-01 13:16:16 +02:00
co-authored by Claude Opus 4.8
commit 475d396f80
147 changed files with 25455 additions and 0 deletions
@@ -0,0 +1,133 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using IcgSoftware.Threema.CoreMsgApi.Messages;
using IcgSoftware.Threema.CoreMsgApi.Results;
namespace IcgSoftware.Threema.CoreMsgApi.Com
{
[ComVisible(true)]
[ProgId("Threema.MsgApi.Com.CryptTool")]
[Guid("0B98D653-CD23-4827-9C5B-AEC0DCFBD142")]
public class CryptToolWrapper : ICryptToolWrapper
{
/// <summary>
/// Wrapper to encrypt text <see cref="Threema.MsgApi.CryptTool.EncryptTextMessage"/>
/// </summary>
/// <param name="text">Text to encrypt</param>
/// <param name="senderPrivateKey">Sender private key as hex-string</param>
/// <param name="recipientPublicKey">Recipient public key as hex-string</param>
/// <returns>Array with encrypted text, nonce and size</returns>
public ArrayList EncryptTextMessage(string text, string senderPrivateKey, string recipientPublicKey)
{
byte[] privateKey = GetKey(senderPrivateKey, Key.KeyType.PRIVATE);
byte[] publicKey = GetKey(recipientPublicKey, Key.KeyType.PUBLIC);
string textEncoded = DataUtils.Utf8Endcode(text);
EncryptResult encryptResult = CryptTool.EncryptTextMessage(textEncoded, privateKey, publicKey);
var result = new ArrayList();
result.Add(DataUtils.ByteArrayToHexString(encryptResult.Result));
result.Add(DataUtils.ByteArrayToHexString(encryptResult.Nonce));
result.Add(encryptResult.Size.ToString());
return result;
}
/// <summary>
/// Wrapper to decrypt box <see cref="Threema.MsgApi.CryptTool.DecryptMessage"/>
/// </summary>
/// <param name="box">Encrypted box as hex-straing</param>
/// <param name="recipientPrivateKey">Recipient private key as hex-string</param>
/// <param name="senderPublicKey">Sender public key as hex-string</param>
/// <param name="nonce">Nonce as hex-string</param>
/// <returns>Array with type and decrypted message</returns>
public ArrayList DecryptMessage(string box, string recipientPrivateKey, string senderPublicKey, string nonce)
{
byte[] privateKey = GetKey(recipientPrivateKey, Key.KeyType.PRIVATE);
byte[] publicKey = GetKey(senderPublicKey, Key.KeyType.PUBLIC);
byte[] nonceBytes = DataUtils.HexStringToByteArray(nonce);
byte[] boxBytes = DataUtils.HexStringToByteArray(box);
ThreemaMessage message = CryptTool.DecryptMessage(boxBytes, privateKey, publicKey, nonceBytes);
var result = new ArrayList();
result.Add(message.GetTypeCode().ToString());
result.Add(message.ToString());
return result;
}
/// <summary>
/// Wrapper to hash email <see cref="Threema.MsgApi.CryptTool.HashEmail"/>
/// </summary>
/// <param name="email">Email adress</param>
/// <returns>Hash of email adress as hex-string</returns>
public string HashEmail(string email)
{
byte[] emailHash = CryptTool.HashEmail(email);
return DataUtils.ByteArrayToHexString(emailHash);
}
/// <summary>
/// Wrapper to hash email <see cref="Threema.MsgApi.CryptTool.HashPhoneNo"/>
/// </summary>
/// <param name="phoneNo">Phone number</param>
/// <returns>Hash of phone number as hex-string</returns>
public string HashPhoneNo(string phoneNo)
{
byte[] phoneHash = CryptTool.HashPhoneNo(phoneNo);
return DataUtils.ByteArrayToHexString(phoneHash);
}
/// <summary>
/// Wrapper to generate key pair <see cref="Threema.MsgApi.CryptTool.GenerateKeyPair"/>
/// </summary>
/// <param name="privateKeyPath">Full path name of private key file</param>
/// <param name="publicKeyPath">Full path name of public key file</param>
public void GenerateKeyPair(string privateKeyPath, string publicKeyPath)
{
byte[] privateKey = new byte[32]; //NaCl.SECRETKEYBYTES
byte[] publicKey = new byte[32]; //NaCl.PUBLICKEYBYTES
CryptTool.GenerateKeyPair(ref privateKey, ref publicKey);
// Write both keys to file
DataUtils.WriteKeyFile(privateKeyPath, new Key(Key.KeyType.PRIVATE, privateKey));
DataUtils.WriteKeyFile(publicKeyPath, new Key(Key.KeyType.PUBLIC, publicKey));
}
/// <summary>
/// Wrapper to derive public key <see cref="CryptTool.DerivePublicKey"/>
/// </summary>
/// <param name="privateKey">private key as file path or hex-string</param>
/// <returns>Public key as hex-string</returns>
public string DerivePublicKey(string privateKey)
{
byte[] privateKeyBytes = GetKey(privateKey, Key.KeyType.PRIVATE);
byte[] publicKey = CryptTool.DerivePublicKey(privateKeyBytes);
return new Key(Key.KeyType.PUBLIC, publicKey).Encode();
}
private byte[] GetKey(string argument, string expectedKeyType)
{
Key key;
if (File.Exists(argument))
{
key = DataUtils.ReadKeyFile(argument, expectedKeyType);
}
else
{
key = IcgSoftware.Threema.CoreMsgApi.Key.DecodeKey(argument, expectedKeyType);
}
return key.key;
}
}
}
@@ -0,0 +1,23 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using IcgSoftware.Threema.CoreMsgApi.Results;
namespace IcgSoftware.Threema.CoreMsgApi.Com
{
[ComVisible(true)]
[Guid("4F376DEB-6F78-460A-836F-B38371712EFF")]
public interface ICryptToolWrapper
{
ArrayList EncryptTextMessage(string text, string senderPrivateKey, string recipientPublicKey);
ArrayList DecryptMessage(string box, string recipientPrivateKey, string senderPublicKey, string nonce);
string HashEmail(string email);
string HashPhoneNo(string phoneNo);
void GenerateKeyPair(string privateKeyPath, string publicKeyPath);
string DerivePublicKey(string privateKey);
}
}
@@ -0,0 +1,26 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace IcgSoftware.Threema.CoreMsgApi.Com
{
[ComVisible(true)]
[Guid("E9E32936-CCAB-4297-BAB6-7F3B5939F3D4")]
public interface IMessageToolWrapper
{
string SendTextMessageSimple(string to, string from, string secret, string text, string apiUrl = APIConnector.DEFAULTAPIURL);
string SendTextMessage(string to, string from, string secret, string privateKey, string text, string apiUrl = APIConnector.DEFAULTAPIURL);
string SendImageMessage(string to, string from, string secret, string privateKey, string imageFilePath, string apiUrl = APIConnector.DEFAULTAPIURL);
string SendFileMessage(string to, string from, string secret, string privateKey, string file, string thumbnail = null, string apiUrl = APIConnector.DEFAULTAPIURL);
string LookupEmail(string email, string from, string secret, string apiUrl = APIConnector.DEFAULTAPIURL);
string LookupPhone(string phoneNo, string from, string secret, string apiUrl = APIConnector.DEFAULTAPIURL);
string LookupKey(string threemaId, string from, string secret, string apiUrl = APIConnector.DEFAULTAPIURL);
ArrayList LookupKeyCapability(string threemaId, string from, string secret, string apiUrl = APIConnector.DEFAULTAPIURL);
int? LookupCredits(string from, string secret, string apiUrl = APIConnector.DEFAULTAPIURL);
ArrayList ReceiveMessage(string id, string from, string secret, string privateKey, string messageId, string nonce, string box, string outputFolder = null, string apiUrl = APIConnector.DEFAULTAPIURL);
}
}
@@ -0,0 +1,221 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using IcgSoftware.Threema.CoreMsgApi.Helpers;
using IcgSoftware.Threema.CoreMsgApi.Results;
namespace IcgSoftware.Threema.CoreMsgApi.Com
{
[ComVisible(true)]
[ProgId("Threema.MsgApi.Com.MessageTool")]
[Guid("097F1D26-B38E-4501-845D-6DE7DBFB5EAA")]
public class MessageToolWrapper : IMessageToolWrapper
{
/// <summary>
/// Wrapper to send simple message <see cref="Threema.MsgApi.APIConnector.SendTextMessageSimple"/>
/// </summary>
/// <param name="to">Recipient id</param>
/// <param name="from">Sender id</param>
/// <param name="secret">Sender sercret</param>
/// <param name="text">Text message</param>
/// <param name="apiUrl">Optional api url</param>
/// <returns>Message id</returns>
public string SendTextMessageSimple(string to, string from, string secret, string text, string apiUrl = APIConnector.DEFAULTAPIURL)
{
APIConnector apiConnector = this.CreateConnector(from, secret, apiUrl);
return apiConnector.SendTextMessageSimple(to, DataUtils.Utf8Endcode(text));
}
/// <summary>
/// Wrapper to send text message E2E <see cref="Threema.MsgApi.E2EHelper.SendTextMessage"/>
/// </summary>
/// <param name="to">Recipient id</param>
/// <param name="from">Sender id</param>
/// <param name="secret">Sender sercret</param>
/// <param name="privateKey">Sender private key</param>
/// <param name="text">Text message</param>
/// <param name="apiUrl">Optional api url</param>
/// <returns>Message id</returns>
public string SendTextMessage(string to, string from, string secret, string privateKey, string text, string apiUrl = APIConnector.DEFAULTAPIURL)
{
byte[] privateKeyBytes = GetKey(privateKey, Key.KeyType.PRIVATE);
E2EHelper e2EHelper = new E2EHelper(this.CreateConnector(from, secret, apiUrl), privateKeyBytes);
return e2EHelper.SendTextMessage(to, DataUtils.Utf8Endcode(text));
}
/// <summary>
/// Wrapper to send image message E2E <see cref="Threema.MsgApi.E2EHelper.SendImageMessage"/>
/// </summary>
/// <param name="to">Recipient id</param>
/// <param name="from">Sender id</param>
/// <param name="secret">Sender sercret</param>
/// <param name="privateKey">Sender private key</param>
/// <param name="imageFilePath">File path to image</param>
/// <param name="apiUrl">Optional api url</param>
/// <returns>Message id</returns>
public string SendImageMessage(string to, string from, string secret, string privateKey, string imageFilePath, string apiUrl = APIConnector.DEFAULTAPIURL)
{
byte[] privateKeyBytes = GetKey(privateKey, Key.KeyType.PRIVATE);
E2EHelper e2EHelper = new E2EHelper(this.CreateConnector(from, secret, apiUrl), privateKeyBytes);
return e2EHelper.SendImageMessage(to, imageFilePath);
}
/// <summary>
/// Wrapper to send file message E2E <see cref="Threema.MsgApi.E2EHelper.SendFileMessage"/>
/// </summary>
/// <param name="to">Recipient id</param>
/// <param name="from">Sender id</param>
/// <param name="secret">Sender sercret</param>
/// <param name="privateKey">Sender private key</param>
/// <param name="file">File path to file</param>
/// <param name="thumbnail">File path to thumbnail</param>
/// <param name="apiUrl">Optional api url</param>
/// <returns>Message id</returns>
public string SendFileMessage(string to, string from, string secret, string privateKey, string file, string thumbnail = null, string apiUrl = APIConnector.DEFAULTAPIURL)
{
byte[] privateKeyBytes = GetKey(privateKey, Key.KeyType.PRIVATE);
FileInfo fileInfo = file != null ? new FileInfo(file) : null;
FileInfo thumbnailInfo = thumbnail != null ? new FileInfo(thumbnail) : null;
E2EHelper e2EHelper = new E2EHelper(this.CreateConnector(from, secret, apiUrl), privateKeyBytes);
return e2EHelper.SendFileMessage(to, fileInfo, thumbnailInfo);
}
/// <summary>
/// Wrapper to id lookup via email <see cref="Threema.MsgApi.APIConnector.LookupEmail"/>
/// </summary>
/// <param name="email">Email for lookup</param>
/// <param name="from">Sender id</param>
/// <param name="secret">Sender secret</param>
/// <param name="apiUrl">Optional api url</param>
/// <returns>id</returns>
public string LookupEmail(string email, string from, string secret, string apiUrl = APIConnector.DEFAULTAPIURL)
{
APIConnector apiConnector = this.CreateConnector(from, secret, apiUrl);
return apiConnector.LookupEmail(email);
}
/// <summary>
/// Wrapper to id lookup via phone number <see cref="Threema.MsgApi.APIConnector.LookupPhone"/>
/// </summary>
/// <param name="phoneNo">Phone number for lookup</param>
/// <param name="from">Sender id</param>
/// <param name="secret">Sender secret</param>
/// <param name="apiUrl">Optional api url</param>
/// <returns>id</returns>
public string LookupPhone(string phoneNo, string from, string secret, string apiUrl = APIConnector.DEFAULTAPIURL)
{
APIConnector apiConnector = this.CreateConnector(from, secret, apiUrl);
return apiConnector.LookupPhone(phoneNo);
}
/// <summary>
/// Wrapper to lookup/fetch public key <see cref="Threema.MsgApi.APIConnector.LookupKey"/>
/// </summary>
/// <param name="threemaId">Id for lookup</param>
/// <param name="from">Sender id</param>
/// <param name="secret">Sender secret</param>
/// <param name="apiUrl">Optional api url</param>
/// <returns>public key has hex-string</returns>
public string LookupKey(string threemaId, string from, string secret, string apiUrl = APIConnector.DEFAULTAPIURL)
{
APIConnector apiConnector = this.CreateConnector(from, secret, apiUrl);
byte[] publicKey = apiConnector.LookupKey(threemaId);
if (publicKey != null)
{
return new Key(Key.KeyType.PUBLIC, publicKey).Encode();
}
return null;
}
/// <summary>
/// Wrapper to lookup capabilities <see cref="Threema.MsgApi.APIConnector.LookupKeyCapability"/>
/// </summary>
/// <param name="threemaId">Id for lookup</param>
/// <param name="from">Sender id</param>
/// <param name="secret">Sender secret</param>
/// <param name="apiUrl">Optional api url</param>
/// <returns>Array with capatilities</returns>
public System.Collections.ArrayList LookupKeyCapability(string threemaId, string from, string secret, string apiUrl = APIConnector.DEFAULTAPIURL)
{
CapabilityResult capabilities = this.CreateConnector(from, secret, apiUrl)
.LookupKeyCapability(threemaId);
var result = new ArrayList();
capabilities.Capabilities.ToList().ForEach(c => result.Add(c));
return result;
}
/// <summary>
/// Wrapper to lookup credits <see cref="Threema.MsgApi.APIConnector.LookupCredits"/>
/// </summary>
/// <param name="from">From id</param>
/// <param name="secret">From secret</param>
/// <param name="apiUrl">Optional api url</param>
/// <returns>credits or null</returns>
public int? LookupCredits(string from, string secret, string apiUrl = APIConnector.DEFAULTAPIURL)
{
return this.CreateConnector(from, secret, apiUrl).LookupCredits();
}
/// <summary>
/// Wrapper to receive message and download files <see cref="Threema.MsgApi.Helpers.E2EHelper.ReceiveMessage"/>
/// </summary>
/// <param name="id">Sender id</param>
/// <param name="from">From id</param>
/// <param name="secret">From secret</param>
/// <param name="privateKey">From private key</param>
/// <param name="messageId">Message id</param>
/// <param name="nonce">Nonce as hex-string</param>
/// <param name="box">Box message as hex-string</param>
/// <param name="outputFolder">Optional path to output folder</param>
/// <param name="apiUrl">Optional api url</param>
/// <returns>Array with message-type, message-id and message</returns>
public ArrayList ReceiveMessage(string id, string from, string secret, string privateKey, string messageId, string nonce, string box, string outputFolder = null, string apiUrl = APIConnector.DEFAULTAPIURL)
{
byte[] privateKeyBytes = GetKey(privateKey, Key.KeyType.PRIVATE);
byte[] nonceBytes = DataUtils.HexStringToByteArray(nonce);
E2EHelper e2EHelper = new E2EHelper(this.CreateConnector(from, secret, apiUrl), privateKeyBytes);
byte[] boxBytes = DataUtils.HexStringToByteArray(box);
ReceiveMessageResult res = e2EHelper.ReceiveMessage(id, messageId, boxBytes, nonceBytes, outputFolder);
ArrayList result = new ArrayList();
result.Add(res.Message.GetTypeCode().ToString());
result.Add(res.MessageId);
result.Add(res.Message.ToString());
return result;
}
private APIConnector CreateConnector(string gatewayId, string secret, string apiUrl)
{
return new APIConnector(gatewayId, secret, apiUrl, new PublicKeyStoreNone());
}
private byte[] GetKey(string argument, string expectedKeyType)
{
Key key;
if (File.Exists(argument))
{
key = DataUtils.ReadKeyFile(argument, expectedKeyType);
}
else
{
key = IcgSoftware.Threema.CoreMsgApi.Key.DecodeKey(argument, expectedKeyType);
}
return key.key;
}
}
}