How to compose a S/MIME encrypted message in Rust? - email

With so many crates available, I doubt the one for composing an encrypted S/MIME message would be missing. I'm aware of pgp which should handle PGP/MIME. I'm also aware of lettre_email emailmessage and mailparse mail-core which could be used to compose a MIME e-mail message...
If there isn't one, I'm asking if someone already does this so I can copy cat and perhaps publish. Or else I'll be battling with it myself and will appreciate a good head start.
The goal is to encrypt messages at rest while stored on a mail server Samotop. Knowing the recipient's public key, I should be able to wrap-encrypt any incoming message for that recipient so that only the user owning the key will be able to decrypt the message. It may well be that S/MIME is not the right fit for this but I'd fancy to make this usable with existing e-mail clients with S/MIME support.
To start off, I suppose there will be a symmetric key that encrypts the message and this key will be encrypted using asymmetric key for the recipient (potentially for multiple recipients) and included in the payload. Here is a sketch of my ideas.
Random symmetric key is made:
let mut key = [0u8; 32];
SystemRandom::new().fill(&mut key).unwrap();
Content gets encrypted:
// Sealing key used to encrypt data
let mut sealing_key = SealingKey::new(
UnboundKey::new(&CHACHA20_POLY1305, key.as_ref()).unwrap(),
Nonces::new(5),
);
// Encrypt data into in_out variable
sealing_key
.seal_in_place_append_tag(Aad::empty(), &mut encrypted)
.unwrap();
Symmetric key gets asymmetrically encrypted for the recipient:
let enc_key = pub_key.encrypt(&mut rng, PaddingScheme::new_pkcs1v15_encrypt(), &key[..]).expect("failed to encrypt");
assert_ne!(&key[..], &enc_key[..]);
Now comes the time to compose the encrypted MIME part... ideas? crates? reference implementations? rfc8551

The only ready solution I've found so far is the openssl binding. It has a Pkcs7 struct that should be able to encrypt(), sign() and produce the mime part with to_smime().
Here is a slightly modified test from the openssl repository:
let cert = X509::from_pem(CERT)?;
let mut certs = Stack::new()?;
certs.push(cert.clone())?;
let flags = Pkcs7Flags::STREAM;
let message = b"secret stuff";
let pkcs7 = Pkcs7::encrypt(&certs.as_ref(), message, Cipher::aes_256_cbc(), flags)?;
let encrypted = pkcs7.to_smime(message, flags).expect("should succeed");

I figured this much. If I have to depend on openssl, I might just as well depend on it externally, running it as a child process. This will enable async io streaming as a bonus which doesn't seem to be supported by rust bindings (yet?).
fn main() -> Result<(), Box<dyn std::error::Error>> {
async_std::task::block_on(main_fut())
}
async fn main_fut() -> Result<(), Box<dyn std::error::Error>> {
let mut sign = async_process::Command::new("openssl")
.arg("smime")
.arg("-stream")
.arg("-in")
.arg("test/msg")
.arg("-sign")
.arg("-inkey")
.arg("test/my.key")
.arg("-signer")
.arg("test/my.crt")
.kill_on_drop(true)
.reap_on_drop(true)
.stdout(async_process::Stdio::piped())
.spawn()?;
let mut encrypt = async_process::Command::new("openssl")
.arg("smime")
.arg("-stream")
.arg("-out")
.arg("test/enc")
.arg("-encrypt")
.arg("test/her.crt")
.kill_on_drop(true)
.reap_on_drop(true)
.stdin(async_process::Stdio::piped())
.spawn()?;
let pipe = async_std::io::copy(
sign.stdout.as_mut().expect("sign output"),
encrypt.stdin.as_mut().expect("encrypt input"),
);
pipe.await?;
println!("sign: {:?}", sign.status().await);
println!("encrypt: {:?}", encrypt.status().await);
Ok(())
}

Related

Cannot send arbitrarily long messages via sockets

