diff --git a/src/tasks/mod.rs b/src/tasks/mod.rs index f4eaab3..6c003b2 100644 --- a/src/tasks/mod.rs +++ b/src/tasks/mod.rs @@ -8,7 +8,7 @@ use crate::utils::{ }; use tasks01::{ block2poly::block2poly, - gcm::gcm_encrypt, + gcm::{gcm_decrypt, gcm_encrypt}, gfmul::gfmul_task, poly2block::poly2block, sea128::sea128, @@ -68,6 +68,14 @@ pub fn task_deploy(testcase: &Testcase) -> Result { Ok(json) } + "gcm_decrypt" => { + let (plaintext, valid) = gcm_decrypt(args)?; + let out_plain = BASE64_STANDARD.encode(&plaintext); + let json = json!({ "authentic" : valid, "plaintext" : out_plain}); + + Ok(json) + } + _ => Err(anyhow!( "Fatal. No compatible action found. Json data was {:?}. Arguments were; {:?}", testcase, @@ -201,4 +209,60 @@ mod tests { Ok(()) } + + #[test] + fn test_task_gcm_encrypt_sea_case() -> Result<()> { + let json = fs::read_to_string("test_json/gcm_encrypt_sea.json").unwrap(); + let parsed = parse_json(json).unwrap(); + + let expected = json!({ "responses" : { "b856d760-023d-4b00-bad2-15d2b6da22fe" : { + "ciphertext": "0cI/Wg4R3URfrVFZ0hw/vg==", + "tag": "ysDdzOSnqLH0MQ+Mkb23gw==", + "L": "AAAAAAAAAEAAAAAAAAAAgA==", + "H": "xhFcAUT66qWIpYz+Ch5ujw==" + }}}); + + assert_eq!( + serde_json::to_value(task_distrubute(&parsed)?).unwrap(), + serde_json::to_value(expected).unwrap() + ); + + Ok(()) + } + + #[test] + fn test_task_gcm_decrypt_aes_case() -> Result<()> { + let json = fs::read_to_string("test_json/gcm_decrypt_aes.json").unwrap(); + let parsed = parse_json(json).unwrap(); + + let expected = json!({ "responses" : { "b856d760-023d-4b00-bad2-15d2b6da22fe" : { + "plaintext": "RGFzIGlzdCBlaW4gVGVzdA==", + "authentic": true, + }}}); + + assert_eq!( + serde_json::to_value(task_distrubute(&parsed)?).unwrap(), + serde_json::to_value(expected).unwrap() + ); + + Ok(()) + } + + #[test] + fn test_task_gcm_decrypt_sea_case() -> Result<()> { + let json = fs::read_to_string("test_json/gcm_decrypt_sea.json").unwrap(); + let parsed = parse_json(json).unwrap(); + + let expected = json!({ "responses" : { "b856d760-023d-4b00-bad2-15d2b6da22fe" : { + "plaintext": "RGFzIGlzdCBlaW4gVGVzdA==", + "authentic": true, + }}}); + + assert_eq!( + serde_json::to_value(task_distrubute(&parsed)?).unwrap(), + serde_json::to_value(expected).unwrap() + ); + + Ok(()) + } } diff --git a/src/tasks/tasks01/gcm.rs b/src/tasks/tasks01/gcm.rs index 4763d53..dd13744 100644 --- a/src/tasks/tasks01/gcm.rs +++ b/src/tasks/tasks01/gcm.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Result}; use base64::prelude::*; use serde_json::Value; -use crate::utils::ciphers::gcm_encrypt_aes; +use crate::utils::ciphers::{gcm_decrypt_aes, gcm_decrypt_sea, gcm_encrypt_aes, gcm_encrypt_sea}; pub fn gcm_encrypt(args: &Value) -> Result<(Vec, Vec, Vec, Vec)> { let nonce_text: String = serde_json::from_value(args["nonce"].clone())?; @@ -21,6 +21,32 @@ pub fn gcm_encrypt(args: &Value) -> Result<(Vec, Vec, Vec, Vec)> match alg_text.as_str() { "aes128" => Ok(gcm_encrypt_aes(nonce, key, plaintext, ad)?), + "sea128" => Ok(gcm_encrypt_sea(nonce, key, plaintext, ad)?), + _ => Err(anyhow!("No compatible algorithm found")), + } +} + +pub fn gcm_decrypt(args: &Value) -> Result<(Vec, bool)> { + let nonce_text: String = serde_json::from_value(args["nonce"].clone())?; + let nonce = BASE64_STANDARD.decode(nonce_text)?; + + let key_text: String = serde_json::from_value(args["key"].clone())?; + let key = BASE64_STANDARD.decode(key_text)?; + + let plaintext_text: String = serde_json::from_value(args["ciphertext"].clone())?; + let plaintext = BASE64_STANDARD.decode(plaintext_text)?; + + let ad_text: String = serde_json::from_value(args["ad"].clone())?; + let ad = BASE64_STANDARD.decode(ad_text)?; + + let tag_text: String = serde_json::from_value(args["tag"].clone())?; + let tag = BASE64_STANDARD.decode(tag_text)?; + + let alg_text: String = serde_json::from_value(args["algorithm"].clone())?; + + match alg_text.as_str() { + "aes128" => Ok(gcm_decrypt_aes(nonce, key, plaintext, ad, tag)?), + "sea128" => Ok(gcm_decrypt_sea(nonce, key, plaintext, ad, tag)?), _ => Err(anyhow!("No compatible algorithm found")), } } diff --git a/src/utils/ciphers.rs b/src/utils/ciphers.rs index 2be30fb..1485ee6 100644 --- a/src/utils/ciphers.rs +++ b/src/utils/ciphers.rs @@ -1,9 +1,9 @@ use std::{io::BufRead, process::Output}; -use anyhow::Result; -use openssl::symm::{Cipher, Crypter, Mode}; - use crate::utils::{field::ByteArray, math::reverse_bits_in_bytevec, poly::gfmul}; +use anyhow::Result; +use base64::prelude::*; +use openssl::symm::{Cipher, Crypter, Mode}; use super::math::xor_bytes; @@ -168,6 +168,158 @@ pub fn gcm_encrypt_aes( Ok((ciphertext, auth_tag, l_field, auth_key_h)) } +pub fn gcm_decrypt_aes( + mut nonce: Vec, + key: Vec, + ciphertext: Vec, + ad: Vec, + tag: Vec, +) -> Result<(Vec, bool)> { + let mut plaintext: Vec = vec![]; + + let mut counter: u32 = 1; + nonce.append(counter.to_be_bytes().to_vec().as_mut()); + //nonce.append(0u8.to_le_bytes().to_vec().as_mut()); + eprintln!("{:001X?}", nonce); + + let auth_tag_xor = aes_128_encrypt(&key, &nonce)?; + + let auth_key_h = aes_128_encrypt(&key, &0u128.to_be_bytes().to_vec())?; + + let ciphertext_chunks: Vec> = ciphertext.chunks(16).map(|x| x.to_vec()).collect(); + + counter = 2; + for chunk in ciphertext_chunks { + nonce.drain(12..); + nonce.append(counter.to_be_bytes().to_vec().as_mut()); + + eprintln!("{:001X?}", nonce); + + let inter1 = aes_128_encrypt(&key, &nonce)?; + + let mut inter2 = xor_bytes(&inter1, chunk.clone())?; + + plaintext.append(inter2.as_mut()); + counter += 1; + } + + let mut l_field: Vec = ((ad.len() * 8) as u64).to_be_bytes().to_vec(); + let mut c_len: Vec = ((ciphertext.len() * 8) as u64).to_be_bytes().to_vec(); + l_field.append(c_len.as_mut()); + + let auth_tag = xor_bytes( + &ghash(auth_key_h.clone(), ad, ciphertext.clone(), l_field.clone())?, + auth_tag_xor, + )?; + + let valid = auth_tag == tag; + + Ok((plaintext, valid)) +} + +pub fn gcm_encrypt_sea( + mut nonce: Vec, + key: Vec, + plaintext: Vec, + ad: Vec, +) -> Result<(Vec, Vec, Vec, Vec)> { + let mut ciphertext: Vec = vec![]; + + let mut counter: u32 = 1; + nonce.append(counter.to_be_bytes().to_vec().as_mut()); + //nonce.append(0u8.to_le_bytes().to_vec().as_mut()); + eprintln!("{:001X?}", nonce); + + let auth_tag_xor = sea_128_encrypt(&key, &nonce)?; + + let auth_key_h = sea_128_encrypt(&key, &0u128.to_be_bytes().to_vec())?; + + let plaintext_chunks: Vec> = plaintext.chunks(16).map(|x| x.to_vec()).collect(); + + counter = 2; + for chunk in plaintext_chunks { + nonce.drain(12..); + nonce.append(counter.to_be_bytes().to_vec().as_mut()); + + eprintln!("{:001X?}", nonce); + + let inter1 = sea_128_encrypt(&key, &nonce)?; + + let mut inter2 = xor_bytes(&inter1, chunk.clone())?; + + ciphertext.append(inter2.as_mut()); + counter += 1; + } + + let mut l_field: Vec = ((ad.len() * 8) as u64).to_be_bytes().to_vec(); + let mut c_len: Vec = ((ciphertext.len() * 8) as u64).to_be_bytes().to_vec(); + l_field.append(c_len.as_mut()); + + let auth_tag = xor_bytes( + &ghash(auth_key_h.clone(), ad, ciphertext.clone(), l_field.clone())?, + auth_tag_xor, + )?; + + Ok((ciphertext, auth_tag, l_field, auth_key_h)) +} + +pub fn gcm_decrypt_sea( + mut nonce: Vec, + key: Vec, + ciphertext: Vec, + ad: Vec, + tag: Vec, +) -> Result<(Vec, bool)> { + let mut plaintext: Vec = vec![]; + + let mut counter: u32 = 1; + nonce.append(counter.to_be_bytes().to_vec().as_mut()); + //nonce.append(0u8.to_le_bytes().to_vec().as_mut()); + eprintln!("Nonce 1: {:001X?}", nonce); + + let auth_tag_xor = sea_128_encrypt(&key, &nonce)?; + + let auth_key_h = sea_128_encrypt(&key, &0u128.to_be_bytes().to_vec())?; + + let plaintext_chunks: Vec> = ciphertext.chunks(16).map(|x| x.to_vec()).collect(); + + eprintln!("{:?}", plaintext_chunks); + + counter = 2; + for chunk in plaintext_chunks { + eprintln!("Inside loop"); + + nonce.drain(12..); + nonce.append(counter.to_be_bytes().to_vec().as_mut()); + + eprintln!("Nonce 2: {:001X?}", nonce); + + let inter1 = sea_128_encrypt(&key, &nonce)?; + + let mut inter2 = xor_bytes(&inter1, chunk.clone())?; + + plaintext.append(inter2.as_mut()); + counter += 1; + } + + let mut l_field: Vec = ((ad.len() * 8) as u64).to_be_bytes().to_vec(); + let mut c_len: Vec = ((plaintext.len() * 8) as u64).to_be_bytes().to_vec(); + l_field.append(c_len.as_mut()); + + eprintln!("Ciphertext: {}", BASE64_STANDARD.encode(&ciphertext)); + + let auth_tag = xor_bytes( + &ghash(auth_key_h.clone(), ad, ciphertext.clone(), l_field.clone())?, + auth_tag_xor, + )?; + + eprintln!("sea dec auth tag: {}", BASE64_STANDARD.encode(&auth_tag)); + + let valid = auth_tag == tag; + + Ok((plaintext, valid)) +} + pub fn ghash( auth_key_h: Vec, mut ad: Vec, @@ -205,6 +357,8 @@ pub fn ghash( let inter4 = xor_bytes(&inter_loop, l_field)?; inter_loop = gfmul(inter4, auth_key_h.clone(), "gcm")?; + eprintln!("GHASH auth tag: {}", BASE64_STANDARD.encode(&inter_loop)); + Ok(inter_loop) } @@ -298,4 +452,86 @@ mod tests { Ok(()) } + + #[test] + fn test_gcm_encrypt_sea() -> Result<()> { + let nonce = BASE64_STANDARD.decode("4gF+BtR3ku/PUQci")?; + let key = BASE64_STANDARD.decode("Xjq/GkpTSWoe3ZH0F+tjrQ==")?; + let plaintext = BASE64_STANDARD.decode("RGFzIGlzdCBlaW4gVGVzdA==")?; + let ad = BASE64_STANDARD.decode("QUQtRGF0ZW4=")?; + + let (ciphertext, auth_tag, l_field, auth_key_h) = + gcm_encrypt_sea(nonce, key, plaintext, ad)?; + + eprintln!( + "Cipher: {:001X?} \n Tag: {:001X?} \n L_Field: {:001X?} \n H: {:001X?}", + BASE64_STANDARD.encode(&ciphertext), + BASE64_STANDARD.encode(&auth_tag), + BASE64_STANDARD.encode(&l_field), + BASE64_STANDARD.encode(&auth_key_h) + ); + + assert_eq!( + BASE64_STANDARD.encode(ciphertext), + "0cI/Wg4R3URfrVFZ0hw/vg==" + ); + assert_eq!(BASE64_STANDARD.encode(auth_tag), "ysDdzOSnqLH0MQ+Mkb23gw=="); + assert_eq!(BASE64_STANDARD.encode(l_field), "AAAAAAAAAEAAAAAAAAAAgA=="); + assert_eq!( + BASE64_STANDARD.encode(auth_key_h), + "xhFcAUT66qWIpYz+Ch5ujw==" + ); + + Ok(()) + } + + #[test] + fn test_gcm_decrypt_aes() -> Result<()> { + let nonce = BASE64_STANDARD.decode("4gF+BtR3ku/PUQci")?; + let key = BASE64_STANDARD.decode("Xjq/GkpTSWoe3ZH0F+tjrQ==")?; + let ciphertext = BASE64_STANDARD.decode("ET3RmvH/Hbuxba63EuPRrw==")?; + let ad = BASE64_STANDARD.decode("QUQtRGF0ZW4=")?; + let tag = BASE64_STANDARD.decode("Mp0APJb/ZIURRwQlMgNN/w==")?; + + let (plaintext, valid) = gcm_decrypt_aes(nonce, key, ciphertext, ad, tag)?; + + eprintln!( + "Cipher: {:001X?} \n Valids: {:001X?}", + BASE64_STANDARD.encode(&plaintext), + &valid, + ); + + assert_eq!( + BASE64_STANDARD.encode(plaintext), + "RGFzIGlzdCBlaW4gVGVzdA==" + ); + assert_eq!(valid, true); + + Ok(()) + } + + #[test] + fn test_gcm_decrypt_sea() -> Result<()> { + let nonce = BASE64_STANDARD.decode("4gF+BtR3ku/PUQci")?; + let key = BASE64_STANDARD.decode("Xjq/GkpTSWoe3ZH0F+tjrQ==")?; + let ciphertext = BASE64_STANDARD.decode("0cI/Wg4R3URfrVFZ0hw/vg==")?; + let ad = BASE64_STANDARD.decode("QUQtRGF0ZW4=")?; + let tag = BASE64_STANDARD.decode("ysDdzOSnqLH0MQ+Mkb23gw==")?; + + let (plaintext, valid) = gcm_decrypt_sea(nonce, key, ciphertext, ad, tag)?; + + eprintln!( + "Plaintext: {:001X?} \n Valid: {:001X?}", + BASE64_STANDARD.encode(&plaintext), + &valid, + ); + + assert_eq!( + BASE64_STANDARD.encode(plaintext), + "RGFzIGlzdCBlaW4gVGVzdA==" + ); + assert_eq!(valid, true); + + Ok(()) + } } diff --git a/test_json/gcm_decrypt_aes.json b/test_json/gcm_decrypt_aes.json new file mode 100644 index 0000000..720881d --- /dev/null +++ b/test_json/gcm_decrypt_aes.json @@ -0,0 +1,14 @@ +{ + "testcases": { + "b856d760-023d-4b00-bad2-15d2b6da22fe": { + "action": "gcm_decrypt", +"arguments": { +"algorithm": "aes128", +"nonce": "4gF+BtR3ku/PUQci", +"key": "Xjq/GkpTSWoe3ZH0F+tjrQ==", +"ciphertext": "ET3RmvH/Hbuxba63EuPRrw==", +"ad": "QUQtRGF0ZW4=", +"tag": "Mp0APJb/ZIURRwQlMgNN/w==" +} } + } +} diff --git a/test_json/gcm_decrypt_sea.json b/test_json/gcm_decrypt_sea.json new file mode 100644 index 0000000..c246686 --- /dev/null +++ b/test_json/gcm_decrypt_sea.json @@ -0,0 +1,14 @@ +{ + "testcases": { + "b856d760-023d-4b00-bad2-15d2b6da22fe": { + "action": "gcm_decrypt", +"arguments": { +"algorithm": "sea128", +"nonce": "4gF+BtR3ku/PUQci", +"key": "Xjq/GkpTSWoe3ZH0F+tjrQ==", +"ciphertext": "0cI/Wg4R3URfrVFZ0hw/vg==", +"ad": "QUQtRGF0ZW4=", +"tag": "ysDdzOSnqLH0MQ+Mkb23gw==" +} } + } +} diff --git a/test_json/gcm_encrypt_sea.json b/test_json/gcm_encrypt_sea.json new file mode 100644 index 0000000..6a2a6a5 --- /dev/null +++ b/test_json/gcm_encrypt_sea.json @@ -0,0 +1,14 @@ +{ + "testcases": { + "b856d760-023d-4b00-bad2-15d2b6da22fe": { + "action": "gcm_encrypt", + "arguments": { +"algorithm": "sea128", +"nonce": "4gF+BtR3ku/PUQci", +"key": "Xjq/GkpTSWoe3ZH0F+tjrQ==", +"plaintext": "RGFzIGlzdCBlaW4gVGVzdA==", +"ad": "QUQtRGF0ZW4=" +} + } + } +}