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:
- Save the record first and 'get' the id of the row
- 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]);
}
}
- 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?