Skip to content

Commit

Permalink
add crypto_box_seal
Browse files Browse the repository at this point in the history
  • Loading branch information
Skycoder42 committed May 25, 2021
1 parent 0ff6a4b commit 781eeaa
Show file tree
Hide file tree
Showing 11 changed files with 642 additions and 15 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# libsodium_dart_bindings
[![Continous Integration for package sodium](https://github.com/Skycoder42/libsodium_dart_bindings/actions/workflows/sodium_ci.yaml/badge.svg)](https://github.com/Skycoder42/libsodium_dart_bindings/actions/workflows/sodium_ci.yaml)
[![Pub Version](https://img.shields.io/pub/v/sodium)](https://pub.dev/packages/sodium)

This repository is a multi package repository for dart bindings of
[libsodium](https://libsodium.gitbook.io/doc/). It consists of the following
packages. Please check the READMEs of the specific packages for more details on
Expand All @@ -8,5 +11,5 @@ If you just landed here and don't know where to start, simply read the
[sodium README](packages/sodium), as that is the primary package of this
repository.

- [sodium](packages/sodium): Dart bindings for libsodium, supporting both the
VM and JS without flutter dependencies.
- **[sodium](packages/sodium)**: Dart bindings for libsodium, supporting both the VM and JS without flutter
dependencies.
4 changes: 4 additions & 0 deletions packages/sodium/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.4] - 2021-05-21
### Added
- New libsodium API: crypto_box_seal

## [0.1.3] - 2021-05-21
### Added
- New libsodium APIs:
Expand Down
2 changes: 1 addition & 1 deletion packages/sodium/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ API based on libsodium version: *1.0.18*
crypto_auth | ✔️ | ✔️ | https://libsodium.gitbook.io/doc/secret-key_cryptography/secret-key_authentication
crypto_box | ✔️ | ✔️ | https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption
crypto_sign | ✔️ | ✔️ | https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures
crypto_box_seal | 🚧 | 🚧 | https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes
crypto_box_seal | ✔️ | ✔️ | https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes
crypto_generichash | 🚧 | 🚧 | https://libsodium.gitbook.io/doc/hashing/generic_hashing
crypto_shorthash | 🚧 | 🚧 | https://libsodium.gitbook.io/doc/hashing/short-input_hashing
crypto_pwhash | ✔️ | ✔️ | https://libsodium.gitbook.io/doc/password_hashing/default_phf
Expand Down
31 changes: 30 additions & 1 deletion packages/sodium/lib/src/api/box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import 'secure_key.dart';
/// A meta class that provides access to all libsodium box APIs.
///
/// This class provides the dart interface for the crypto operations documented
/// in https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption.
/// in https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption
/// and https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes.
/// Please refer to that documentation for more details about these APIs.
abstract class Box {
const Box._(); // coverage:ignore-line
Expand Down Expand Up @@ -40,6 +41,11 @@ abstract class Box {
/// See https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption#constants
int get seedBytes;

/// Provides crypto_box_SEALBYTES.
///
/// See https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes#constants
int get sealBytes;

/// Provides crypto_box_keypair.
///
/// See https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption#key-pair-generation
Expand Down Expand Up @@ -90,6 +96,23 @@ abstract class Box {
required Uint8List senderPublicKey,
required SecureKey recipientSecretKey,
});

/// Provides crypto_box_seal.
///
/// See https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes#usage
Uint8List seal({
required Uint8List message,
required Uint8List recipientPublicKey,
});

/// Provides crypto_box_seal_open.
///
/// See https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes#usage
Uint8List sealOpen({
required Uint8List cipherText,
required Uint8List recipientPublicKey,
required SecureKey recipientSecretKey,
});
}

@internal
Expand Down Expand Up @@ -129,4 +152,10 @@ mixin BoxValidations implements Box {
macBytes,
'cipherText',
);

void validateSealCipherText(Uint8List cipherText) => Validations.checkAtLeast(
cipherText.length,
sealBytes,
'cipherText',
);
}
77 changes: 77 additions & 0 deletions packages/sodium/lib/src/ffi/api/box_ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class BoxFFI with BoxValidations implements Box {
@override
int get seedBytes => sodium.crypto_box_seedbytes();

@override
int get sealBytes => sodium.crypto_box_sealbytes();

@override
KeyPair keyPair() {
SecureKeyFFI? secretKey;
Expand Down Expand Up @@ -304,4 +307,78 @@ class BoxFFI with BoxValidations implements Box {
publicKeyPtr?.dispose();
}
}

@override
Uint8List seal({
required Uint8List message,
required Uint8List recipientPublicKey,
}) {
validatePublicKey(recipientPublicKey);

SodiumPointer<Uint8>? dataPtr;
SodiumPointer<Uint8>? publicKeyPtr;
try {
dataPtr = SodiumPointer.alloc(
sodium,
count: message.length + sealBytes,
)
..fill(List<int>.filled(sealBytes, 0))
..fill(message, offset: sealBytes);
publicKeyPtr = recipientPublicKey.toSodiumPointer(
sodium,
memoryProtection: MemoryProtection.readOnly,
);

final result = sodium.crypto_box_seal(
dataPtr.ptr,
dataPtr.viewAt(sealBytes).ptr,
message.length,
publicKeyPtr.ptr,
);
SodiumException.checkSucceededInt(result);

return dataPtr.copyAsList();
} finally {
dataPtr?.dispose();
publicKeyPtr?.dispose();
}
}

@override
Uint8List sealOpen({
required Uint8List cipherText,
required Uint8List recipientPublicKey,
required SecureKey recipientSecretKey,
}) {
validateSealCipherText(cipherText);
validatePublicKey(recipientPublicKey);
validateSecretKey(recipientSecretKey);

SodiumPointer<Uint8>? dataPtr;
SodiumPointer<Uint8>? publicKeyPtr;
try {
dataPtr = cipherText.toSodiumPointer(sodium);
publicKeyPtr = recipientPublicKey.toSodiumPointer(
sodium,
memoryProtection: MemoryProtection.readOnly,
);

final result = recipientSecretKey.runUnlockedNative(
sodium,
(secretKeyPtr) => sodium.crypto_box_seal_open(
dataPtr!.viewAt(sealBytes).ptr,
dataPtr.ptr,
dataPtr.count,
publicKeyPtr!.ptr,
secretKeyPtr.ptr,
),
);
SodiumException.checkSucceededInt(result);

return dataPtr.viewAt(sealBytes).copyAsList();
} finally {
dataPtr?.dispose();
publicKeyPtr?.dispose();
}
}
}
39 changes: 39 additions & 0 deletions packages/sodium/lib/src/js/api/box_js.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class BoxJS with BoxValidations implements Box {
@override
int get seedBytes => sodium.crypto_box_SEEDBYTES.toSafeUInt32();

@override
int get sealBytes => sodium.crypto_box_SEALBYTES.toSafeUInt32();

@override
KeyPair keyPair() {
final keyPair = JsError.wrap(() => sodium.crypto_box_keypair());
Expand Down Expand Up @@ -155,4 +158,40 @@ class BoxJS with BoxValidations implements Box {
),
);
}

@override
Uint8List seal({
required Uint8List message,
required Uint8List recipientPublicKey,
}) {
validatePublicKey(recipientPublicKey);

return JsError.wrap(
() => sodium.crypto_box_seal(
message,
recipientPublicKey,
),
);
}

@override
Uint8List sealOpen({
required Uint8List cipherText,
required Uint8List recipientPublicKey,
required SecureKey recipientSecretKey,
}) {
validateSealCipherText(cipherText);
validatePublicKey(recipientPublicKey);
validateSecretKey(recipientSecretKey);

return JsError.wrap(
() => recipientSecretKey.runUnlockedSync(
(secretKeyData) => sodium.crypto_box_seal_open(
cipherText,
recipientPublicKey,
secretKeyData,
),
),
);
}
}
6 changes: 3 additions & 3 deletions packages/sodium/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: sodium
description: Dart bindings for libsodium, for the Dart-VM and for the Web
version: 0.1.3
version: 0.1.4
homepage: https://github.com/Skycoder42/libsodium_dart_bindings

environment:
Expand All @@ -13,13 +13,13 @@ dependencies:
meta: ^1.3.0

dev_dependencies:
build_runner: ^2.0.3
build_runner: ^2.0.4
coverage: ^1.0.2
dart_pre_commit: ^2.3.1
ffigen: ^3.0.0
freezed: ^0.14.2
lint: ^1.5.3
mocktail: ^0.1.2
mocktail: ^0.1.3
path: ^1.8.0
test: ^1.17.4
tuple: ^2.0.0
Expand Down
59 changes: 59 additions & 0 deletions packages/sodium/test/integration/cases/box_test_case.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class BoxTestCase extends TestCase {
expect(sut.macBytes, 16, reason: 'macBytes');
expect(sut.nonceBytes, 24, reason: 'nonceBytes');
expect(sut.seedBytes, 32, reason: 'seedBytes');
expect(sut.sealBytes, 48, reason: 'sealBytes');
});

test('keyPair generates different correct length keys', () {
Expand Down Expand Up @@ -245,5 +246,63 @@ class BoxTestCase extends TestCase {
);
});
});

group('seal', () {
test('can encrypt and decrypt data', () {
final recipientKey = sut.keyPair();
final message = Uint8List.fromList(
List.generate(32, (index) => index * 2),
);

printOnFailure(
'recipientKey.secretKey: ${recipientKey.secretKey.extractBytes()}',
);
printOnFailure('recipientKey.publicKey: ${recipientKey.publicKey}');
printOnFailure('message: $message');

final cipherText = sut.seal(
message: message,
recipientPublicKey: recipientKey.publicKey,
);

printOnFailure('cipherText: $cipherText');

final restored = sut.sealOpen(
cipherText: cipherText,
recipientPublicKey: recipientKey.publicKey,
recipientSecretKey: recipientKey.secretKey,
);

printOnFailure('restored: $restored');

expect(restored, message);
});

test('fails if data is invalid', () {
final recipientKey = sut.keyPair();

printOnFailure(
'recipientKey.secretKey: ${recipientKey.secretKey.extractBytes()}',
);
printOnFailure('recipientKey.publicKey: ${recipientKey.publicKey}');

final cipherText = sut.seal(
message: Uint8List.fromList(const [1, 2, 3]),
recipientPublicKey: recipientKey.publicKey,
);

printOnFailure('cipherText: $cipherText');
cipherText[0] = cipherText[0] ^ 0xFF;

expect(
() => sut.sealOpen(
cipherText: cipherText,
recipientPublicKey: recipientKey.publicKey,
recipientSecretKey: recipientKey.secretKey,
),
throwsA(isA<SodiumException>()),
);
});
});
}
}
6 changes: 6 additions & 0 deletions packages/sodium/test/unit/api/box_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,11 @@ void main() {
source: () => sutMock.macBytes,
sut: (value) => sutMock.validateEasyCipherText(Uint8List(value)),
);

testCheckAtLeast(
'validateSealCipherText',
source: () => sutMock.sealBytes,
sut: (value) => sutMock.validateSealCipherText(Uint8List(value)),
);
});
}
Loading

0 comments on commit 781eeaa

Please sign in to comment.