add(conduit): refactoring conduit

This commit is contained in:
Patrick Stewart 2024-09-03 13:16:55 -07:00
parent 1e1d0ad6a3
commit fdbbe2eab5
5 changed files with 233 additions and 1 deletions

View file

@ -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.

View 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';

View 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() {}
}

View 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));
}