Skip to content
JavaScript Basics: Calculate SHA-256 Digests

JavaScript Basics: Calculate SHA-256 Digests

October 17, 2026

A digest is a fixed-size representation of bytes. Use Node.js to hash text, combine chunks, and detect a changed input. These examples do not encrypt data or authenticate its sender.

These examples use built-in features and were tested on Linux with Node.js v24.19.0. No packages are needed. Assertions check the results before the program finishes.

Prepare a scratch directory

You need Bash, a text editor, Node.js on your PATH, and the mktemp, rm, and rmdir utilities. Keep using the same terminal. These commands stop the shell if setup fails. Save each named source file as UTF-8; .mjs selects ES modules.

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

1. Hash exact text bytes

Save this as hash-text.mjs:

import assert from 'node:assert/strict';

import { createHash } from 'node:crypto';
const digest = createHash('sha256').update('abc', 'utf8').digest('hex');
assert.equal(digest, 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad');
console.log(`SHA-256: ${digest}`);
console.log(`Hex characters: ${digest.length}`);

Run it:

node hash-text.mjs

Output:

SHA-256: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Hex characters: 64

createHash() starts a hash calculation. update() supplies bytes, using UTF-8 here. digest("hex") finalizes it and returns hexadecimal text. SHA-256 produces 32 bytes, represented by 64 hex characters.

2. Feed more than one chunk

Save this as hash-chunks.mjs:

import assert from 'node:assert/strict';

import { createHash } from 'node:crypto';
const chunkedHash = createHash('sha256');
chunkedHash.update('a', 'utf8');
chunkedHash.update('bc', 'utf8');
const chunkedDigest = chunkedHash.digest('hex');
const wholeDigest = createHash('sha256').update('abc', 'utf8').digest('hex');
assert.equal(chunkedDigest, wholeDigest);
assert.throws(() => chunkedHash.update('more'));
console.log(`Chunked equals whole: ${chunkedDigest === wholeDigest}`);
console.log('Update after digest: rejected');

Run it:

node hash-chunks.mjs

Output:

Chunked equals whole: true
Update after digest: rejected

Chunked and whole-input hashes match when the chunks supply exactly the same bytes in the same order. Each string chunk is encoded separately, so splitting a UTF-16 surrogate pair can change the bytes. For Unicode text, encode the whole string to a Buffer before splitting it into chunks. Chunk boundaries are not included in the digest. After finalization, start a new hash object for another calculation. For large files, feed a stream rather than loading the entire file into memory.

3. Detect a changed input

Save this as compare-digests.mjs:

import assert from 'node:assert/strict';

import { createHash } from 'node:crypto';
function digestText(text) {
  return createHash('sha256').update(text, 'utf8').digest('hex');
}
const original = digestText('report');
const unchanged = digestText('report');
const withNewline = digestText('report\n');
assert.equal(original, unchanged);
assert.notEqual(original, withNewline);
console.log(`Same bytes match: ${original === unchanged}`);
console.log(`Added newline matches: ${original === withNewline}`);

Run it:

node compare-digests.mjs

Output:

Same bytes match: true
Added newline matches: false

A newline changes the bytes and this digest. Matching hashes are useful for integrity checks, but the expected hash must come from a trusted source to detect malicious replacement. An attacker who can replace both data and digest defeats this comparison. Plain SHA-256 is not password storage, encryption, or a message-authentication mechanism; use purpose-built password hashing or authentication protocols for those jobs.

Clean up

In the same terminal, remove only the example files, return to your original directory, and remove the empty scratch directory:

rm -- hash-text.mjs hash-chunks.mjs compare-digests.mjs
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

Acknowledgements

This post was created with assistance from AI.

Last updated on