working
This commit is contained in:
92
enforcer.py
92
enforcer.py
@@ -1,83 +1,81 @@
|
|||||||
import sys
|
|
||||||
import time
|
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
# Function to calculate the hash for the given modified file content
|
# Function to calculate the hash for the given file content with proper newline handling
|
||||||
def calculate_hash(file_lines, num_chars):
|
def calculate_hash(file_lines_with_newline, num_chars):
|
||||||
return sha256("\n".join(file_lines).encode()).hexdigest()[-num_chars:]
|
# Join file lines with '\n' to ensure correct structure
|
||||||
|
file_content = "".join(file_lines_with_newline)
|
||||||
|
return sha256(file_content.encode()).hexdigest()[-num_chars:]
|
||||||
|
|
||||||
# Function to generate a modified file by adding spaces based on the bit pattern
|
# Function to generate a modified fake file by adding spaces based on the bit pattern
|
||||||
def modify_and_hash(args):
|
def modify_and_hash(args):
|
||||||
real_og, fake_og, num_chars, bit_pattern = args
|
fake_og, num_chars, bit_pattern, real_hash = args
|
||||||
|
|
||||||
# Modify the real and fake files based on the bit pattern
|
# Modify the fake file based on the bit pattern
|
||||||
real_modified = [
|
|
||||||
line + " " * ((bit_pattern >> idx) & 1)
|
|
||||||
for idx, line in enumerate(real_og)
|
|
||||||
]
|
|
||||||
fake_modified = [
|
fake_modified = [
|
||||||
line + " " * ((bit_pattern >> idx) & 1)
|
line.rstrip() + " " * ((bit_pattern >> idx) & 1) + "\n"
|
||||||
for idx, line in enumerate(fake_og)
|
for idx, line in enumerate(fake_og)
|
||||||
]
|
]
|
||||||
|
|
||||||
# Calculate hashes for both modified files
|
# Calculate the hash for the modified fake file
|
||||||
real_hash = calculate_hash(real_modified, num_chars)
|
|
||||||
fake_hash = calculate_hash(fake_modified, num_chars)
|
fake_hash = calculate_hash(fake_modified, num_chars)
|
||||||
|
|
||||||
return (real_hash, fake_hash, real_modified, fake_modified)
|
# Return if the hashes match
|
||||||
|
return (fake_hash == real_hash, fake_hash, fake_modified)
|
||||||
|
|
||||||
def main(real_file, fake_file, num_chars):
|
def main(real_file, fake_file, num_chars):
|
||||||
# Read the original files
|
# Read the original real file and retain newline characters
|
||||||
with open(real_file) as f:
|
with open(real_file, 'r') as f:
|
||||||
real_og = f.read().splitlines()
|
real_og = f.readlines() # This keeps newlines intact
|
||||||
with open(fake_file) as f:
|
|
||||||
fake_og = f.read().splitlines()
|
# Read the original fake file and retain newline characters
|
||||||
|
with open(fake_file, 'r') as f:
|
||||||
|
fake_og = f.readlines() # This keeps newlines intact
|
||||||
|
|
||||||
all_real_hashes = {}
|
# Calculate the hash of the real file (unmodified)
|
||||||
all_fake_hashes = {}
|
real_hash = calculate_hash(real_og, num_chars)
|
||||||
|
print(f"Real file hash: {real_hash}")
|
||||||
|
|
||||||
found_collision = False
|
hash_counter = 0 # To track the number of hashes calculated
|
||||||
total_hashes = 0
|
start_time = time.time() # Start the timer
|
||||||
batch_size = 100 # Number of combinations to process in parallel
|
|
||||||
start_time = time.time() # Start time to measure hashes per second
|
|
||||||
|
|
||||||
# Use multiprocessing Pool
|
# Use multiprocessing Pool
|
||||||
with Pool() as pool:
|
with Pool() as pool:
|
||||||
i = 0
|
i = 0
|
||||||
|
batch_size = 100
|
||||||
|
found_collision = False
|
||||||
|
|
||||||
while not found_collision:
|
while not found_collision:
|
||||||
# Prepare a batch of bit patterns to process in parallel
|
# Prepare a batch of bit patterns to process in parallel
|
||||||
bit_patterns = [(real_og, fake_og, num_chars, pattern) for pattern in range(i, i + batch_size)]
|
bit_patterns = [(fake_og, num_chars, pattern, real_hash) for pattern in range(i, i + batch_size)]
|
||||||
|
|
||||||
# Process the batch in parallel
|
# Process the batch in parallel
|
||||||
results = pool.map(modify_and_hash, bit_patterns)
|
results = pool.map(modify_and_hash, bit_patterns)
|
||||||
|
|
||||||
# Update the total count of hashes processed
|
# Update the hash counter
|
||||||
total_hashes += len(results)
|
hash_counter += batch_size
|
||||||
|
|
||||||
# Check the results for a hash collision
|
# Display progress on the same line
|
||||||
for real_hash, fake_hash, real_modified, fake_modified in results:
|
elapsed_time = time.time() - start_time
|
||||||
all_real_hashes[real_hash] = real_modified
|
sys.stdout.write(f"\rProcessed {hash_counter} hashes in {elapsed_time:.2f} seconds.")
|
||||||
all_fake_hashes[fake_hash] = fake_modified
|
sys.stdout.flush()
|
||||||
|
|
||||||
if real_hash in all_fake_hashes or fake_hash in all_real_hashes:
|
# Check the results to see if a collision was found
|
||||||
collision_hash = real_hash if real_hash in all_fake_hashes else fake_hash
|
for match, fake_hash, fake_modified in results:
|
||||||
print(f"\n[+] Collision found! {real_file}.out and {fake_file}.out have the same hash: {collision_hash}")
|
if match:
|
||||||
|
elapsed_time = time.time() - start_time
|
||||||
|
print(f"\nCollision found! The fake file's hash matches the real file's hash: {real_hash}")
|
||||||
|
print(f"Total hashes processed: {hash_counter} in {elapsed_time:.2f} seconds.")
|
||||||
|
|
||||||
with open(f"{real_file}.out", 'w') as f_out:
|
# Write the modified fake file to the output
|
||||||
f_out.writelines("\n".join(all_real_hashes[collision_hash]))
|
|
||||||
with open(f"{fake_file}.out", 'w') as f_out:
|
with open(f"{fake_file}.out", 'w') as f_out:
|
||||||
f_out.writelines("\n".join(all_fake_hashes[collision_hash]))
|
f_out.writelines(fake_modified)
|
||||||
|
|
||||||
found_collision = True
|
found_collision = True
|
||||||
break
|
break
|
||||||
|
|
||||||
# Update progress every batch
|
|
||||||
elapsed_time = time.time() - start_time
|
|
||||||
hashes_per_sec = total_hashes / elapsed_time
|
|
||||||
print(f"\rProcessed {total_hashes} hashes in {elapsed_time:.2f} seconds, {hashes_per_sec:.2f} H/s", end='')
|
|
||||||
|
|
||||||
# Increment the bit pattern range
|
|
||||||
i += batch_size
|
i += batch_size
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
use rayon::prelude::*;
|
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
use std::fs::{self, File};
|
|
||||||
use std::io::{BufRead, BufReader};
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use std::sync::{Arc};
|
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
// Function to calculate the hash for the given file content
|
|
||||||
fn calculate_hash(file_content: &[String], num_chars: usize) -> String {
|
|
||||||
let concatenated = file_content.concat(); // Concatenate without joining with newlines
|
|
||||||
let hash = Sha256::digest(concatenated.as_bytes());
|
|
||||||
format!("{:x}", hash)[64 - num_chars..].to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to generate a modified file by adding spaces based on the bit pattern
|
|
||||||
fn modify_fake_file(fake_og: &[String], bit_pattern: u64) -> Vec<String> {
|
|
||||||
fake_og
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(idx, line)| format!("{}{}", line, " ".repeat(((bit_pattern >> idx) & 1) as usize)))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to read a file while preserving line endings
|
|
||||||
fn read_file_preserving_newlines(file_path: &str) -> Vec<String> {
|
|
||||||
let file = File::open(file_path).expect("Failed to open file");
|
|
||||||
let reader = BufReader::new(file);
|
|
||||||
|
|
||||||
reader
|
|
||||||
.lines()
|
|
||||||
.map(|line| {
|
|
||||||
let mut line = line.expect("Failed to read line");
|
|
||||||
line.push('\n'); // Ensure the newline is preserved
|
|
||||||
line
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Command line arguments
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
|
||||||
if args.len() != 4 {
|
|
||||||
eprintln!("Usage: {} <real_file> <fake_file> <num_chars>", args[0]);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let real_file = &args[1];
|
|
||||||
let fake_file = &args[2];
|
|
||||||
let num_chars: usize = args[3].parse().expect("Invalid number of characters");
|
|
||||||
|
|
||||||
// Read the original files while preserving line endings
|
|
||||||
let real_og = read_file_preserving_newlines(real_file);
|
|
||||||
let fake_og = read_file_preserving_newlines(fake_file);
|
|
||||||
|
|
||||||
// Calculate the target hash of the original real file
|
|
||||||
let target_real_hash = calculate_hash(&real_og, num_chars);
|
|
||||||
println!("Target hash (real file): {}", target_real_hash);
|
|
||||||
|
|
||||||
// Atomic flag to stop threads once a hash match is found
|
|
||||||
let found_collision = Arc::new(AtomicBool::new(false));
|
|
||||||
|
|
||||||
let mut total_hashes = 0;
|
|
||||||
let batch_size = 10_000;
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
// Main loop to modify the fake file until its hash matches the real file's hash
|
|
||||||
let mut i = 0;
|
|
||||||
while !found_collision.load(Ordering::SeqCst) {
|
|
||||||
let bit_patterns: Vec<u64> = (i..i + batch_size).collect();
|
|
||||||
|
|
||||||
// Parallel processing using Rayon
|
|
||||||
bit_patterns.into_par_iter().for_each(|bit_pattern| {
|
|
||||||
if found_collision.load(Ordering::SeqCst) {
|
|
||||||
return; // Stop processing if a collision is found
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modify the fake file based on the current bit pattern
|
|
||||||
let fake_modified = modify_fake_file(&fake_og, bit_pattern);
|
|
||||||
|
|
||||||
// Calculate the hash of the modified fake file
|
|
||||||
let fake_hash = calculate_hash(&fake_modified, num_chars);
|
|
||||||
|
|
||||||
// Check if the modified fake file's hash matches the real file's hash
|
|
||||||
if fake_hash == target_real_hash {
|
|
||||||
// Print collision message
|
|
||||||
println!(
|
|
||||||
"\n[+] Collision found! The modified fake file matches the hash of the real file: {}",
|
|
||||||
target_real_hash
|
|
||||||
);
|
|
||||||
|
|
||||||
// Write the real and fake output files
|
|
||||||
let real_output = format!("{}.out", real_file);
|
|
||||||
let fake_output = format!("{}.out", fake_file);
|
|
||||||
|
|
||||||
// Write the real output (original real file)
|
|
||||||
fs::write(&real_output, real_og.concat())
|
|
||||||
.expect("Failed to write real output file");
|
|
||||||
|
|
||||||
// Write the fake output (modified fake file)
|
|
||||||
fs::write(&fake_output, fake_modified.concat())
|
|
||||||
.expect("Failed to write fake output file");
|
|
||||||
|
|
||||||
// Set the flag to stop further processing
|
|
||||||
found_collision.store(true, Ordering::SeqCst);
|
|
||||||
return; // Stop further processing
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
total_hashes += batch_size;
|
|
||||||
|
|
||||||
// Update progress
|
|
||||||
let elapsed = start_time.elapsed();
|
|
||||||
let hashes_per_sec = total_hashes as f64 / elapsed.as_secs_f64();
|
|
||||||
print!(
|
|
||||||
"\rProcessed {} hashes in {:.2?}, {:.2} H/s",
|
|
||||||
total_hashes, elapsed, hashes_per_sec
|
|
||||||
);
|
|
||||||
|
|
||||||
i += batch_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "hash_collision"
|
name = "rust"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rayon = "1.5"
|
rayon = "1.5"
|
||||||
sha2 = "0.9"
|
sha2 = "0.10"
|
||||||
|
|
||||||
30
rust/confession_fake.txt
Normal file
30
rust/confession_fake.txt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
This is the secret confession of Richard Buckland
|
||||||
|
to be revealed by anonymous email if I should
|
||||||
|
mysteriously vanish. I have left the last few hex
|
||||||
|
digits of the SHA256 hash of this message with my
|
||||||
|
trusted solicitor, Dennis Denuto, which will verify
|
||||||
|
that this is indeed my intended and unaltered
|
||||||
|
confession written by me Richard Buckland.
|
||||||
|
|
||||||
|
Dennis has not seen this confession he has only seen
|
||||||
|
the last few digits of the hash. I have also sent copies
|
||||||
|
of the last few digits to my bank manager and to my priest
|
||||||
|
Father Brown.
|
||||||
|
|
||||||
|
On the 10th of February I saw Mark Zukerberg near my
|
||||||
|
house and we struck up a conversation. He explained all
|
||||||
|
the things he was doing to ensure that Facebook respects
|
||||||
|
privacy - both of its users and of others. It was very
|
||||||
|
impressive.
|
||||||
|
|
||||||
|
I feel awful that I have been criticising Facebook publicly
|
||||||
|
for so long. I apologised to him in our conversation and
|
||||||
|
now I want to confess to the world that actually Facebook
|
||||||
|
has more than enough privacy features, and that the reason
|
||||||
|
I spend so much time criticising Facebook is that I am
|
||||||
|
envious of Mark and wish I was a clever and smart and wise
|
||||||
|
as he is. I feel so bad for having been so mean to him for
|
||||||
|
so many years that I am considering retreating to the outback.
|
||||||
|
I may well cut off all contact with the world and live as a
|
||||||
|
hermit from now on. So do not worry if I vanish it is just
|
||||||
|
that I feel so guilty that I have been so unfair to Facebook.
|
||||||
22
rust/confession_real.txt
Normal file
22
rust/confession_real.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
This is the secret confession of Richard Buckland
|
||||||
|
to be revealed by anonymous email if I should
|
||||||
|
mysteriously vanish. I have left the last few hex
|
||||||
|
digits of the SHA256 hash of this message with my
|
||||||
|
trusted solicitor, Dennis Denuto, which will verify
|
||||||
|
that this is indeed my intended and unaltered
|
||||||
|
confession written by me Richard Buckland.
|
||||||
|
|
||||||
|
Dennis has not seen this confession he has only seen
|
||||||
|
the last few digits of the hash. I have also sent copies
|
||||||
|
of the last few digits to my bank manager and to my priest
|
||||||
|
Father Brown.
|
||||||
|
|
||||||
|
On the 10th of February I saw Mark Zukerberg peeping
|
||||||
|
through my window and recording my private and personal
|
||||||
|
conversation with my friend.
|
||||||
|
|
||||||
|
I confronted him and he was very embarrassed. He
|
||||||
|
promised to pay me $1 million a year if I would stay
|
||||||
|
silent and not tell anyone I had seen him do this. I
|
||||||
|
agreed but now I worry that it would be cheaper for him
|
||||||
|
to make me vanish than to keep paying me.
|
||||||
124
rust/src/main.rs
Normal file
124
rust/src/main.rs
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
use rayon::prelude::*;
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
use std::fs;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::time::Instant;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
// Function to calculate the hash for the given file content
|
||||||
|
fn calculate_hash(file_lines: &[String], num_chars: usize) -> String {
|
||||||
|
let content = file_lines.join(""); // Join lines with no separator (newlines are preserved)
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(content.as_bytes());
|
||||||
|
let result = hasher.finalize();
|
||||||
|
format!("{:x}", result)[64 - num_chars..].to_string() // Get the last `num_chars` hex digits
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to generate a modified fake file by adding spaces based on the bit pattern
|
||||||
|
fn modify_and_hash(
|
||||||
|
fake_og: &[String],
|
||||||
|
num_chars: usize,
|
||||||
|
bit_pattern: u64,
|
||||||
|
real_hash: &str,
|
||||||
|
) -> Option<Vec<String>> {
|
||||||
|
let fake_modified: Vec<String> = fake_og
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, line)| {
|
||||||
|
let trimmed_line = line.trim_end(); // Remove trailing whitespace
|
||||||
|
let spaces_to_add = if (bit_pattern >> idx) & 1 == 1 { " " } else { "" };
|
||||||
|
format!("{}{}\n", trimmed_line, spaces_to_add)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Calculate the hash for the modified fake file
|
||||||
|
let fake_hash = calculate_hash(&fake_modified, num_chars);
|
||||||
|
|
||||||
|
// Return the modified file if the hash matches
|
||||||
|
if fake_hash == real_hash {
|
||||||
|
Some(fake_modified)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Get command line arguments
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
if args.len() != 4 {
|
||||||
|
eprintln!("Usage: {} <real_file> <fake_file> <num_chars>", args[0]);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let real_file = &args[1];
|
||||||
|
let fake_file = &args[2];
|
||||||
|
let num_chars: usize = args[3].parse().expect("Invalid number of characters");
|
||||||
|
|
||||||
|
// Read the real and fake files
|
||||||
|
let real_og: Vec<String> = fs::read_to_string(real_file)
|
||||||
|
.expect("Failed to read real file")
|
||||||
|
.lines()
|
||||||
|
.map(|line| format!("{}\n", line)) // Preserve newlines
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let fake_og: Vec<String> = fs::read_to_string(fake_file)
|
||||||
|
.expect("Failed to read fake file")
|
||||||
|
.lines()
|
||||||
|
.map(|line| format!("{}\n", line)) // Preserve newlines
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Calculate the hash of the real file (unmodified)
|
||||||
|
let real_hash = calculate_hash(&real_og, num_chars);
|
||||||
|
println!("Real file hash: {}", real_hash);
|
||||||
|
|
||||||
|
let mut found_collision = false;
|
||||||
|
let mut hash_counter = 0;
|
||||||
|
let start_time = Instant::now(); // Start the timer
|
||||||
|
|
||||||
|
// Search for a matching hash in parallel
|
||||||
|
while !found_collision {
|
||||||
|
// Generate a batch of bit patterns
|
||||||
|
let batch_size = 100;
|
||||||
|
let bit_patterns: Vec<u64> = (hash_counter..hash_counter + batch_size as u64).collect();
|
||||||
|
|
||||||
|
// Process the batch in parallel
|
||||||
|
let results: Vec<Option<Vec<String>>> = bit_patterns
|
||||||
|
.par_iter()
|
||||||
|
.map(|&pattern| modify_and_hash(&fake_og, num_chars, pattern, &real_hash))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Check if a collision was found
|
||||||
|
for result in results {
|
||||||
|
if let Some(fake_modified) = result {
|
||||||
|
let elapsed_time = start_time.elapsed();
|
||||||
|
println!(
|
||||||
|
"\nCollision found! The fake file's hash matches the real file's hash: {}",
|
||||||
|
real_hash
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"Total hashes processed: {} in {:.2?} seconds.",
|
||||||
|
hash_counter, elapsed_time
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write the modified fake file to the output
|
||||||
|
let out_file = format!("{}.out", fake_file);
|
||||||
|
let mut file = fs::File::create(out_file).expect("Failed to create output file");
|
||||||
|
for line in fake_modified {
|
||||||
|
file.write_all(line.as_bytes()).expect("Failed to write to file");
|
||||||
|
}
|
||||||
|
|
||||||
|
found_collision = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the hash counter
|
||||||
|
hash_counter += batch_size;
|
||||||
|
let elapsed_time = start_time.elapsed();
|
||||||
|
print!(
|
||||||
|
"\rProcessed {} hashes in {:.2?} seconds.",
|
||||||
|
hash_counter, elapsed_time
|
||||||
|
);
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user