I'm writing a traceroute implementation and I'm deliberately trying to send a long message for the purposes of path maximum transmission unit detection. However, when I try to send something longer than 1500 bytes, I get:
thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 90, kind: Uncategorized, message: "Message too long" }', src/main.rs:115:26
I'm expecting to see a Fragmentation needed ICMP message.
The socket comes from the raw-socket crate.
It seems that PMTU is hard-coded somewhere deep. Or am I misdiagnosing this? What would prevent me from sending arbitrarily long messages via sockets?
MCVE:
use raw_socket::{RawSocket, Domain, Protocol, Type};
use raw_socket::option::{Level, Name};
use raw_socket::ffi::c_int;
fn main() {
let sock = RawSocket::new(Domain::ipv4(), Type::raw(), Protocol::from(255).into()).unwrap();
let buf = [0u8; 9000];
const IP_MTU_DISCOVER: i32 = 10;
const IP_PMTU_DISC_PROBE: i32 = 3;
sock.set_sockopt(Level::IPV4, Name::from(IP_MTU_DISCOVER), &(IP_PMTU_DISC_PROBE as c_int))
.unwrap();
sock.send_to(&buf, "142.250.201.206:33434").unwrap();
}

NETunnelProvider stop receiving packet on iOS 14?

I'm having a Local VPN app that using "NETunnelProvider / NetworkExtentsion", In my solution, I created a split tunnel on the device itself to track the DNS request, using NEKit I was able to peek inside the packets and filter the ongoing request based on the destination address (let's call ita UDP listener for DNS requests).
This solution was working fine on iOS 13.7 and less, recently apple release iOS 14, and my solution stop working, VPN connection still established but the user can't access any webSite, I debugged the code and found out the networkExtision does not receive any packets from user activity only.
I'm using the CocoaAsyncSocket library.
func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?) {
let host = GCDAsyncUdpSocket.host(fromAddress: address)
guard let message = DNSMessage(payload: data) else {
return
}
guard let session = pendingSession.removeValue(forKey: message.transactionID) else {
return
}
session.realResponseMessage = message
session.realIP = message.resolvedIPv4Address
let domain = session.requestMessage.queries[0].name
let udpParser = UDPProtocolParser()
udpParser.sourcePort = Port(port: dnsServerPort)
udpParser.destinationPort = (session.requestIPPacket!.protocolParser as! UDPProtocolParser).sourcePort
udpParser.payload = session.realResponseMessage!.payload
let ipPacket = IPPacket()
ipPacket.sourceAddress = IPAddress(fromString: dnsServerAddress)
ipPacket.destinationAddress = session.requestIPPacket!.sourceAddress
ipPacket.protocolParser = udpParser
ipPacket.transportProtocol = .udp
ipPacket.buildPacket()
packetFlow.writePackets([ipPacket.packetData], withProtocols: [NSNumber(value: AF_INET as Int32)])
}
let dummyTunnelAddress = "127.0.0.1"
let dnsServerAddress = "8.8.4.4"
let dnsServerPort: UInt16 = 53
// Tunnel confg.
let tunnelAddress = "192.168.0.1"
let tunnelSubnetMask = "255.255.255.0"
Regarding triggering"Local Network permissions" which is not the issue here (I don't think my solution need to have this permission), Based on the apple document some apps need to request local network permissions, I added the permission to the info.plist but local network permissions are not triggered.
==========================
Update #1
============================
I found out that I was able to capture the packets and do my own things then write packets back to the packetFlow packetFlow.writePackets, But on iOS 14 browsers not loading the websites and keep loading until show time out.
I have an idea, and maybe a solution for you. Starting in iOS version 14, the C function connect() started failing in network extensions, where VPNs have to run, with the following log message from the kernel:
Sandbox: VPN Extensio(8335) deny(1) network-outbound*:<port #>
However, it does work in the app, which is next to useless if you need it in the extension. GCDAsyncUdpSocket is a thin layer that right under the covers is calling socket() and connect().
NWConnection does work in a network extension, and it should work for you if it is feasible to port your code and you don't need the actual socket descriptor. But you will have to conditionally compile if you have to support devices < ios 12.

Is it possible to decrypt my XMPP server traffic using private key?

I can provide more detail if necessary, but my question is basically thus:
If I'm running an openfire server that encrypts traffic using an RSA pub/priv key combo that I created (and have), is there a way (preferably in Java) to sniff packets off the wire and then decrypt them using my private key? Currently I can encrypt/decrypt a string using the following:
public class TLSDecryptTest {
Cipher Ecipher;
Cipher Dcipher;
public TLSDecryptTest(String pubpath, String privpath){
byte[] publicKeyContentsAsByteArray;
RSAPublicKey pubKey;
try {
this.Ecipher = Cipher.getInstance("RSA");
String path1 = new String("C:\\Users\\peter.marino\\Desktop\\javapub.key");
File pubFile = new File(path1);
publicKeyContentsAsByteArray = new byte[(int)pubFile.length()];
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(pubFile));
publicKeyContentsAsByteArray = new byte[(int)pubFile.length()];
bis.read(publicKeyContentsAsByteArray);
bis.close();
CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(publicKeyContentsAsByteArray));
pubKey = (RSAPublicKey) certificate.getPublicKey();
this.Ecipher.init(Cipher.ENCRYPT_MODE, pubKey);
} catch(Exception e) {
System.out.println("Exception" + e);
}
try {
this.Dcipher = Cipher.getInstance("RSA");
String path2 = new String("C:\\Users\\peter.marino\\Desktop\\java.key");
File privFile = new File(path2);
byte[] privateKeyContentsAsByteArray = new byte[(int)privFile.length()];
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(privFile));
privateKeyContentsAsByteArray = new byte[(int)privFile.length()];
bis.read(privateKeyContentsAsByteArray);
bis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
KeySpec ks = new PKCS8EncodedKeySpec(privateKeyContentsAsByteArray);
RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(ks);
System.out.println("PRIVATE KEY:::: " + new String(privKey.getEncoded()).equals(new String(privateKeyContentsAsByteArray)));
this.Dcipher.init(Cipher.DECRYPT_MODE, privKey);
} catch(Exception e) {
System.out.println("Exception" + e);
}
}
public byte[] en(byte[] decryptedMessage) throws Exception {
byte[] encryptedMessage = this.Ecipher.doFinal(decryptedMessage);
//byte[] encryptedMessage = this.Ecipher.doFinal(decryptedMessage);
return (encryptedMessage);
}
public byte[] de(byte[] encryptedMessage) throws Exception {
byte[] decryptedMessage = this.Dcipher.doFinal(encryptedMessage);
return (decryptedMessage);
}
public static void main(String args[]) throws Exception{
TLSDecryptTest t = new TLSDecryptTest(null,null);
String s = ("Testing decryption.1Testing decryption.2Testing decryption.3Testing decryption.4");
System.out.println("S: " + s);
byte[] todo = s.getBytes();
byte[] e = t.en(todo);
String es = new String(e);
System.out.println("E: " + es);
byte[] d = t.de(e);
String ds = new String(d);
System.out.println("D: " + ds);
}
}
which works fine. However, if I sniff a few packets off the wire and then try to decrypt it, I get errors. I even tried only decrypting the first 256 bytes of it, seeing as that's the limitation of my RSA key, but it still throws errors. Most notably, a BadPaddingException at the doFinal() line.
Any ideas?
Thanks in advance.
If you are talking about SSL-protected session, then man-in-the-middle attack is possible if you have a legitimate server's private key (and can obtain the certificate which is public anyway). For practical purpose you should be able to use Wireshark to spy on your traffic.
But you can't decrypt the traffic as is. Partially because it's not encrypted using public key cryptography - data is encrypted using symmetric key generated per session.
Wireshark will allow you to decrypt if you have the server's private key. Docs are here.
First, go to Edit/Preferences/Protocols/SSL, click the Edit button next to RSA Keys:
Next, click New. Fill out the form with information that describes when the key should be used. This should be the IP address and port of the server:
Your key file may or may not require a passphrase. Hit OK three times. Capture as usual.
No. With public key encryption, you can only ever decrypt with the opposite key. e.g.
encrypted with private key => decrypt with public key
encryptd with public key => decrypt with private key
consider the chaos that would happen if
encrypted with public key => decrypt with public key
were possible - since the public key is floating around "in the open" for everyone to see, you'd essentially be giftwrapping your data in saran wrap, because everyone would have the key to decrypt it already. This would completely torpedo the entire SSL security model.

How to flush the socket using boost

I am implementing a server that sends xml to clients using boost. The problem I am facing is that the buffer doesn't get sent immediately and accumulates to a point then sends the whole thing. This cause a problem on my client side, when it parses the xml, it may have incomplete xml tag (incomplete message). Is there a way in boost to flush out the socket whenever it needs to send out a message? Below is server's write code.
void
ClientConnection::handle_write(const boost::system::error_code& error)
{
if (!error)
{
m_push_message_queue.pop_front ();
if (!m_push_message_queue.empty () && !m_disconnected)
{
boost::asio::async_write(m_socket,
boost::asio::buffer(m_push_message_queue.front().data(),
m_push_message_queue.front().length()),
boost::bind(&ClientConnection::handle_write, this,
boost::asio::placeholders::error));
}
}
else
{
std::err << "Error writting out message...\n";
m_disconnected = true;
m_server->DisconnectedClient (this);
}
}
Typically when creating applications using TCP byte streams the sender sends a fixed length header so the receiver knows how many bytes to expect. Then the receiver reads that many bytes and parses the resulting buffer into an XML object.
I assume you are using TCP connection. TCP is stream type, so you can't assume your packet will come in one big packet. You need to fix your communication design, by sending size length first like San Miller answer, or sending flag or delimiter after all xml data has been sent.
Assuming you are definitely going to have some data on the socket you want to clear, you could do something like this:
void fulsh_socket()
{
boost::asio::streambuf b;
boost::asio::streambuf::mutable_buffers_type bufs = b.prepare(BUFFER_SIZE);
std::size_t bytes = socket_.receive(bufs); // !!! This will block until some data becomes available
b.commit(bytes);
boost::asio::socket_base::bytes_readable command(true);
socket_.io_control(command);
while(command.get())
{
bufs = b.prepare(BUFFER_SIZE);
bytes = socket_.receive(bufs);
b.commit(bytes);
socket_.io_control(command); // reset for bytes pending
}
return;
}
where socket_ is a member variable.
HTH

