From d6d345207530ec3232d937aeee3b0c9255b33129 Mon Sep 17 00:00:00 2001 From: Andreas Grois Date: Sun, 9 Oct 2022 14:00:38 +0200 Subject: Also add url_parsing to this crate. --- src/lib.rs | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 140 insertions(+), 6 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index ad4ae5c..e9181c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,17 @@ +#![warn(missing_docs)] +//! Library that should allow quick implementation of tools that are compatible with PasswordMaker Pro. +//! +//! It forms the core of an upcoming PasswordMaker Pro compatible Sailfish OS App (as of yet unnamed). +//! This library intentionally does not depend on any specific implementation of the cryptographic hashes +//! it relies on. To see an example of how to integrate with the [Rust Crypto Hashes](https://github.com/RustCrypto/hashes), +//! see the integration tests. +//! +//! There are two main functions in this library: [`generate_password`][PasswordMaker::generate_password] and +//! [`parse()`][UrlParsing::parse]. + + mod passwordmaker; +mod url_parsing; use passwordmaker::{PasswordPartParameters, PasswordAssemblyParameters}; use passwordmaker::leet::LeetReplacementTable; use std::error::Error; @@ -7,8 +20,13 @@ use std::marker::PhantomData; /// Trait you need to implement for the various hash functions you need to provide. /// Currently only a single function, that computes the hash of a string slice, is needed. This may change in a later version. +/// +/// Beware: There is currently no way to put constraints on associated constants in Rust, so Block Size is not exposed. +/// It's anyhow the same (currently hardcoded) value for all supported algorithms. pub trait Hasher { + /// The output type of the respective hash function. Typically some form of byte array. type Output; + /// Function that takes a byte array as input, and generates the cryptographic hash of it as output. fn hash(input : &[u8]) -> Self::Output; } @@ -25,18 +43,23 @@ pub trait Ripemd160 : Hasher {} /// List of hash functions to use. Trait may change in later versions to include constructors for actual hasher objects. pub trait HasherList { + /// The type that offers MD4 hashing. See the [`Md4`] trait. type MD4 : Md4; + /// The type that offers MD5 hashing. See the [`Md5`] trait. type MD5 : Md5; + /// The type that offers SHA1 hashing. See the [`Sha1`] trait. type SHA1 : Sha1; + /// The type that offers SHA256 hashing. See the [`Sha256`] trait. type SHA256 : Sha256; + /// The type that offers Ripemd160 hashing. See the [`Ripemd160`] trait. type RIPEMD160 : Ripemd160; } /// A single-use instance of PasswordMaker, created after all inputs are verified to be usable. /// Only has one method, which is to generate the password. pub struct PasswordMaker<'a, T : HasherList>{ - data : &'a str, //aka url aka used text - key : &'a str, //aka master password + data : String, //aka url aka used text + key : String, //aka master password username : &'a str, modifier : &'a str, password_part_parameters : PasswordPartParameters<'a>, //contains pre_leet, as this is different for different algorithms @@ -46,7 +69,41 @@ pub struct PasswordMaker<'a, T : HasherList>{ } impl<'a, T : HasherList> PasswordMaker<'a, T>{ - /// Validates user input and returns a PasswordMaker if the input is valid. + /// Generates a password in a way that's (hopefully) compatible to PasswordMaker Pro. Returns an error for unusable input. + /// + /// `data` is the string to use, typically a URL or a part of it. + /// `key` is the master password. + /// `hash_algorithm` is a PasswordMaker Pro algorithm selection. + /// `use_leet` details when to use leet, if at all. + /// `characters` is the list of output password characters. Actually this is not true. It's the list of grapheme clusters. + /// `username` is the "username" field of PasswordMaker Pro. + /// `modifier` is the "modifier" field of PasswordMaker Pro. + /// `password_length` is the desired password length to generate. + /// `prefix` is the prefix to which the password gets appended. Counts towards `password_length`. + /// `suffix` is the suffix appended to the password. Counts towards `password_length`. + pub fn generate_password( + data : String, + key: String, + hash_algorithm : HashAlgorithm, + use_leet : UseLeetWhenGenerating, + characters : &'a str, + username : &'a str, + modifier: &'a str, + password_length : usize, + prefix : &'a str, + suffix : &'a str, + ) -> Result{ + Ok( + Self::validate_input(data, key, hash_algorithm, use_leet, characters, username, modifier, password_length, prefix, suffix)? + .generate() + ) + } + + + /// Validates user input and returns a `PasswordMaker` object if the input is valid. + /// Use this if you want to split input validation from actual password computation. + /// Otherwise, consider using the `generate_password` function for shorter code. + /// /// `data` is the string to use, typically a URL or a part of it. /// `key` is the master password. /// `hash_algorithm` is a PasswordMaker Pro algorithm selection. @@ -58,8 +115,8 @@ impl<'a, T : HasherList> PasswordMaker<'a, T>{ /// `prefix` is the prefix to which the password gets appended. Counts towards `password_length`. /// `suffix` is the suffix appended to the password. Counts towards `password_length`. pub fn validate_input( - data : &'a str, - key: &'a str, + data : String, + key: String, hash_algorithm : HashAlgorithm, use_leet : UseLeetWhenGenerating, characters : &'a str, @@ -107,14 +164,23 @@ impl<'a, T : HasherList> PasswordMaker<'a, T>{ #[cfg_attr(test, derive(strum_macros::EnumIter))] #[derive(Debug,Clone, Copy)] pub enum LeetLevel { + /// First Leet level: ["4", "b", "c", "d", "3", "f", "g", "h", "i", "j", "k", "1", "m", "n", "0", "p", "9", "r", "s", "7", "u", "v", "w", "x", "y", "z"] One, + /// Second Leet level: ["4", "b", "c", "d", "3", "f", "g", "h", "1", "j", "k", "1", "m", "n", "0", "p", "9", "r", "5", "7", "u", "v", "w", "x", "y", "2"] Two, + /// Third Leet level: ["4", "8", "c", "d", "3", "f", "6", "h", "'", "j", "k", "1", "m", "n", "0", "p", "9", "r", "5", "7", "u", "v", "w", "x", "'/", "2"] Three, + /// Fourth Leet level: ["@", "8", "c", "d", "3", "f", "6", "h", "'", "j", "k", "1", "m", "n", "0", "p", "9", "r", "5", "7", "u", "v", "w", "x", "'/", "2"] Four, + /// Fifth Leet level: ["@", "|3", "c", "d", "3", "f", "6", "#", "!", "7", "|<", "1", "m", "n", "0", "|>", "9", "|2", "$", "7", "u", "\\/", "w", "x", "'/", "2"] Five, + /// Sixth Leet level: ["@", "|3", "c", "|)", "&", "|=", "6", "#", "!", ",|", "|<", "1", "m", "n", "0", "|>", "9", "|2", "$", "7", "u", "\\/", "w", "x", "'/", "2"] Six, + /// Seventh Leet level: ["@", "|3", "[", "|)", "&", "|=", "6", "#", "!", ",|", "|<", "1", "^^", "^/", "0", "|*", "9", "|2", "5", "7", "(_)", "\\/", "\\/\\/", "><", "'/", "2"] Seven, + /// Eigth Leet level: ["@", "8", "(", "|)", "&", "|=", "6", "|-|", "!", "_|", "|(", "1", "|\\/|", "|\\|", "()", "|>", "(,)", "|2", "$", "|", "|_|", "\\/", "\\^/", ")(", "'/", "\"/_"] Eight, + /// Ninth Leet level: ["@", "8", "(", "|)", "&", "|=", "6", "|-|", "!", "_|", "|{", "|_", "/\\/\\", "|\\|", "()", "|>", "(,)", "|2", "$", "|", "|_|", "\\/", "\\^/", ")(", "'/", "\"/_"] Nine, } @@ -129,37 +195,102 @@ pub enum LeetLevel { /// The `HmacMd5Version06` is similarly ignoring the supplied characters and using hexadecimal numbers as output. #[derive(Debug,Clone, Copy)] pub enum HashAlgorithm { + /// Regular Md4 PasswordMaker Pro setting. Md4, + /// HAMC Md4 PasswordMaker Pro setting. Encodes input as UTF-16 and discards upper byte (just as PasswordMaker Pro does for HMAC). HmacMd4, + /// Regular Md5 PasswordMaker Pro setting. Md5, + /// Md5 as computed by PasswordMaker Pro version 0.6. Encodes input as UTF-16 and discards upper byte and outputs MD5 as hex number. Md5Version06, + /// HMAC Md5 PasswordMaker Pro setting. Encodes input as UTF-16 and discards upper byte (just as PasswordMaker Pro does for HMAC). HmacMd5, + /// HMAC Md5 as computed by PasswordMaker Pro version 0.6. Encodes input as UTF-16 and discards upper byte and outputs MD5 as hex number. HmacMd5Version06, + /// Regular Sha1 PasswordMaker Pro setting. Sha1, + /// HAMC Sha1 PasswordMaker Pro setting. Encodes input as UTF-16 and discards upper byte (just as PasswordMaker Pro does for HMAC). HmacSha1, + /// Regular Sha256 PasswordMaker Pro setting. Sha256, + /// HAMC Sha256 PasswordMaker Pro setting. Encodes input as UTF-16 and discards upper byte (just as PasswordMaker Pro does for HMAC). HmacSha256, + /// Regular Ripemd160 PasswordMaker Pro setting. Ripemd160, + /// HAMC Ripemd160 PasswordMaker Pro setting. Encodes input as UTF-16 and discards upper byte (just as PasswordMaker Pro does for HMAC). HmacRipemd160, } -/// When the leet replacement shown in leet.rs is applied. It is always applied to each password part when the required password length +/// When the Leet replacement shown in leet.rs is applied. +/// If Leet is enabled, the input will be converted to lower case. +/// It is always applied to each password part when the required password length /// is longer than the length obtained by computing a single hash. This is important if the input data or output charset contains certain /// characters where the lower case representation depends on context (e.g. 'Σ'). #[derive(Debug,Clone, Copy)] pub enum UseLeetWhenGenerating { + /// Do not apply Leet on input or output. NotAtAll, + /// Apply Leet on the input before computing a password part. Before { + /// The Leet level to apply to the input. level : LeetLevel, }, + /// Apply Leet on the generated password-part. Beware that this will force the password to lower-case characters. After { + /// The Leet level to apply to the generated password parts. level : LeetLevel, }, + /// Apply Leet both, to the input for the hasher, and the generated password parts. Beware that this will force the password to lower-case characters. BeforeAndAfter { + /// The Leet level to apply to both, input and generated password parts. level : LeetLevel, }, } +/// Settings for the parsing of the user's input URL. +/// This is used to generate the `data` parameter for [`PasswordMaker`]. +#[allow(clippy::struct_excessive_bools)] +#[derive(Debug, Clone)] +pub struct UrlParsing { + use_protocol : ProtocolUsageMode, + use_userinfo : bool, + use_subdomains : bool, + use_domain : bool, + use_port_path : bool, +} + +impl UrlParsing { + /// Creates a new `UrlParsing` instance with the given settings. + pub fn new( + use_protocol : ProtocolUsageMode, + use_userinfo : bool, + use_subdomains : bool, + use_domain : bool, + use_port_path : bool, + ) -> Self{ + UrlParsing{ use_protocol, use_userinfo, use_subdomains, use_domain, use_port_path, } + } + + /// Parses an input string, applying the settings in `self`, and generates a string suitable for + /// the `data` parameter of [`PasswordMaker`] + pub fn parse(&self, input : &str) -> String{ + self.make_used_text_from_url(input) + } +} + +/// The "Use Protocol" checkbox in PasswordMaker Pro Javascript Edition has some weird behaviour, that's probably a bug. +/// This enum lets you select how to hande the case that the user wants to use the Protocol, but the input string doesn't contain one. +#[derive(Debug, Clone, Copy)] +pub enum ProtocolUsageMode{ + /// The protocol part of the URI is not used in the output. + Ignored, + /// The protocol part of the URI is used in the output, if it's non-empty in the input. Otherwise it isn't. + Used, + /// The protocol part of the URI is used in the output, if it's non-empty in the input. Otherwise the string "undefined" is used in the output. + /// This mirrors behaviour of the PasswordMaker Pro Javascript Edition. + UsedWithUndefinedIfEmpty, +} + /// Error returned if the supplied input did not meet expectations. /// The two "missing" variants are self-explanatory, but the `InsufficientCharset` might need some explanation: /// `InsufficientCharset` means that the output character set does not contain at least two grapheme clusters. @@ -167,8 +298,11 @@ pub enum UseLeetWhenGenerating { /// any number of grapheme clusters lower than 2 forms a nonsensical input. There simply is no base-1 or base-0 number system. #[derive(Debug, Clone, Copy)] pub enum GenerationError { + /// Password generation failed, because the user did not supply a master password. MissingMasterPassword, + /// Password generation failed, because the user did not supply a text-to-use. MissingTextToUse, + /// Password generation failed, because the character set supplied by the user did not contain at least 2 grapheme clusters. InsufficientCharset } -- cgit v1.2.3