diff --git a/packages/hashing/README.md b/packages/hashing/README.md
index 757f4c9..8b55e73 100644
--- a/packages/hashing/README.md
+++ b/packages/hashing/README.md
@@ -1 +1,39 @@
-
\ No newline at end of file
+
+
+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.
diff --git a/packages/hashing/lib/hashing.dart b/packages/hashing/lib/hashing.dart
new file mode 100644
index 0000000..f961244
--- /dev/null
+++ b/packages/hashing/lib/hashing.dart
@@ -0,0 +1,20 @@
+/*
+ * This file is part of the Protevus Platform.
+ *
+ * (C) Protevus
+ *
+ * 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';
diff --git a/packages/hashing/lib/src/.gitkeep b/packages/hashing/lib/src/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/packages/hashing/lib/src/pbkdf2.dart b/packages/hashing/lib/src/pbkdf2.dart
new file mode 100644
index 0000000..0aa94f0
--- /dev/null
+++ b/packages/hashing/lib/src/pbkdf2.dart
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the Protevus Platform.
+ *
+ * (C) Protevus
+ *
+ * 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 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 {
+ _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 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() {}
+}
diff --git a/packages/hashing/lib/src/salt.dart b/packages/hashing/lib/src/salt.dart
new file mode 100644
index 0000000..2e5ca8d
--- /dev/null
+++ b/packages/hashing/lib/src/salt.dart
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the Protevus Platform.
+ *
+ * (C) Protevus
+ *
+ * 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 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));
+}