mod error;
pub mod legacy;
mod serde;
mod util;
pub use error::{DecryptError, EnconError, EncryptError, MapToJsonError};
use indexmap::map::IndexMap;
use serde_json::Value;
use sodiumoxide::crypto::aead::chacha20poly1305_ietf as aead;
use sodiumoxide::crypto::pwhash;
use sodiumoxide::randombytes::randombytes;
use std::fmt;
use std::io::Write as _;
use std::ops::Deref;
use std::ops::DerefMut;
use std::sync::Arc;
pub const SIGNATURE: [u8; 4] = [0xC2, 0x0A, 0x4B, 0xED];
pub fn init() -> Result<(), ()> {
sodiumoxide::init()
}
#[derive(Clone)]
pub struct Password {
password: Arc<String>,
}
impl Password {
pub fn new(password: impl Into<String>) -> Self {
Password {
password: Arc::new(password.into()),
}
}
pub fn encrypt(&self, bytes: impl AsRef<[u8]>) -> Result<Vec<u8>, EncryptError> {
let bytes = bytes.as_ref();
let mut output = Vec::default();
output.write_all(&SIGNATURE).map_err(EncryptError::write)?;
let salt = pwhash::gen_salt();
output.write_all(&salt.0).map_err(EncryptError::write)?;
let mut key = [0u8; aead::KEYBYTES];
pwhash::derive_key_interactive(&mut key, self.password.as_bytes(), &salt).unwrap();
let key = aead::Key(key);
let nonce_bytes = randombytes(aead::NONCEBYTES);
let nonce = aead::Nonce::from_slice(&nonce_bytes).expect("Nonce::from_slice");
output
.write_all(&nonce_bytes)
.map_err(EncryptError::write)?;
let sealed = aead::seal(bytes, None, &nonce, &key);
output.write_all(&sealed).map_err(EncryptError::write)?;
Ok(output)
}
pub fn decrypt(&self, bytes: impl AsRef<[u8]>) -> Result<Vec<u8>, DecryptError> {
let bytes = bytes.as_ref();
if bytes.len() <= (pwhash::SALTBYTES + aead::NONCEBYTES + SIGNATURE.len()) {
return Err(DecryptError::InputTooShort);
}
let mut offset = 0;
let mut salt = [0u8; pwhash::SALTBYTES];
let mut signature = [0u8; 4];
signature.copy_from_slice(&bytes[offset..offset + SIGNATURE.len()]);
offset += signature.len();
if signature == legacy::SIGNATURE {
let legacy = legacy::Password::from(self.clone());
return legacy.decrypt(bytes);
}
salt.copy_from_slice(&bytes[offset..offset + pwhash::SALTBYTES]);
offset += salt.len();
let salt = pwhash::Salt(salt);
let mut nonce = [0u8; aead::NONCEBYTES];
nonce.copy_from_slice(&bytes[offset..offset + aead::NONCEBYTES]);
offset += nonce.len();
let nonce = aead::Nonce(nonce);
let mut key = [0u8; aead::KEYBYTES];
pwhash::derive_key_interactive(&mut key, self.password.as_bytes(), &salt)
.map_err(|_| DecryptError::DeriveKey)?;
let key = aead::Key(key);
let output = aead::open(&bytes[offset..], None, &nonce, &key)
.map_err(|_| DecryptError::LikelyWrongPassword)?;
Ok(output)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum EncryptableKind {
Encrypted,
Plain,
}
impl fmt::Display for EncryptableKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Encrypted => "Encrypted",
Self::Plain => "Plain",
}
)
}
}
#[derive(Debug, Clone)]
pub enum Encryptable {
Encrypted(Vec<u8>),
Plain(Value),
}
impl Encryptable {
pub fn kind(&self) -> EncryptableKind {
match self {
Self::Encrypted(_) => EncryptableKind::Encrypted,
Self::Plain(_) => EncryptableKind::Plain,
}
}
pub fn is_encrypted(&self) -> bool {
match self {
Self::Encrypted(_) => true,
_ => false,
}
}
pub fn as_encrypted(&self) -> Option<&[u8]> {
match self {
Self::Encrypted(value) => Some(&*value),
_ => None,
}
}
pub fn as_plain(&self) -> Option<&Value> {
match self {
Self::Plain(value) => Some(value),
_ => None,
}
}
pub fn to_encrypted(&self, password: &Password) -> Result<Self, EncryptError> {
Ok(Self::Encrypted(self.encrypt(password)?))
}
pub fn to_decrypted(&self, password: &Password) -> Result<Self, DecryptError> {
Ok(Self::Plain(self.decrypt(password)?))
}
pub fn encrypt(&self, password: &Password) -> Result<Vec<u8>, EncryptError> {
match self {
Self::Encrypted(bytes) => Ok(bytes.clone()),
Self::Plain(value) => {
let json = serde_json::to_string(value).map_err(EncryptError::serialize)?;
let bytes = password.encrypt(&json)?;
Ok(bytes)
}
}
}
pub fn decrypt(&self, password: &Password) -> Result<Value, DecryptError> {
match self {
Self::Plain(inner) => Ok(inner.clone()),
Self::Encrypted(bytes) => {
let bytes = password.decrypt(bytes)?;
let json = String::from_utf8(bytes).map_err(DecryptError::utf8)?;
let value: Value =
serde_json::from_str(&json).map_err(DecryptError::deserialize)?;
Ok(value)
}
}
}
pub fn with_intent_encrypted(self) -> WithIntent {
WithIntent {
intent: EncryptableKind::Encrypted,
inner: self,
}
}
pub fn with_intent_plain(self) -> WithIntent {
WithIntent {
intent: EncryptableKind::Plain,
inner: self,
}
}
}
pub fn string(s: impl ToString) -> Encryptable {
Encryptable::Plain(s.to_string().into())
}
#[derive(Debug, Clone)]
pub struct WithIntent {
intent: EncryptableKind,
inner: Encryptable,
}
impl WithIntent {
pub fn into_inner(self) -> Encryptable {
self.inner
}
pub fn intent(&self) -> EncryptableKind {
self.intent
}
pub fn intent_mut(&mut self) -> &mut EncryptableKind {
&mut self.intent
}
pub fn intend_encrypted(&mut self) -> &mut Self {
self.intent = EncryptableKind::Encrypted;
self
}
pub fn intend_plain(&mut self) -> &mut Self {
self.intent = EncryptableKind::Plain;
self
}
pub fn apply_intent(&mut self, password: &Password) -> Result<(), EnconError> {
use EncryptableKind::{Encrypted, Plain};
match (self.inner.kind(), self.intent) {
(Encrypted, Plain) => {
self.inner = self
.inner
.to_decrypted(password)
.map_err(EnconError::from)
.map_err(|err| err.apply_intent(Plain))?;
}
(Plain, Encrypted) => {
self.inner = self
.inner
.to_encrypted(password)
.map_err(EnconError::from)
.map_err(|err| err.apply_intent(Encrypted))?;
}
(Plain, Plain) => {}
(Encrypted, Encrypted) => {}
}
Ok(())
}
pub fn to_decrypted(&self, password: &Password) -> Result<Self, DecryptError> {
Ok(Self {
inner: self.inner.to_decrypted(password)?,
intent: self.intent,
})
}
pub fn to_encrypted(&self, password: &Password) -> Result<Self, EncryptError> {
Ok(Self {
inner: self.inner.to_encrypted(password)?,
intent: self.intent,
})
}
}
impl From<Encryptable> for WithIntent {
fn from(inner: Encryptable) -> Self {
Self {
intent: inner.kind(),
inner,
}
}
}
impl Deref for WithIntent {
type Target = Encryptable;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for WithIntent {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
#[derive(Debug, Clone, Default)]
pub struct Map {
inner: IndexMap<String, WithIntent>,
}
impl Map {
pub fn new() -> Self {
Self::default()
}
pub fn decrypt_all_in_place(&mut self, password: &Password) -> Result<(), EnconError> {
let mut good_keys = vec![];
let mut bad_keys = vec![];
let mut first_error = None;
for (key, value) in &mut self.inner {
match value.to_decrypted(password) {
Ok(decrypted) => {
good_keys.push(key.clone());
*value = decrypted;
}
Err(err) => {
bad_keys.push(key.clone());
if first_error.is_none() {
first_error = Some(err);
}
}
}
}
if let Some(source) = first_error {
Err(EnconError::DecryptAll {
good_keys,
bad_keys,
source: Box::new(source),
})
} else {
Ok(())
}
}
pub fn apply_all_intents(&mut self, password: &Password) -> Result<(), EnconError> {
for (_, value) in &mut self.inner {
value.apply_intent(password)?;
}
Ok(())
}
fn to_json_pre(&self) -> Result<(), MapToJsonError> {
for (_, value) in &self.inner {
if value.intent != value.intent() {
return Err(MapToJsonError::ApplyRequired);
}
}
Ok(())
}
pub fn to_json_pretty(&self) -> Result<String, MapToJsonError> {
self.to_json_pre()?;
serde_json::to_string_pretty(&self.inner).map_err(MapToJsonError::Serde)
}
pub fn to_json_compact(&self) -> Result<String, MapToJsonError> {
self.to_json_pre()?;
serde_json::to_string(&self.inner).map_err(MapToJsonError::Serde)
}
pub fn to_plain_map(&self, password: &Password) -> Result<PlainMap, EnconError> {
let mut copy = self.clone();
copy.decrypt_all_in_place(password)?;
let mut plain_map = PlainMap {
inner: IndexMap::with_capacity(self.len()),
};
for (key, with_intent) in copy {
match with_intent.into_inner() {
Encryptable::Plain(value) => {
plain_map.insert(key, value);
}
_ => panic!(
"Expected key {:?} to be decrypted after decrypt_all_in_place",
key
),
}
}
Ok(plain_map)
}
pub fn insert(
&mut self,
key: impl Into<String>,
value: impl Into<WithIntent>,
) -> Option<WithIntent> {
self.inner.insert(key.into(), value.into())
}
pub fn insert_before(
&mut self,
index: usize,
key: impl Into<String>,
value: impl Into<WithIntent>,
) -> Option<WithIntent> {
if index >= self.len() {
return self.insert(key, value);
}
let key = key.into();
let mut to_insert = Some((key.clone(), value.into()));
let current_index = self.inner.get_index_of(&key);
let new_map = IndexMap::with_capacity(if current_index.is_some() {
self.inner.len()
} else {
self.inner.len() + 1
});
let inner = std::mem::replace(&mut self.inner, new_map);
let mut ret = None;
for (i, (k, v)) in inner.into_iter().enumerate() {
if i == index {
if let Some((key, value)) = to_insert.take() {
self.inner.insert(key, value);
}
}
if current_index == Some(i) {
ret = Some(v);
} else {
self.inner.insert(k, v);
}
}
ret
}
pub fn keys(&self) -> impl Iterator<Item = &String> {
self.inner.keys()
}
pub fn values(&self) -> impl Iterator<Item = &WithIntent> {
self.inner.values()
}
pub fn entry(&mut self, key: String) -> indexmap::map::Entry<'_, String, WithIntent> {
self.inner.entry(key)
}
pub fn get(&self, key: impl Into<String>) -> Option<&WithIntent> {
self.inner.get(&key.into())
}
pub fn get_mut(&mut self, key: impl Into<String>) -> Option<&mut WithIntent> {
self.inner.get_mut(&key.into())
}
pub fn iter(&self) -> indexmap::map::Iter<String, WithIntent> {
self.inner.iter()
}
pub fn iter_mut(&mut self) -> indexmap::map::IterMut<String, WithIntent> {
self.inner.iter_mut()
}
pub fn remove(&mut self, key: impl Into<String>) -> Option<WithIntent> {
self.inner.remove(&key.into())
}
pub fn sort_keys(&mut self) {
self.inner.sort_keys()
}
pub fn reverse(&mut self) {
self.inner.reverse()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
impl IntoIterator for Map {
type Item = (String, WithIntent);
type IntoIter = indexmap::map::IntoIter<String, WithIntent>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<'a> IntoIterator for &'a Map {
type Item = (&'a String, &'a WithIntent);
type IntoIter = indexmap::map::Iter<'a, String, WithIntent>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a mut Map {
type Item = (&'a String, &'a mut WithIntent);
type IntoIter = indexmap::map::IterMut<'a, String, WithIntent>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
#[derive(Debug, Clone, Default)]
pub struct PlainMap {
inner: IndexMap<String, Value>,
}
impl PlainMap {
pub fn new() -> Self {
Self::default()
}
pub fn to_json_pretty(&self) -> Result<String, MapToJsonError> {
serde_json::to_string_pretty(&self.inner).map_err(MapToJsonError::Serde)
}
pub fn to_json_compact(&self) -> Result<String, MapToJsonError> {
serde_json::to_string(&self.inner).map_err(MapToJsonError::Serde)
}
pub fn to_value(&self) -> Value {
let map: serde_json::Map<String, Value> =
self.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
map.into()
}
pub fn insert(&mut self, key: impl Into<String>, value: impl Into<Value>) -> Option<Value> {
self.inner.insert(key.into(), value.into())
}
pub fn insert_before(
&mut self,
index: usize,
key: impl Into<String>,
value: impl Into<Value>,
) -> Option<Value> {
if index >= self.len() {
return self.insert(key, value);
}
let key = key.into();
let mut to_insert = Some((key.clone(), value.into()));
let current_index = self.inner.get_index_of(&key);
let new_map = IndexMap::with_capacity(if current_index.is_some() {
self.inner.len()
} else {
self.inner.len() + 1
});
let inner = std::mem::replace(&mut self.inner, new_map);
let mut ret = None;
for (i, (k, v)) in inner.into_iter().enumerate() {
if i == index {
if let Some((key, value)) = to_insert.take() {
self.inner.insert(key, value);
}
}
if current_index == Some(i) {
ret = Some(v);
} else {
self.inner.insert(k, v);
}
}
ret
}
pub fn keys(&self) -> impl Iterator<Item = &String> {
self.inner.keys()
}
pub fn values(&self) -> impl Iterator<Item = &Value> {
self.inner.values()
}
pub fn entry(&mut self, key: String) -> indexmap::map::Entry<'_, String, Value> {
self.inner.entry(key)
}
pub fn get(&self, key: impl Into<String>) -> Option<&Value> {
self.inner.get(&key.into())
}
pub fn get_mut(&mut self, key: impl Into<String>) -> Option<&mut Value> {
self.inner.get_mut(&key.into())
}
pub fn iter(&self) -> indexmap::map::Iter<String, Value> {
self.inner.iter()
}
pub fn iter_mut(&mut self) -> indexmap::map::IterMut<String, Value> {
self.inner.iter_mut()
}
pub fn remove(&mut self, key: impl Into<String>) -> Option<Value> {
self.inner.remove(&key.into())
}
pub fn sort_keys(&mut self) {
self.inner.sort_keys()
}
pub fn reverse(&mut self) {
self.inner.reverse()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
impl IntoIterator for PlainMap {
type Item = (String, Value);
type IntoIter = indexmap::map::IntoIter<String, Value>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<'a> IntoIterator for &'a PlainMap {
type Item = (&'a String, &'a Value);
type IntoIter = indexmap::map::Iter<'a, String, Value>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a mut PlainMap {
type Item = (&'a String, &'a mut Value);
type IntoIter = indexmap::map::IterMut<'a, String, Value>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
#[cfg(test)]
mod tests {
use super::Encryptable;
use serde_json::json;
#[test]
fn deserialize_encrypted() {
let enc: Encryptable = serde_json::from_value(json!({
"_encrypted": [
"010203",
"040506"
]
}))
.expect("deserialize");
assert_eq!(
enc.as_encrypted().expect("as_encrypted"),
&[1, 2, 3, 4, 5, 6]
);
}
#[test]
fn deserialize_other_object() {
let value = json!({
"_encrypted": [
"NOT_HEX",
"040506"
]
});
let enc: Encryptable = serde_json::from_value(value.clone()).expect("deserialize");
assert_eq!(enc.as_plain().expect("as_plain"), &value);
}
}