Skip to content

Commit

Permalink
Argon2 (by @Jujumba) (#67)
Browse files Browse the repository at this point in the history
* argon2: Initial commit

* readme: fix readme

* argon2: impl `From` trait for Argon2 structs

* argon2: add switch

* argon2: impl `process_argon2`

* argon2: fix serialization

* argon2: Add rfc link

* argon2: input: add `set_version` closure

* argon2: fix `[u8]` to `String` conversion

* argon2: fixes

* info: Add `Argon2` and `Bcrypt`

* argon2: generate random salt if empty one is supplied

* argon2: fix random salt generation

* argon2: impl `TryFrom<&str>` for `Argon2Variant`

* argon2: add salt input

* argon2: remove duplicate entries in the about page

* argon2: remove extra `data` field

* argon2: fix random salt generation and base64 padding character

* argon2: fix salt encoding & decoding

* argon2: fix hash computation

* argon2: fix `Default` impl for `Argon2HashAction`

* argon2: fix error types

* argon2: fix

* argon2: add hash verification

* argon2: run formatter

* feat(crypto-helper): update rust toolchain and inproved argon2 ui;

---------

Co-authored-by: Jujumba <[email protected]>
  • Loading branch information
TheBestTvarynka and Jujumba authored Mar 27, 2024
1 parent 75a0f51 commit 2196f39
Show file tree
Hide file tree
Showing 12 changed files with 471 additions and 83 deletions.
173 changes: 104 additions & 69 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,15 @@ hmac-sha512 = { version = "1.1", features = ["sha384"] }
rsa = "0.9"
bcrypt = "0.15"
flate2 = { version = "1.0", features = ["zlib"] }
rand = { version = "0.9.0-alpha.0", default-features = false, features = ["small_rng"] }
rand = { version = "0.9.0-alpha.0", features = ["small_rng"] }
rand_chacha = "0.9.0-alpha.0"
argon2 = "0.5"
password-hash = "0.5"

# asn1
asn1-parser = { path = "./crates/asn1-parser", features = ["std"] }
oid = { version = "0.2", default-features = false }
paste = "1.0"

# diff
similar = { version = "2.4", features = ["serde"] }
similar = { version = "2.4", features = ["serde"] }
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,26 @@ Visit this tool at [crypto.qkation.com](https://crypto.qkation.com).

Table of content:

* [Features](#features)
* [Development](#development)
* [Meta](#meta)
* [Contributing](#contributing)
- [crypto-helper](#crypto-helper)
- [Features](#features)
- [Development](#development)
- [Meta](#meta)
- [Contributing](#contributing)

![](/public/img/example.png)
![](/public/img/sha.png)
![](/public/img/jwt.png)

The crypto-helper is an online app that helps to work with the different crypto algorithms. This app can hash/hmac, encrypt/decrypt, and sign/verify the data.
The crypto-helper is a web app that helps to work with the different crypto algorithms. This app can hash/hmac, encrypt/decrypt, and sign/verify the data.

All computations are performed on the client side. This tool never sends the data to any server.

### Features

* Written in [Rust](https://github.com/rust-lang/rust) :crab: using [yew](https://github.com/yewstack/yew) :sparkles:
* `MD5`
* `Argon2`
* `BCRYPT`
* `SHA1`/`SHA256`/`SHA384`/`SHA512`
* Kerberos ciphers: `AES128-CTS-HMAC-SHA1-96`/`AES256-CTS-HMAC-SHA1-96`
* Kerberos HMAC: `HMAC-SHA1-96-AES128`/`HMAC-SHA1-96-AES256`
Expand Down Expand Up @@ -55,7 +58,7 @@ export APP_HOST=<url>
# example:
# export APP_HOST=https://crypto-helper.qkation.com
```
This env variable is uses for the url generation when you click the *share by url* button.
This env variable is used for the url generation when you click the *share by url* button.

3. Run `trunk serve` in your terminal.
4. Go to http://127.0.0.1:8080 in your browser.
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[toolchain]
channel = "1.76.0"
channel = "1.77.0"
components = [ "rustfmt", "clippy" ]
2 changes: 2 additions & 0 deletions src/about.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub fn about() -> Html {
<span>{"Crypto-helper"}</span>
<span>{"The crypto-helper is an online app that helps to work with the diferent crypto algorithms:"}</span>
<ul>
<li>{"Argon2"}</li>
<li>{"BCrypt"}</li>
<li>{"MD5"}</li>
<li>{"SHA1/SHA256/SHA384/SHA512"}</li>
<li>{"Kerberos ciphers: AES128-CTS-HMAC-SHA1-96/AES256-CTS-HMAC-SHA1-96"}</li>
Expand Down
3 changes: 2 additions & 1 deletion src/crypto_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use yew::{function_component, html, use_effect_with, use_state, Callback, Html};
use yew_hooks::{use_clipboard, use_local_storage, use_location};
use yew_notifications::{use_notification, Notification, NotificationType};

use self::computations::{process_krb_cipher, process_krb_hmac, process_rsa, process_zlib};
use self::computations::{process_argon2, process_krb_cipher, process_krb_hmac, process_rsa, process_zlib};
use crate::crypto_helper::computations::process_bcrypt;
use crate::url_query_params::generate_crypto_helper_link;

Expand All @@ -40,6 +40,7 @@ fn convert(algrithm: &Algorithm) -> Result<Vec<u8>, String> {
Algorithm::Rsa(input) => process_rsa(input),
Algorithm::Bcrypt(input) => process_bcrypt(input),
Algorithm::Zlib(input) => process_zlib(input),
Algorithm::Argon2(input) => process_argon2(input),
}
}

Expand Down
179 changes: 176 additions & 3 deletions src/crypto_helper/algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ pub const RSA: &str = "RSA";
pub const SHA384: &str = "SHA384";
pub const BCRYPT: &str = "BCRYPT";
pub const ZLIB: &str = "ZLIB";
pub const ARGON2: &str = "ARGON2";

pub const SUPPORTED_ALGORITHMS: [&str; 12] = [
pub const SUPPORTED_ALGORITHMS: [&str; 13] = [
MD5,
SHA1,
SHA256,
Expand All @@ -32,9 +33,10 @@ pub const SUPPORTED_ALGORITHMS: [&str; 12] = [
SHA384,
BCRYPT,
ZLIB,
ARGON2,
];

pub const HASHING_ALGOS: [&str; 6] = [MD5, SHA1, SHA256, SHA384, SHA512, BCRYPT];
pub const HASHING_ALGOS: [&str; 7] = [MD5, SHA1, SHA256, SHA384, SHA512, BCRYPT, ARGON2];

pub const ENCRYPTION_ALGOS: [&str; 3] = [AES128_CTS_HMAC_SHA1_96, AES256_CTS_HMAC_SHA1_96, RSA];

Expand Down Expand Up @@ -336,7 +338,6 @@ pub enum ZlibMode {
Compress,
Decompress,
}

impl From<ZlibMode> for bool {
fn from(mode: ZlibMode) -> Self {
match mode {
Expand All @@ -362,6 +363,174 @@ pub struct ZlibInput {
pub data: Vec<u8>,
}

#[derive(Eq, Clone, Copy, PartialEq, Debug, Serialize, Deserialize, Default)]
pub enum Argon2Variant {
Argon2i,
Argon2d,
#[default]
Argon2id,
}

#[derive(Eq, Clone, Copy, PartialEq, Debug, Serialize, Deserialize, Default)]
pub enum Argon2Version {
Version10,
#[default]
Version13,
}

#[derive(Eq, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Argon2HashAction {
pub memory: u32,
pub iters: u32,
pub paralelism: u32,
pub output_len: usize,
#[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")]
pub salt: Vec<u8>,
#[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")]
pub secret: Vec<u8>,
pub variant: Argon2Variant,
pub version: Argon2Version,
}

#[derive(Eq, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub enum Argon2Action {
Hash(Argon2HashAction),
Verify(#[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")] Vec<u8>),
}

#[derive(Eq, Clone, PartialEq, Debug, Serialize, Deserialize, Default)]
pub struct Argon2Input {
pub action: Argon2Action,
#[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")]
pub data: Vec<u8>,
}

#[non_exhaustive]
#[derive(Eq, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub enum Argon2Error<'a> {
InvalidVersion(&'a str),
InvalidVariant(&'a str),
}

impl<'a> std::fmt::Display for Argon2Error<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidVersion(version) => write!(f, "InvalidVersion({version})"),
Self::InvalidVariant(variant) => write!(f, "InvalidVariant({variant})"),
}
}
}
impl<'a> std::error::Error for Argon2Error<'a> {}

impl Argon2Input {
pub fn with_variant(&self, variant: Argon2Variant) -> Self {
let mut input = self.clone();
if let Argon2Action::Hash(ref mut action) = input.action {
action.variant = variant;
}
input
}

pub fn with_version(&self, version: Argon2Version) -> Self {
let mut input = self.clone();
if let Argon2Action::Hash(ref mut action) = input.action {
action.version = version;
}
input
}
}
impl<'a> TryFrom<&'a str> for Argon2Version {
type Error = Argon2Error<'a>;

fn try_from(value: &'a str) -> Result<Self, Self::Error> {
match value {
"Argon13" => Ok(Self::Version13),
"Argon10" => Ok(Self::Version10),
invalid => Err(Argon2Error::InvalidVersion(invalid)),
}
}
}
impl From<Argon2Variant> for argon2::Algorithm {
fn from(value: Argon2Variant) -> Self {
match value {
Argon2Variant::Argon2d => argon2::Algorithm::Argon2d,
Argon2Variant::Argon2i => argon2::Algorithm::Argon2i,
Argon2Variant::Argon2id => argon2::Algorithm::Argon2id,
}
}
}
impl<'a> TryFrom<&'a str> for Argon2Variant {
type Error = Argon2Error<'a>;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
match value {
"Argon2i" => Ok(Self::Argon2i),
"Argon2d" => Ok(Self::Argon2d),
"Argon2id" => Ok(Self::Argon2id),
invalid => Err(Argon2Error::InvalidVariant(invalid)),
}
}
}

impl From<Argon2Version> for argon2::Version {
fn from(value: Argon2Version) -> Self {
match value {
Argon2Version::Version10 => argon2::Version::V0x10,
Argon2Version::Version13 => argon2::Version::V0x13,
}
}
}

impl From<&Argon2HashAction> for argon2::Params {
fn from(value: &Argon2HashAction) -> Self {
argon2::ParamsBuilder::new()
.m_cost(value.memory)
.p_cost(value.paralelism)
.t_cost(value.iters)
.build()
.expect("argon2::ParamsBuilder should never fail")
}
}

impl Default for Argon2HashAction {
fn default() -> Self {
use argon2::Params;
Self {
memory: Params::DEFAULT_M_COST,
iters: Params::DEFAULT_T_COST,
paralelism: Params::DEFAULT_P_COST,
output_len: Params::DEFAULT_OUTPUT_LEN,
salt: Default::default(),
secret: Default::default(),
variant: Default::default(),
version: Default::default(),
}
}
}

impl Default for Argon2Action {
fn default() -> Self {
Self::Hash(Argon2HashAction::default())
}
}

impl From<bool> for Argon2Action {
fn from(value: bool) -> Self {
match value {
true => Argon2Action::Verify(Default::default()),
false => Argon2Action::Hash(Default::default()),
}
}
}

impl From<&Argon2Action> for bool {
fn from(value: &Argon2Action) -> Self {
match value {
Argon2Action::Verify(_) => true,
Argon2Action::Hash(_) => false,
}
}
}

#[allow(clippy::large_enum_variant)]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum Algorithm {
Expand All @@ -382,6 +551,7 @@ pub enum Algorithm {
Rsa(RsaInput),
Bcrypt(BcryptInput),
Zlib(ZlibInput),
Argon2(Argon2Input),
}

impl TryFrom<&str> for Algorithm {
Expand Down Expand Up @@ -412,6 +582,8 @@ impl TryFrom<&str> for Algorithm {
return Ok(Algorithm::Bcrypt(Default::default()));
} else if value == ZLIB {
return Ok(Algorithm::Zlib(Default::default()));
} else if value == ARGON2 {
return Ok(Algorithm::Argon2(Default::default()));
}

Err(format!(
Expand All @@ -436,6 +608,7 @@ impl From<&Algorithm> for &str {
Algorithm::Rsa(_) => RSA,
Algorithm::Bcrypt(_) => BCRYPT,
Algorithm::Zlib(_) => ZLIB,
Algorithm::Argon2(_) => ARGON2,
}
}
}
Expand Down
43 changes: 42 additions & 1 deletion src/crypto_helper/computations.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::convert::TryInto;
use std::io::Write;

use argon2::{PasswordHasher, PasswordVerifier};
use base64::Engine;
use bcrypt::Version;
use flate2::write::{ZlibDecoder, ZlibEncoder};
use flate2::Compression;
Expand All @@ -10,7 +12,8 @@ use rsa::rand_core::OsRng;
use rsa::Pkcs1v15Encrypt;

use super::algorithm::{
BcryptAction, BcryptInput, KrbInput, KrbInputData, KrbMode, RsaAction, RsaInput, ZlibInput, ZlibMode,
Argon2Action, Argon2Input, BcryptAction, BcryptInput, KrbInput, KrbInputData, KrbMode, RsaAction, RsaInput,
ZlibInput, ZlibMode,
};

pub fn process_rsa(input: &RsaInput) -> Result<Vec<u8>, String> {
Expand Down Expand Up @@ -91,3 +94,41 @@ pub fn process_zlib(input: &ZlibInput) -> Result<Vec<u8>, String> {
}
}
}

pub fn process_argon2(input: &Argon2Input) -> Result<Vec<u8>, String> {
match &input.action {
Argon2Action::Hash(hash_action) => {
let argon2ctx = argon2::Argon2::new(
hash_action.variant.into(),
hash_action.version.into(),
hash_action.into(),
);

let salt = if hash_action.salt.is_empty() {
password_hash::SaltString::generate(OsRng)
} else {
password_hash::SaltString::from_b64(
&base64::engine::general_purpose::STANDARD_NO_PAD.encode(&hash_action.salt),
)
.map_err(|e| e.to_string())?
};

let hash = argon2ctx
.hash_password(&input.data, salt.as_salt())
.map_err(|err| err.to_string())?
.to_string();

Ok(hash.into_bytes())
}
Argon2Action::Verify(data) => {
let str = String::from_utf8(data.clone()).map_err(|e| e.to_string())?;
let hash = argon2::PasswordHash::new(&str).map_err(|e| e.to_string())?;

if argon2::Argon2::default().verify_password(&input.data, &hash).is_ok() {
Ok(vec![1])
} else {
Ok(vec![0])
}
}
}
}
5 changes: 5 additions & 0 deletions src/crypto_helper/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ fn get_algorithm_info(algorithm: &Algorithm) -> Html {
<a href="https://www.rfc-editor.org/rfc/rfc1950">{"RFC"}</a>
</span>
},
Algorithm::Argon2(_) => html! {
<span>{"Use Argon2 to encrypt/verify your data."}
<a href="https://www.rfc-editor.org/rfc/inline-errata/rfc9106.html">{"RFC"}</a>
</span>
},
}
}

Expand Down
Loading

0 comments on commit 2196f39

Please sign in to comment.