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

Add: builtin NASL hash functions #1616

Merged
merged 4 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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),
Kraemii marked this conversation as resolved.
Show resolved Hide resolved
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
Loading