Skip to content

Commit

Permalink
Adds XChaCha20, XSalsa20, XChaCha20Poly1305, XSalsa20Poly1305
Browse files Browse the repository at this point in the history
  • Loading branch information
dipu-bd committed Sep 9, 2024
1 parent 6ca9d27 commit bc0a363
Show file tree
Hide file tree
Showing 38 changed files with 2,110 additions and 529 deletions.
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## 0.0.13
## 1.0.0

- `XOR` cipher.
- `Salsa20` cipher with `Poly1305` tag.
- `ChaCha20` cipher with `Poly1305` tag.
- `AES` in ECB, CBC, CTR, CFB, OFB, GCM, XTS, IGE, PCBC modes.
- `XChaCha20`, `ChaCha20` cipher with `Poly1305` tag.
- `XSalsa20`, `Salsa20` cipher with `Poly1305` tag.
- `XOR` cipher.
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@ There are only 2 dependencies used by this package:

## Features

| Ciphers | Public class and methods | Source |
| ----------------- | ---------------------------------------------------------------- | :-----------: |
| AES | `AES`, | NIST.FIPS.197 |
| XOR | `XOR`, `xor`, `xorStream` | Wikipedia |
| ChaCha20 | `ChaCha20`, `chacha20`, `chacha20Stream` | RFC-8439 |
| ChaCha20/Poly1305 | `ChaCha20Poly1305`, `chacha20poly1305`, `chacha20poly1305Stream` | RFC-8439 |
| Salsa20 | `Salsa20`, `salsa20`, `salsa20Stream` | Snuffle-2005 |
| Salsa20/Poly1305 | `Salsa20Poly1305`, `salsa20poly1305`, `salsa20poly1305Stream` | Snuffle-2005 |
| Ciphers | Public class and methods | Source |
| ------------------ | ------------------------------------------- | :-----------: |
| AES | `AES`, | NIST.FIPS.197 |
| XOR | `XOR`, `xor`, `xorStream` | Wikipedia |
| ChaCha20 | `ChaCha20`, `chacha20`, `chacha20Stream` | RFC-8439 |
| ChaCha20/Poly1305 | `ChaCha20Poly1305`, `chacha20poly1305` | RFC-8439 |
| XChaCha20 | `XChaCha20`, `xchacha20`, `xchacha20Stream` | libsodium |
| XChaCha20/Poly1305 | `XChaCha20Poly1305`, `xchacha20poly1305` | libsodium |
| Salsa20 | `Salsa20`, `salsa20`, `salsa20Stream` | Snuffle-2005 |
| Salsa20/Poly1305 | `Salsa20Poly1305`, `salsa20poly1305` | Snuffle-2005 |
| XSalsa20 | `XSalsa20`, `xsalsa20`, `xsalsa20Stream` | libsodium |
| XSalsa20/Poly1305 | `XSalsa20Poly1305`, `xsalsa20poly1305` | libsodium |

Available modes for AES:

Expand Down
4 changes: 2 additions & 2 deletions benchmark/chacha20.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ class PointyCastleBenchmark extends Benchmark {

PointyCastleBenchmark(int size, int iter)
: key = Uint8List.fromList(List.filled(32, 0x9f)),
nonce = Uint8List.fromList(List.filled(8, 0x2f)),
nonce = Uint8List.fromList(List.filled(12, 0x2f)),
super('PointyCastle', size, iter);

@override
void run() {
var instance = pc.StreamCipher('ChaCha20/20');
var instance = pc.StreamCipher('ChaCha7539/20');
instance.init(
true,
pc.ParametersWithIV(pc.KeyParameter(key), nonce),
Expand Down
4 changes: 2 additions & 2 deletions benchmark/salsa20.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class CipherlibBenchmark extends Benchmark {

@override
void run() {
cipher.Salsa20(key, nonce: nonce).convert(input);
cipher.Salsa20(key, nonce).convert(input);
}
}

Expand Down Expand Up @@ -60,7 +60,7 @@ class CipherlibStreamBenchmark extends AsyncBenchmark {

@override
Future<void> run() async {
await cipher.Salsa20(key, nonce: nonce).stream(inputStream).drain();
await cipher.Salsa20(key, nonce).stream(inputStream).drain();
}
}

Expand Down
26 changes: 13 additions & 13 deletions example/cipherlib_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,6 @@ void main() {
}
print('');

print('----- XOR -----');
{
var key = [0x54];
var inp = [0x03, 0xF1];
var cipher = xor(inp, key);
var plain = xor(cipher, key);
print(' Text: ${toBinary(inp)}');
print(' Key: ${toBinary(key)}');
print(' XOR: ${toBinary(cipher)}');
print(' Plain: ${toBinary(plain)}');
}
print('');

print('----- ChaCha20 -----');
{
var text = "Hide me!";
Expand Down Expand Up @@ -66,5 +53,18 @@ void main() {
print('Cipher: ${toHex(res.data)}');
print(' Tag: ${res.tag.hex()}');
print(' Plain: ${fromUtf8(plain)}');

print('----- XOR -----');
{
var key = [0x54];
var inp = [0x03, 0xF1];
var cipher = xor(inp, key);
var plain = xor(cipher, key);
print(' Text: ${toBinary(inp)}');
print(' Key: ${toBinary(key)}');
print(' XOR: ${toBinary(cipher)}');
print(' Plain: ${toBinary(plain)}');
}
print('');
}
}
78 changes: 39 additions & 39 deletions lib/src/algorithms/aead_cipher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,31 @@ class AEADResult {
/// The message authentication code
final HashDigest tag;

const AEADResult._({
required this.tag,
required this.data,
});
const AEADResult._(this.data, this.tag);

/// Returns whether the generated [tag] (message authentication code) is
/// equal to the provided tag [digest].
bool verify(List<int>? digest) => tag.isEqual(digest);

/// Creates a new instance of AEADResult with IV parameter
AEADResultWithIV withIV(Uint8List iv) =>
AEADResultWithIV._(tag: tag, data: data, iv: iv);
AEADResultWithIV withIV(Uint8List iv) => AEADResultWithIV._(data, tag, iv);
}

class AEADResultWithIV extends AEADResult {
/// The IV, available if and only if cipher does supports it.
final Uint8List iv;

const AEADResultWithIV._({
required this.iv,
required HashDigest tag,
required Uint8List data,
}) : super._(tag: tag, data: data);
const AEADResultWithIV._(
Uint8List data,
HashDigest tag,
this.iv,
) : super._(data, tag);
}

/// Extends the base [AEADCipherSink] to generate message digest for cipher
/// algorithms.
class AEADCipherSink<C extends CipherSink, H extends HashDigestSink>
extends CipherSink {
implements CipherSink {
final H _sink;
final C _cipher;
final List<int>? _aad;
Expand Down Expand Up @@ -82,25 +78,15 @@ class AEADCipherSink<C extends CipherSink, H extends HashDigestSink>
_verifyMode = forVerification;
}

/// Finalizes the message-digest and returns a [HashDigest].
///
/// Throws [StateError] if this sink is not closed before generating digest.
HashDigest digest() {
if (!closed) {
close();
}
return _sink.digest();
}

@override
Uint8List add(
List<int> data, [
bool last = false,
int start = 0,
int? end,
bool last = false,
]) {
end ??= data.length;
var cipher = _cipher.add(data, start, end, last);
var cipher = _cipher.add(data, last, start, end);
if (_verifyMode) {
_dataLength += end - start;
_sink.add(data, start, end);
Expand Down Expand Up @@ -135,6 +121,23 @@ class AEADCipherSink<C extends CipherSink, H extends HashDigestSink>
}
return cipher;
}

@override
Uint8List close() {
final r = add([], true);
_sink.close();
return r;
}

/// Returns the current tag as [HashDigest] after sink is closed.
///
/// Throws [StateError] if this sink is not closed before generating digest.
HashDigest digest() {
if (!closed) {
throw StateError('The sink is not yet closed');
}
return _sink.digest();
}
}

/// Provides support for AEAD (Authenticated Encryption with Associated Data) to
Expand Down Expand Up @@ -169,25 +172,22 @@ abstract class AEADCipher<C extends Cipher, M extends MACHashBase>
verifyMode,
);

/// Transforms the [message] with an authentication tag.
/// Transforms the [message]. Alias for [sign].
@pragma('vm:prefer-inline')
AEADResult convert(List<int> message) {
AEADResult convert(List<int> message) => sign(message);

/// Signs the [message] with an authentication tag.
AEADResult sign(List<int> message) {
var sink = createSink();
var cipher = sink.add(message, 0, null, true);
var cipher = sink.add(message, true);
var digest = sink.digest();
return AEADResult._(
tag: digest,
data: cipher,
);
return AEADResult._(cipher, digest);
}

/// Returns true if [message] can be verified with the authentication [tag].
bool verify(List<int> message, List<int> tag) {
var sink = createSink(true);
sink.add(message, 0, null, true);
var digest = sink.digest();
return digest.isEqual(tag);
}
@pragma('vm:prefer-inline')
bool verify(List<int> message, List<int> tag) =>
(createSink(true)..add(message, true)).digest().isEqual(tag);

@override
Stream<Uint8List> bind(
Expand All @@ -202,7 +202,7 @@ abstract class AEADCipher<C extends Cipher, M extends MACHashBase>
}
cache = data;
}
yield sink.add(cache ?? [], 0, null, true);
yield sink.add(cache ?? [], true);
if (onDigest != null) {
onDigest(sink.digest());
}
Expand All @@ -225,7 +225,7 @@ abstract class AEADCipher<C extends Cipher, M extends MACHashBase>
p = 0;
}
}
for (var e in sink.add(chunk, 0, p, true)) {
for (var e in sink.add(chunk, true, 0, p)) {
yield e;
}
if (onDigest != null) {
Expand Down
16 changes: 12 additions & 4 deletions lib/src/algorithms/aes/cbc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'package:hashlib/hashlib.dart' show randomBytes;
import '_core.dart';

/// The sink used for encryption by the [AESInCBCModeEncrypt] algorithm.
class AESInCBCModeEncryptSink extends CipherSink {
class AESInCBCModeEncryptSink implements CipherSink {
AESInCBCModeEncryptSink(
this._key,
this._iv,
Expand Down Expand Up @@ -46,9 +46,9 @@ class AESInCBCModeEncryptSink extends CipherSink {
@override
Uint8List add(
List<int> data, [
bool last = false,
int start = 0,
int? end,
bool last = false,
]) {
if (_closed) {
throw StateError('The sink is closed');
Expand Down Expand Up @@ -104,10 +104,14 @@ class AESInCBCModeEncryptSink extends CipherSink {
return output.sublist(0, p);
}
}

@override
@pragma('vm:prefer-inline')
Uint8List close() => add([], true);
}

/// The sink used for decryption by the [AESInCBCModeDecrypt] algorithm.
class AESInCBCModeDecryptSink extends CipherSink {
class AESInCBCModeDecryptSink implements CipherSink {
AESInCBCModeDecryptSink(
this._key,
this._iv,
Expand Down Expand Up @@ -145,9 +149,9 @@ class AESInCBCModeDecryptSink extends CipherSink {
@override
Uint8List add(
List<int> data, [
bool last = false,
int start = 0,
int? end,
bool last = false,
]) {
if (_closed) {
throw StateError('The sink is closed');
Expand Down Expand Up @@ -204,6 +208,10 @@ class AESInCBCModeDecryptSink extends CipherSink {
return output.sublist(0, p);
}
}

@override
@pragma('vm:prefer-inline')
Uint8List close() => add([], true);
}

/// Provides encryption for AES cipher in CBC mode.
Expand Down
16 changes: 12 additions & 4 deletions lib/src/algorithms/aes/cfb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'package:hashlib/hashlib.dart' show randomBytes;
import '_core.dart';

/// The sink used for encryption by the [AESInCFBModeEncrypt] algorithm.
class AESInCFBModeEncryptSink extends CipherSink {
class AESInCFBModeEncryptSink implements CipherSink {
AESInCFBModeEncryptSink(
this._key,
this._iv,
Expand Down Expand Up @@ -46,9 +46,9 @@ class AESInCFBModeEncryptSink extends CipherSink {
@override
Uint8List add(
List<int> data, [
bool last = false,
int start = 0,
int? end,
bool last = false,
]) {
if (_closed) {
throw StateError('The sink is closed');
Expand Down Expand Up @@ -81,10 +81,14 @@ class AESInCFBModeEncryptSink extends CipherSink {

return output;
}

@override
@pragma('vm:prefer-inline')
Uint8List close() => add([], true);
}

/// The sink used for decryption by the [AESInCFBModeDecrypt] algorithm.
class AESInCFBModeDecryptSink extends CipherSink {
class AESInCFBModeDecryptSink implements CipherSink {
AESInCFBModeDecryptSink(
this._key,
this._iv,
Expand Down Expand Up @@ -119,9 +123,9 @@ class AESInCFBModeDecryptSink extends CipherSink {
@override
Uint8List add(
List<int> data, [
bool last = false,
int start = 0,
int? end,
bool last = false,
]) {
if (_closed) {
throw StateError('The sink is closed');
Expand Down Expand Up @@ -153,6 +157,10 @@ class AESInCFBModeDecryptSink extends CipherSink {

return output;
}

@override
@pragma('vm:prefer-inline')
Uint8List close() => add([], true);
}

/// Provides encryption for AES cipher in CFB mode.
Expand Down
Loading

0 comments on commit bc0a363

Please sign in to comment.