...
This commit is contained in:
@@ -1,29 +1,76 @@
|
||||
//! Public API for keypair operations.
|
||||
|
||||
use crate::core::keypair;
|
||||
use crate::core::symmetric;
|
||||
use crate::core::error::CryptoError;
|
||||
|
||||
/// Initializes a new keypair for signing and verification.
|
||||
/// Creates a new key space with the given name.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the key space.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` if the keypair was initialized successfully.
|
||||
/// * `Err(CryptoError::KeypairAlreadyInitialized)` if a keypair was already initialized.
|
||||
pub fn new() -> Result<(), CryptoError> {
|
||||
keypair::keypair_new()
|
||||
/// * `Ok(())` if the key space was created successfully.
|
||||
/// * `Err(CryptoError)` if an error occurred.
|
||||
pub fn create_space(name: &str) -> Result<(), CryptoError> {
|
||||
keypair::create_space(name)
|
||||
}
|
||||
|
||||
/// Gets the public key of the initialized keypair.
|
||||
/// Creates a new keypair in the current space.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the keypair.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` if the keypair was created successfully.
|
||||
/// * `Err(CryptoError::NoActiveSpace)` if no space is active.
|
||||
/// * `Err(CryptoError::KeypairAlreadyExists)` if a keypair with this name already exists.
|
||||
pub fn create_keypair(name: &str) -> Result<(), CryptoError> {
|
||||
keypair::create_keypair(name)
|
||||
}
|
||||
|
||||
/// Selects a keypair for use.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the keypair to select.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` if the keypair was selected successfully.
|
||||
/// * `Err(CryptoError::NoActiveSpace)` if no space is active.
|
||||
/// * `Err(CryptoError::KeypairNotFound)` if the keypair was not found.
|
||||
pub fn select_keypair(name: &str) -> Result<(), CryptoError> {
|
||||
keypair::select_keypair(name)
|
||||
}
|
||||
|
||||
/// Lists all keypair names in the current space.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<String>)` containing the keypair names.
|
||||
/// * `Err(CryptoError::NoActiveSpace)` if no space is active.
|
||||
pub fn list_keypairs() -> Result<Vec<String>, CryptoError> {
|
||||
keypair::list_keypairs()
|
||||
}
|
||||
|
||||
/// Gets the public key of the selected keypair.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<u8>)` containing the public key bytes.
|
||||
/// * `Err(CryptoError::KeypairNotInitialized)` if no keypair has been initialized.
|
||||
/// * `Err(CryptoError::NoActiveSpace)` if no space is active.
|
||||
/// * `Err(CryptoError::NoKeypairSelected)` if no keypair is selected.
|
||||
/// * `Err(CryptoError::KeypairNotFound)` if the selected keypair was not found.
|
||||
pub fn pub_key() -> Result<Vec<u8>, CryptoError> {
|
||||
keypair::keypair_pub_key()
|
||||
}
|
||||
|
||||
/// Signs a message using the initialized keypair.
|
||||
/// Signs a message using the selected keypair.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
@@ -32,7 +79,9 @@ pub fn pub_key() -> Result<Vec<u8>, CryptoError> {
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<u8>)` containing the signature bytes.
|
||||
/// * `Err(CryptoError::KeypairNotInitialized)` if no keypair has been initialized.
|
||||
/// * `Err(CryptoError::NoActiveSpace)` if no space is active.
|
||||
/// * `Err(CryptoError::NoKeypairSelected)` if no keypair is selected.
|
||||
/// * `Err(CryptoError::KeypairNotFound)` if the selected keypair was not found.
|
||||
pub fn sign(message: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
keypair::keypair_sign(message)
|
||||
}
|
||||
@@ -48,8 +97,49 @@ pub fn sign(message: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
///
|
||||
/// * `Ok(true)` if the signature is valid.
|
||||
/// * `Ok(false)` if the signature is invalid.
|
||||
/// * `Err(CryptoError::KeypairNotInitialized)` if no keypair has been initialized.
|
||||
/// * `Err(CryptoError::NoActiveSpace)` if no space is active.
|
||||
/// * `Err(CryptoError::NoKeypairSelected)` if no keypair is selected.
|
||||
/// * `Err(CryptoError::KeypairNotFound)` if the selected keypair was not found.
|
||||
/// * `Err(CryptoError::SignatureFormatError)` if the signature format is invalid.
|
||||
pub fn verify(message: &[u8], signature: &[u8]) -> Result<bool, CryptoError> {
|
||||
keypair::keypair_verify(message, signature)
|
||||
}
|
||||
|
||||
/// Encrypts a key space with a password.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `password` - The password to encrypt with.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(String)` containing the serialized encrypted key space.
|
||||
/// * `Err(CryptoError::NoActiveSpace)` if no space is active.
|
||||
/// * `Err(CryptoError)` if encryption fails.
|
||||
pub fn encrypt_space(password: &str) -> Result<String, CryptoError> {
|
||||
let space = keypair::get_current_space()?;
|
||||
let encrypted = symmetric::encrypt_key_space(&space, password)?;
|
||||
symmetric::serialize_encrypted_space(&encrypted)
|
||||
}
|
||||
|
||||
/// Decrypts a key space with a password and sets it as the current space.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `encrypted_space` - The serialized encrypted key space.
|
||||
/// * `password` - The password to decrypt with.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` if the key space was decrypted and set successfully.
|
||||
/// * `Err(CryptoError)` if decryption fails.
|
||||
pub fn decrypt_space(encrypted_space: &str, password: &str) -> Result<(), CryptoError> {
|
||||
let encrypted = symmetric::deserialize_encrypted_space(encrypted_space)?;
|
||||
let space = symmetric::decrypt_key_space(&encrypted, password)?;
|
||||
keypair::set_current_space(space)
|
||||
}
|
||||
|
||||
/// Clears the current session (logout).
|
||||
pub fn logout() {
|
||||
keypair::clear_session();
|
||||
}
|
@@ -12,6 +12,19 @@ pub fn generate_key() -> [u8; 32] {
|
||||
symmetric::generate_symmetric_key()
|
||||
}
|
||||
|
||||
/// Derives a 32-byte key from a password.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `password` - The password to derive the key from.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A 32-byte array containing the derived key.
|
||||
pub fn derive_key_from_password(password: &str) -> [u8; 32] {
|
||||
symmetric::derive_key_from_password(password)
|
||||
}
|
||||
|
||||
/// Encrypts data using ChaCha20Poly1305.
|
||||
///
|
||||
/// A random nonce is generated internally and appended to the ciphertext.
|
||||
@@ -46,4 +59,40 @@ pub fn encrypt(key: &[u8], message: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
/// * `Err(CryptoError::DecryptionFailed)` if decryption fails or the ciphertext is too short.
|
||||
pub fn decrypt(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
symmetric::decrypt_symmetric(key, ciphertext)
|
||||
}
|
||||
|
||||
/// Encrypts data using a password.
|
||||
///
|
||||
/// The password is used to derive a key, which is then used to encrypt the data.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `password` - The password to encrypt with.
|
||||
/// * `message` - The message to encrypt.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<u8>)` containing the ciphertext.
|
||||
/// * `Err(CryptoError)` if encryption fails.
|
||||
pub fn encrypt_with_password(password: &str, message: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
let key = symmetric::derive_key_from_password(password);
|
||||
symmetric::encrypt_symmetric(&key, message)
|
||||
}
|
||||
|
||||
/// Decrypts data using a password.
|
||||
///
|
||||
/// The password is used to derive a key, which is then used to decrypt the data.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `password` - The password to decrypt with.
|
||||
/// * `ciphertext` - The ciphertext to decrypt.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<u8>)` containing the decrypted message.
|
||||
/// * `Err(CryptoError)` if decryption fails.
|
||||
pub fn decrypt_with_password(password: &str, ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
let key = symmetric::derive_key_from_password(password);
|
||||
symmetric::decrypt_symmetric(&key, ciphertext)
|
||||
}
|
@@ -18,6 +18,22 @@ pub enum CryptoError {
|
||||
DecryptionFailed,
|
||||
/// The key length is invalid.
|
||||
InvalidKeyLength,
|
||||
/// No space is currently active.
|
||||
NoActiveSpace,
|
||||
/// No keypair is currently selected.
|
||||
NoKeypairSelected,
|
||||
/// The specified keypair was not found.
|
||||
KeypairNotFound,
|
||||
/// A keypair with this name already exists.
|
||||
KeypairAlreadyExists,
|
||||
/// The space with the given name was not found.
|
||||
SpaceNotFound,
|
||||
/// A space with this name already exists.
|
||||
SpaceAlreadyExists,
|
||||
/// Invalid password for the space.
|
||||
InvalidPassword,
|
||||
/// Error during serialization or deserialization.
|
||||
SerializationError,
|
||||
/// Other error with description.
|
||||
#[allow(dead_code)]
|
||||
Other(String),
|
||||
@@ -33,6 +49,14 @@ impl std::fmt::Display for CryptoError {
|
||||
CryptoError::EncryptionFailed => write!(f, "Encryption failed"),
|
||||
CryptoError::DecryptionFailed => write!(f, "Decryption failed"),
|
||||
CryptoError::InvalidKeyLength => write!(f, "Invalid key length"),
|
||||
CryptoError::NoActiveSpace => write!(f, "No active space"),
|
||||
CryptoError::NoKeypairSelected => write!(f, "No keypair selected"),
|
||||
CryptoError::KeypairNotFound => write!(f, "Keypair not found"),
|
||||
CryptoError::KeypairAlreadyExists => write!(f, "Keypair already exists"),
|
||||
CryptoError::SpaceNotFound => write!(f, "Space not found"),
|
||||
CryptoError::SpaceAlreadyExists => write!(f, "Space already exists"),
|
||||
CryptoError::InvalidPassword => write!(f, "Invalid password"),
|
||||
CryptoError::SerializationError => write!(f, "Serialization error"),
|
||||
CryptoError::Other(s) => write!(f, "Crypto error: {}", s),
|
||||
}
|
||||
}
|
||||
@@ -50,6 +74,14 @@ pub fn error_to_status_code(err: CryptoError) -> i32 {
|
||||
CryptoError::EncryptionFailed => -5,
|
||||
CryptoError::DecryptionFailed => -6,
|
||||
CryptoError::InvalidKeyLength => -7,
|
||||
CryptoError::NoActiveSpace => -8,
|
||||
CryptoError::NoKeypairSelected => -9,
|
||||
CryptoError::KeypairNotFound => -10,
|
||||
CryptoError::KeypairAlreadyExists => -11,
|
||||
CryptoError::SpaceNotFound => -12,
|
||||
CryptoError::SpaceAlreadyExists => -13,
|
||||
CryptoError::InvalidPassword => -14,
|
||||
CryptoError::SerializationError => -15,
|
||||
CryptoError::Other(_) => -99,
|
||||
}
|
||||
}
|
@@ -1,87 +1,312 @@
|
||||
//! Core implementation of keypair functionality.
|
||||
|
||||
use k256::ecdsa::{SigningKey, VerifyingKey, signature::{Signer, Verifier}, Signature};
|
||||
use once_cell::sync::OnceCell;
|
||||
use rand::rngs::OsRng;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::collections::HashMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::error::CryptoError;
|
||||
|
||||
/// A keypair for signing and verifying messages.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct KeyPair {
|
||||
pub name: String,
|
||||
#[serde(with = "verifying_key_serde")]
|
||||
pub verifying_key: VerifyingKey,
|
||||
#[serde(with = "signing_key_serde")]
|
||||
pub signing_key: SigningKey,
|
||||
}
|
||||
|
||||
/// Global keypair instance.
|
||||
pub static KEYPAIR: OnceCell<KeyPair> = OnceCell::new();
|
||||
// Serialization helpers for VerifyingKey
|
||||
mod verifying_key_serde {
|
||||
use super::*;
|
||||
use serde::{Serializer, Deserializer};
|
||||
use serde::de::{self, Visitor};
|
||||
use std::fmt;
|
||||
|
||||
/// Initializes the global keypair.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` if the keypair was initialized successfully.
|
||||
/// * `Err(CryptoError::KeypairAlreadyInitialized)` if the keypair was already initialized.
|
||||
pub fn keypair_new() -> Result<(), CryptoError> {
|
||||
let signing_key = SigningKey::random(&mut OsRng);
|
||||
let verifying_key = VerifyingKey::from(&signing_key);
|
||||
let keypair = KeyPair { verifying_key, signing_key };
|
||||
|
||||
KEYPAIR.set(keypair).map_err(|_| CryptoError::KeypairAlreadyInitialized)
|
||||
}
|
||||
|
||||
/// Gets the public key bytes.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<u8>)` containing the public key bytes.
|
||||
/// * `Err(CryptoError::KeypairNotInitialized)` if the keypair has not been initialized.
|
||||
pub fn keypair_pub_key() -> Result<Vec<u8>, CryptoError> {
|
||||
KEYPAIR.get()
|
||||
.ok_or(CryptoError::KeypairNotInitialized)
|
||||
.map(|kp| kp.verifying_key.to_sec1_bytes().to_vec())
|
||||
}
|
||||
|
||||
/// Signs a message.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `message` - The message to sign.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<u8>)` containing the signature bytes.
|
||||
/// * `Err(CryptoError::KeypairNotInitialized)` if the keypair has not been initialized.
|
||||
pub fn keypair_sign(message: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
KEYPAIR.get()
|
||||
.ok_or(CryptoError::KeypairNotInitialized)
|
||||
.map(|kp| {
|
||||
let signature: Signature = kp.signing_key.sign(message);
|
||||
signature.to_bytes().to_vec()
|
||||
})
|
||||
}
|
||||
|
||||
/// Verifies a message signature.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `message` - The message that was signed.
|
||||
/// * `signature_bytes` - The signature to verify.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(true)` if the signature is valid.
|
||||
/// * `Ok(false)` if the signature is invalid.
|
||||
/// * `Err(CryptoError::KeypairNotInitialized)` if the keypair has not been initialized.
|
||||
/// * `Err(CryptoError::SignatureFormatError)` if the signature format is invalid.
|
||||
pub fn keypair_verify(message: &[u8], signature_bytes: &[u8]) -> Result<bool, CryptoError> {
|
||||
let keypair = KEYPAIR.get().ok_or(CryptoError::KeypairNotInitialized)?;
|
||||
|
||||
let signature = Signature::from_bytes(signature_bytes.into())
|
||||
.map_err(|_| CryptoError::SignatureFormatError)?;
|
||||
|
||||
match keypair.verifying_key.verify(message, &signature) {
|
||||
Ok(_) => Ok(true),
|
||||
Err(_) => Ok(false), // Verification failed, but operation was successful
|
||||
pub fn serialize<S>(key: &VerifyingKey, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let bytes = key.to_sec1_bytes();
|
||||
serializer.serialize_bytes(&bytes)
|
||||
}
|
||||
|
||||
struct VerifyingKeyVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for VerifyingKeyVisitor {
|
||||
type Value = VerifyingKey;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a byte array representing a verifying key")
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
VerifyingKey::from_sec1_bytes(v).map_err(|_| E::custom("invalid verifying key"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<VerifyingKey, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_bytes(VerifyingKeyVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
// Serialization helpers for SigningKey
|
||||
mod signing_key_serde {
|
||||
use super::*;
|
||||
use serde::{Serializer, Deserializer};
|
||||
use serde::de::{self, Visitor};
|
||||
use std::fmt;
|
||||
|
||||
pub fn serialize<S>(key: &SigningKey, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let bytes = key.to_bytes();
|
||||
serializer.serialize_bytes(&bytes)
|
||||
}
|
||||
|
||||
struct SigningKeyVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for SigningKeyVisitor {
|
||||
type Value = SigningKey;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a byte array representing a signing key")
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
SigningKey::from_bytes(v.into()).map_err(|_| E::custom("invalid signing key"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<SigningKey, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_bytes(SigningKeyVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyPair {
|
||||
/// Creates a new keypair with the given name.
|
||||
pub fn new(name: &str) -> Self {
|
||||
let signing_key = SigningKey::random(&mut OsRng);
|
||||
let verifying_key = VerifyingKey::from(&signing_key);
|
||||
|
||||
KeyPair {
|
||||
name: name.to_string(),
|
||||
verifying_key,
|
||||
signing_key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the public key bytes.
|
||||
pub fn pub_key(&self) -> Vec<u8> {
|
||||
self.verifying_key.to_sec1_bytes().to_vec()
|
||||
}
|
||||
|
||||
/// Signs a message.
|
||||
pub fn sign(&self, message: &[u8]) -> Vec<u8> {
|
||||
let signature: Signature = self.signing_key.sign(message);
|
||||
signature.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
/// Verifies a message signature.
|
||||
pub fn verify(&self, message: &[u8], signature_bytes: &[u8]) -> Result<bool, CryptoError> {
|
||||
let signature = Signature::from_bytes(signature_bytes.into())
|
||||
.map_err(|_| CryptoError::SignatureFormatError)?;
|
||||
|
||||
match self.verifying_key.verify(message, &signature) {
|
||||
Ok(_) => Ok(true),
|
||||
Err(_) => Ok(false), // Verification failed, but operation was successful
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of keypairs.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct KeySpace {
|
||||
pub name: String,
|
||||
pub keypairs: HashMap<String, KeyPair>,
|
||||
}
|
||||
|
||||
impl KeySpace {
|
||||
/// Creates a new key space with the given name.
|
||||
pub fn new(name: &str) -> Self {
|
||||
KeySpace {
|
||||
name: name.to_string(),
|
||||
keypairs: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new keypair to the space.
|
||||
pub fn add_keypair(&mut self, name: &str) -> Result<(), CryptoError> {
|
||||
if self.keypairs.contains_key(name) {
|
||||
return Err(CryptoError::KeypairAlreadyExists);
|
||||
}
|
||||
|
||||
let keypair = KeyPair::new(name);
|
||||
self.keypairs.insert(name.to_string(), keypair);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets a keypair by name.
|
||||
pub fn get_keypair(&self, name: &str) -> Result<&KeyPair, CryptoError> {
|
||||
self.keypairs.get(name).ok_or(CryptoError::KeypairNotFound)
|
||||
}
|
||||
|
||||
/// Lists all keypair names in the space.
|
||||
pub fn list_keypairs(&self) -> Vec<String> {
|
||||
self.keypairs.keys().cloned().collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Session state for the current key space and selected keypair.
|
||||
pub struct Session {
|
||||
pub current_space: Option<KeySpace>,
|
||||
pub selected_keypair: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for Session {
|
||||
fn default() -> Self {
|
||||
Session {
|
||||
current_space: None,
|
||||
selected_keypair: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Global session state.
|
||||
static SESSION: Lazy<Mutex<Session>> = Lazy::new(|| {
|
||||
Mutex::new(Session::default())
|
||||
});
|
||||
|
||||
/// Creates a new key space with the given name.
|
||||
pub fn create_space(name: &str) -> Result<(), CryptoError> {
|
||||
let mut session = SESSION.lock().unwrap();
|
||||
|
||||
// Create a new space
|
||||
let space = KeySpace::new(name);
|
||||
|
||||
// Set as current space
|
||||
session.current_space = Some(space);
|
||||
session.selected_keypair = None;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the current key space.
|
||||
pub fn set_current_space(space: KeySpace) -> Result<(), CryptoError> {
|
||||
let mut session = SESSION.lock().unwrap();
|
||||
session.current_space = Some(space);
|
||||
session.selected_keypair = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the current key space.
|
||||
pub fn get_current_space() -> Result<KeySpace, CryptoError> {
|
||||
let session = SESSION.lock().unwrap();
|
||||
session.current_space.clone().ok_or(CryptoError::NoActiveSpace)
|
||||
}
|
||||
|
||||
/// Clears the current session (logout).
|
||||
pub fn clear_session() {
|
||||
let mut session = SESSION.lock().unwrap();
|
||||
session.current_space = None;
|
||||
session.selected_keypair = None;
|
||||
}
|
||||
|
||||
/// Creates a new keypair in the current space.
|
||||
pub fn create_keypair(name: &str) -> Result<(), CryptoError> {
|
||||
let mut session = SESSION.lock().unwrap();
|
||||
|
||||
if let Some(ref mut space) = session.current_space {
|
||||
if space.keypairs.contains_key(name) {
|
||||
return Err(CryptoError::KeypairAlreadyExists);
|
||||
}
|
||||
|
||||
let keypair = KeyPair::new(name);
|
||||
space.keypairs.insert(name.to_string(), keypair);
|
||||
|
||||
// Automatically select the new keypair
|
||||
session.selected_keypair = Some(name.to_string());
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CryptoError::NoActiveSpace)
|
||||
}
|
||||
}
|
||||
|
||||
/// Selects a keypair for use.
|
||||
pub fn select_keypair(name: &str) -> Result<(), CryptoError> {
|
||||
let mut session = SESSION.lock().unwrap();
|
||||
|
||||
if let Some(ref space) = session.current_space {
|
||||
if !space.keypairs.contains_key(name) {
|
||||
return Err(CryptoError::KeypairNotFound);
|
||||
}
|
||||
|
||||
session.selected_keypair = Some(name.to_string());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CryptoError::NoActiveSpace)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the currently selected keypair.
|
||||
pub fn get_selected_keypair() -> Result<KeyPair, CryptoError> {
|
||||
let session = SESSION.lock().unwrap();
|
||||
|
||||
if let Some(ref space) = session.current_space {
|
||||
if let Some(ref keypair_name) = session.selected_keypair {
|
||||
if let Some(keypair) = space.keypairs.get(keypair_name) {
|
||||
return Ok(keypair.clone());
|
||||
}
|
||||
return Err(CryptoError::KeypairNotFound);
|
||||
}
|
||||
return Err(CryptoError::NoKeypairSelected);
|
||||
}
|
||||
|
||||
Err(CryptoError::NoActiveSpace)
|
||||
}
|
||||
|
||||
/// Lists all keypair names in the current space.
|
||||
pub fn list_keypairs() -> Result<Vec<String>, CryptoError> {
|
||||
let session = SESSION.lock().unwrap();
|
||||
|
||||
if let Some(ref space) = session.current_space {
|
||||
Ok(space.keypairs.keys().cloned().collect())
|
||||
} else {
|
||||
Err(CryptoError::NoActiveSpace)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the public key of the selected keypair.
|
||||
pub fn keypair_pub_key() -> Result<Vec<u8>, CryptoError> {
|
||||
let keypair = get_selected_keypair()?;
|
||||
Ok(keypair.pub_key())
|
||||
}
|
||||
|
||||
/// Signs a message with the selected keypair.
|
||||
pub fn keypair_sign(message: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
let keypair = get_selected_keypair()?;
|
||||
Ok(keypair.sign(message))
|
||||
}
|
||||
|
||||
/// Verifies a message signature with the selected keypair.
|
||||
pub fn keypair_verify(message: &[u8], signature_bytes: &[u8]) -> Result<bool, CryptoError> {
|
||||
let keypair = get_selected_keypair()?;
|
||||
keypair.verify(message, signature_bytes)
|
||||
}
|
@@ -3,8 +3,11 @@
|
||||
use chacha20poly1305::{ChaCha20Poly1305, KeyInit, Nonce};
|
||||
use chacha20poly1305::aead::Aead;
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sha2::{Sha256, Digest};
|
||||
|
||||
use super::error::CryptoError;
|
||||
use super::keypair::KeySpace;
|
||||
|
||||
/// The size of the nonce in bytes.
|
||||
const NONCE_SIZE: usize = 12;
|
||||
@@ -20,6 +23,25 @@ pub fn generate_symmetric_key() -> [u8; 32] {
|
||||
key
|
||||
}
|
||||
|
||||
/// Derives a 32-byte key from a password.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `password` - The password to derive the key from.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A 32-byte array containing the derived key.
|
||||
pub fn derive_key_from_password(password: &str) -> [u8; 32] {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(password.as_bytes());
|
||||
let result = hasher.finalize();
|
||||
|
||||
let mut key = [0u8; 32];
|
||||
key.copy_from_slice(&result);
|
||||
key
|
||||
}
|
||||
|
||||
/// Encrypts data using ChaCha20Poly1305 with an internally generated nonce.
|
||||
///
|
||||
/// The nonce is appended to the ciphertext so it can be extracted during decryption.
|
||||
@@ -87,4 +109,110 @@ pub fn decrypt_symmetric(key: &[u8], ciphertext_with_nonce: &[u8]) -> Result<Vec
|
||||
// Decrypt message
|
||||
cipher.decrypt(nonce, ciphertext)
|
||||
.map_err(|_| CryptoError::DecryptionFailed)
|
||||
}
|
||||
|
||||
/// Metadata for an encrypted key space.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct EncryptedKeySpaceMetadata {
|
||||
pub name: String,
|
||||
pub created_at: u64,
|
||||
pub last_accessed: u64,
|
||||
}
|
||||
|
||||
/// An encrypted key space with metadata.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct EncryptedKeySpace {
|
||||
pub metadata: EncryptedKeySpaceMetadata,
|
||||
pub encrypted_data: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Encrypts a key space using a password.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `space` - The key space to encrypt.
|
||||
/// * `password` - The password to encrypt with.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(EncryptedKeySpace)` containing the encrypted key space.
|
||||
/// * `Err(CryptoError)` if encryption fails.
|
||||
pub fn encrypt_key_space(space: &KeySpace, password: &str) -> Result<EncryptedKeySpace, CryptoError> {
|
||||
// Serialize the key space
|
||||
let serialized = serde_json::to_vec(space)
|
||||
.map_err(|_| CryptoError::SerializationError)?;
|
||||
|
||||
// Derive key from password
|
||||
let key = derive_key_from_password(password);
|
||||
|
||||
// Encrypt the serialized data
|
||||
let encrypted_data = encrypt_symmetric(&key, &serialized)?;
|
||||
|
||||
// Create metadata
|
||||
let now = js_sys::Date::now() as u64;
|
||||
let metadata = EncryptedKeySpaceMetadata {
|
||||
name: space.name.clone(),
|
||||
created_at: now,
|
||||
last_accessed: now,
|
||||
};
|
||||
|
||||
Ok(EncryptedKeySpace {
|
||||
metadata,
|
||||
encrypted_data,
|
||||
})
|
||||
}
|
||||
|
||||
/// Decrypts a key space using a password.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `encrypted_space` - The encrypted key space.
|
||||
/// * `password` - The password to decrypt with.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(KeySpace)` containing the decrypted key space.
|
||||
/// * `Err(CryptoError)` if decryption fails.
|
||||
pub fn decrypt_key_space(encrypted_space: &EncryptedKeySpace, password: &str) -> Result<KeySpace, CryptoError> {
|
||||
// Derive key from password
|
||||
let key = derive_key_from_password(password);
|
||||
|
||||
// Decrypt the data
|
||||
let decrypted_data = decrypt_symmetric(&key, &encrypted_space.encrypted_data)?;
|
||||
|
||||
// Deserialize the key space
|
||||
let space: KeySpace = serde_json::from_slice(&decrypted_data)
|
||||
.map_err(|_| CryptoError::SerializationError)?;
|
||||
|
||||
Ok(space)
|
||||
}
|
||||
|
||||
/// Serializes an encrypted key space to a JSON string.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `encrypted_space` - The encrypted key space to serialize.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(String)` containing the serialized encrypted key space.
|
||||
/// * `Err(CryptoError)` if serialization fails.
|
||||
pub fn serialize_encrypted_space(encrypted_space: &EncryptedKeySpace) -> Result<String, CryptoError> {
|
||||
serde_json::to_string(encrypted_space)
|
||||
.map_err(|_| CryptoError::SerializationError)
|
||||
}
|
||||
|
||||
/// Deserializes an encrypted key space from a JSON string.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `serialized` - The serialized encrypted key space.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(EncryptedKeySpace)` containing the deserialized encrypted key space.
|
||||
/// * `Err(CryptoError)` if deserialization fails.
|
||||
pub fn deserialize_encrypted_space(serialized: &str) -> Result<EncryptedKeySpace, CryptoError> {
|
||||
serde_json::from_str(serialized)
|
||||
.map_err(|_| CryptoError::SerializationError)
|
||||
}
|
69
src/lib.rs
69
src/lib.rs
@@ -26,16 +26,60 @@ pub fn main_js() -> Result<(), JsValue> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- WebAssembly Exports ---
|
||||
// --- WebAssembly Exports for Key Space Management ---
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn keypair_new() -> i32 {
|
||||
match keypair::new() {
|
||||
pub fn create_key_space(name: &str) -> i32 {
|
||||
match keypair::create_space(name) {
|
||||
Ok(_) => 0, // Success
|
||||
Err(e) => error_to_status_code(e),
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn encrypt_key_space(password: &str) -> Result<String, JsValue> {
|
||||
keypair::encrypt_space(password)
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn decrypt_key_space(encrypted_space: &str, password: &str) -> i32 {
|
||||
match keypair::decrypt_space(encrypted_space, password) {
|
||||
Ok(_) => 0, // Success
|
||||
Err(e) => error_to_status_code(e),
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn logout() {
|
||||
keypair::logout();
|
||||
}
|
||||
|
||||
// --- WebAssembly Exports for Keypair Management ---
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn create_keypair(name: &str) -> i32 {
|
||||
match keypair::create_keypair(name) {
|
||||
Ok(_) => 0, // Success
|
||||
Err(e) => error_to_status_code(e),
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn select_keypair(name: &str) -> i32 {
|
||||
match keypair::select_keypair(name) {
|
||||
Ok(_) => 0, // Success
|
||||
Err(e) => error_to_status_code(e),
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn list_keypairs() -> Result<Vec<JsValue>, JsValue> {
|
||||
keypair::list_keypairs()
|
||||
.map(|names| names.into_iter().map(JsValue::from).collect())
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn keypair_pub_key() -> Result<Vec<u8>, JsValue> {
|
||||
keypair::pub_key()
|
||||
@@ -54,11 +98,18 @@ pub fn keypair_verify(message: &[u8], signature: &[u8]) -> Result<bool, JsValue>
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||||
}
|
||||
|
||||
// --- WebAssembly Exports for Symmetric Encryption ---
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn generate_symmetric_key() -> Vec<u8> {
|
||||
symmetric::generate_key().to_vec()
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn derive_key_from_password(password: &str) -> Vec<u8> {
|
||||
symmetric::derive_key_from_password(password).to_vec()
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn encrypt_symmetric(key: &[u8], message: &[u8]) -> Result<Vec<u8>, JsValue> {
|
||||
symmetric::encrypt(key, message)
|
||||
@@ -70,3 +121,15 @@ pub fn decrypt_symmetric(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, JsVal
|
||||
symmetric::decrypt(key, ciphertext)
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn encrypt_with_password(password: &str, message: &[u8]) -> Result<Vec<u8>, JsValue> {
|
||||
symmetric::encrypt_with_password(password, message)
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn decrypt_with_password(password: &str, ciphertext: &[u8]) -> Result<Vec<u8>, JsValue> {
|
||||
symmetric::decrypt_with_password(password, ciphertext)
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||||
}
|
||||
|
Reference in New Issue
Block a user