From 9017da92b264c2263fb8a187d4a90aa958781fca Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Tue, 9 May 2023 17:11:17 +0200 Subject: [PATCH] add password checker example --- riscv/tests/riscv.rs | 7 ++ .../riscv_data/password_checker/Cargo.toml | 11 ++ .../riscv_data/password_checker/src/lib.rs | 101 ++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 riscv/tests/riscv_data/password_checker/Cargo.toml create mode 100644 riscv/tests/riscv_data/password_checker/src/lib.rs diff --git a/riscv/tests/riscv.rs b/riscv/tests/riscv.rs index caebc5fdb..d5a7d1688 100644 --- a/riscv/tests/riscv.rs +++ b/riscv/tests/riscv.rs @@ -64,6 +64,13 @@ fn test_keccak() { verify_crate(case, vec![]); } +#[test] +#[ignore = "Too slow"] +fn test_password() { + let case = "password_checker"; + verify_crate(case, vec![]); +} + #[test] #[ignore = "Too slow"] // TODO: instead of just checking for panic, we could check the stdout for the actual message. diff --git a/riscv/tests/riscv_data/password_checker/Cargo.toml b/riscv/tests/riscv_data/password_checker/Cargo.toml new file mode 100644 index 000000000..d08d110fb --- /dev/null +++ b/riscv/tests/riscv_data/password_checker/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "password_checker" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +runtime = { path = "../../../runtime" } + +[workspace] diff --git a/riscv/tests/riscv_data/password_checker/src/lib.rs b/riscv/tests/riscv_data/password_checker/src/lib.rs new file mode 100644 index 000000000..3a0af9b41 --- /dev/null +++ b/riscv/tests/riscv_data/password_checker/src/lib.rs @@ -0,0 +1,101 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] + +pub struct PasswordRequest { + pub password: &'static str, + pub salt: [u8; 32], +} + +#[no_mangle] +pub fn main() { + let request = PasswordRequest { + // Uncomment \/ to see it fail + //password: "12345678", + password: "S00perSecr1t!!!", + salt: [0xaa; 32] + }; + + let policy = PasswordPolicy { + min_length: 3, + max_length: 64, + min_numeric: 2, + min_uppercase: 2, + min_lowercase: 2, + min_special_chars: 1, + }; + + assert!(policy.is_valid(&request.password)); +} + +struct PasswordPolicy { + pub min_length: usize, + pub max_length: usize, + pub min_uppercase: usize, + pub min_lowercase: usize, + pub min_numeric: usize, + pub min_special_chars: usize, +} + +impl PasswordPolicy { + pub fn is_valid(&self, pw: &str) -> bool { + let metrics = PasswordMetrics::new(pw); + self.correct_length(pw) + && (metrics.numeric >= self.min_numeric) + && (metrics.uppercase >= self.min_uppercase) + && (metrics.lowercase >= self.min_lowercase) + && (metrics.special >= self.min_special_chars) + } + + fn correct_length(&self, password: &str) -> bool { + password.len() > (self.min_length - 1) && password.len() < (self.max_length + 1) + } +} + +struct PasswordMetrics { + pub numeric: usize, + pub special: usize, + pub uppercase: usize, + pub lowercase: usize, +} + +impl PasswordMetrics { + pub fn new(password: &str) -> Self { + let mut numeric = 0; + let mut special = 0; + let mut uppercase = 0; + let mut lowercase = 0; + for ch in password.chars() { + if ch.is_ascii_digit() { + numeric += 1; + } + if ch.is_ascii_punctuation() { + special += 1; + } + if ch.is_ascii_uppercase() { + uppercase += 1; + } + if ch.is_ascii_lowercase() { + lowercase += 1; + } + } + PasswordMetrics { + numeric, + special, + uppercase, + lowercase, + } + } +}