Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grain128 engine #107

Open
nicolasgarnet opened this issue Jun 14, 2021 · 3 comments
Open

Grain128 engine #107

nicolasgarnet opened this issue Jun 14, 2021 · 3 comments

Comments

@nicolasgarnet
Copy link

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,

@AKushWarrior
Copy link
Contributor

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

@nicolasgarnet
Copy link
Author

It would be great, thank you.

@nicolasgarnet nicolasgarnet changed the title Grain128a engine Grain128 engine Feb 22, 2022
@redDwarf03
Copy link
Contributor

redDwarf03 commented Mar 9, 2023

Here is a draft.
I contacted @Ephenodrom to help me to integrate correctly this class and to write correctly processmethod
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();
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants