GordianKnot KeySets

Overview

KeySets are supported via the GordianKeySetFactory interface.

GordianKnot supports the notion of a KeySet, which is a set of keys of each available algorithm with a 128-bit blockSize. The keySet may be used to encrypt/decrypt and to secure/derive keys or keySets

KeySet Generation

A keySet can be generated by specifying a GordianKeySetSpec. Each key in the keySet is generated randomly.

Sample


/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianKeySetFactory myKeySetFactory = myBaseFactory.getKeySetFactory();

/* Create keySet */
final GordianKeySetSpec mySpec = new GordianKeySetSpec(GordianLength.LEN_256);
final GordianKeySet myKeySet = myKeySetFactory.generateKeySet(mySpec);
                    

KeySet Derivation

The keySet derivation algorithm that generates each of the keys KA for an algorithm A from a secret S is as follows.

  1. Obtain a seed X from the first 4 bytes of S and obtain a seeded random R using X and P1
  2. Obtain a set of two distinct HMACs H1 and H2 from the seededRandom R
  3. Use HKDF with the two HMACs to expand the result S into KA. Use the following info to personalise and differentiate the keys.
    1. Algorithm Name.
    2. Key Length.
    3. 8 bytes from seeded random.
    4. Personalisation value P.
    5. Personalisation value I.

KeySet Encryption

Algorithm

Encryption works by selecting a subSet of the available keys and encrypting the message using each key in turn. The number of keys selected and hence the number of encryption steps is specified in the GordianKeySetSpec and can vary betweeen 3 and 6

  1. A random 32-bit seed S and a random 128-bit initVector V are generated.
  2. A subSet of n keys is selected (all different) using a seededRandom based on S and the personalisation value I2
  3. Two initiation vectors V1 and V2 are calculated by xor-ing V with IV1 and IV2 respectively
  4. The first encryption is performed on the message M using K1 in SIC mode using V1 as the initialisation vector to create C1
  5. The second encryption is performed on C1 using K2 in ECB mode with PKCS7 padding to create C2.
  6. Further intermediate encryptions are performed on Cx-1 using Kx in ECB mode with no padding to produce Cx.
  7. The final encryption is performed on Cn-1 using Kn in SIC mode using V2 as the initialisation vector to create Cn
  8. The result is the concatenation of S||V||Cn

Sample


/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianKeySetFactory myKeySetFactory = myBaseFactory.getKeySetFactory();

/* Create keySet */
final GordianKeySetSpec mySpec = new GordianKeySetSpec(GordianLength.LEN_256);
final GordianKeySet myKeySet = myKeySetFactory.generateKeySet(mySpec);

/* Encrypt data as one-off */
final byte[] myMessage = ...
byte[] myEncrypted = myKeySet.encryptBytes(myMessage);
byte[] myResult = myKeySet.decryptBytes(myEncrypted);

/* Encrypt data as cipher */
final GordianKeySetCipher myCipher = myKeySet.createCipher();
myCipher.initForEncrypt();
myEncrypted = myCipher.finish(myMessage, 0, myMessage.length);
myCipher.initForDecrypt();
myResult = myCipher.finish(myEncrypted, 0, myEncrypted.length);
                    

KeySet AAD Encryption

Algorithm

AAD is supported by calculating a Mac and appending it to the encrypted cipherText. On decryption the Mac is recalculated and compared to the trailing bytes of the cipherText. Encryption works as per normal and the only difference is in the calculation of the Mac. The Mac used is a raw Poly1305 instance, and the algorithm is as follows.

  1. A random 512-bit digest D is selected from the seededRandom used to select keys for the keySet algorithm.
  2. A random SymKey KS is selected via the seededRandom from the keys in the keySet that have not been used for the encryption.
  3. Two input values X1 and X2 are calculated by xor-ing V with IV3 and IV4 respectively
  4. The input values are encrypted using KS in ECB mode with no padding to produce K1 and K2
  5. The key for the Poly1305 Mac is the concatenation of K1||K2.
  6. The digest D is calculated on the plainText
  7. The Mac is calculated on data in a similar fashion to ChaCha20Poly1305 with minor differences as follows
    • The AAD data A
    • From 0 to 15 zeroes to bring data up to 16-byte boundary
    • The cipherText C
    • From 0 to 15 zeroes to bring data up to 16-byte boundary
    • AAD dataLength as 8-byte big-endian long
    • PlainText dataLength as 8-byte big-endian long
    • The result of the digest D
  8. Finally the Mac result M is encrypted using KS in ECB mode with no padding to produce MK, and the result in the concatenation of C||MK

Sample


/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianKeySetFactory myKeySetFactory = myBaseFactory.getKeySetFactory();

/* Create keySet */
final GordianKeySetSpec mySpec = new GordianKeySetSpec(GordianLength.LEN_256);
final GordianKeySet myKeySet = myKeySetFactory.generateKeySet(mySpec);

/* Encrypt data as one-off */
final byte[] myAADe = ...
final byte[] myMessage = ...
byte[] myEncrypted = myKeySet.encryptAADBytes(myMessage, myAAD);
byte[] myResult = myKeySet.decryptAADBytes(myEncrypted, myAAD);

/* Encrypt data as cipher */
final GordianKeySetAADCipher myCipher = myKeySet.createAADCipher();
myCipher.initForEncrypt(myAAD);
myEncrypted = myCipher.finish(myMessage, 0, myMessage.length);
myCipher.initForDecrypt(myAAD);
myResult = myCipher.finish(myEncrypted, 0, myEncrypted.length);
                    

KeySet Wrapping

Algorithm

Wrapping works by selecting a subSet of the available keys and initially encrypting the message with the first key and then using the remaining keys to wrap the result using the standard wrapping algorithm. In each case where the normal cipher is used to encrypt/decrypt, the remaining set of keys is used to encrypt/decrypt in turn.

  1. A random 32-bit seed S and a random 128-bit initVector V are generated.
  2. A subSet of n keys is selected (all different) using a seededRandom based on S and the personalisation value I2
  3. An initiation vector V1 is calculated by xor-ing V with IV1
  4. The first encryption is performed on the bytes B using K1 in SIC mode using V1 as the initialisation vector to create W1
  5. The result W1 is then wrapped using keys K2 to Kx to produce W2
  6. The result is the concatenation of S||V||W2

Sample


/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianKeySetFactory myKeySetFactory = myBaseFactory.getKeySetFactory();

/* Create keySet */
final GordianKeySetSpec mySpec = new GordianKeySetSpec(GordianLength.LEN_256);
final GordianKeySet myKeySet = myKeySetFactory.generateKeySet(mySpec);

/* Secure key */
final GordianKey<GordianSymKeySpec> myKey =  ...
final byte[] mySecured = myKeySet.secureKey(myKey);
final GordianKey<GordianSymKeySpec> myKResult = myKeySet.deriveKey(mySecured, myKeySpec);

/* Secure keySet */
final GordianKeySet myOtherKeySet =  ...
final byte[] mySecured = myKeySet.secureKeySet(myOtherKeySet);
final GordianKeySet myKSResult = myKeySet.deriveKeySet(mySecured);

/* Secure keyPair */
final GordianKeyPair myKeyPair =  ...
final GordianKeyPairGenerator myGenerator = ...;
final byte[] mySecured = myKeySet.securePrivateKey(myKeyPair);
final X509EncodedKeySpec myEncoded = myGenerator.getX509Encoding(myKeyPair);
final GordianKeyPair myKPResult = myKeySet.deriveKeyPair(myEncoded, mySecured);