Skip to content
This repository was archived by the owner on Dec 23, 2020. It is now read-only.

authenticated encryption #153

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
# Name <email address>

Iván Zaera Avellón <[email protected]>
Steven Roose <[email protected]>
Steven Roose <[email protected]>
Rik Bellens <[email protected]>
2 changes: 2 additions & 0 deletions lib/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import "src/registry/registry.dart";

part "src/api/algorithm.dart";
part "src/api/registry_factory_exception.dart";
part 'src/api/aead_block_cipher.dart';
part 'src/api/aead_parameters.dart';
part "src/api/assymetric_block_cipher.dart";
part "src/api/assymetric_key.dart";
part "src/api/assymetric_key_pair.dart";
Expand Down
124 changes: 124 additions & 0 deletions lib/block/modes/cbc_hmac.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright (c) 2013-present, the authors of the Pointy Castle project
// This library is dually licensed under LGPL 3 and MPL 2.0.
// See file LICENSE for more information.
library pointycastle.impl.block_cipher.modes.cbc_hmac;

import "dart:typed_data";

import "package:pointycastle/api.dart";
import "package:pointycastle/src/registry/registry.dart";
import "package:pointycastle/src/impl/base_aead_block_cipher.dart";
import "package:pointycastle/macs/hmac.dart";
import "package:pointycastle/digests/sha256.dart";
import "package:pointycastle/digests/sha384.dart";
import "package:pointycastle/digests/sha512.dart";
import "package:pointycastle/padded_block_cipher/padded_block_cipher_impl.dart";
import "package:pointycastle/paddings/pkcs7.dart";

import "cbc.dart";

/// Implementation of [Authenticated Encryption with AES-CBC and HMAC-SHA]
/// (https://tools.ietf.org/html/rfc7518#section-5.2)
class CBCHMACAuthenticatedEncryptionCipher extends BaseAEADBlockCipher {

/// Intended for internal use.
static final FactoryConfig FACTORY_CONFIG =
new DynamicFactoryConfig.suffix(BlockCipher, "/CBC_HMAC",
(_, final Match match) =>
() {
BlockCipher underlying = new BlockCipher(match.group(1));
return new CBCHMACAuthenticatedEncryptionCipher(underlying);
});

Mac _underlyingMac;
Uint8List _hash;

CBCHMACAuthenticatedEncryptionCipher(BlockCipher underlyingCipher) :
super(new PaddedBlockCipherImpl(new PKCS7Padding(), new CBCBlockCipher(underlyingCipher)));

@override
String get algorithmName => "${underlyingCipher.algorithmName.substring(0,7)}_HMAC";



@override
void prepare(KeyParameter keyParam) {
var key = keyParam.key;

var macKey = new Uint8List.view(key.buffer, key.offsetInBytes, key.length~/2);
var encKey = new Uint8List.view(key.buffer, key.offsetInBytes+key.length~/2);

switch (macSize) {
case 16:
_underlyingMac = new HMac(new SHA256Digest(), 64);
break;
case 24:
_underlyingMac = new HMac(new SHA384Digest(), 64);
break;
case 64:
_underlyingMac = new HMac(new SHA512Digest(), 64);
break;
default:
throw new ArgumentError("Invalid mac size $macSize");
}
_underlyingMac.init(new KeyParameter(macKey));
underlyingCipher.init(forEncryption, new PaddedBlockCipherParameters(
new ParametersWithIV(
new KeyParameter(encKey),
nonce,
), null));

}

@override
void processAADBytes(Uint8List inp, int inpOff, int len) {
_underlyingMac.update(inp, inpOff, len);
_underlyingMac.update(nonce, 0, nonce.length);
}

@override
int processBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) {
var l = underlyingCipher.processBlock(inp, inpOff, out, outOff);
if (forEncryption) {
_underlyingMac.update(out, outOff, l);
} else {
_underlyingMac.update(inp, inpOff, blockSize);
}
return l;
}