Encode/Decode xxxclass to byte[] than send it to Remote PC with C#

Hi i have got a TCP/IP Socket project.
i can send string messages to Server with Client side and i can get responses from server.
But getting one string message and sending only one string (or any other object).I wanna Encode Personel class to Byte array after send to Clients from server side.And Decode it. than get values from my class.
//SERVER SIDE CODE Connect() starts at on form load
private void Connect()
{
// start listen socket
dinleyiciSoket = new TcpListener(System.Net.IPAddress.Any, 10048);
dinleyiciSoket.Start();
Socket istemciSoketi = dinleyiciSoket.AcceptSocket();
NetworkStream agAkisi = new NetworkStream(istemciSoketi);
BinaryReader binaryOkuyucu = new BinaryReader(agAkisi);
BinaryWriter binaryYazici = new BinaryWriter(agAkisi);
string alinanMetin = binaryOkuyucu.ReadString();
MessageBox.Show(alinanMetin, "Yeni Genelge", MessageBoxButtons.OK);
binaryYazici.Write(true);
dinleyiciSoket.Stop();
Connect();
}
////////// CLIENT SIDE //////////////
private string IpAdresi(string host)
{
string address = "";
IPAddress[] addresslist = Dns.GetHostAddresses(host);
foreach (IPAddress theaddress in addresslist)
{
if (theaddress.AddressFamily == AddressFamily.InterNetwork)
{
address = theaddress.ToString();
}
}
return address;
}
bool onay;
private void button1_Click(object sender, EventArgs e)
{
//create socket connection
Socket istemciBaglantisi = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//Bağlantıyı gerçekleştir
if (istemciBaglantisi.Connected != true)
{
istemciBaglantisi.Connect(IPAddress.Parse(IpAdresi(txtHost.Text)), 10048);
}
agAkisi = new NetworkStream(istemciBaglantisi);
binaryYazici = new BinaryWriter(agAkisi);
binaryOkuyucu = new BinaryReader(agAkisi);
binaryYazici.Write(txtMesaj.Text);
onay = binaryOkuyucu.ReadBoolean();
MessageBox.Show(onay.ToString());
istemciBaglantisi.Close();
}
Take a look at object serialization. See here for examples. That should get you going in the right direction.
You can use google's protocol buffers. It is a fast and compact mechanism for serializing objects. There are two implementations on .NET: protobuf-net and protobuf.
I'd use object serialization or XmlSerialization, both available in .NET. I would not look at Google's protocol buffers, because that RPC encoding has little advantage over what's already in .NET, but it is obscure, especially in the .NET world, and especially now. I wouldn't bet on it becoming mainstream for .net devs. As a result, you will only make your code harder to maintain by using this RPC encoding.
I don't really see the need for protobufs when the apps that are interconnecting are homogeneous, and are NOT on the scale of Google's datacenters. I also don't see the need even when heterogeneity is the rule, because we already have JSON and XML. They are both readable and serviceable, where protobufs are not.
In any case .NET has what you need for this, built-in.