add(conduit): refactoring conduit
This commit is contained in:
parent
1e1d0ad6a3
commit
fdbbe2eab5
5 changed files with 233 additions and 1 deletions
|
@ -1 +1,39 @@
|
|||
<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>
|
||||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/developing-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
||||
|
|
20
packages/hashing/lib/hashing.dart
Normal file
20
packages/hashing/lib/hashing.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/// This library provides hashing functionality for the Protevus Platform.
|
||||
///
|
||||
/// It exports two main components:
|
||||
/// - PBKDF2 (Password-Based Key Derivation Function 2) implementation
|
||||
/// - Salt generation utilities
|
||||
///
|
||||
/// These components are essential for secure password hashing and storage.
|
||||
library hashing;
|
||||
|
||||
export 'package:protevus_hashing/src/pbkdf2.dart';
|
||||
export 'package:protevus_hashing/src/salt.dart';
|
140
packages/hashing/lib/src/pbkdf2.dart
Normal file
140
packages/hashing/lib/src/pbkdf2.dart
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
/// Instances of this type derive a key from a password, salt, and hash function.
|
||||
///
|
||||
/// https://en.wikipedia.org/wiki/PBKDF2
|
||||
class PBKDF2 {
|
||||
/// Creates instance capable of generating a key.
|
||||
///
|
||||
/// [hashAlgorithm] defaults to [sha256].
|
||||
PBKDF2({Hash? hashAlgorithm}) {
|
||||
this.hashAlgorithm = hashAlgorithm ?? sha256;
|
||||
}
|
||||
|
||||
Hash get hashAlgorithm => _hashAlgorithm;
|
||||
set hashAlgorithm(Hash algorithm) {
|
||||
_hashAlgorithm = algorithm;
|
||||
_blockSize = _hashAlgorithm.convert([1, 2, 3]).bytes.length;
|
||||
}
|
||||
|
||||
late Hash _hashAlgorithm;
|
||||
late int _blockSize;
|
||||
|
||||
/// Hashes a [password] with a given [salt].
|
||||
///
|
||||
/// The length of this return value will be [keyLength].
|
||||
///
|
||||
/// See [generateAsBase64String] for generating a random salt.
|
||||
///
|
||||
/// See also [generateBase64Key], which base64 encodes the key returned from this method for storage.
|
||||
List<int> generateKey(
|
||||
String password,
|
||||
String salt,
|
||||
int rounds,
|
||||
int keyLength,
|
||||
) {
|
||||
if (keyLength > (pow(2, 32) - 1) * _blockSize) {
|
||||
throw PBKDF2Exception("Derived key too long");
|
||||
}
|
||||
|
||||
final numberOfBlocks = (keyLength / _blockSize).ceil();
|
||||
final hmac = Hmac(hashAlgorithm, utf8.encode(password));
|
||||
final key = ByteData(keyLength);
|
||||
var offset = 0;
|
||||
|
||||
final saltBytes = utf8.encode(salt);
|
||||
final saltLength = saltBytes.length;
|
||||
final inputBuffer = ByteData(saltBytes.length + 4)
|
||||
..buffer.asUint8List().setRange(0, saltBytes.length, saltBytes);
|
||||
|
||||
for (var blockNumber = 1; blockNumber <= numberOfBlocks; blockNumber++) {
|
||||
inputBuffer.setUint8(saltLength, blockNumber >> 24);
|
||||
inputBuffer.setUint8(saltLength + 1, blockNumber >> 16);
|
||||
inputBuffer.setUint8(saltLength + 2, blockNumber >> 8);
|
||||
inputBuffer.setUint8(saltLength + 3, blockNumber);
|
||||
|
||||
final block = _XORDigestSink.generate(inputBuffer, hmac, rounds);
|
||||
var blockLength = _blockSize;
|
||||
if (offset + blockLength > keyLength) {
|
||||
blockLength = keyLength - offset;
|
||||
}
|
||||
key.buffer.asUint8List().setRange(offset, offset + blockLength, block);
|
||||
|
||||
offset += blockLength;
|
||||
}
|
||||
|
||||
return key.buffer.asUint8List();
|
||||
}
|
||||
|
||||
/// Hashed a [password] with a given [salt] and base64 encodes the result.
|
||||
///
|
||||
/// This method invokes [generateKey] and base64 encodes the result.
|
||||
String generateBase64Key(
|
||||
String password,
|
||||
String salt,
|
||||
int rounds,
|
||||
int keyLength,
|
||||
) {
|
||||
const converter = Base64Encoder();
|
||||
|
||||
return converter.convert(generateKey(password, salt, rounds, keyLength));
|
||||
}
|
||||
}
|
||||
|
||||
/// Thrown when [PBKDF2] throws an exception.
|
||||
class PBKDF2Exception implements Exception {
|
||||
PBKDF2Exception(this.message);
|
||||
String message;
|
||||
|
||||
@override
|
||||
String toString() => "PBKDF2Exception: $message";
|
||||
}
|
||||
|
||||
class _XORDigestSink implements Sink<Digest> {
|
||||
_XORDigestSink(ByteData inputBuffer, Hmac hmac) {
|
||||
lastDigest = hmac.convert(inputBuffer.buffer.asUint8List()).bytes;
|
||||
bytes = ByteData(lastDigest.length)
|
||||
..buffer.asUint8List().setRange(0, lastDigest.length, lastDigest);
|
||||
}
|
||||
|
||||
static Uint8List generate(ByteData inputBuffer, Hmac hmac, int rounds) {
|
||||
final hashSink = _XORDigestSink(inputBuffer, hmac);
|
||||
|
||||
// If rounds == 1, we have already run the first hash in the constructor
|
||||
// so this loop won't run.
|
||||
for (var round = 1; round < rounds; round++) {
|
||||
final hmacSink = hmac.startChunkedConversion(hashSink);
|
||||
hmacSink.add(hashSink.lastDigest);
|
||||
hmacSink.close();
|
||||
}
|
||||
|
||||
return hashSink.bytes.buffer.asUint8List();
|
||||
}
|
||||
|
||||
late ByteData bytes;
|
||||
late List<int> lastDigest;
|
||||
|
||||
@override
|
||||
void add(Digest digest) {
|
||||
lastDigest = digest.bytes;
|
||||
for (var i = 0; i < digest.bytes.length; i++) {
|
||||
bytes.setUint8(i, bytes.getUint8(i) ^ lastDigest[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void close() {}
|
||||
}
|
34
packages/hashing/lib/src/salt.dart
Normal file
34
packages/hashing/lib/src/salt.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* This file is part of the Protevus Platform.
|
||||
*
|
||||
* (C) Protevus <developers@protevus.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
/// Generates a random salt of [length] bytes from a cryptographically secure random number generator.
|
||||
///
|
||||
/// Each element of this list is a byte.
|
||||
List<int> generate(int length) {
|
||||
final buffer = Uint8List(length);
|
||||
final rng = Random.secure();
|
||||
for (var i = 0; i < length; i++) {
|
||||
buffer[i] = rng.nextInt(256);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// Generates a random salt of [length] bytes from a cryptographically secure random number generator and encodes it to Base64.
|
||||
///
|
||||
/// [length] is the number of bytes generated, not the [length] of the base64 encoded string returned. Decoding
|
||||
/// the base64 encoded string will yield [length] number of bytes.
|
||||
String generateAsBase64String(int length) {
|
||||
const encoder = Base64Encoder();
|
||||
return encoder.convert(generate(length));
|
||||
}
|
Loading…
Reference in a new issue