@override
int doFinal(Uint8List out, int outOff) {
var l = 0;

if (remainingInput.isNotEmpty) {
l += (underlyingCipher as PaddedBlockCipher).doFinal(remainingInput, 0, out, outOff);
if (forEncryption) {
_underlyingMac.update(out, outOff, blockSize);
} else {
_underlyingMac.update(remainingInput, 0, blockSize);
}
outOff += l;
}

var al = new Uint8List.view((new Uint64List(1)..[0] = aad.length*8).buffer);
al = new Uint8List.fromList(al.reversed.toList());
_underlyingMac.update(al, 0, 8);

_hash = new Uint8List(macSize*2);
_underlyingMac.doFinal(_hash, 0);
_hash = new Uint8List.view(_hash.buffer, 0, macSize);
if (forEncryption) {
out.setAll(outOff, _hash);
l += _hash.length;
}

validateMac();

return l;

}

@override
Uint8List get mac => _hash;
}
208 changes: 208 additions & 0 deletions lib/block/modes/gcm.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// Copyright (c) 2013-present, the authors of the Pointy Castle project
// This library is dually licensed under LGPL 3 and MPL 2.0.
// See file LICENSE for more information.
library pointycastle.impl.block_cipher.modes.gcm;

import "dart:typed_data";

import "package:pointycastle/api.dart";
import "package:pointycastle/src/registry/registry.dart";
import "package:pointycastle/src/impl/base_aead_block_cipher.dart";
import 'dart:math' show min;

class GCMBlockCipher extends BaseAEADBlockCipher {

/// Intended for internal use.
static final FactoryConfig FACTORY_CONFIG =
new DynamicFactoryConfig.suffix(BlockCipher, "/GCM",
(_, final Match match) =>
() {
BlockCipher underlying = new BlockCipher(match.group(1));
return new GCMBlockCipher(underlying);
});

Uint8List _h;
Uint8List _counter;
Uint8List _e;
Uint8List _e0;
Uint8List _x;
int _processedBytes;


GCMBlockCipher(BlockCipher cipher) : super(cipher);

@override
String get algorithmName => "${underlyingCipher.algorithmName}/GCM";

@override
void prepare(KeyParameter keyParam) {
if (macSize!=16) {
throw new ArgumentError("macSize should be equal to 16 for GCM");
}

underlyingCipher.init(true, keyParam);

_h = new Uint8List(blockSize);
underlyingCipher.processBlock(_h, 0, _h, 0);

_counter = _computeInitialCounter(nonce);

_e0 = new Uint8List(16);
_computeE(_counter, _e0);

_e = new Uint8List(16);

_x = new Uint8List(16);

_processedBytes = 0;
}

Uint8List _computeInitialCounter(Uint8List iv) {
Uint8List counter = new Uint8List(16);

if (iv.length==12) {
counter.setAll( 0, iv );
counter[15] = 1;
} else {
_gHASH(counter, iv);
var length = new Uint8List.view((new Uint64List(2)..[0] = iv.length*8).buffer);
length = new Uint8List.fromList(length.reversed.toList());

_gHASHBlock(counter, length);

}
return counter;

}

int processBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) {

var length = blockSize < inp.length-inpOff ? blockSize : inp.length-inpOff;

var i = new Uint8List(blockSize);
i.setAll(0, inp.skip(inpOff).take(length));

_processedBytes += length;

_getNextCTRBlock(_e);

var o = new Uint8List.fromList(i);
_xor(o, _e);
if (length<blockSize) o.fillRange(length, blockSize, 0);

out.setRange(outOff, outOff+length, o);

var c = forEncryption ? o : i;

_gHASHBlock(_x, c);

return length;
}

void _gHASH(Uint8List x, Uint8List y) {
var block = new Uint8List(16);
for (var i=0;i<y.length;i+=16) {
block.setAll(0, y.sublist(i,min(i+16,y.length)));
block.fillRange(min(i+16,y.length)-i, 16, 0);
_gHASHBlock(x, block);
}
}

