Skip to content

Commit

Permalink
Add: builtin NASL hash functions (#1616)
Browse files Browse the repository at this point in the history
* Add: builtin NASL hash functions

Jira: SC-1049

* Fix: update h2 to 0.3.26 for fixing RUSTSEC-2024-0332

* Fix: hexstr() builtin nasl function for `Data` type
  • Loading branch information
jjnicola authored Apr 16, 2024
1 parent b338f0a commit 5575c0f
Show file tree
Hide file tree
Showing 8 changed files with 632 additions and 145 deletions.
481 changes: 347 additions & 134 deletions rust/Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions rust/nasl-builtin-cryptographic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ sha2 = "0.10.6"

# depend on c libraries and are considered unstable for now
nasl-c-lib = {path = "../nasl-c-lib", optional = true}
md4 = "0.10.2"

[dev-dependencies]
nasl-interpreter = {path = "../nasl-interpreter"}
12 changes: 6 additions & 6 deletions rust/nasl-builtin-cryptographic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,19 @@ let functions = nasl_builtin_utils::NaslfunctionRegisterBuilder::new()
- HMAC_SHA256
- HMAC_SHA384
- HMAC_SHA512

## Not yet implemented

- DES
- MD2
- MD4
- MD5
- NTLMv1_HASH
- NTLMv2_HASH
- RIPEMD160
- SHA1
- SHA256
- SHA512

## Not yet implemented

- DES
- NTLMv1_HASH
- NTLMv2_HASH
- bf_cbc_decrypt
- bf_cbc_encrypt
- bn_cmp
Expand Down
89 changes: 89 additions & 0 deletions rust/nasl-builtin-cryptographic/src/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-FileCopyrightText: 2024 Greenbone AG
//
// SPDX-License-Identifier: GPL-2.0-or-later

use digest::Digest;
use md2::Md2;
use md4::Md4;
use md5::Md5;
use nasl_builtin_utils::error::FunctionErrorKind;
use ripemd::Ripemd160;
use sha1::Sha1;
use sha2::{Sha256, Sha512};

use crate::NaslFunction;
use nasl_builtin_utils::{Context, Register};
use nasl_syntax::NaslValue;

fn nasl_hash<D: Digest>(register: &Register) -> Result<NaslValue, FunctionErrorKind>
where
D::OutputSize: std::ops::Add,
<D::OutputSize as std::ops::Add>::Output: digest::generic_array::ArrayLength<u8>,
{
let positional = register.positional();
if positional.is_empty() {
return Ok(NaslValue::Null)
};
let data = match &positional[0] {
NaslValue::String(x) => x.as_bytes(),
NaslValue::Data(x) => x,
NaslValue::Null => return Ok(NaslValue::Null),
x => return Err(("data", "string", x).into()),
};

let mut hash = D::new();
hash.update(data);
Ok(NaslValue::Data(hash.finalize().as_slice().to_vec()))
}

/// NASL function to get MD2 hash
pub fn hash_md2<K>(register: &Register, _: &Context<K>) -> Result<NaslValue, FunctionErrorKind> {
nasl_hash::<Md2>(register)
}

/// NASL function to get MD4 hash
pub fn hash_md4<K>(register: &Register, _: &Context<K>) -> Result<NaslValue, FunctionErrorKind> {
nasl_hash::<Md4>(register)
}

/// NASL function to get MD5 hash
pub fn hash_md5<K>(register: &Register, _: &Context<K>) -> Result<NaslValue, FunctionErrorKind> {
nasl_hash::<Md5>(register)
}

/// NASL function to get SHA1 hash
pub fn hash_sha1<K>(register: &Register, _: &Context<K>) -> Result<NaslValue, FunctionErrorKind> {
nasl_hash::<Sha1>(register)
}

/// NASL function to get SHA256 hash
pub fn hash_sha256<K>(register: &Register, _: &Context<K>) -> Result<NaslValue, FunctionErrorKind> {
nasl_hash::<Sha256>(register)
}

/// NASL function to get SHA512 hash
pub fn hash_sha512<K>(register: &Register, _: &Context<K>) -> Result<NaslValue, FunctionErrorKind> {
nasl_hash::<Sha512>(register)
}

/// NASL function to get RIPemd160 hash
pub fn hash_ripemd160<K>(
register: &Register,
_: &Context<K>,
) -> Result<NaslValue, FunctionErrorKind> {
nasl_hash::<Ripemd160>(register)
}

/// Returns found function for key or None when not found
pub fn lookup<K>(key: &str) -> Option<NaslFunction<K>> {
match key {
"MD2" => Some(hash_md2),
"MD4" => Some(hash_md4),
"MD5" => Some(hash_md5),
"RIPEMD160" => Some(hash_ripemd160),
"SHA1" => Some(hash_sha1),
"SHA256" => Some(hash_sha256),
"SHA512" => Some(hash_sha512),
_ => None,
}
}
2 changes: 2 additions & 0 deletions rust/nasl-builtin-cryptographic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod aes_cmac;
pub mod aes_ctr;
pub mod aes_gcm;
pub mod aes_gmac;
pub mod hash;
pub mod hmac;

enum Crypt {
Expand All @@ -35,6 +36,7 @@ where
.or_else(|| aes_gcm::lookup(function_name))
.or_else(|| aes_cmac::lookup(function_name))
.or_else(|| aes_gmac::lookup(function_name))
.or_else(|| hash::lookup(function_name))
}

pub struct Cryptographic;
Expand Down
174 changes: 174 additions & 0 deletions rust/nasl-builtin-cryptographic/tests/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// SPDX-FileCopyrightText: 2024 Greenbone AG
//
// SPDX-License-Identifier: GPL-2.0-or-later

mod helper;

#[cfg(test)]
mod tests {
use nasl_interpreter::*;

#[test]
fn hash_md5() {
let code = r#"
a = MD5("hola mundo");
a = MD5('hola mundo');
a = MD5();
"#;
let mut register = Register::default();
let binding = ContextBuilder::default();
let context = binding.build();
let mut interpreter = Interpreter::new(&mut register, &context);
let mut parser =
parse(code).map(|x| interpreter.resolve(&x.expect("no parse error expected")));
assert_eq!(
parser.next(),
Some(Ok(NaslValue::Data(
[10, 208, 102, 165, 210, 159, 63, 42, 42, 28, 124, 23, 221, 8, 42, 121].to_vec()
)))
);
assert_eq!(
parser.next(),
Some(Ok(NaslValue::Data(
[10, 208, 102, 165, 210, 159, 63, 42, 42, 28, 124, 23, 221, 8, 42, 121].to_vec()
)))
);
assert_eq!(
parser.next(),
Some(Ok(NaslValue::Null))
);
}

#[test]
fn hash_md4() {
let code = r#"
MD4("hola mundo");
"#;
let mut register = Register::default();
let binding = ContextBuilder::default();
let context = binding.build();
let mut interpreter = Interpreter::new(&mut register, &context);
let mut parser =
parse(code).map(|x| interpreter.resolve(&x.expect("no parse error expected")));
assert_eq!(
parser.next(),
Some(Ok(NaslValue::Data(
[150, 189, 216, 54, 225, 218, 147, 16, 141, 155, 247, 14, 153, 134, 239, 236]
.to_vec()
)))
);
}

#[test]
fn hash_md2() {
let code = r#"
MD2("hola mundo");
"#;
let mut register = Register::default();
let binding = ContextBuilder::default();
let context = binding.build();
let mut interpreter = Interpreter::new(&mut register, &context);
let mut parser =
parse(code).map(|x| interpreter.resolve(&x.expect("no parse error expected")));
assert_eq!(
parser.next(),
Some(Ok(NaslValue::Data(
[45, 30, 74, 180, 247, 157, 181, 203, 252, 239, 123, 54, 5, 214, 55, 45].to_vec()
)))
);
}

#[test]
fn hash_sha1() {
let code = r#"
SHA1("hola mundo");
"#;
let mut register = Register::default();
let binding = ContextBuilder::default();
let context = binding.build();
let mut interpreter = Interpreter::new(&mut register, &context);
let mut parser =
parse(code).map(|x| interpreter.resolve(&x.expect("no parse error expected")));
assert_eq!(
parser.next(),
Some(Ok(NaslValue::Data(
[
69, 149, 103, 211, 189, 228, 65, 139, 127, 227, 2, 255, 152, 9, 196, 176, 190,
250, 247, 221
]
.to_vec()
)))
);
}
#[test]

fn hash_sha256() {
let code = r#"
SHA256("hola mundo");
"#;
let mut register = Register::default();
let binding = ContextBuilder::default();
let context = binding.build();
let mut interpreter = Interpreter::new(&mut register, &context);
let mut parser =
parse(code).map(|x| interpreter.resolve(&x.expect("no parse error expected")));
assert_eq!(
parser.next(),
Some(Ok(NaslValue::Data(
[
11, 137, 65, 102, 211, 51, 100, 53, 200, 0, 190, 163, 111, 242, 27, 41, 234,
168, 1, 165, 47, 88, 76, 0, 108, 73, 40, 154, 13, 207, 110, 47
]
.to_vec()
)))
);
}

#[test]
fn hash_sha512() {
let code = r#"
SHA512("hola mundo");
"#;
let mut register = Register::default();
let binding = ContextBuilder::default();
let context = binding.build();
let mut interpreter = Interpreter::new(&mut register, &context);
let mut parser =
parse(code).map(|x| interpreter.resolve(&x.expect("no parse error expected")));
assert_eq!(
parser.next(),
Some(Ok(NaslValue::Data(
[
227, 97, 236, 195, 31, 42, 172, 32, 102, 163, 16, 61, 59, 20, 220, 99, 181,
152, 75, 2, 143, 159, 45, 9, 222, 230, 116, 96, 206, 39, 2, 188, 129, 103, 58,
207, 88, 16, 155, 85, 51, 36, 133, 44, 98, 162, 39, 217, 167, 93, 76, 47, 104,
101, 128, 39, 15, 225, 67, 4, 143, 71, 195, 60
]
.to_vec()
)))
);
}

#[test]
fn hash_ripemd160() {
let code = r#"
RIPEMD160("hola mundo");
"#;
let mut register = Register::default();
let binding = ContextBuilder::default();
let context = binding.build();
let mut interpreter = Interpreter::new(&mut register, &context);
let mut parser =
parse(code).map(|x| interpreter.resolve(&x.expect("no parse error expected")));
assert_eq!(
parser.next(),
Some(Ok(NaslValue::Data(
[
224, 38, 197, 40, 255, 116, 162, 102, 178, 240, 158, 34, 193, 190, 227, 99, 44,
6, 233, 21
]
.to_vec()
)))
);
}
}
6 changes: 3 additions & 3 deletions rust/nasl-builtin-string/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
.collect()
}

