Compare commits
2 Commits
fae04ba3b8
...
fae07d395d
Author | SHA1 | Date |
---|---|---|
Anna | fae07d395d | |
Anna | fae02b3423 |
16
README.md
16
README.md
|
@ -9,3 +9,19 @@ Supports:
|
|||
- various methods (date increment/decrement, random text, counter text, header)
|
||||
- multithreading
|
||||
- config file for saving common options
|
||||
|
||||
## Performance
|
||||
|
||||
Performance depends on hardware, method, and number of threads. The tests below
|
||||
were performed on Framework Laptop (A6) with an 11th Gen Intel® Core™ i7-1165G7
|
||||
@ 2.80GHz. Each method was tested attempting to generate the prefix `deadbeef`
|
||||
for 30 seconds using one thread.
|
||||
|
||||
```
|
||||
counter: 163,504,123 hashes (5,461,682.8102/s)
|
||||
header: 124,617,875 hashes (4,150,993.226/s)
|
||||
random: 110,886,440 hashes (3,715,452.2177/s)
|
||||
increment: 107,137,532 hashes (3,573,438.8338/s)
|
||||
sequoia: 136,952 hashes (4,486.4209/s)
|
||||
gpg-agent: 353 hashes (11.7519/s)
|
||||
```
|
||||
|
|
72
src/main.rs
72
src/main.rs
|
@ -254,6 +254,24 @@ fn unlock_key_file(path: &str, check_secrets: bool, return_password: bool) -> Re
|
|||
}
|
||||
|
||||
const PW_SERVICE: &str = "lgbt.anna.git-vain";
|
||||
const NIBBLES: [[u8; 16]; 16] = [
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
|
||||
[32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47],
|
||||
[48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63],
|
||||
[64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
|
||||
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95],
|
||||
[96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111],
|
||||
[112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127],
|
||||
[128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143],
|
||||
[144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159],
|
||||
[160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175],
|
||||
[176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191],
|
||||
[192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207],
|
||||
[208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223],
|
||||
[224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239],
|
||||
[240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255],
|
||||
];
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let args = Cli::parse();
|
||||
|
@ -311,6 +329,30 @@ fn main() -> Result<()> {
|
|||
let prefix = args.prefix
|
||||
.expect("clap should handle this")
|
||||
.to_lowercase();
|
||||
|
||||
if !prefix.chars().all(|c| c.is_ascii_hexdigit()) {
|
||||
anyhow::bail!("prefix must be hexadecimal");
|
||||
}
|
||||
|
||||
let (byte_prefix, nibble_values) = if prefix.len() % 2 == 0 {
|
||||
// even
|
||||
let bytes = data_encoding::HEXLOWER.decode(prefix.as_bytes())
|
||||
.context("could not decode prefix as hex")?;
|
||||
(bytes, None)
|
||||
} else {
|
||||
// odd
|
||||
let bytes = data_encoding::HEXLOWER.decode(prefix[..prefix.len() - 1].as_bytes())
|
||||
.context("could not decode prefix as hex")?;
|
||||
let last = prefix.as_bytes()[prefix.len() - 1];
|
||||
let idx = match last {
|
||||
48..=57 => last - 48,
|
||||
97..=102 => last - 97,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let nibble = NIBBLES[idx as usize];
|
||||
(bytes, Some(nibble))
|
||||
};
|
||||
|
||||
let threads = match args.threads.or_else(|| config.as_ref().and_then(|c| c.threads)) {
|
||||
None | Some(0) => num_cpus::get(),
|
||||
Some(x) => x,
|
||||
|
@ -382,6 +424,8 @@ fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
let stripped_header = header_lines.join("\n");
|
||||
|
||||
let message = commit.message_raw()
|
||||
.context("commit had an invalid message")?
|
||||
.to_string();
|
||||
|
@ -427,10 +471,12 @@ fn main() -> Result<()> {
|
|||
let found = Arc::clone(&found);
|
||||
let timestamp = Arc::clone(×tamp);
|
||||
let message = message.clone();
|
||||
let prefix = prefix.clone();
|
||||
let byte_prefix = byte_prefix.clone();
|
||||
let nibble_values = nibble_values.clone();
|
||||
let mut author_parts = author_parts.clone();
|
||||
let mut committer_parts = committer_parts.clone();
|
||||
let mut header_lines = header_lines.clone();
|
||||
let stripped_header = stripped_header.clone();
|
||||
let method = args.method
|
||||
.or_else(|| config.as_ref().and_then(|c| c.method))
|
||||
.unwrap_or(Method::Counter);
|
||||
|
@ -503,11 +549,17 @@ fn main() -> Result<()> {
|
|||
line.push_str(buffer.format(count));
|
||||
}
|
||||
|
||||
let mut header = header_lines.join("\n");
|
||||
let mut header = match method {
|
||||
// counter and random don't mutate the header, so don't
|
||||
// allocate for it
|
||||
Method::Counter | Method::Random => Cow::from(&stripped_header),
|
||||
_ => Cow::from(header_lines.join("\n")),
|
||||
};
|
||||
|
||||
// NOTE: don't need to handle append here, since we'll never be
|
||||
// both appending *and* signing
|
||||
if let Some(ctx) = &mut gpg {
|
||||
let header = header.to_mut();
|
||||
let to_sign = format!("{header}\n{message}");
|
||||
let mut output = Vec::new();
|
||||
ctx.sign(SignMode::Detached, to_sign, &mut output)
|
||||
|
@ -524,6 +576,7 @@ fn main() -> Result<()> {
|
|||
header.push('\n');
|
||||
}
|
||||
} else if let Some(key) = &mut seq_key {
|
||||
let header = header.to_mut();
|
||||
let to_sign = format!("{header}\n{message}");
|
||||
let sig = SignatureBuilder::new(SignatureType::Binary)
|
||||
.sign_message(key, to_sign)
|
||||
|
@ -564,7 +617,7 @@ fn main() -> Result<()> {
|
|||
sha1.update("commit ");
|
||||
sha1.update(buffer.format(header.len() + message_len + 1));
|
||||
sha1.update([0]);
|
||||
sha1.update(&header);
|
||||
sha1.update(header.as_bytes());
|
||||
sha1.update("\n");
|
||||
match &append {
|
||||
Some(a) => {
|
||||
|
@ -577,10 +630,18 @@ fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
let hash = sha1.finalize_reset();
|
||||
let hash = data_encoding::HEXLOWER.encode(&hash);
|
||||
// let hash = data_encoding::HEXLOWER.encode(&hash);
|
||||
|
||||
bar.inc(1);
|
||||
if hash.starts_with(&prefix) {
|
||||
// if hash.starts_with(&prefix) {
|
||||
if hash[0..byte_prefix.len()] == byte_prefix {
|
||||
// check the nibble
|
||||
if let Some(values) = nibble_values {
|
||||
if !values.contains(&hash[byte_prefix.len()]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
found.store(true, Ordering::SeqCst);
|
||||
let message = match append {
|
||||
Some(append) => Cow::from(format!("{message}\n{append}\n")),
|
||||
|
@ -588,6 +649,7 @@ fn main() -> Result<()> {
|
|||
};
|
||||
let total = format!("{header}\n{message}");
|
||||
let raw_commit = format!("commit {}\0", total.len());
|
||||
let hash = data_encoding::HEXLOWER.encode(&hash);
|
||||
commit_tx.send((hash, format!("{raw_commit}{total}"))).unwrap();
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue