2
votes

My question is about what I actually need to store in the DB re encrypted values and how this can be used for the nonce for GCM specifically.

For reference these two answers provide sample code for encrypting data: Cbc and gcm.

As I understand it, CBC requires the IV to be totally random. I understand that for CBC (and for all encryption really) the Key/IV should always be unique. If it is repeated it is 'bad' for CBC and a fundamental flaw if using GCM. GCM however does not need it to be totally random as long as it never repeats.

On these assumptions, we plan to have a 'key' somewhere in the app (i.e. separate the key from the encrypted data in the DB). We will roll the key every 'n' months and only use it for 'x' number of encryption before generating a new one (limit scope of use etc), however, we will be using the same 'key' for two or more records. Hence the unique key/iv comes into play.

Note: We need to be able to read out the data again later and 'use' it. This is not one way encryption.

In designing the DB we were going to use columns similar to the following.

DB columns for CBC mode (I have seen something similar on stack overflow... sorry can't find link):

  • ID (primary key, int)(unique)
  • Encrypted Value
  • Salt
  • Iterations
  • (Other... standard things like created date etc etc)

Db Columns for Gcm mode:

  • ID (primary key, int)(unique)
  • Encrypted Value
  • Associated Text
  • (Other... standard things like created date etc etc)

IV/Nonce Handling Question

For the CBC mode, we were just going to use e.g. Rfc2898DeriveBytes and get the IV from that and not store it.

Re the GCM mode, the idea here was to do the following:

  1. Save the record first and 'get' the id of the row
  2. convert the row id into a byte[16]... e.g. (example only...)
    var rowID = 123456789.ToString();
    var tempByte = new List<byte>(16);
    tempByte.AddRange
    (123456789.ToString()
        .Select(Convert.ToByte));

    if (rowID.Length < 16)
    {
        for (var i = 0; i < (16 - rowID.Length); i++)
        {
        // Note: something to fill the rest.....
            var someNewByteFromSecureRandom = new[] {Convert.ToByte(1)};
            tempByte.Add(someNewByteFromSecureRandom[0]);
        }
    }
  1. Save the encrypted data into the row.

This way, the nonce is always going to be unique for the GCM encryption. Even if we always use the same key, the key/iv will never repeat. (Note: this approach would as we understand it not work for CBC because of its requirement that the iv be truely random and not a counter)

Basically, is there a problem with either of these approaches? (Ignoring the obvious dual save (insert/update) of the record when using the GCM approach outline to get the nonce). Are we saving something we should not? Are we not saving something we should?

1
This is more about secure database design than programming. As such I would recommend IT security over StackOverflow (if you decide to move, create an account on the security site, enter a new question, edit question here copying content, paste into new question, delete old question and post new question).Maarten Bodewes
Please let me know if you do this - I'll delete my answer and answer at the security site.Maarten Bodewes

1 Answers

2
votes

Presumption: you are only protecting data at rest, i.e. active attacks on the data while the database is being utilized are not considered.

  • CBC requires the IV to appear random to an attacker, CBC could use an encrypted counter (the row ID) as IV;
  • to use a row ID as a nonce, you need to start the IV (rightmost bytes) with the row ID (converted into a 12 byte unsigned big endian number) - the leftmost bytes should be kept empty for maximum compatibility
  • CBC doesn't require PBKDF2 (so no salt or iteration count) if you already have a key - you could just store a random IV instead of the salt
  • "Associated Text" has no meaning on its own. GCM takes Additional Associated Data - or AAD - as input parameter. So the AAD is just any data in your database that you want to include in the authentication tag. GCM already includes the IV into the authentication tag so you don't have to include the row ID;
  • CBC is enough for data at rest as nobody should be able to change the data (that would be an active attacker, which is ruled out for data at rest); that said, GCM does catch any wrong key/wrong data error so it could be used to simplify error handling for your application.

Note that using a nonce of 12 bytes will limit the plaintext to 2^36 bytes (64 GiB). That should not be a problem for database entries (I hope). You could throw an error or runtime exception if you ever encounter such a large plaintext though.