aboutsummaryrefslogtreecommitdiffstats
path: root/src/cipherstring.rs
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2020-04-05 02:53:59 -0400
committerJesse Luehrs <doy@tozt.net>2020-04-05 02:53:59 -0400
commit4ad2f0a0dc3abb4cb10a6b82ca6a1f3a829eb1fb (patch)
tree7daaf7d6d79ab4e11efec32ed2e3d27d269a567f /src/cipherstring.rs
parentcd894c27e0b0d5746b95b9c2933da3ba6e9a3f5b (diff)
downloadrbw-4ad2f0a0dc3abb4cb10a6b82ca6a1f3a829eb1fb.tar.gz
rbw-4ad2f0a0dc3abb4cb10a6b82ca6a1f3a829eb1fb.zip
rename
Diffstat (limited to 'src/cipherstring.rs')
-rw-r--r--src/cipherstring.rs95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/cipherstring.rs b/src/cipherstring.rs
new file mode 100644
index 0000000..75edec4
--- /dev/null
+++ b/src/cipherstring.rs
@@ -0,0 +1,95 @@
+use crate::prelude::*;
+
+use block_modes::BlockMode as _;
+use hmac::Mac as _;
+
+pub struct CipherString {
+ ty: u8,
+ iv: Vec<u8>,
+ ciphertext: Vec<u8>,
+ mac: Option<Vec<u8>>,
+}
+
+impl CipherString {
+ pub fn new(s: &str) -> Result<Self> {
+ let parts: Vec<&str> = s.split('.').collect();
+ if parts.len() != 2 {
+ return Err(Error::InvalidCipherString);
+ }
+
+ let ty = parts[0].as_bytes();
+ if ty.len() != 1 {
+ return Err(Error::InvalidCipherString);
+ }
+
+ let ty = ty[0] - b'0';
+ let contents = parts[1];
+
+ let parts: Vec<&str> = contents.split('|').collect();
+ if parts.len() < 2 || parts.len() > 3 {
+ return Err(Error::InvalidCipherString);
+ }
+
+ let iv =
+ base64::decode(parts[0]).context(crate::error::InvalidBase64)?;
+ let ciphertext =
+ base64::decode(parts[1]).context(crate::error::InvalidBase64)?;
+ let mac = if parts.len() > 2 {
+ Some(
+ base64::decode(parts[2])
+ .context(crate::error::InvalidBase64)?,
+ )
+ } else {
+ None
+ };
+
+ Ok(Self {
+ ty,
+ iv,
+ ciphertext,
+ mac,
+ })
+ }
+
+ pub fn decrypt(&self, enc_key: &[u8], mac_key: &[u8]) -> Result<Vec<u8>> {
+ if self.ty != 2 {
+ unimplemented!()
+ }
+
+ if let Some(mac) = &self.mac {
+ let mut digest = hmac::Hmac::<sha2::Sha256>::new_varkey(mac_key)
+ .map_err(|_| Error::InvalidMacKey)?;
+ digest.input(&self.iv);
+ digest.input(&self.ciphertext);
+ let calculated_mac = digest.result().code();
+
+ if !macs_equal(mac, &calculated_mac, mac_key)? {
+ return Err(Error::InvalidMac);
+ }
+ }
+
+ let cipher = block_modes::Cbc::<
+ aes::Aes256,
+ block_modes::block_padding::Pkcs7,
+ >::new_var(enc_key, &self.iv)
+ .context(crate::error::CreateBlockMode)?;
+
+ cipher
+ .decrypt_vec(&self.ciphertext)
+ .context(crate::error::Decrypt)
+ }
+}
+
+fn macs_equal(mac1: &[u8], mac2: &[u8], mac_key: &[u8]) -> Result<bool> {
+ let mut digest = hmac::Hmac::<sha2::Sha256>::new_varkey(mac_key)
+ .map_err(|_| Error::InvalidMacKey)?;
+ digest.input(mac1);
+ let hmac1 = digest.result().code();
+
+ let mut digest = hmac::Hmac::<sha2::Sha256>::new_varkey(mac_key)
+ .map_err(|_| Error::InvalidMacKey)?;
+ digest.input(mac2);
+ let hmac2 = digest.result().code();
+
+ Ok(hmac1 == hmac2)
+}