8

I want to de/encrypt several binary files rather frequently on a normally battery-powered device. My goal is to use AES-256 when the device is AC-powered and a highly performant algorithm when it's battery-powered to save power. I appreciate that the level of security will be compromised.

Is there a performant algorithm that provides "decent" security? I see that Blowfish fares a bit better according to this analysis, but the difference is rather small over AES-128. Given that the paper is almost 10 years old, are there good alternatives out there today?

Is AES-128 supposed to be the standard for something decently secured and highly performant?

otus
  • 32,462
  • 5
  • 75
  • 167
Kar
  • 473
  • 3
  • 9

4 Answers4

7

Yes, AES-128 is intended to be the standard block cipher for building a secure and efficient symmetric cryptosystem using some block cipher operating mode, like CTR for encryption or GCM for authenticated encryption; efficiency can be particularly good when there is hardware support for AES and GCM.

There might be better choices in the case at hand, like for a 32-bit CPU ChaCha (perhaps combined with Poly1305 for authenticated encryption), or one of a plethora of lightweight block ciphers. If the device is 8-bit, and speed/efficiency paramount, one might even consider the deprecated and partially broken RC4, although its security is questionable even when discarding the beginning of the keystream.

Notice that keeping secret the key of either of these algorithm is hard in a non-security CPU assumed in the hands of an adversary, to the point of being best considered impossible facing a competent and determined adversary. And in a modern security CPU, you might have AES in hardware (thought often, optimized for security rather than speed).

fgrieu
  • 149,326
  • 13
  • 324
  • 622
4

For encryption or decryption of data that is encrypted on the fly, you can do that. For data you store you obviously have to use the same decryption, so if you encrypt a huge file using AES, you have to decrypt it using AES, even when on battery power.

First thing: If there is hardware support for AES, AES will be faster and using less power almost certain.

If you want to use a faster alternative, do the usual thing: look what people with bigger budgets for research and the same problem use.

I know what Google does for TLS. That's not exactly your use-case, but should be close enough. There are two blog posts I can recommend by Cloudflare and Google (images broken, shame on them).

I will summarize the corner points: Google was needing a fast and secure cipher that uses less power on mobile devices (say Android). They decided to use the ChaCha20 stream cipher with Poly1305 MAC.

It is included in Chrome since late 2013 and the google servers support it. Chrome prefers AES-GCM, if there is hardware support for it and else uses ChaCha20-Poly1305.

This seems like a good choice for you too and should be sufficiently secure. It was a finalist in eSTREAM and also is from djb, whom I hold in high regard as cryptographer.

Because it is in a similar security league as AES-128, you could also just use the faster of them, if you know the device. If you target multiple devices, I would go the Chrome way and switch between AES and ChaCha, depending on hardware support.

Josef
  • 362
  • 4
  • 13
3

As you said you are OK with a little bit less security in the more performant algorithm, I suggest Speck for that. It was developed by the NSA, so although some might worry about a backdoor (I personally doubt there is one though I would not know the difference if there were), it probably also means that ordinary people will not break it easily if at all. Indeed it has been out 2 years and has been analyzed but not broken. It comes in a variety of key and block sizes. I actually have C++ code (maybe not the most optimized, but it does pass test vectors) for 32/64 (block size 32, key size 64) and for 128/128. Will post both in this answer. They are very fast and small.

32/64

#include "Speck.h"
#include <iostream>
#include <stdint.h>
using namespace std;

#define M 4
#define NUMBER_OF_ROUNDS 22
#define BIG_BIT_SHIFT 7
#define SMALL_BIT_SHIFT 2

#define ROR(x, r) ((x >> r) | (x << (16 - r)))
#define ROL(x, r) ((x << r) | (x >> (16 - r)))
#define R(x, y, k) (x = ROR(x, BIG_BIT_SHIFT), x += y, x ^= k, y = ROL(y, SMALL_BIT_SHIFT), y ^= x)
#define UNR(x, y, k) (y ^= x,y = ROR(y, SMALL_BIT_SHIFT),x ^= k,x -= y,x = ROL(x, BIG_BIT_SHIFT))

void Speck_32_64::generateRoundKeys(uint16_t *K, uint16_t *RK)
{
    uint16_t keyChunks[M];
    keyChunks[0] = K[0];
    keyChunks[1] = K[1];
    keyChunks[2] = K[2];
    keyChunks[3] = K[3];

    uint64_t i;
    uint16_t indexer = 0;

    for (i = 0; i < NUMBER_OF_ROUNDS; i++)
    {
        RK[i] = keyChunks[0];
        indexer = 1 + i % (M-1);
        R(keyChunks[indexer], keyChunks[0], i);
    }
}

void Speck_32_64::encrypt(uint16_t *pt, uint16_t *ct, uint16_t *K)
{
    uint64_t i, B = K[1], A = K[0];
    ct[0] = pt[0]; ct[1] = pt[1];

    uint16_t RK[NUMBER_OF_ROUNDS];
    generateRoundKeys(K, RK);

    for (i = 0; i < NUMBER_OF_ROUNDS; i++)
    {
        R(ct[1], ct[0], RK[i]);
    }
}

void Speck_32_64::decrypt(uint16_t *pt, uint16_t *ct, uint16_t *K)
{
    uint64_t i, B = K[1], A = K[0];
    pt[0] = ct[0]; pt[1] = ct[1];

    uint16_t RK[NUMBER_OF_ROUNDS];
    generateRoundKeys(K, RK);
    for (i = NUMBER_OF_ROUNDS; i > 0; i--)
    {
        UNR(pt[1], pt[0], RK[i - 1]);
    }
}

128/128

#include "Speck.h"
#include <iostream>
#include <stdint.h>
using namespace std;

#define ROR(x, r) ((x >> r) | (x << (64 - r)))
#define ROL(x, r) ((x << r) | (x >> (64 - r)))
#define R(x, y, k) (x = ROR(x, 8), x += y, x ^= k, y = ROL(y, 3), y ^= x)
#define UNR(x, y, k) (y ^= x,y = ROR(y, 3),x ^= k,x -= y,x = ROL(x, 8))

void Speck_128_128::generateRoundKeys(uint64_t *K, uint64_t *RK)
{
    uint64_t i, B = K[1], A = K[0];

    for (i = 0; i < 32; i++)
    {
        RK[i] = A;
        R(B, A, i);
    }
}

void Speck_128_128::encrypt(uint64_t *pt, uint64_t *ct, uint64_t *K)
{
    uint64_t i, B = K[1], A = K[0];
    ct[0] = pt[0]; ct[1] = pt[1];

    uint64_t RK[32];
    generateRoundKeys(K, RK);

    for (i = 0; i < 32; i++)
    {
        R(ct[1], ct[0], RK[i]);
    }
}

void Speck_128_128::decrypt(uint64_t *pt, uint64_t *ct, uint64_t *K)
{
    uint64_t i, B = K[1], A = K[0];
    pt[0] = ct[0]; pt[1] = ct[1];

    uint64_t RK[32];
    generateRoundKeys(K, RK);
    for (i = 32; i > 0; i--)
    {
        UNR(pt[1], pt[0], RK[i - 1]);
    }
}
WDS
  • 266
  • 3
  • 9
3

The best solution depends on the details of your application, but I have one comment and one suggestion.

First the comment: You say you have AES hardware available. I don't know the details of your platform, but this hardware is likely to be much more efficient in time/power than a software implementation of any reasonable encryption algorithm. You need to do some analysis/benchmarking here.

Now a suggestion that may work (depending on your particular application): If The amount of data that you need to encrypt while on battery power is fairly small, you can use pre-computation while on AC power to help. For example, while on AC power you can AES encrypt a nonce/counter and store the result. Then in battery mode you can xor these results with your plaintexts. So you are effectively implementing AES in CTR mode, but the only operation you need to perform on battery power is an xor.

The above is just an example to get you to think about application-specific alternatives. Perhaps if you share more details about your application you may get better suggestions.