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