DEV Community

William Baptist
William Baptist

Posted on

Building a File Fingerprint in Python

Calculate a file fingerprint, save it as a reference, then change the file and compare the results.

You can also follow my free guide on Tidy Desk Digital.

For a browser-based option, use my free file fingerprint checker.

A hash turns data into a fixed-length value, often called a fingerprint. We'll use SHA-256, a hash method producing 32 bytes, small units of stored data. It displays them as 64 hexadecimal characters: digits 0 to 9 and letters a to f.

You'll need Python 3, a plain-text editor and a command window. No extra packages are required.

1. Create the example file

A text editor may add a line ending even when you only type abc. Let Python write the exact three bytes instead. Save this as MakeExample.py in your practice folder:

from pathlib import Path


Path("Example.txt").write_bytes(b"abc")
Enter fullscreen mode Exit fullscreen mode

Run it from a command window in that folder:

python3 MakeExample.py
Enter fullscreen mode Exit fullscreen mode

Use your installation's Python 3 command if it is not named python3. This creates or replaces Example.txt, so use a practice folder without a file you need to keep. b"abc" is a bytes value; write_bytes adds no line ending. The file now contains exactly three bytes, regardless of your editor's line-ending setting.

2. Save the program

Save this as FingerprintFile.py:

import hashlib as HashLib
import os as Os
import stat as Stat
import sys as Sys


def FingerprintFile(FilePath):
    Fingerprint = HashLib.sha256()
    Total = 0
    with open(FilePath, "rb") as InputFile:
        if not Stat.S_ISREG(Os.fstat(InputFile.fileno()).st_mode):
            raise ValueError("input must be a regular file")
        while True:
            Block = InputFile.read(65536)
            if not Block:
                break
            Fingerprint.update(Block)
            Total += len(Block)
    return Fingerprint.hexdigest(), Total


def Main():
    if len(Sys.argv) != 2:
        print("Usage: python3 FingerprintFile.py input-file", file=Sys.stderr)
        return 2
    try:
        Fingerprint, Total = FingerprintFile(Sys.argv[1])
    except (OSError, ValueError) as Problem:
        print(f"Fingerprint stopped: {Problem}", file=Sys.stderr)
        return 2
    print(f"SHA-256: {Fingerprint}")
    print(f"Bytes read: {Total}")
    return 0


if __name__ == "__main__":
    Sys.exit(Main())
Enter fullscreen mode Exit fullscreen mode

HashLib.sha256() starts the calculation. Each update adds the next block of bytes; adding blocks works like adding their combined contents at once. After the file ends, hexdigest returns the printable value.

The input opens in rb, read-only binary mode, without translating line endings. with closes it when reading finishes. Nothing is printed as a complete fingerprint until the full read succeeds.

Use known regular files, such as documents. Stat.S_ISREG checks the opened file's type, but a special file can block during opening before that check runs.

3. Calculate the fingerprint

Open a command window in your folder and run:

python3 FingerprintFile.py Example.txt
Enter fullscreen mode Exit fullscreen mode

Use your installation's Python 3 command if it is not named python3. The output is:

SHA-256: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Bytes read: 3
Enter fullscreen mode Exit fullscreen mode

The exit code, a small result number another script can check, is 0 when calculation succeeds or 2 for an input/command error. This program calculates one value; it does not compare values or return a mismatch code.

4. Change the file and compare

Copy the example to another file and calculate its fingerprint. The values should match. Add a character to the copy and calculate again; its stored contents changed, so expect a different value.

Keep the reference separately from the checked file. If someone can replace both, they can calculate a new value and make the replacement match. A fingerprint from the same untrusted page as a download adds no independent reason to trust it.

The program passed 15 checks, including fixed expected values for empty input and abc, binary bytes, block boundaries, line endings and errors. Other successful cases were checked against a separate one-call calculation. Tested input bytes remained unchanged.

A match checks content against a reference, not the author's identity or whether the file is safe to open. Different files can share a fixed-length value. When both stable files are available and you need a direct byte-for-byte answer, compare their contents directly.

Use a file that will not change during reading. The program does not lock it or take a frozen copy, and follows symbolic links, paths pointing to other files. Reading may affect recorded last-access times. Small read blocks do not impose file-size or time limits.

Save output to a new report filename, not over the input. Do not use this calculation as a password-storage design; protecting passwords is a different job.

Reference

Top comments (0)