Open nicolasgarnet opened 3 years ago
It doesn't look too complicated; I can try giving it a shot. Reference implementation: https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
It would be great, thank you.
Here is a draft.
I contacted @Ephenodrom to help me to integrate correctly this class and to write correctly process
method
After that, we will add tests.
import 'dart:typed_data';
import '../api.dart';
class Grain128Engine implements StreamCipher {
static const STATE_SIZE = 4;
late Uint8List workingKey;
late Uint8List workingIV;
late Uint8List out;
late List<int> lfsr;
late List<int> nfsr;
int output = 0;
int index = 4;
bool initialised = false;
@override
String get algorithmName => 'Grain-128';
@override
void init(bool forEncryption, CipherParameters? params) {
if (!(params is ParametersWithIV)) {
throw ArgumentError('Grain-128 Init parameters must include an IV');
}
final ivParams = params;
final iv = ivParams.iv;
if (iv.length != 12) {
throw ArgumentError('Grain-128 requires exactly 12 bytes of IV');
}
if (!(ivParams.parameters is KeyParameter)) {
throw ArgumentError('Grain-128 init parameters must include a key');
}
final key = ivParams.parameters as KeyParameter;
final keyBytes = key.key;
if (keyBytes.length != 16) {
throw ArgumentError('Grain-128 key must be 128 bits long');
}
/**
* Initialize variables.
*/
workingIV = Uint8List.fromList(keyBytes);
workingKey = Uint8List.fromList(keyBytes);
lfsr = List.generate(STATE_SIZE, (_) => 0);
nfsr = List.generate(STATE_SIZE, (_) => 0);
out = Uint8List(4);
initialised = true;
reset();
}
/// 256 clocks initialization phase.
void initGrain() {
for (var i = 0; i < 8; i++) {
output = getOutput();
nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0] ^ output);
lfsr = shift(lfsr, getOutputLFSR() ^ output);
}
initialised = true;
}
/// Get output from non-linear function g(x).
///
/// @return Output from NFSR.
int getOutputNFSR() {
final b0 = nfsr[0];
final b3 = nfsr[0] >> 3 | nfsr[1] << 29;
final b11 = nfsr[0] >> 11 | nfsr[1] << 21;
final b13 = nfsr[0] >> 13 | nfsr[1] << 19;
final b17 = nfsr[0] >> 17 | nfsr[1] << 15;
final b18 = nfsr[0] >> 18 | nfsr[1] << 14;
final b26 = nfsr[0] >> 26 | nfsr[1] << 6;
final b27 = nfsr[0] >>> 27 | nfsr[1] << 5;
final b40 = nfsr[1] >>> 8 | nfsr[2] << 24;
var b48 = nfsr[1] >>> 16 | nfsr[2] << 16;
var b56 = nfsr[1] >>> 24 | nfsr[2] << 8;
var b59 = nfsr[1] >>> 27 | nfsr[2] << 5;
var b61 = nfsr[1] >>> 29 | nfsr[2] << 3;
var b65 = nfsr[2] >>> 1 | nfsr[3] << 31;
var b67 = nfsr[2] >>> 3 | nfsr[3] << 29;
var b68 = nfsr[2] >>> 4 | nfsr[3] << 28;
var b84 = nfsr[2] >>> 20 | nfsr[3] << 12;
var b91 = nfsr[2] >>> 27 | nfsr[3] << 5;
var b96 = nfsr[3];
return b0 ^
b26 ^
b56 ^
b91 ^
b96 ^
b3 & b67 ^
b11 & b13 ^
b17 & b18 ^
b27 & b59 ^
b40 & b48 ^
b61 & b65 ^
b68 & b84;
}
/// Get output from linear function f(x).
///
/// @return Output from LFSR.
int getOutputLFSR() {
var s0 = lfsr[0];
var s7 = lfsr[0] >>> 7 | lfsr[1] << 25;
var s38 = lfsr[1] >>> 6 | lfsr[2] << 26;
var s70 = lfsr[2] >>> 6 | lfsr[3] << 26;
var s81 = lfsr[2] >>> 17 | lfsr[3] << 15;
var s96 = lfsr[3];
return s0 ^ s7 ^ s38 ^ s70 ^ s81 ^ s96;
}
/// Get output from output function h(x).
///
/// @return Output from h(x).
int getOutput() {
var b2 = nfsr[0] >>> 2 | nfsr[1] << 30;
var b12 = nfsr[0] >>> 12 | nfsr[1] << 20;
var b15 = nfsr[0] >>> 15 | nfsr[1] << 17;
var b36 = nfsr[1] >>> 4 | nfsr[2] << 28;
var b45 = nfsr[1] >>> 13 | nfsr[2] << 19;
var b64 = nfsr[2];
var b73 = nfsr[2] >>> 9 | nfsr[3] << 23;
var b89 = nfsr[2] >>> 25 | nfsr[3] << 7;
var b95 = nfsr[2] >>> 31 | nfsr[3] << 1;
var s8 = lfsr[0] >>> 8 | lfsr[1] << 24;
var s13 = lfsr[0] >>> 13 | lfsr[1] << 19;
var s20 = lfsr[0] >>> 20 | lfsr[1] << 12;
var s42 = lfsr[1] >>> 10 | lfsr[2] << 22;
var s60 = lfsr[1] >>> 28 | lfsr[2] << 4;
var s79 = lfsr[2] >>> 15 | lfsr[3] << 17;
var s93 = lfsr[2] >>> 29 | lfsr[3] << 3;
var s94 = lfsr[2] >>> 31 | lfsr[3] << 1;
return b12 & s8 ^
s13 & s20 ^
b95 & s42 ^
s60 & s79 ^
b12 & b95 & s94 ^
s93 ^
b2 ^
b15 ^
b36 ^
b45 ^
b64 ^
b73 ^
b89;
}
/// Shift array 32 bits and add val to index.length - 1.
///
/// @param array The array to shift.
/// @param val The value to shift in.
/// @return The shifted array with val added to index.length - 1.
List<int> shift(List<int> array, int val) {
array[0] = array[1];
array[1] = array[2];
array[2] = array[3];
array[3] = val;
return array;
}
/// Set keys, reset cipher.
///
/// @param keyBytes The key.
/// @param ivBytes The IV.
void setKey(Uint8List keyBytes, Uint8List ivBytes) {
ivBytes[12] = 0xFF;
ivBytes[13] = 0xFF;
ivBytes[14] = 0xFF;
ivBytes[15] = 0xFF;
workingKey = keyBytes;
workingIV = ivBytes;
/**
* Load NFSR and LFSR
*/
var j = 0;
for (var i = 0; i < nfsr.length; i++) {
nfsr[i] = ((workingKey[j + 3]) << 24) |
((workingKey[j + 2]) << 16) & 0x00FF0000 |
((workingKey[j + 1]) << 8) & 0x0000FF00 |
((workingKey[j]) & 0x000000FF);
lfsr[i] = ((workingIV[j + 3]) << 24) |
((workingIV[j + 2]) << 16) & 0x00FF0000 |
((workingIV[j + 1]) << 8) & 0x0000FF00 |
((workingIV[j]) & 0x000000FF);
j += 4;
}
}
@override
int processBytes(
Uint8List _in, int inOff, int len, Uint8List out, int outOff) {
if (!initialised) {
throw StateError(algorithmName + ' not initialised');
}
if ((inOff + len) > _in.length) {
throw ArgumentError('input buffer too short');
}
if ((outOff + len) > out.length) {
throw ArgumentError('output buffer too short');
}
for (var i = 0; i < len; i++) {
out[outOff + i] = (_in[inOff + i] ^ getKeyStream());
}
return len;
}
@override
void reset() {
index = 4;
setKey(workingKey, workingIV);
initGrain();
}
/// Run Grain one round(i.e. 32 bits).
void oneRound() {
output = getOutput();
out[0] = output;
out[1] = (output >> 8);
out[2] = (output >> 16);
out[3] = (output >> 24);
nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0]);
lfsr = shift(lfsr, getOutputLFSR());
}
@override
int returnByte(int _in) {
if (!initialised) {
throw StateError(algorithmName + ' not initialised');
}
return _in ^ getKeyStream();
}
int getKeyStream() {
if (index > 3) {
oneRound();
index = 0;
}
return out[index++];
}
@override
Uint8List process(Uint8List data) {
// TODO: implement process
throw UnimplementedError();
}
}
Hi,
I see that the grain128a stream cipher is available in the original java bouncycastle.
Is there any plan to implement it here as well ?
Thank you,