Skip to content
Draft
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