The documentation for Node.js FS says that NodeJS' readFileSync only returns the contents of the target file. That link is to documentation for readFile but the section on readFileSync says to refer to readFiles docs. Allegedly, NodeJS readFile "asynchronously reads the entire contents of a file."
I want to generate a hash of a file. I use the crypto library but am willing to use other libraries or methods. The hash cannot be based on just the contents of the file. It has to use all the bytes of the file itself. For example, assume the path targeted a file that uses Microsoft's PE format. The bytes in the file header that represent the Machine, NumberOfSections, and etc. should be used in the hash. I want to do that to a text file. Also, this is a link to a Microsoft article on the PE Format: PE Format. So few bytes are printed to console by the code below, implying that only the contents are read.
const crypto = require('crypto')
function hash (path) {
const hash_object = crypto.createHash('sha256');
const file_as_buffer = fs.readFileSync(path)
hash_object.update(file_as_buffer);
const hash_string = hash_object.digest('hex');
console.log("Has input", file_as_buffer);
return hash_string;
}
hash('test.txt');
Assuming the file only contains a newline character, the output in the console for this is a single byte. How do I use the bytes that represent the entire text file - including metadata like author and creation date - as input for the hash function?
I confirmed that on identical copies of the file, using the code above returns the same hash for different copies of the same file. This happens even if their file name is different or they were created a few minutes apart. How do I avoid that?
If you need the hash to be different for different files with the same contents, you will have to consider the file metadata which you can get from fs.stat.
function hash (path) {
const hash_object = crypto.createHash('sha256');
const file_as_buffer = fs.readFileSync(path);
const file_meta = fs.statSync(path); // Get file stats
const allFile = Buffer.concat([file_as_buffer, Buffer.from(JSON.stringify(file_meta))]); //Add them to the input buffer for hashing
hash_object.update(allFile);
const hash_string = hash_object.digest('hex');
console.log("Has input", file_as_buffer);
return hash_string;
}
This is the final answer I used. It is nearly the same as Charlie's answer. I just accepted the suggestions in the comments to select specific items from the results of fs.statSync. I took the sum of mtime (time modified, I assume), ctime (time created), and size. The next operations are all done in one line. I explicitly converted the sum into a string and created a buffer from the string. Last, I concatenated the two buffers.
function hash(path: string): string {
const hash_object = crypto.createHash('sha256');
const file_as_buffer = fs.readFileSync(path)
let blockfile_stat = fs.statSync(path);
let blockfile_stat_num: number = blockfile_stat.mtimeMs +
blockfile_stat.ctimeMs + blockfile_stat.size;
hash_object.update(Buffer.concat(
[file_as_buffer, Buffer.from(String(blockfile_stat_num))])
);
const hash_string = hash_object.digest('hex');
return hash_string;
}