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 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> myResult = myKeySet.deriveKey(mySecured, myKeySpec);
                

KeySetHash

A keySet can also be derived from a password and a hash.

Creation Algorithm

  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) in a using a seededRandom based on S and the personalisation value I3
  3. A set of three hMacs (all different) HM, HA and HS are selected in a deterministic fashion using a seededRandom based on S and the personalisation value I3. In addition a 512-bit length digest X is selected from the same seeded Random.
  4. Each of the hMacs is initialised with the password as the key
  5. Each of the macs is updated with I, IV and the number of iterations L
  6. Set input DM, DA and DS to V
  7. Repeat the following loop L times
    1. Update HM with DM
    2. Update HA with DA
    3. Update HS with DS, DM and DA
    4. Build new DM as the hash of HM and xor the result into CM. Repeat for other hashes.
  8. Update digest X with DM and DA and calculate the hash as RV
  9. Update digest X with CM and CA and calculate the hash as RX
  10. Calculate external hash as the concatenation of S||V||RX
  11. Create keys for the keySet using CS as the secret and RV as the initVector

Derivation Algorithm

  1. Extract S, V and RX from the hash.
  2. Repeat creation algorithm with S, V and the password
  3. Compare the calculated RX with the one extracted from the hash. Only create the keySet if it matches.

Sample

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

                    /* Create the hash and access the resultant keySet */
                    final GordianKeySetSpec mySpec = new GordianKeySetHashSpec(new GordianKeySetSpec(GordianLength.LEN_256));
                    final char[] myPassword = ...
                    final GordianKeySetHash myKeySetHash = myKeySetFactory.generateKeySetHash(mySpec, myPassword);
                    final GordianKeySet myKeySet = myKeySetHash.getKeySet();

                    /* Access hash and derive keySet from hash and password */
                    final byte[] myHash = myKeySetHash.getHash();
                    final GordianKeySetHash myResolved = myKeySetFactory.deriveKeySetHash(myHash, myPassword);