Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid extra memory use during encryption and decryption?

So I have a basic crypto class. Note that this is a simplified implementation to illustrate the question.

Now to my mind both these methods have an extra byte array and string instance.

xmlString and bytes in Encrypt

and

decryptedString and decryptedBytes in Decrypt

So how can I rework the usage of streams in this class to minimize the memory usage?

class Crypto 
{
    Rijndael rijndael;

    public Crypto()
    {
        rijndael = Rijndael.Create();
        rijndael.Key = Encoding.ASCII.GetBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); ;
        rijndael.IV = Encoding.ASCII.GetBytes("bbbbbbbbbbbbbbbb"); ;
        rijndael.Padding = PaddingMode.PKCS7;
    }

    public byte[] Encrypt(object obj)
    {
        var settings = new XmlWriterSettings
            {
                OmitXmlDeclaration = true
            };

        var ns = new XmlSerializerNamespaces();
        ns.Add("", "");

        var sb = new StringBuilder();
        var xmlSerializer = new XmlSerializer(obj.GetType());
        using (var xmlWriter = XmlWriter.Create(sb, settings))
        {
            xmlSerializer.Serialize(xmlWriter, obj, ns);
            xmlWriter.Flush();
        }

        var xmlString = sb.ToString();
        var bytes = Encoding.UTF8.GetBytes(xmlString);
        using (var encryptor = rijndael.CreateEncryptor())
        using (var stream = new MemoryStream())
        using (var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
        {
            crypto.Write(bytes, 0, bytes.Length);
            crypto.FlushFinalBlock();
            stream.Position = 0;
            var encrypted = new byte[stream.Length];
            stream.Read(encrypted, 0, encrypted.Length);
            return encrypted;
        }
    }

    public T Decrypt<T>(byte[] encryptedValue)
    {
        byte[] decryptedBytes;
        using (var decryptor = rijndael.CreateDecryptor())
        using (var stream = new MemoryStream())
        using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
        {
            crypto.Write(encryptedValue, 0, encryptedValue.Length);
            crypto.FlushFinalBlock();
            stream.Position = 0;
            decryptedBytes = new Byte[stream.Length];
            stream.Read(decryptedBytes, 0, decryptedBytes.Length);
        }

        var ser = new XmlSerializer(typeof(T));

        var decryptedString = Encoding.UTF8.GetString(decryptedBytes);
        using (var stringReader = new StringReader(decryptedString))
        using (var xmlReader = new XmlTextReader(stringReader))
        {
            return (T)ser.Deserialize(xmlReader);

        }
    }

}

And here is a unit test

[TestFixture]
public class Tests
{
    [Test]
    public void Run()
    {
        var before = new MyClassForSerialize()
            {
                Property = "Sdf"
            };

        var dataEncryptor = new Crypto();
        var encrypted = dataEncryptor.Encrypt(before);
        var after = dataEncryptor.Decrypt<MyClassForSerialize>(encrypted);
        Assert.AreEqual(before.Property, after.Property);
    }
}
public class MyClassForSerialize
{
    public string Property { get; set; }
}

=== Edit ===

Based on the anser from Damien_The_Unbeliever I tried this. Which fails the unit test

public byte[] Encrypt(object obj)
{
    var settings = new XmlWriterSettings
    {
        OmitXmlDeclaration = true
    };

    var ns = new XmlSerializerNamespaces();
    ns.Add("", "");
    var xmlSerializer = new XmlSerializer(obj.GetType());

    using (var encryptor = rijndael.CreateEncryptor())
    using (var stream = new MemoryStream())
    using (var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
    {
        using (var xmlWriter = XmlWriter.Create(crypto, settings))
        {
            xmlSerializer.Serialize(xmlWriter, obj, ns);
            xmlWriter.Flush();
        }
        crypto.FlushFinalBlock();
        stream.Position = 0;
        return stream.ToArray();
    }
}
like image 423
Simon Avatar asked Dec 19 '12 07:12

Simon


People also ask

How does encryption memory work?

Secure memory encryption (SME) works by marking individual pages of memory as encrypted using standard x86 page tables. A page that is marked encrypted will be automatically decrypted when read from DRAM and encrypted when written to DRAM.

Can you encrypt data in memory?

One of the techniques to prevent the data accessed across different guests/domains/zones/realms is memory encryption. With memory encryption in place, even if any of the isolation techniques have been compromised, the data being accessed is still protected by cryptography.

What is encrypted RAM?

Memory encryption is the standard technique to protect data and code against attackers with physical access to a memory. It is widely deployed in state-of-the-art systems, such as in iOS [2], Android [22], Mac OS X [1], Windows [19], and Linux [26, 36].

How are encryption key and decryption key related?

A symmetric key is used during both the encryption and decryption processes. To decrypt a particular piece of ciphertext, the key that was used to encrypt the data must be used. The goal of every encryption algorithm is to make it as difficult as possible to decrypt the generated ciphertext without using the key.


1 Answers

You can construct your XmlWriter directly on top of your CryptoStream (pass crypto to XmlWriter.Create), rather than using a separate buffer. (Ditto for decryption)

And MemoryStream has a ToArray method so you don't have to manually allocate, re-position and read from it.

Other than that, it looks like a reasonable implementation - are there specific issues that need fixing?


Based on your edit, if I change decrypt to:

    public T Decrypt<T>(byte[] encryptedValue)
    {
        using (var decryptor = rijndael.CreateDecryptor())
        using (var stream = new MemoryStream(encryptedValue))
            using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
            using (var xmlReader = XmlReader.Create(crypto))
            {
                var ser = new XmlSerializer(typeof(T));
                return (T)ser.Deserialize(xmlReader);

            }
    }

Then it seems to work for me.


The new version is including an XML BOM, whereas the old one wasn't. The XmlReader should cope, I'd have thought, but appears not to. Try the following settings in Encrypt:

var settings = new XmlWriterSettings
{
    OmitXmlDeclaration = true,
    Encoding = new UTF8Encoding(false)
};

And now it works with the old Decrypt function.

Full Solution

Encrypt

public byte[] Encrypt(object obj)
{
    var settings = new XmlWriterSettings
        {
            OmitXmlDeclaration = true,
            Encoding = new UTF8Encoding(false)
        };

    var ns = new XmlSerializerNamespaces();
    ns.Add("", "");

    var xmlSerializer = new XmlSerializer(obj.GetType());
    using (var encryptor = rijndael.CreateEncryptor())
    using (var stream = new MemoryStream())
    using (var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
    {
        using (var xmlWriter = XmlWriter.Create(crypto, settings))
        {
            xmlSerializer.Serialize(xmlWriter, obj, ns);
            xmlWriter.Flush();
        }
        crypto.FlushFinalBlock();
        return stream.ToArray();
    }
}

Decrypt

public T Decrypt<T>(byte[] encryptedValue)
{
    using (var decryptor = rijndael.CreateDecryptor())
    using (var stream = new MemoryStream(encryptedValue))
    using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
    {
        var ser = new XmlSerializer(typeof(T));
        return (T)ser.Deserialize(crypto);
    }
}
like image 163
Damien_The_Unbeliever Avatar answered Sep 27 '22 18:09

Damien_The_Unbeliever