GordianKnot Symmetric Block Ciphers
Overview
Block Ciphers are supported via the GordianCipherFactory interface.
GordianKnot supports most Symmetric Block Ciphers available from BouncyCastle plus some additional ciphers.
A cipher for block encryption/decryption is created by specifying a GordianSymCipherSpec which comprises the GordianSymKeySpec (algorithm/blockSize/keySize) with the cipherMode and padding (if required). The cipher can be used in much the same way as a JCA Cipher, allowing init, update and finalise methods.
A cipher for blockCipher keyWrapping is created by specifying the symKey. The cipher allows init, secure and derive methods
A keyGenerator for a GordianSymKeySpec can be created, which can be used to generate random keys for the keySpec
JCA provides a subset of available algorithms as indicated
Key Generation
Algorithms are represented by GordianSymKeySpec. A GordianKeyGenerator is obtained via the keySpec, and then keys are generated by the generator.
Sample
/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianCipherFactory myCipherFactory = myBaseFactory.getCipherFactory();
/* Access keyGenerator */
final GordianSymKeySpec mySpec = GordianSymKeySpecBuilder.aes(GordianLength.LEN_256);
final GordianKeyGenerator<GordianSymKeySpec> myGenerator = myCipherFactory.getKeyGenerator(mySpec);
final GordianKey<GordianSymKeySpec> myKey = myGenerator.generateKey();
Algorithms
The following symKey algorithms are supported.
Algorithm | BlockSize | JCA | 128 | 192 | 256 | 512 | 1024 |
---|---|---|---|---|---|---|---|
AES | 128 | Y | Y | Y | Y | ||
Serpent | 128 | Y | Y | Y | Y | ||
TwoFish | 128 | Y | Y | Y | Y | ||
Camellia | 128 | Y | Y | Y | Y | ||
CAST6 | 128 | Y | Y | Y | Y | ||
RC6 | 128 | Y | Y | Y | Y | ||
ARIA | 128 | Y | Y | Y | Y | ||
Kalyna | 128 | Y | Y | Y | |||
256 | Y | Y | Y | ||||
Kuznyechik | 128 | Y | Y | ||||
ThreeFish | 256 | Y | Y | Y | Y | ||
NoeKeon | 64 | Y | Y | ||||
SM4 | 64 | Y | Y | ||||
SEED | 64 | Y | Y | ||||
SKIPJACK | 64 | Y | Y | ||||
BlowFish | 64 | Y | Y | Y | Y | ||
RC2 | 64 | Y | Y | Y | Y | Y | Y |
DESede | 64 | Y | Y | Y | |||
CAST5 | 64 | Y | Y | ||||
RC5 | 64 | Y | Y | ||||
128 | Y | Y | |||||
IDEA | 64 | Y | Y | ||||
TEA | 64 | Y | Y | ||||
XTEA | 64 | Y | Y | ||||
Magma | 64 | Y | Y | ||||
SHACAL2 | 256 | Y | Y | Y | |||
Speck | 128 | Y | Y | Y | |||
Simon | 128 | Y | Y | Y | |||
MARS | 128 | Y | Y | Y | |||
Anubis | 128 | Y | Y | Y | |||
LEA | 128 | Y | Y | Y |
Cipher Usage
Cipher Algorithms are represented by GordianSymCipherSpec. A GordianSymCipher is obtained via the cipherSpec, and then messages are encrypted/decrypted by the cipher.
Sample
/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianCipherFactory myCipherFactory = myBaseFactory.getCipherFactory();
/* Create key */
final GordianSymKeySpec myKeySpec = GordianSymKeySpecBuilder.aes(GordianLength.LEN_256);
final GordianKeyGenerator<GordianSymKeySpec> myGenerator = myCipherFactory.getKeyGenerator(myKeySpec);
final GordianKey<GordianSymKeySpec> myKey = myGenerator.generateKey();
/* Create cipher */
final GordianSymCipherSpec myCipherSpec = GordianSymCipherSpecBuilder.cbc(myKeySpec, GordianPadding.PKCS7);
final GordianSymCipher myCipher = myCipherFactory.createSymKeyCipher(myCipherSpec);
/* Encrypt message with random nonce */
GordianCipherParameters myParams = GordianCipherParameters.keyWithRandomNonce(myKey);
myCipher.initForEncrypt(myParams);
final byte[] myMessage = ...
int myOutLen = myCipher.getOutputLength(myMessage.length);
final byte[] myEncrypted = new byte[myOutLen];
int myProcessed = myCipher.update(myMessage, 0, myMessage.length, myEncrypted);
myCipher.finish(myEncrypted, myProcessed);
/* Decrypt message */
myParams = GordianCipherParameters.keyWithNonce(myKey, myCipher.getNonce());
myCipher.initForDecrypt(myParams);
myOutLen = myCipher.getOutputLength(myEncrypted.length);
final byte[] myResult = new byte[myOutLen];
myProcessed = myCipher.update(myEncrypted, 0, myEncrypted.length, myResult);
myCipher.finish(myResult, myProcessed);
Cipher Modes
The following modes can be used
Mode | JCA | Notes |
---|---|---|
ECB | Y | |
CBC | Y | JCA does not support for Kuznyechik |
G3413CBC | Y | Only available for Kuznyechik |
SIC | Y | |
KCTR | Y | Only available for Kalyna |
G3413CTR | Y | Only available for Kuznyechik |
OFB | Y | Jca does not support for Magma and Kuznyechik |
GOFB | Only available for Magma | |
G3413OFB | Y | Only available for Kuznyechik |
CFB | Y | Jca does not support for Magma and Kuznyechik |
GCFB | Only available for Magma | |
G3413CFB | Y | Only available for Kuznyechik |
CCM | Y | Jca does not support for Kalyna |
KCCM | Only available for Kalyna | |
GCM | Y | Jca does not support for Kalyna |
KGCM | Only available for Kalyna | |
EAX | Y | |
OCB | Y | JCA does not support for Kuznyechik or Kalyna |
GCMSIV | Only available for 128-bit/256-bit keys and 128-bit blocks |
Padding
The following paddings can be used for ECB and CBC modes
Padding | JCA | Notes |
---|---|---|
CTS | Y | |
ISO7816-4 | Y | |
PKCS7 | Y | |
X9.63 | Y | |
TBC | Y | |
NONE | Y | Implicitly used for modes other than ECB/CBC |
Key Wrapping
Key Wrapping is performed by GordianKnot using a variant of the AES Key Wrapping algorithm specified in RFC 5649
The differences are necessary to enable support of ciphers that do not have the standard blockSize of 128 bits, since RFC 5649 assumes a 128 bit blockSize.
- For blockSizes of 256 bits, the 32-bit Integrity Vector of 0xA65959A6 is expanded to the 96-bit 0xA65959A6A65959A6A65959A6 so that when combined with the 32-bit dataLen of the wrapped data it comprises the half-block needed for the wrapping algorithm. This method can be extended to support any block size >= 128.
- For blockSizes of 64, the Integrity Vector using the above adjustment becomes zero length, which is not very useful. In this case, we retain the standard 32-bit Integrity Vector of 0xA65959A6 and prefix the 32-bit dataLen to the data to be wrapped.
In addition, a block of random data is inserted immediately prior to the data to be wrapped.
Sample
/* Access factory */
final GordianFactory myBaseFactory = GordianGenerator.createFactory();
final GordianCipherFactory myCipherFactory = myBaseFactory.getCipherFactory();
/* Create keys */
final GordianSymKeySpec myKeySpec = GordianSymKeySpecBuilder.aes(GordianLength.LEN_256);
final GordianKeyGenerator<GordianSymKeySpec> myGenerator = myCipherFactory.getKeyGenerator(myKeySpec);
final GordianKey<GordianSymKeySpec> myKey = myGenerator.generateKey();
final GordianKey<GordianSymKeySpec> myKeyToWrap = myGenerator.generateKey();
/* Create keyWrapper */
final GordianKeyWrapper myWrapper = myCipherFactory.createKeyWrapper(myKey);
/* Secure key */
final byte[] myWrapped = myWrapper.secureKey(myKeyToWrap);
/* Derive Key */
final GordianKey<GordianSymKeySpec> myResult = myWrapper.deriveKey(myWrapped, myKeySpec);