void _gHASHBlock(Uint8List x, Uint8List y) {
_xor(x,y);
_mult(x, _h);
}

void _getNextCTRBlock(Uint8List out) {
_counter[15]++;
for (var i=15;i>=12&&_counter[i]==256;i--) {
_counter[i] = 0;
if (i>12) _counter[i-1]++;
}

_computeE(_counter, out);
}

void _computeE(Uint8List inp, Uint8List out) {
underlyingCipher.processBlock( inp, 0, out, 0 );
}

final Uint8List r = new Uint8List(16)..[0] = 0xe1;

void _mult(Uint8List x, Uint8List y) {
var v = x;
var z = new Uint8List(x.length);

for (var i=0;i<128;i++) {
if (_bit(y,i)) {
_xor(z,v);
}
if (_shiftRight(v)) {
_xor(v,r);
}
}

x.setAll(0, z);
}

void _xor(Uint8List x, Uint8List y) {
for (var i=0;i<x.length;i++) {
x[i] ^= y[i];
}
}

bool _bit(Uint8List x, int n) {
int byte = n~/8;
int mask = 1<<(7-n%8);
return x[byte]&mask==mask;
}

bool _shiftRight(Uint8List x) {
bool overflow = false;
for (var i=0;i<x.length;i++) {
var nextOverflow = x[i]&0x1==0x1;
x[i] >>= 1;
if (overflow) x[i] |= 0x80;
overflow = nextOverflow;
}
return overflow;
}

@override
int doFinal(Uint8List out, int outOff) {

var result = remainingInput.length>0 ? processBlock(remainingInput, 0, out, outOff) : 0;

var len = new Uint8List.view((new Uint64List(2)..[1] = aad.length*8..[0] = _processedBytes*8).buffer);
len = new Uint8List.fromList(len.reversed.toList());

_gHASHBlock(_x, len);

_xor(_x, _e0);

if (forEncryption) {
out.setAll(outOff+result, _x);
result+=_x.length;
}

validateMac();

return result;
}

@override
Uint8List get mac => _x;

@override
void processAADBytes(Uint8List inp, int inpOff, int len) {
var block = new Uint8List(16);
for (var i=0;i<len;i+=16) {
block.fillRange(0, 16, 0);
block.setAll(0, inp.sublist(inpOff+i,inpOff+min(i+16,len)));
_gHASHBlock(_x, block);
}
}

}


2 changes: 2 additions & 0 deletions lib/export.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export "package:pointycastle/block/modes/ecb.dart";
export "package:pointycastle/block/modes/gctr.dart";
export "package:pointycastle/block/modes/ofb.dart";
export "package:pointycastle/block/modes/sic.dart";
export "package:pointycastle/block/modes/gcm.dart";
export "package:pointycastle/block/modes/cbc_hmac.dart";

// digests
export "package:pointycastle/digests/md2.dart";
Expand Down
32 changes: 32 additions & 0 deletions lib/src/api/aead_block_cipher.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

// Copyright (c) 2013-present, the authors of the Pointy Castle project
// This library is dually licensed under LGPL 3 and MPL 2.0.
// See file LICENSE for more information.

part of pointycastle.api;

/// A block cipher mode that includes authenticated encryption
abstract class AEADBlockCipher implements BlockCipher {

/// Process [len] bytes from [inp] starting at offset [inpOff] and output the
/// result to [out] at offset [outOff].
///
/// Returns the number of bytes written to the output.
int processBytes(Uint8List inp, int inpOff, int len, Uint8List out, int outOff);

/// Finish the operation either appending or verifying the MAC at the end of
/// the data.
///
/// Returns the number of bytes written to the output.
int doFinal(Uint8List out, int outOff);

}

class InvalidCipherTextException implements Exception {

final String message;

InvalidCipherTextException(this.message);

}

Loading