Skip to content

Commit

Permalink
add crypto_generichash
Browse files Browse the repository at this point in the history
  • Loading branch information
Skycoder42 committed May 27, 2021
1 parent f011d73 commit 39aad85
Show file tree
Hide file tree
Showing 24 changed files with 2,047 additions and 4 deletions.
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.5] - 2021-05-27
### Added
- New libsodium API: crypto_generichash

## [0.1.4] - 2021-05-21
### Added
- New libsodium API: crypto_box_seal
Expand Down
2 changes: 1 addition & 1 deletion packages/sodium/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ API based on libsodium version: *1.0.18*
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_generichash | 🚧 | 🚧 | https://libsodium.gitbook.io/doc/hashing/generic_hashing
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
crypto_kdf | 🚧 | 🚧 | https://libsodium.gitbook.io/doc/key_derivation
Expand Down
1 change: 1 addition & 0 deletions packages/sodium/lib/sodium.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export 'src/api/auth.dart' hide AuthValidations;
export 'src/api/box.dart' hide BoxValidations;
export 'src/api/crypto.dart';
export 'src/api/detached_cipher_result.dart';
export 'src/api/generic_hash.dart' hide GenericHashValidations;
export 'src/api/key_pair.dart';
export 'src/api/pwhash.dart' hide PwHashValidations;
export 'src/api/randombytes.dart';
Expand Down
6 changes: 6 additions & 0 deletions packages/sodium/lib/src/api/crypto.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'auth.dart';
import 'box.dart';
import 'generic_hash.dart';
import 'pwhash.dart';
import 'secret_box.dart';
import 'secret_stream.dart';
Expand Down Expand Up @@ -34,6 +35,11 @@ abstract class Crypto {
/// This provides all APIs that start with `crypto_sign`.
Sign get sign;

/// An instance of [GenericHash].
///
/// This provides all APIs that start with `crypto_generichash`.
GenericHash get genericHash;

/// An instance of [Pwhash].
///
/// This provides all APIs that start with `crypto_pwhash`.
Expand Down
152 changes: 152 additions & 0 deletions packages/sodium/lib/src/api/generic_hash.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import 'dart:async';
import 'dart:typed_data';

import 'package:meta/meta.dart';

import 'helpers/validations.dart';
import 'secure_key.dart';

/// A typed [StreamConsumer], which is used to generate a hash from a stream of
/// data.
///
/// See [GenericHash.createConsumer] for more details.
abstract class GenericHashConsumer implements StreamConsumer<Uint8List> {
const GenericHashConsumer._(); // coverage:ignore-line

/// A future that resolves to the hash of the data.
///
/// This is the same future as returned by [close]. It will be resolved as
/// soon as the consumer is closed and will either produce the actual
/// hash of the consumed data, or an error if something went wrong.
Future<Uint8List> get hash;

/// Closes the consumer and calculates the hash.
///
/// This internally finalizes the consumer and calculates the hash over all
/// the received data. Once done, the hash is returned, or an error thrown, if
/// it failed. The returned future is the same as the on provided via [hash].
///
/// After having been closed, no more streams can be added to the consumer.
/// See [StreamConsumer.close] for more details.
@override
Future<Uint8List> close();
}

/// A meta class that provides access to all libsodium generichash APIs.
///
/// This class provides the dart interface for the crypto operations documented
/// in https://libsodium.gitbook.io/doc/hashing/generic_hashing.
/// Please refer to that documentation for more details about these APIs.
abstract class GenericHash {
const GenericHash._(); // coverage:ignore-line

/// Provides crypto_generichash_BYTES.
///
/// See https://libsodium.gitbook.io/doc/hashing/generic_hashing#constants
int get bytes;

/// Provides crypto_generichash_BYTES_MIN.
///
/// See https://libsodium.gitbook.io/doc/hashing/generic_hashing#constants
int get bytesMin;

/// Provides crypto_generichash_BYTES_MAX.
///
/// See https://libsodium.gitbook.io/doc/hashing/generic_hashing#constants
int get bytesMax;

/// Provides crypto_generichash_KEYBYTES.
///
/// See https://libsodium.gitbook.io/doc/hashing/generic_hashing#constants
int get keyBytes;

/// Provides crypto_generichash_KEYBYTES_MIN.
///
/// See https://libsodium.gitbook.io/doc/hashing/generic_hashing#constants
int get keyBytesMin;

/// Provides crypto_generichash_KEYBYTES_MAX.
///
/// See https://libsodium.gitbook.io/doc/hashing/generic_hashing#constants
int get keyBytesMax;

/// Provides crypto_generichash_keygen.
///
/// See https://libsodium.gitbook.io/doc/hashing/generic_hashing#usage
SecureKey keygen();

/// Provides crypto_generichash.
///
/// See https://libsodium.gitbook.io/doc/hashing/generic_hashing#usage
Uint8List call({
required Uint8List message,
int? outLen,
SecureKey? key,
});

/// Creates a [StreamConsumer] for generating a hash from a stream.
///
/// The returned [GenericHashConsumer] is basically a typed [StreamConsumer],
/// that wraps the generichash streaming APIs. Creating the consumer will call
/// crypto_generichash_init, adding messages to it via
/// [GenericHashConsumer.addStream] will call crypto_generichash_update for
/// every event in the stream. After you are done adding messages, you can
/// [GenericHashConsumer.close] it, which will call crypto_generichash_final
/// internally and return the hash of the data.
///
/// Optionally, you can pass [outLen] to modify the length of the generated
/// hash and [key] if you want to use the hash as MAC.
///
/// For simpler usage, if you only have a single input [Stream] and simply
/// want to get the hash from it, you ca use [stream] instead.
///
/// See https://libsodium.gitbook.io/doc/hashing/generic_hashing#usage
GenericHashConsumer createConsumer({
int? outLen,
SecureKey? key,
});

/// Get the hash from an aynchronous stream of data.
///
/// This is a shortcut for [createConsumer], which simply calls [Stream.pipe]
/// on the [messages] stream and pipes it into the consumer. The returned
/// result is the hash over all the data in the [messages] stream, optionally
/// with a modified [outLen] or a [key].
///
/// See https://libsodium.gitbook.io/doc/hashing/generic_hashing#usage
Future<Uint8List> stream({
required Stream<Uint8List> messages,
int? outLen,
SecureKey? key,
});
}

@internal
mixin GenericHashValidations implements GenericHash {
void validateOutLen(int outLen) => Validations.checkInRange(
outLen,
bytesMin,
bytesMax,
'outLen',
);

void validateKey(SecureKey key) => Validations.checkInRange(
key.length,
keyBytesMin,
keyBytesMax,
'key',
);

@override
Future<Uint8List> stream({
required Stream<Uint8List> messages,
int? outLen,
SecureKey? key,
}) =>
messages
.pipe(createConsumer(
outLen: outLen,
key: key,
))
.then((dynamic value) => value as Uint8List);
}
5 changes: 5 additions & 0 deletions packages/sodium/lib/src/ffi/api/crypto_ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import 'package:meta/meta.dart';
import '../../api/auth.dart';
import '../../api/box.dart';
import '../../api/crypto.dart';
import '../../api/generic_hash.dart';
import '../../api/pwhash.dart';
import '../../api/secret_box.dart';
import '../../api/secret_stream.dart';
import '../../api/sign.dart';
import '../bindings/libsodium.ffi.dart';
import 'auth_ffi.dart';
import 'box_ffi.dart';
import 'generic_hash_ffi.dart';
import 'pwhash_ffi.dart';
import 'secret_box_ffi.dart';
import 'secret_stream_ffi.dart';
Expand All @@ -36,6 +38,9 @@ class CryptoFFI implements Crypto {
@override
late final Sign sign = SignFFI(sodium);

@override
late final GenericHash genericHash = GenericHashFFI(sodium);

@override
late final Pwhash pwhash = PwhashFFI(sodium);
}
109 changes: 109 additions & 0 deletions packages/sodium/lib/src/ffi/api/generic_hash_ffi.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import 'dart:ffi';
import 'dart:typed_data';

import '../../api/generic_hash.dart';
import '../../api/secure_key.dart';
import '../../api/sodium_exception.dart';
import '../bindings/libsodium.ffi.dart';
import '../bindings/memory_protection.dart';
import '../bindings/secure_key_native.dart';
import '../bindings/sodium_pointer.dart';
import 'helpers/generic_hash/generic_hash_consumer_ffi.dart';
import 'helpers/keygen_mixin.dart';

class GenericHashFFI
with GenericHashValidations, KeygenMixin
implements GenericHash {
final LibSodiumFFI sodium;

GenericHashFFI(this.sodium);

@override
int get bytes => sodium.crypto_generichash_bytes();

@override
int get bytesMin => sodium.crypto_generichash_bytes_min();

@override
int get bytesMax => sodium.crypto_generichash_bytes_max();

@override
int get keyBytes => sodium.crypto_generichash_keybytes();

@override
int get keyBytesMin => sodium.crypto_generichash_keybytes_min();

@override
int get keyBytesMax => sodium.crypto_generichash_keybytes_max();

@override
SecureKey keygen() => keygenImpl(
sodium: sodium,
keyBytes: keyBytes,
implementation: sodium.crypto_generichash_keygen,
);

@override
Uint8List call({
required Uint8List message,
int? outLen,
SecureKey? key,
}) {
if (outLen != null) {
validateOutLen(outLen);
}
if (key != null) {
validateKey(key);
}

SodiumPointer<Uint8>? outPtr;
SodiumPointer<Uint8>? inPtr;
try {
outPtr = SodiumPointer.alloc(
sodium,
count: outLen ?? bytes,
);
inPtr = message.toSodiumPointer(
sodium,
memoryProtection: MemoryProtection.readOnly,
);

final result = key.runMaybeUnlockedNative(
sodium,
(keyPtr) => sodium.crypto_generichash(
outPtr!.ptr,
outPtr.count,
inPtr!.ptr,
inPtr.count,
keyPtr?.ptr ?? nullptr,
keyPtr?.count ?? 0,
),
);
SodiumException.checkSucceededInt(result);

return outPtr.copyAsList();
} finally {
outPtr?.dispose();
inPtr?.dispose();
}
}

@override
GenericHashConsumer createConsumer({
int? outLen,
SecureKey? key,
}) {
if (outLen != null) {
validateOutLen(outLen);
}
if (key != null) {
validateKey(key);
}

return GenericHashConsumerFFI(
sodium: sodium,
outLen: outLen ?? bytes,
key: key,
);
}
}
Loading

0 comments on commit 39aad85

Please sign in to comment.