Skip to content

Feat/decryption #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 10 commits into
base: feat/encryptionv38e2e
Choose a base branch
from
5 changes: 5 additions & 0 deletions chain-common/proto/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "transaction.proto";
import "validation.proto";
import "persona.proto";
import "post-encryption.proto";
import "post-decryption.proto";

message MWRequest {
oneof request {
Expand Down Expand Up @@ -39,6 +40,8 @@ message MWRequest {
PersonaGenerationParam param_generate_persona = 26;

PostEncryptionParam param_post_encryption = 27;

PostDecryptionParam param_post_decryption = 28;
}
}

Expand Down Expand Up @@ -69,6 +72,8 @@ message MWResponse {
PersonaGenerationResp resp_generate_persona = 25;

PostEncryptedResp resp_post_encryption = 26;

PostDecryptionResp resp_post_decryption = 27;
}
}

Expand Down
15 changes: 15 additions & 0 deletions chain-common/proto/post-decryption.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
syntax = "proto3";

package api;

import "base.proto";

message PostDecryptionParam {
string postContent = 1;
optional string postIdentifier = 2;
optional bytes localKeyData = 3;
}

message PostDecryptionResp {
string contentText = 1;
}
22 changes: 20 additions & 2 deletions chain-common/src/generated/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,22 @@ pub enum PublicKeyAlgorithm {
Secp256k1Algr = 2,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct PostDecryptionParam {
#[prost(string, tag="1")]
pub post_content: ::prost::alloc::string::String,
#[prost(string, optional, tag="2")]
pub post_identifier: ::core::option::Option<::prost::alloc::string::String>,
#[prost(bytes="vec", optional, tag="3")]
pub local_key_data: ::core::option::Option<::prost::alloc::vec::Vec<u8>>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct PostDecryptionResp {
#[prost(string, tag="1")]
pub content_text: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct MwRequest {
#[prost(oneof="mw_request::Request", tags="1, 2, 3, 4, 5, 10, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27")]
#[prost(oneof="mw_request::Request", tags="1, 2, 3, 4, 5, 10, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28")]
pub request: ::core::option::Option<mw_request::Request>,
}
/// Nested message and enum types in `MWRequest`.
Expand Down Expand Up @@ -536,11 +550,13 @@ pub mod mw_request {
ParamGeneratePersona(super::PersonaGenerationParam),
#[prost(message, tag="27")]
ParamPostEncryption(super::PostEncryptionParam),
#[prost(message, tag="28")]
ParamPostDecryption(super::PostDecryptionParam),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct MwResponse {
#[prost(oneof="mw_response::Response", tags="1, 2, 3, 4, 5, 6, 11, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 25, 26")]
#[prost(oneof="mw_response::Response", tags="1, 2, 3, 4, 5, 6, 11, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 25, 26, 27")]
pub response: ::core::option::Option<mw_response::Response>,
}
/// Nested message and enum types in `MWResponse`.
Expand Down Expand Up @@ -585,6 +601,8 @@ pub mod mw_response {
RespGeneratePersona(super::PersonaGenerationResp),
#[prost(message, tag="26")]
RespPostEncryption(super::PostEncryptedResp),
#[prost(message, tag="27")]
RespPostDecryption(super::PostDecryptionResp),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
Expand Down
3 changes: 1 addition & 2 deletions crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,4 @@ pbkdf2 = { version = "0.11", default-features = false }
hmac = { version = "0.12.1" }
ctr = { version = "0.9.1" }
aes-gcm = { version = "0.9.4" }
rmp = { version = "0.8.1" }

rmp = { version = "0.8.11" }
3 changes: 3 additions & 0 deletions crypto/src/encryption_constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub(crate) const SHARED_KEY_ENCODED: &str = "3Bf8BJ3ZPSMUM2jg2ThODeLuRRD_-_iwQEaeLdcQXpg";
pub(crate) const IV_SIZE: usize = 16;
pub(crate) const AES_KEY_SIZE: usize = 32;
9 changes: 9 additions & 0 deletions crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub mod post_encryption;
pub mod jwk;
pub mod pbkdf2;

mod encryption_constants;
mod payload_decode_v37;
pub mod payload_decode_v38;

#[derive(Debug, PartialOrd, PartialEq)]
pub enum Error {
KdfParamsInvalid,
Expand Down Expand Up @@ -48,6 +52,8 @@ pub enum Error {
NotSupportedCipher,

InvalidLocalKey,

DecryptContentFailed,
}

impl Error {
Expand All @@ -67,6 +73,7 @@ impl Error {
Error::NotSupportedCurve => "-3012".to_owned(),
Error::NotSupportedCipher => "-3013".to_owned(),
Error::InvalidLocalKey => "-3014".to_owned(),
Error::DecryptContentFailed => "-3015".to_owned(),
}
}

Expand All @@ -88,6 +95,8 @@ impl Error {
Error::InvalidLocalKey => {
"Invalid local key. Local key is required to encrypt private message".to_owned()
}

Error::DecryptContentFailed => "Failed to decrypt post content".to_owned(),
}
}
}
Expand Down
178 changes: 178 additions & 0 deletions crypto/src/payload_decode_v37.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
use std::str::from_utf8;

use super::payload_encode_v37::Index;
use super::Error;
use rmp::decode::*;

struct DecodedData {
network: String,
author_id: String,
algorithm: i64,
author_pub_key: Vec<u8>,
aes_key: Vec<u8>,
iv: Vec<u8>,
encrypted_content: Vec<u8>,
}

fn decode_with_container(encrypted: &[u8]) -> Result<DecodedData, Error> {
let mut content = Bytes::new(encrypted);
let map_len = read_map_len(&mut content).map_err(|_| Error::InvalidCiphertext)?;
if map_len != 2 {
return Err(Error::InvalidCiphertext);
}

let flag: i64 = read_int(&mut content).map_err(|_| Error::InvalidCiphertext)?;
if flag != 0 {
return Err(Error::InvalidCiphertext);
}
let _ = read_bin_len(&mut content).map_err(|_| Error::InvalidCiphertext)?;

let data_map_len = read_map_len(&mut content).map_err(|_| Error::InvalidCiphertext)?;
if data_map_len != 6 {
return Err(Error::InvalidCiphertext);
}

// network
let author_network =
decode_str(&mut content, Index::AuthorNetwork).map_err(|_| Error::InvalidCiphertext)?;

// author_id
let author_decoded_id =
decode_str(&mut content, Index::AuthorID).map_err(|_| Error::InvalidCiphertext)?;

// algorithm
let author_pub_key_algorithm: i64 = decode_int64(&mut content, Index::AuthorPublicKeyAlgorithm)
.map_err(|_| Error::InvalidCiphertext)?;

// author_pub_key
let author_pub_key = decode_bin(&mut content, Option::Some(Index::AuthorPublicKey))
.map_err(|_| Error::InvalidCiphertext)?;

// iv and aes_key
let encryption_index: i64 = read_int(&mut content).map_err(|_| Error::InvalidCiphertext)?;
match encryption_index.try_into()? {
Index::Encryption => {}
_ => return Err(Error::InvalidCiphertext),
}
let array_len = read_array_len(&mut content).map_err(|_| Error::InvalidCiphertext)?;
if array_len != 3 {
return Err(Error::InvalidCiphertext);
}
let flag: i64 = read_int(&mut content).map_err(|_| Error::InvalidCiphertext)?;
if flag != 0 {
return Err(Error::InvalidCiphertext);
}
let aes_key = decode_bin(&mut content, Option::None).map_err(|_| Error::InvalidCiphertext)?;
let decoded_iv =
decode_bin(&mut content, Option::None).map_err(|_| Error::InvalidCiphertext)?;

// encrypted text
let decoded_data = decode_bin(&mut content, Option::Some(Index::Data))
.map_err(|_| Error::InvalidCiphertext)?;

Ok(DecodedData {
network: author_network,
author_id: author_decoded_id,
algorithm: author_pub_key_algorithm,
author_pub_key,
aes_key,
iv: decoded_iv,
encrypted_content: decoded_data,
})
}

fn decode_str(bytes: &mut Bytes, index: Index) -> Result<String, Error> {
let index_value: i64 = rmp::decode::read_int(bytes).map_err(|_| Error::InvalidCiphertext)?;
let index_value: Index = index_value.try_into()?;

if index_value != index {
return Err(Error::InvalidCiphertext);
}

let str_len = read_str_len(bytes).map_err(|_| Error::InvalidCiphertext)?;
let mut str_buf = [0u8; 1].repeat(str_len as usize);
let _ = bytes
.read_exact_buf(&mut str_buf)
.map_err(|_| Error::InvalidCiphertext)?;

match from_utf8(&str_buf) {
Ok(decoded) => Ok(decoded.to_owned()),
_ => Err(Error::InvalidCiphertext),
}
}

fn decode_int64(bytes: &mut Bytes, index: Index) -> Result<i64, Error> {
let decoded_index: i64 = rmp::decode::read_int(bytes).map_err(|_| Error::InvalidCiphertext)?;
let decoded_index: Index = decoded_index.try_into()?;
if decoded_index != index {
return Err(Error::InvalidCiphertext);
}

match read_int(bytes) {
Ok(decoded) => Ok(decoded),
_ => Err(Error::InvalidCiphertext),
}
}

fn decode_bin(bytes: &mut Bytes, index: Option<Index>) -> Result<Vec<u8>, Error> {
if let Some(index) = index {
let data_index: i64 = read_int(bytes).map_err(|_| Error::InvalidCiphertext)?;
let data_index: Index = data_index.try_into()?;
if data_index != index {
return Err(Error::InvalidCiphertext);
}
}

let data_len = read_bin_len(bytes).map_err(|_| Error::InvalidCiphertext)?;
let mut decoded_data = [0u8; 1].repeat(data_len as usize);
bytes
.read_exact_buf(&mut decoded_data)
.map_err(|_| Error::InvalidCiphertext)?;

Ok(decoded_data.to_vec())
}

#[cfg(test)]
mod tests {
use super::super::number_util::random_iv;
use super::super::payload_encode_v37::*;
use super::super::Error;
use super::*;

const IV_SIZE: usize = 16;
const AES_KEY_SIZE: usize = 32;

#[test]
fn test_decode_v37() -> Result<(), Error> {
let post_iv = random_iv(IV_SIZE);
let post_key_iv = random_iv(AES_KEY_SIZE);
let author_key = random_iv(33);
let text_content = random_iv(32);
let network = "eht";
let author_id = "1331444";
let algr = 2;

let encrypted = encode_with_container(
network,
author_id,
algr,
&post_key_iv,
&author_key,
&post_iv,
&text_content,
)
.unwrap();

let result = decode_with_container(&encrypted).map_err(|_| Error::InvalidCiphertext)?;

assert_eq!(result.network, network);
assert_eq!(result.author_id, author_id);
assert_eq!(result.algorithm, algr as i64);
assert_eq!(result.author_pub_key, post_key_iv);
assert_eq!(result.aes_key, author_key);
assert_eq!(result.iv, post_iv);
assert_eq!(result.encrypted_content, text_content);

Ok(())
}
}
Loading