SHA-1 lost its security argument in 2017 and still names every object in your git repository
Both statements hold at once, and the reason is worth understanding before you decide whether a SHA-1 in your codebase is a finding or a non-issue. Collisions against SHA-1 are real, published and reproducible on rented hardware. Git ships collision detection and treats the digest as a name rather than a promise. Where your own use sits between those poles decides everything.
Reading the 40 characters as five registers
SHA-1 keeps a working state of five 32-bit words, named h0 through h4 in FIPS 180-4. They start at fixed constants, every 512-bit block of your message stirs them through 80 rounds, and the final values written end to end in big-endian order are the digest. The register strip above splits the output on those boundaries so the structure stays visible.
Hash the word abc and the five registers read a9993e36, 4706816a, ba3e2571, 7850c26c and 9cd0d89d. That is the test vector printed in the FIPS specification, so a mismatch anywhere in your own implementation shows up on the very first case.
160 bits sounds close to the 256 of its successor. The gap matters more than the ratio suggests: a birthday attack works over half the digest width, which puts the theoretical collision cost at 280 for SHA-1 against 2128 for SHA-256. Cryptanalysis then pulled the real figure well below 280.
Padding, blocks, and why an empty input still produces a digest
SHA-1 never hashes your bytes on their own. It pads the message first, following a rule inherited from the Merkle-Damgard construction:
- Append a single
1bit, which is the byte0x80for byte-aligned input. - Append zero bytes until the length sits 8 bytes short of a 64-byte boundary.
- Append the original message length in bits as a 64-bit big-endian integer.
The facts row above reports the block count and padding size for whatever you typed. Feed it 55 bytes and the padding fits inside a single block. Feed it 56 and a second block appears, because the 9 bytes of mandatory padding no longer fit. This is the whole reason an empty string hashes to da39a3ee5e6b4b0d3255bfef95601890afd80709 rather than to nothing: zero bytes of message still leave one padded block to process.
The 64-bit length field also caps the algorithm at 264 bits of input, roughly 2 exabytes. No practical file reaches it.
What broke, precisely
Two results ended SHA-1 as a signature hash, and they differ in kind.
| Result | Year | Cost | What it gives an attacker |
|---|---|---|---|
| SHAttered, by CWI Amsterdam and Google | 2017 | About 6,500 CPU years plus 100 GPU years, run in parallel | Two PDF files with different visible content and one identical SHA-1 |
| Chosen-prefix collision, by Leurent and Peyrin | 2019 and 2020 | Around 45,000 US dollars of rented GPU time | Two files starting with different attacker-picked content and still colliding, which is what forging a certificate or a PGP identity needs |
The second result is the serious one. An identical-prefix collision needs both documents built by the attacker from the start. A chosen-prefix collision lets an attacker take content someone else insists on, such as a real certificate subject or a signed message header, and construct a colliding partner around it. Every attack in the wild people worried about needed exactly that step.
Preimage resistance is a separate property, and it holds. Nobody has recovered an input from a SHA-1 digest, and no published attack comes close. Short inputs still fall to a dictionary or a rainbow table, which is a statement about password entropy rather than about SHA-1, and the reason password storage needs bcrypt or Argon2 instead of any raw digest.
Git mode, and the header behind a mismatch
Switch on the git checkbox above and the tool stops hashing your input directly. It builds the object git would store:
blob <byte length><NUL><content>Run echo "hello world" | git hash-object --stdin and git returns 3b18e512dba79e4c8300dd08aeb37f8e728b8dad. The content there is 12 bytes, because echo appends a newline, so the bytes fed to SHA-1 are blob 12, a zero byte, then hello world and that newline. Drop the newline with echo -n and the ID changes to 95d09f2b10159347eece71399a7e2e907ea3df4f. Neither matches sha1sum on the same content, which returns the plain digest with no header at all.
This explains a question that comes up constantly: a file's checksum and its git object ID are different numbers for the same bytes, and neither one is wrong. Commits, trees and tags use the same scheme with their own type words and their own serialized bodies, so this page covers blobs only.
Where SHA-1 is banned now
- TLS certificates. Chrome, Firefox and Edge stopped trusting SHA-1 signed certificates in early 2017. A public CA has not issued one in years.
- Code signing and PDF signatures. Microsoft Authenticode dropped SHA-1 signatures and timestamps, and Adobe treats SHA-1 signed PDFs as unverified.
- Tag and commit signing on high-value repositories. A signed tag inherits the object hash, so a chosen-prefix collision reaches the signature through it.
- Anything the US federal government touches. NIST disallowed SHA-1 for digital signature generation from 2013 and set a retirement date of 31 December 2030 for the remaining uses.
- New protocol design. Reviewers reject it on sight, and defending the choice costs more time than switching does.
Where it still earns its keep
- Git object identity. A collision inside a repository needs attacker-controlled content on both sides, and git has run the hardened SHA-1DC variant since version 2.13, which detects the known collision patterns and refuses the object. SHA-256 repositories exist and stay experimental for interoperability reasons.
- HMAC-SHA1. The collision attacks target a property HMAC does not lean on, so no practical forgery exists. AWS Signature Version 2, OAuth 1.0a and TOTP under RFC 6238 all still specify it. Use the HMAC generator for those.
- Old checksum lists. Vendors, mirrors and archive sites published SHA-1 sums for years. Verifying a download against one detects a truncated or corrupted transfer perfectly well.
- Deduplication and cache keys. Content addressing over your own data has no adversary, and 160 bits of identity is plenty.
The dividing line is whether an attacker gains anything by finding two inputs with one digest. Accidental collisions do not happen at this width. Deliberate ones are for sale.
Reproducing this outside the browser
| Environment | Command or call |
|---|---|
| Linux shell | printf '%s' 'abc' | sha1sum |
| macOS shell | printf '%s' 'abc' | shasum -a 1 |
| OpenSSL | openssl dgst -sha1 report.pdf |
| PowerShell | Get-FileHash -Algorithm SHA1 report.pdf |
| PHP | sha1('abc') for text, sha1_file($path) for a file |
| Python | hashlib.sha1(b'abc').hexdigest() |
| Node | crypto.createHash('sha1').update('abc').digest('hex') |
| Git blob ID | git hash-object -t blob file.txt |
Use printf rather than echo when comparing against this page. Plain echo adds a trailing newline on most shells, and that one byte rewrites the entire digest.
Where this tool stops
- Blobs only in git modeCommit, tree and tag objects serialize differently before hashing. Reproducing those needs the real repository, so run
git cat-fileinstead. - UTF-8 text, no encoding switchTyped text is read as UTF-8 bytes, matching browsers and modern tooling. Content stored as Latin-1 or UTF-16 gives a different digest. Save it to a file and use the file mode to hash the real bytes.
- Browser memory sets the file ceilingFiles stream through in 4 MB slices, which handles a few gigabytes on a desktop and much less on a phone. Past that point the command line is the better tool.
- No reverse lookupNothing here turns a digest back into text. That question needs a wordlist and a cracking tool, not a hash function.
- No collision detectionGit uses the hardened SHA-1DC variant to spot known collision patterns. This page runs plain SHA-1, so it hashes a SHAttered PDF without complaint.
- Nothing is stored or sentText, files and results stay in the tab and vanish on reload. Check the network panel while typing to confirm.
