diff --git a/util/RustSdk/RustSdkException.cs b/util/RustSdk/RustSdkException.cs
new file mode 100644
index 0000000000..52cbc3635f
--- /dev/null
+++ b/util/RustSdk/RustSdkException.cs
@@ -0,0 +1,19 @@
+namespace Bit.RustSDK;
+
+///
+/// Exception thrown when the Rust SDK operations fail
+///
+public class RustSdkException : Exception
+{
+ public RustSdkException() : base("An error occurred in the Rust SDK operation")
+ {
+ }
+
+ public RustSdkException(string message) : base(message)
+ {
+ }
+
+ public RustSdkException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+}
diff --git a/util/RustSdk/RustSdkService.cs b/util/RustSdk/RustSdkService.cs
new file mode 100644
index 0000000000..dcb139dd0f
--- /dev/null
+++ b/util/RustSdk/RustSdkService.cs
@@ -0,0 +1,83 @@
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Bit.RustSDK;
+
+///
+/// Service implementation that provides a C# friendly interface to the Rust SDK
+///
+public class RustSdkService
+{
+ ///
+ /// Adds two integers using the native implementation
+ ///
+ /// First integer
+ /// Second integer
+ /// The sum of x and y
+ public int Add(int x, int y)
+ {
+ try
+ {
+ return NativeMethods.my_add(x, y);
+ }
+ catch (Exception ex)
+ {
+ throw new RustSdkException($"Failed to perform addition operation: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// Hashes a password using the native implementation
+ ///
+ /// User email
+ /// User password
+ /// The hashed password as a string
+ /// Thrown when email or password is null
+ /// Thrown when email or password is empty
+ /// Thrown when the native operation fails
+ public unsafe string HashPassword(string email, string password)
+ {
+ // Convert strings to null-terminated byte arrays
+ var emailBytes = Encoding.UTF8.GetBytes(email + '\0');
+ var passwordBytes = Encoding.UTF8.GetBytes(password + '\0');
+
+ try
+ {
+ fixed (byte* emailPtr = emailBytes)
+ fixed (byte* passwordPtr = passwordBytes)
+ {
+ var resultPtr = NativeMethods.hash_password(emailPtr, passwordPtr);
+
+ var result = TakeAndDestroyRustString(resultPtr);
+
+ return result;
+ }
+ }
+ catch (RustSdkException)
+ {
+ throw; // Re-throw our custom exceptions
+ }
+ catch (Exception ex)
+ {
+ throw new RustSdkException($"Failed to hash password: {ex.Message}", ex);
+ }
+ }
+
+ private static unsafe string TakeAndDestroyRustString(byte* ptr)
+ {
+ if (ptr == null)
+ {
+ throw new RustSdkException("Pointer is null");
+ }
+
+ var result = Marshal.PtrToStringUTF8((IntPtr)ptr);
+ NativeMethods.free_c_string(ptr);
+
+ if (result == null)
+ {
+ throw new RustSdkException("Failed to convert native result to string");
+ }
+
+ return result;
+ }
+}
diff --git a/util/RustSdk/RustSdkServiceFactory.cs b/util/RustSdk/RustSdkServiceFactory.cs
new file mode 100644
index 0000000000..5a1e44eb22
--- /dev/null
+++ b/util/RustSdk/RustSdkServiceFactory.cs
@@ -0,0 +1,30 @@
+namespace Bit.RustSDK;
+
+///
+/// Factory for creating Rust SDK service instances
+///
+public static class RustSdkServiceFactory
+{
+ ///
+ /// Creates a new instance of the Rust SDK service
+ ///
+ /// A new IRustSdkService instance
+ public static RustSdkService Create()
+ {
+ return new RustSdkService();
+ }
+
+ ///
+ /// Creates a singleton instance of the Rust SDK service (thread-safe)
+ ///
+ /// A singleton IRustSdkService instance
+ public static RustSdkService CreateSingleton()
+ {
+ return SingletonHolder.Instance;
+ }
+
+ private static class SingletonHolder
+ {
+ internal static readonly RustSdkService Instance = new RustSdkService();
+ }
+}
diff --git a/util/RustSdk/rust/src/lib.rs b/util/RustSdk/rust/src/lib.rs
index d6b2fcdfa4..1678944508 100644
--- a/util/RustSdk/rust/src/lib.rs
+++ b/util/RustSdk/rust/src/lib.rs
@@ -1,4 +1,49 @@
+use std::{
+ ffi::{c_char, CStr, CString},
+ num::NonZeroU32,
+};
+
+use bitwarden_crypto::{HashPurpose, Kdf, MasterKey};
+
#[no_mangle]
pub extern "C" fn my_add(x: i32, y: i32) -> i32 {
x + y
}
+
+/// # Safety
+///
+/// The `email` and `password` pointers must be valid null-terminated C strings.
+/// Both pointers must be non-null and point to valid memory for the duration of the function call.
+#[no_mangle]
+pub unsafe extern "C" fn hash_password(
+ email: *const c_char,
+ password: *const c_char,
+) -> *const c_char {
+ let kdf = Kdf::PBKDF2 {
+ iterations: NonZeroU32::new(600_000).unwrap(),
+ };
+
+ let email = CStr::from_ptr(email).to_str().unwrap();
+ let password = CStr::from_ptr(password).to_str().unwrap();
+
+ let master_key = MasterKey::derive(password, email, &kdf).unwrap();
+
+ let res = master_key
+ .derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)
+ .unwrap();
+
+ let res = CString::new(res).unwrap();
+
+ res.into_raw()
+}
+
+/// # Safety
+///
+/// The `str` pointer must be a valid pointer previously returned by `CString::into_raw`
+/// and must not have already been freed. After calling this function, the pointer must not be used again.
+#[no_mangle]
+pub unsafe extern "C" fn free_c_string(str: *mut c_char) {
+ unsafe {
+ drop(CString::from_raw(str));
+ }
+}
diff --git a/util/Seeder/Factories/UserSeeder.cs b/util/Seeder/Factories/UserSeeder.cs
index 9c9d86e8d7..481f214637 100644
--- a/util/Seeder/Factories/UserSeeder.cs
+++ b/util/Seeder/Factories/UserSeeder.cs
@@ -8,12 +8,16 @@ public class UserSeeder
{
public static User CreateUser(string email)
{
+ var nativeService = RustSdkServiceFactory.CreateSingleton();
Console.WriteLine(NativeMethods.my_add(2, 3));
+
+ var password = nativeService.HashPassword(email, "asdfasdfasdf");
+
return new User
{
Id = Guid.NewGuid(),
Email = email,
- MasterPassword = "AQAAAAIAAYagAAAAEBATmF66OHMpHuHKc1CsGZQ1ltHUHyhYK+7e4re3bVFi16SOpLpDfzdFswnvFQs2Rg==",
+ MasterPassword = password,
SecurityStamp = "4830e359-e150-4eae-be2a-996c81c5e609",
Key = "2.z/eLKFhd62qy9RzXu3UHgA==|fF6yNupiCIguFKSDTB3DoqcGR0Xu4j+9VlnMyT5F3PaWIcGhzQKIzxdB95nhslaCQv3c63M7LBnvzVo1J9SUN85RMbP/57bP1HvhhU1nvL8=|IQPtf8v7k83MFZEhazSYXSdu98BBU5rqtvC4keVWyHM=",
PublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Ww2chogqCpaAR7Uw448am4b7vDFXiM5kXjFlGfXBlrAdAqTTggEvTDlMNYqPlCo+mBM6iFmTTUY9rpZBvFskMnKvsvpJ47/fehAH2o2e3Ulv/5NFevaVCMCmpkBDtbMbO1A4a3btdRtCP8DsKWMefHauEpaoLxNTLWnOIZVfCMjsSgx2EvULHAZPTtbFwm4+UVKniM4ds4jvOsD85h4jn2aLs/jWJXFfxN8iVSqEqpC2TBvsPdyHb49xQoWWfF0Z6BiNqeNGKEU9Uos1pjL+kzhEzzSpH31PZT/ufJ/oo4+93wrUt57hb6f0jxiXhwd5yQ+9F6wVwpbfkq0IwhjOwIDAQAB",