/// Encodes giveen bytes to a hex string
/// Encodes given bytes to a hex string
pub fn encode_hex(bytes: &[u8]) -> Result<String, FunctionErrorKind> {
let mut s = String::with_capacity(bytes.len() * 2);
for &b in bytes {
Expand Down Expand Up @@ -220,13 +220,13 @@ fn hexstr<K>(register: &Register, _: &Context<K>) -> Result<NaslValue, FunctionE
let hexler = |x: &str| -> Result<NaslValue, FunctionErrorKind> {
let mut s = String::with_capacity(2 * x.len());
for byte in x.as_bytes() {
write!(s, "{byte:02X}")?
write!(s, "{byte:02x}")?
}
Ok(s.into())
};
match positional.first() {
Some(NaslValue::String(x)) => hexler(x),
Some(NaslValue::Data(x)) => hexler(&x.iter().map(|x| *x as char).collect::<String>()),
Some(NaslValue::Data(x)) => Ok(NaslValue::String(encode_hex(x)?.to_string())),
_ => Ok(NaslValue::Null),
}
}
Expand Down
12 changes: 10 additions & 2 deletions rust/nasl-builtin-string/tests/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod tests {
hexstr('foo', "I will be ignored");
hexstr(6);
hexstr();
hexstr(raw_string(10, 208, 102, 165, 210, 159, 63, 42, 42, 28, 124, 23, 221, 8, 42, 121));
"#;
let mut register = Register::default();
let binding = ContextBuilder::default();
Expand All @@ -21,10 +22,17 @@ mod tests {
let mut parser =
parse(code).map(|x| interpreter.resolve(&x.expect("no parse error expected")));
parser.next();
assert_eq!(parser.next(), Some(Ok("666F6F".into())));
assert_eq!(parser.next(), Some(Ok("666F6F".into())));
assert_eq!(parser.next(), Some(Ok("666f6f".into())));
assert_eq!(parser.next(), Some(Ok("666f6f".into())));
assert_eq!(parser.next(), Some(Ok(NaslValue::Null)));
assert_eq!(parser.next(), Some(Ok(NaslValue::Null)));
assert_eq!(
parser.next(),
Some(Ok(NaslValue::String(
"0ad066a5d29f3f2a2a1c7c17dd082a79".to_string()
)))
);

}
#[test]
fn raw_string() {
Expand Down

0 comments on commit 5575c0f

Please sign in to comment.