mirror of
https://github.com/bitwarden/server
synced 2025-12-06 00:03:34 +00:00
[PM-22263] Integate Rust SDK to Seeder (#6150)
Adds a Rust SDK for performing seed related cryptograhic operations. It depends on internal portions of our Rust SDK. Primarily parts of the bitwarden-crypto crate.
This commit is contained in:
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
@@ -96,6 +96,9 @@ src/Admin/Views/Tools @bitwarden/team-billing-dev
|
||||
# The PushType enum is expected to be editted by anyone without need for Platform review
|
||||
src/Core/Platform/Push/PushType.cs
|
||||
|
||||
# SDK
|
||||
util/RustSdk @bitwarden/team-sdk-sme
|
||||
|
||||
# Multiple owners - DO NOT REMOVE (BRE)
|
||||
**/packages.lock.json
|
||||
Directory.Build.props
|
||||
|
||||
6
.github/renovate.json5
vendored
6
.github/renovate.json5
vendored
@@ -2,6 +2,7 @@
|
||||
$schema: "https://docs.renovatebot.com/renovate-schema.json",
|
||||
extends: ["github>bitwarden/renovate-config"], // Extends our default configuration for pinned dependencies
|
||||
enabledManagers: [
|
||||
"cargo",
|
||||
"dockerfile",
|
||||
"docker-compose",
|
||||
"github-actions",
|
||||
@@ -9,6 +10,11 @@
|
||||
"nuget",
|
||||
],
|
||||
packageRules: [
|
||||
{
|
||||
groupName: "cargo minor",
|
||||
matchManagers: ["cargo"],
|
||||
matchUpdateTypes: ["minor"],
|
||||
},
|
||||
{
|
||||
groupName: "dockerfile minor",
|
||||
matchManagers: ["dockerfile"],
|
||||
|
||||
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
@@ -32,6 +32,14 @@ jobs:
|
||||
- name: Set up .NET
|
||||
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
|
||||
|
||||
- name: Install rust
|
||||
uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # stable
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Cache cargo registry
|
||||
uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
dotnet --info
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -216,6 +216,8 @@ bitwarden_license/src/Sso/wwwroot/assets
|
||||
.mono
|
||||
src/Core/MailTemplates/Mjml/out
|
||||
src/Core/MailTemplates/Mjml/out-hbs
|
||||
NativeMethods.g.cs
|
||||
util/RustSdk/rust/target
|
||||
|
||||
src/Admin/Admin.zip
|
||||
src/Api/Api.zip
|
||||
|
||||
@@ -133,6 +133,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Seeder", "util\Seeder\Seede
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbSeederUtility", "util\DbSeederUtility\DbSeederUtility.csproj", "{17A89266-260A-4A03-81AE-C0468C6EE06E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RustSdk", "util\RustSdk\RustSdk.csproj", "{D1513D90-E4F5-44A9-9121-5E46E3E4A3F7}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedWeb.Test", "test\SharedWeb.Test\SharedWeb.Test.csproj", "{AD59537D-5259-4B7A-948F-0CF58E80B359}"
|
||||
EndProject
|
||||
Global
|
||||
@@ -339,6 +340,10 @@ Global
|
||||
{17A89266-260A-4A03-81AE-C0468C6EE06E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{17A89266-260A-4A03-81AE-C0468C6EE06E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{17A89266-260A-4A03-81AE-C0468C6EE06E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D1513D90-E4F5-44A9-9121-5E46E3E4A3F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D1513D90-E4F5-44A9-9121-5E46E3E4A3F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D1513D90-E4F5-44A9-9121-5E46E3E4A3F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D1513D90-E4F5-44A9-9121-5E46E3E4A3F7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AD59537D-5259-4B7A-948F-0CF58E80B359}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AD59537D-5259-4B7A-948F-0CF58E80B359}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AD59537D-5259-4B7A-948F-0CF58E80B359}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -397,6 +402,7 @@ Global
|
||||
{3631BA42-6731-4118-A917-DAA43C5032B9} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||
{9A612EBA-1C0E-42B8-982B-62F0EE81000A} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
||||
{17A89266-260A-4A03-81AE-C0468C6EE06E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
||||
{D1513D90-E4F5-44A9-9121-5E46E3E4A3F7} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
||||
{AD59537D-5259-4B7A-948F-0CF58E80B359} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
|
||||
58
util/RustSdk/NativeMethods.cs
Normal file
58
util/RustSdk/NativeMethods.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Bit.RustSDK;
|
||||
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/dotnet/standard/native-interop/cross-platform
|
||||
// Library path will search
|
||||
// win => __DllName, __DllName.dll
|
||||
// linux, osx => __DllName.so, __DllName.dylib
|
||||
|
||||
static NativeMethods()
|
||||
{
|
||||
NativeLibrary.SetDllImportResolver(typeof(NativeMethods).Assembly, DllImportResolver);
|
||||
}
|
||||
|
||||
static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
||||
{
|
||||
if (libraryName != __DllName) return IntPtr.Zero;
|
||||
|
||||
var path = "runtimes/";
|
||||
var extension = "";
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
path += "win-";
|
||||
extension = ".dll";
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
path += "osx-";
|
||||
extension = ".dylib";
|
||||
}
|
||||
else
|
||||
{
|
||||
path += "linux-";
|
||||
extension = ".so";
|
||||
}
|
||||
|
||||
if (RuntimeInformation.ProcessArchitecture == Architecture.X86)
|
||||
{
|
||||
path += "x86";
|
||||
}
|
||||
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
{
|
||||
path += "x64";
|
||||
}
|
||||
else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
path += "arm64";
|
||||
}
|
||||
|
||||
path += "/native/" + __DllName + extension;
|
||||
|
||||
return NativeLibrary.Load(Path.Combine(AppContext.BaseDirectory, path), assembly, searchPath);
|
||||
}
|
||||
}
|
||||
41
util/RustSdk/RustSdk.csproj
Normal file
41
util/RustSdk/RustSdk.csproj
Normal file
@@ -0,0 +1,41 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Bit.RustSDK</RootNamespace>
|
||||
<UserSecretsId>Bit.RustSDK</UserSecretsId>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="rust/target/release/libsdk*.dylib">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Link>runtimes/osx-arm64/native/libsdk.dylib</Link>
|
||||
</Content>
|
||||
<Content Include="./rust/target/release/libsdk*.so">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Link>runtimes/linux-x64/native/libsdk.dylib</Link>
|
||||
</Content>
|
||||
<Content Include="./rust/target/release/libsdk*.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Link>runtimes/windows-x64/native/libsdk.dylib</Link>
|
||||
</Content>
|
||||
|
||||
<!-- This is a work around because this file is compiled by the PreBuild event below, and won't
|
||||
always be detected -->
|
||||
<Compile Remove="NativeMethods.g.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
<Exec Command="cargo build --release" WorkingDirectory="$(ProjectDir)/rust" />
|
||||
<ItemGroup>
|
||||
<Compile Include="NativeMethods.g.cs" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
19
util/RustSdk/RustSdkException.cs
Normal file
19
util/RustSdk/RustSdkException.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace Bit.RustSDK;
|
||||
|
||||
/// <summary>
|
||||
/// Exception thrown when the Rust SDK operations fail
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
104
util/RustSdk/RustSdkService.cs
Normal file
104
util/RustSdk/RustSdkService.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Bit.RustSDK;
|
||||
|
||||
public class UserKeys
|
||||
{
|
||||
public required string MasterPasswordHash { get; set; }
|
||||
/// <summary>
|
||||
/// Base64 encoded UserKey
|
||||
/// </summary>
|
||||
public required string Key { get; set; }
|
||||
public required string EncryptedUserKey { get; set; }
|
||||
public required string PublicKey { get; set; }
|
||||
public required string PrivateKey { get; set; }
|
||||
}
|
||||
|
||||
public class OrganizationKeys
|
||||
{
|
||||
/// <summary>
|
||||
/// Base64 encoded SymmetricCryptoKey
|
||||
/// </summary>
|
||||
public required string Key { get; set; }
|
||||
|
||||
public required string PublicKey { get; set; }
|
||||
public required string PrivateKey { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service implementation that provides a C# friendly interface to the Rust SDK
|
||||
/// </summary>
|
||||
public class RustSdkService
|
||||
{
|
||||
private static readonly JsonSerializerOptions CaseInsensitiveOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
public unsafe UserKeys GenerateUserKeys(string email, string password)
|
||||
{
|
||||
var emailBytes = StringToRustString(email);
|
||||
var passwordBytes = StringToRustString(password);
|
||||
|
||||
fixed (byte* emailPtr = emailBytes)
|
||||
fixed (byte* passwordPtr = passwordBytes)
|
||||
{
|
||||
var resultPtr = NativeMethods.generate_user_keys(emailPtr, passwordPtr);
|
||||
|
||||
var result = TakeAndDestroyRustString(resultPtr);
|
||||
|
||||
return JsonSerializer.Deserialize<UserKeys>(result, CaseInsensitiveOptions)!;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe OrganizationKeys GenerateOrganizationKeys()
|
||||
{
|
||||
var resultPtr = NativeMethods.generate_organization_keys();
|
||||
|
||||
var result = TakeAndDestroyRustString(resultPtr);
|
||||
|
||||
return JsonSerializer.Deserialize<OrganizationKeys>(result, CaseInsensitiveOptions)!;
|
||||
}
|
||||
|
||||
public unsafe string GenerateUserOrganizationKey(string userKey, string orgKey)
|
||||
{
|
||||
var userKeyBytes = StringToRustString(userKey);
|
||||
var orgKeyBytes = StringToRustString(orgKey);
|
||||
|
||||
fixed (byte* userKeyPtr = userKeyBytes)
|
||||
fixed (byte* orgKeyPtr = orgKeyBytes)
|
||||
{
|
||||
var resultPtr = NativeMethods.generate_user_organization_key(userKeyPtr, orgKeyPtr);
|
||||
|
||||
var result = TakeAndDestroyRustString(resultPtr);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static byte[] StringToRustString(string str)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(str + '\0');
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
21
util/RustSdk/RustSdkServiceFactory.cs
Normal file
21
util/RustSdk/RustSdkServiceFactory.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace Bit.RustSDK;
|
||||
|
||||
/// <summary>
|
||||
/// Factory for creating Rust SDK service instances
|
||||
/// </summary>
|
||||
public static class RustSdkServiceFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a singleton instance of the Rust SDK service (thread-safe)
|
||||
/// </summary>
|
||||
/// <returns>A singleton IRustSdkService instance</returns>
|
||||
public static RustSdkService CreateSingleton()
|
||||
{
|
||||
return SingletonHolder.Instance;
|
||||
}
|
||||
|
||||
private static class SingletonHolder
|
||||
{
|
||||
internal static readonly RustSdkService Instance = new();
|
||||
}
|
||||
}
|
||||
3147
util/RustSdk/rust/Cargo.lock
generated
Normal file
3147
util/RustSdk/rust/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
33
util/RustSdk/rust/Cargo.toml
Normal file
33
util/RustSdk/rust/Cargo.toml
Normal file
@@ -0,0 +1,33 @@
|
||||
[package]
|
||||
name = "sdk"
|
||||
publish = false
|
||||
|
||||
version = "0.1.0"
|
||||
authors = ["Bitwarden Inc"]
|
||||
edition = "2021"
|
||||
homepage = "https://bitwarden.com"
|
||||
repository = "https://github.com/bitwarden/server"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.22.1"
|
||||
bitwarden-core = { git = "https://github.com/bitwarden/sdk-internal.git", rev = "1461b3ba6bb6e2d0114770eb4572a1398b4789ef" }
|
||||
bitwarden-crypto = { git = "https://github.com/bitwarden/sdk-internal.git", rev = "1461b3ba6bb6e2d0114770eb4572a1398b4789ef" }
|
||||
serde = "=1.0.219"
|
||||
serde_json = "=1.0.141"
|
||||
|
||||
[build-dependencies]
|
||||
csbindgen = "=1.9.3"
|
||||
|
||||
# Compile all dependencies with some optimizations when building this crate on debug
|
||||
# This slows down clean builds by about 50%, but the resulting binaries can be orders of magnitude faster
|
||||
# As clean builds won't occur very often, this won't slow down the development process
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 2
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
opt-level = 3
|
||||
9
util/RustSdk/rust/build.rs
Normal file
9
util/RustSdk/rust/build.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
fn main() {
|
||||
csbindgen::Builder::default()
|
||||
.input_extern_file("src/lib.rs")
|
||||
.csharp_dll_name("libsdk")
|
||||
.csharp_namespace("Bit.RustSDK")
|
||||
.csharp_class_accessibility("public")
|
||||
.generate_csharp_file("../NativeMethods.g.cs")
|
||||
.unwrap();
|
||||
}
|
||||
152
util/RustSdk/rust/src/lib.rs
Normal file
152
util/RustSdk/rust/src/lib.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
use std::{
|
||||
ffi::{c_char, CStr, CString},
|
||||
num::NonZeroU32,
|
||||
};
|
||||
|
||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||
|
||||
use bitwarden_crypto::{
|
||||
AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyBytes, HashPurpose, Kdf,
|
||||
KeyEncryptable, MasterKey, RsaKeyPair, SpkiPublicKeyBytes, SymmetricCryptoKey,
|
||||
UnsignedSharedKey, UserKey,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn generate_user_keys(
|
||||
email: *const c_char,
|
||||
password: *const c_char,
|
||||
) -> *const c_char {
|
||||
let email = CStr::from_ptr(email).to_str().unwrap();
|
||||
let password = CStr::from_ptr(password).to_str().unwrap();
|
||||
|
||||
println!("Generating keys for {email}");
|
||||
println!("Password: {password}");
|
||||
|
||||
let kdf = Kdf::PBKDF2 {
|
||||
iterations: NonZeroU32::new(5_000).unwrap(),
|
||||
};
|
||||
|
||||
let master_key = MasterKey::derive(password, email, &kdf).unwrap();
|
||||
|
||||
let master_password_hash =
|
||||
master_key.derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization);
|
||||
|
||||
println!("Master password hash: {}", master_password_hash);
|
||||
|
||||
let (user_key, encrypted_user_key) = master_key.make_user_key().unwrap();
|
||||
|
||||
let keypair = keypair(&user_key.0);
|
||||
|
||||
let json = serde_json::json!({
|
||||
"masterPasswordHash": master_password_hash,
|
||||
"key": user_key.0.to_base64(),
|
||||
"encryptedUserKey": encrypted_user_key.to_string(),
|
||||
"publicKey": keypair.public.to_string(),
|
||||
"privateKey": keypair.private.to_string(),
|
||||
})
|
||||
.to_string();
|
||||
|
||||
let result = CString::new(json).unwrap();
|
||||
|
||||
result.into_raw()
|
||||
}
|
||||
|
||||
fn keypair(key: &SymmetricCryptoKey) -> RsaKeyPair {
|
||||
const RSA_PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXRVrCX+2hfOQS
|
||||
8HzYUS2oc/jGVTZpv+/Ryuoh9d8ihYX9dd0cYh2tl6KWdFc88lPUH11Oxqy20Rk2
|
||||
e5r/RF6T9yM0Me3NPnaKt+hlhLtfoc0h86LnhD56A9FDUfuI0dVnPcrwNv0YJIo9
|
||||
4LwxtbqBULNvXl6wJ7WAbODrCQy5ZgMVg+iH+gGpwiqsZqHt+KuoHWcN53MSPDfa
|
||||
F4/YMB99U3TziJMOOJask1TEEnakMPln11PczNDazT17DXIxYrbPfutPdh6sLs6A
|
||||
QOajdZijfEvepgnOe7cQ7aeatiOJFrjTApKPGxOVRzEMX4XS4xbyhH0QxQeB6l16
|
||||
l8C0uxIBAgMBAAECggEASaWfeVDA3cVzOPFSpvJm20OTE+R6uGOU+7vh36TX/POq
|
||||
92qBuwbd0h0oMD32FxsXywd2IxtBDUSiFM9699qufTVuM0Q3tZw6lHDTOVG08+tP
|
||||
dr8qSbMtw7PGFxN79fHLBxejjO4IrM9lapjWpxEF+11x7r+wM+0xRZQ8sNFYG46a
|
||||
PfIaty4BGbL0I2DQ2y8I57iBCAy69eht59NLMm27fRWGJIWCuBIjlpfzET1j2HLX
|
||||
UIh5bTBNzqaN039WH49HczGE3mQKVEJZc/efk3HaVd0a1Sjzyn0QY+N1jtZN3jTR
|
||||
buDWA1AknkX1LX/0tUhuS3/7C3ejHxjw4Dk1ZLo5/QKBgQDIWvqFn0+IKRSu6Ua2
|
||||
hDsufIHHUNLelbfLUMmFthxabcUn4zlvIscJO00Tq/ezopSRRvbGiqnxjv/mYxuc
|
||||
vOUBeZtlus0Q9RTACBtw9TGoNTmQbEunJ2FOSlqbQxkBBAjgGEppRPt30iGj/VjA
|
||||
hCATq2MYOa/X4dVR51BqQAFIEwKBgQDBSIfTFKC/hDk6FKZlgwvupWYJyU9Rkyfs
|
||||
tPErZFmzoKhPkQ3YORo2oeAYmVUbS9I2iIYpYpYQJHX8jMuCbCz4ONxTCuSIXYQY
|
||||
UcUq4PglCKp31xBAE6TN8SvhfME9/MvuDssnQinAHuF0GDAhF646T3LLS1not6Vs
|
||||
zv7brwSoGwKBgQC88v/8cGfi80ssQZeMnVvq1UTXIeQcQnoY5lGHJl3K8mbS3TnX
|
||||
E6c9j417Fdz+rj8KWzBzwWXQB5pSPflWcdZO886Xu/mVGmy9RWgLuVFhXwCwsVEP
|
||||
jNX5ramRb0/vY0yzenUCninBsIxFSbIfrPtLUYCc4hpxr+sr2Mg/y6jpvQKBgBez
|
||||
MRRs3xkcuXepuI2R+BCXL1/b02IJTUf1F+1eLLGd7YV0H+J3fgNc7gGWK51hOrF9
|
||||
JBZHBGeOUPlaukmPwiPdtQZpu4QNE3l37VlIpKTF30E6mb+BqR+nht3rUjarnMXg
|
||||
AoEZ18y6/KIjpSMpqC92Nnk/EBM9EYe6Cf4eA9ApAoGAeqEUg46UTlJySkBKURGp
|
||||
Is3v1kkf5I0X8DnOhwb+HPxNaiEdmO7ckm8+tPVgppLcG0+tMdLjigFQiDUQk2y3
|
||||
WjyxP5ZvXu7U96jaJRI8PFMoE06WeVYcdIzrID2HvqH+w0UQJFrLJ/0Mn4stFAEz
|
||||
XKZBokBGnjFnTnKcs7nv/O8=
|
||||
-----END PRIVATE KEY-----";
|
||||
|
||||
let private_key = AsymmetricCryptoKey::from_pem(RSA_PRIVATE_KEY).unwrap();
|
||||
let public_key = private_key.to_public_key().to_der().unwrap();
|
||||
|
||||
let p = private_key.to_der().unwrap();
|
||||
|
||||
RsaKeyPair {
|
||||
private: p.encrypt_with_key(key).unwrap(),
|
||||
public: public_key.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn generate_organization_keys() -> *const c_char {
|
||||
let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
|
||||
|
||||
let key = UserKey::new(key);
|
||||
let keypair = key.make_key_pair().expect("Failed to generate key pair");
|
||||
|
||||
let json = serde_json::json!({
|
||||
"key": key.0.to_base64(),
|
||||
"publicKey": keypair.public.to_string(),
|
||||
"privateKey": keypair.private.to_string(),
|
||||
})
|
||||
.to_string();
|
||||
|
||||
let result = CString::new(json).unwrap();
|
||||
|
||||
result.into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn generate_user_organization_key(
|
||||
user_public_key: *const c_char,
|
||||
organization_key: *const c_char,
|
||||
) -> *const c_char {
|
||||
let user_public_key = CStr::from_ptr(user_public_key).to_str().unwrap().to_owned();
|
||||
let organization_key = CStr::from_ptr(organization_key)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
|
||||
let user_public_key = STANDARD.decode(user_public_key).unwrap();
|
||||
let organization_key = STANDARD.decode(organization_key).unwrap();
|
||||
|
||||
let encapsulation_key =
|
||||
AsymmetricPublicCryptoKey::from_der(&SpkiPublicKeyBytes::from(user_public_key)).unwrap();
|
||||
|
||||
let encrypted_key = UnsignedSharedKey::encapsulate_key_unsigned(
|
||||
&SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(organization_key)).unwrap(),
|
||||
&encapsulation_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = CString::new(encrypted_key.to_string()).unwrap();
|
||||
|
||||
result.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));
|
||||
}
|
||||
}
|
||||
@@ -41,4 +41,18 @@ public static class OrgnaizationExtensions
|
||||
Status = OrganizationUserStatusType.Confirmed
|
||||
};
|
||||
}
|
||||
|
||||
public static OrganizationUser CreateSdkOrganizationUser(this Organization organization, User user)
|
||||
{
|
||||
return new OrganizationUser
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user.Id,
|
||||
|
||||
Key = "4.rY01mZFXHOsBAg5Fq4gyXuklWfm6mQASm42DJpx05a+e2mmp+P5W6r54WU2hlREX0uoTxyP91bKKwickSPdCQQ58J45LXHdr9t2uzOYyjVzpzebFcdMw1eElR9W2DW8wEk9+mvtWvKwu7yTebzND+46y1nRMoFydi5zPVLSlJEf81qZZ4Uh1UUMLwXz+NRWfixnGXgq2wRq1bH0n3mqDhayiG4LJKgGdDjWXC8W8MMXDYx24SIJrJu9KiNEMprJE+XVF9nQVNijNAjlWBqkDpsfaWTUfeVLRLctfAqW1blsmIv4RQ91PupYJZDNc8nO9ZTF3TEVM+2KHoxzDJrLs2Q==",
|
||||
Type = OrganizationUserType.Admin,
|
||||
Status = OrganizationUserStatusType.Confirmed
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Infrastructure.EntityFramework.Models;
|
||||
using Bit.RustSDK;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Bit.Seeder.Factories;
|
||||
|
||||
@@ -22,4 +24,29 @@ public class UserSeeder
|
||||
KdfIterations = 600_000,
|
||||
};
|
||||
}
|
||||
|
||||
public static (User user, string userKey) CreateSdkUser(IPasswordHasher<Bit.Core.Entities.User> passwordHasher, string email)
|
||||
{
|
||||
var nativeService = RustSdkServiceFactory.CreateSingleton();
|
||||
var keys = nativeService.GenerateUserKeys(email, "asdfasdfasdf");
|
||||
|
||||
var user = new User
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Email = email,
|
||||
MasterPassword = null,
|
||||
SecurityStamp = "4830e359-e150-4eae-be2a-996c81c5e609",
|
||||
Key = keys.EncryptedUserKey,
|
||||
PublicKey = keys.PublicKey,
|
||||
PrivateKey = keys.PrivateKey,
|
||||
ApiKey = "7gp59kKHt9kMlks0BuNC4IjNXYkljR",
|
||||
|
||||
Kdf = KdfType.PBKDF2_SHA256,
|
||||
KdfIterations = 5_000,
|
||||
};
|
||||
|
||||
user.MasterPassword = passwordHasher.HashPassword(user, keys.MasterPasswordHash);
|
||||
|
||||
return (user, keys.Key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<ProjectReference Include="..\..\src\Core\Core.csproj" />
|
||||
<ProjectReference Include="..\..\src\Infrastructure.EntityFramework\Infrastructure.EntityFramework.csproj" />
|
||||
<ProjectReference Include="..\..\src\SharedWeb\SharedWeb.csproj" />
|
||||
<ProjectReference Include="..\RustSdk\RustSdk.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user