Lesson 01 / 21
Hashing, Encryption, and Encoding
Separating three operations in terms of reversibility: the encoded, encrypted, and hashed form of the same meter reading, the number of secret items needed for reversal being 0, 2, and none respectively, only four of the twelve matches between four fields and three operations coming out appropriate, and a wrong match destroying the reversal of 24,800 records.
Published Updated
In this lesson
- Separates hashing, encryption, and encoding by the number of secret items needed for reversal
- Measures the output length and determinism of the three operations on the same input
- Counts the cost of using an operation outside its purpose in terms of reversal items and lost records
Contents
The previous course counted the claims on the network and arrived at the same place across twenty lessons: a claim is accepted only to the extent it can be proven. The question left open at the close concerned the proof itself — what does that proof rest on, who holds the key, where exactly does trust begin? This course names that starting point and counts what falls when it breaks.
Before naming it, a distinction is needed. In the fictional regional measurement network, a meter reading passes through three separate operations on its way from the field to the center, and all three render the output unreadable. To someone looking at a record dump, all three look the same: a meaningless string of letters and digits. Yet the security value of the three is independent of one another, and the difference between them is measured by a single question — how many secret items does one need on hand to go from this output back to the input?
Three Operations, Three Separate Questions
CF1: encoding is a change of representation; it writes the data’s bytes in a different alphabet, and reversal needs no secret item. Its purpose is portability: a byte passing through the transport path without corruption.
CF2: encryption is a reversible transformation; reversal depends on a countable number of secret items. Its purpose is confidentiality: a party without the item being unable to learn the content.
CF3: hashing is a one-way digest; no item suffices to go from the output back to the input, because the inverse is not a defined operation. Its purpose is integrity and comparison: testing whether two pieces of data are the same without holding the data itself.
What the three sentences share is that none of them uses the word “strong” or “secure.” All three say something countable: the number of items needed for reversal is, respectively, zero, more than one, and none.
The Same Input Passes Through Three Operations
The unit below passes a fictional meter reading through three operations. The key is derived from a fixed seed so the output stays the same from run to run; it is not a real key, and in a real setup a key is not produced this way.
// crypto/triad.mjs — passes the same input through three operations and measures // reversibility: encoding (base64), encryption (AES-256-GCM), hashing (SHA-256). The input is // a meter reading from the fictional regional measurement network. There is no real key; the key // is derived from a fixed seed so the output stays the same from run to run. No randomness. import { createHash, createCipheriv, createDecipheriv, scryptSync } from "node:crypto"; // CF1: encoding is a change of representation; reversal needs no secret item. // CF2: encryption is a reversible transformation; reversal depends on exactly a // countable number of secret items. // CF3: hashing is a one-way digest; no item gives back the reversal. export const READING = "SAY-4417|2026-03-01T06:00Z|41982"; export const LONG = READING + "|SAY-4418|2026-03-01T06:00Z|8813304"; const KEY = scryptSync("kurgu-tohum", "kurgu-tuz", 32, { N: 2 ** 12, r: 8, p: 1 }); const NONCE = Buffer.alloc(12, 7); // fictional, fixed export const encode = (m) => Buffer.from(m).toString("base64"); export const hash = (m) => createHash("sha256").update(m).digest("base64"); export function encrypt(m) { const s = createCipheriv("aes-256-gcm", KEY, NONCE); const g = Buffer.concat([s.update(m, "utf8"), s.final()]); return Buffer.concat([g, s.getAuthTag()]).toString("base64"); } export function decrypt(g64, key = KEY) { const t = Buffer.from(g64, "base64"); const c = createDecipheriv("aes-256-gcm", key, NONCE); c.setAuthTag(t.subarray(t.length - 16)); return Buffer.concat([c.update(t.subarray(0, t.length - 16)), c.final()]).toString(); } export const attempt = (f) => { try { return f() === READING ? "yes" : "no"; } catch { return "no"; } }; export const WRONG = scryptSync("baska-tohum", "kurgu-tuz", 32, { N: 2 ** 12, r: 8, p: 1 });
// crypto/measure.mjs — the output form, determinism, and reversal of the three operations. import { READING, LONG, encode, hash, encrypt, decrypt, attempt, WRONG } from "./triad.mjs"; const printRow = (g, ...s) => console.log(s.map((v, i) => (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join("")); const A = [-11, 7, 7, 15, 14]; printRow(A, "operation", "short", "long", "deterministic", "secret item"); for (const [name, f, item] of [["encoding", encode, 0], ["encryption", encrypt, 2], ["hashing", hash, "-"]]) printRow(A, name, f(READING).length, f(LONG).length, f(READING) === f(READING) ? "yes" : "no", item); console.log("\nreversal actually attempted (did it give back the exact input):"); const B = [-34, 6]; for (const [name, f] of [ ["encoding, no item on hand", () => Buffer.from(encode(READING), "base64").toString()], ["encryption, with the correct key", () => decrypt(encrypt(READING))], ["encryption, with the wrong key", () => decrypt(encrypt(READING), WRONG)], ["encryption, with one byte changed", () => { const b = Buffer.from(encrypt(READING), "base64"); b[3] ^= 1; return decrypt(b.toString("base64")); }], ]) printRow(B, name, attempt(f)); printRow(B, "hashing, no inverse function", "no"); console.log("\nthe three output forms of the same input:"); console.log(" encoding :", encode(READING)); console.log(" encryption:", encrypt(READING)); console.log(" hashing :", hash(READING));
operation short long deterministic secret item encoding 44 92 yes 0 encryption 64 112 yes 2 hashing 44 44 yes - reversal actually attempted (did it give back the exact input): encoding, no item on hand yes encryption, with the correct key yes encryption, with the wrong key no encryption, with one byte changed no hashing, no inverse function no the three output forms of the same input: encoding : U0FZLTQ0MTd8MjAyNi0wMy0wMVQwNjowMFp8NDE5ODI= encryption: KtcWlbMqgsctrQ9mH5TfZXklUgBZe3RQghkBaCASMIF/zJIXfxBHDQ8B0sIX7nzA hashing : 6MaKWYn3eYX57Bdf2685XV76MBSSJCOE1SLYc6Rz7zg=
What Is Measured: The Items Needed for Reversal
The last three lines are the lesson’s starting point. All three outputs look unreadable, and all three are roughly the same length. Appearance says nothing; the tables do.
The length columns give the first distinction. As the input grows, encoding’s output goes from 44 to 92, and encryption’s output goes from 64 to 112 — both grow with the input, because both carry the entire input. Hashing’s output is 44 characters for both inputs. That fixed length is direct proof that hashing does not carry the input: a 64-character input and a 6-kilobyte file both come down to the same 32 bytes. Information that does not fit inside cannot be extracted back out.
In the determinism column, all three give yes; the same input always produces the same
output. This property is what makes verification possible, and it is also the source of a
constraint the second lesson measures: if two records are equal, their outputs are equal too.
The reversal table is the real measurement, and all four rows were actually attempted. The encoded value was opened with zero items on hand — reversal is an operation anyone who sees the output can perform. The encrypted value opened with the correct key and did not open with the wrong key. The third row shows the second secret item: when a single byte of the ciphertext changed, decryption failed, because the mode of operation in use appends an authentication tag to the output and checks it before decrypting. Encryption’s reversal therefore depends on not one but two items: the key and an uncorrupted ciphertext. The hashing row has nothing to even attempt; there is no such thing as an inverse function.
Where Encoding Fits and Telling It Apart from the Output
Encoding not having a security function does not mean it is unnecessary. In the fictional
measurement network, the packet transferred between branches passes through a text-carrying
channel, and part of the raw bytes gets interpreted on that channel as a delimiter or control
character. Encoding solves exactly that: it writes the bytes into an alphabet the channel can
carry safely. That is why encoding comes out appropriate in the table’s transfer packet row —
what is wanted in that field is not confidentiality, but arriving without corruption.
The confusion comes from an encoded value looking unreadable. Telling the three apart by
looking at the output is nevertheless possible, and the distinguishing marks are right there in
the output above. An encoded value’s length is proportional to the input’s length, and decoding
it yields meaningful text; the trailing = padding is also a mark of the alphabet in use.
Hashing’s output is always the same length regardless of the input — 32 bytes, 44 characters in
this alphabet. An encrypted value grows with the input but decrypting it yields meaningless
bytes and it usually carries a fixed overhead: the 16-byte authentication tag here is that
overhead.
If a record dump shows fixed-length 44-character fields, that is a hashing column; fields whose length varies from row to row are encoding or encryption. The distinction is not a guess — it is a measurable property, and it is the first column to check during an audit.
The Countable Result of Mixing Them Up
Separating the three operations is not a terminology debate. The fictional measurement network has four fields, and each field has two known properties: whether the value needs to be retrieved later, and whether the value needs to stay secret. These two properties determine the correct operation; a wrong choice costs two separate numbers.
// crypto/match.mjs — matching the four fields' purpose against the three operations in // the fictional measurement network (model): how many items each wrong match results in. // CF4: the cost of using an operation outside its purpose is measured not in appearance but // in item count: the items needed for reversal and the records whose reversal is lost. const FIELD = [ // record counts are fictional { name: "meter reading value", records: 12400, reversed: true, secret: true, correct: "encryption" }, { name: "field crew password", records: 340, reversed: false, secret: true, correct: "hashing" }, { name: "device serial digest", records: 12400, reversed: false, secret: false, correct: "hashing" }, { name: "transfer packet", records: 12400, reversed: true, secret: false, correct: "encoding" }, ]; const REVERSAL = { encoding: 0, encryption: 1, hashing: null }; // items needed for reversal function decide(a, op) { if (op === a.correct) return ["fits", REVERSAL[op], 0]; if (op === "hashing") return ["no reversal", null, a.records]; if (op === "encoding" && a.secret) return ["reads with 0 items", 0, 0]; if (op === "encoding") return ["raw value stays", 0, 0]; if (!a.reversed) return ["single root opens everything", 1, 0]; return ["unnecessary root", 1, 0]; } const printRow = (g, ...s) => console.log(s.map((v, i) => (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join("")); const G = [-22, -11, 9, 8, 10, 8, -32]; printRow(G, "field", "operation", "reversed", "secret", "reversal", "lost", " decision"); let fitCount = 0, zeroCount = 0, lostTotal = 0; for (const a of FIELD) for (const op of ["encoding", "encryption", "hashing"]) { const [d, rv, lost] = decide(a, op); if (d === "fits") fitCount++; else if (rv === 0) zeroCount++; lostTotal += lost; printRow(G, a.name, op, a.reversed ? "yes" : "no", a.secret ? "yes" : "no", rv === null ? "-" : rv, lost || "-", " " + d); } console.log(`\n${FIELD.length * 3} matches, ${fitCount} fit; ${zeroCount} matches bring the ` + `raw value back with 0 items, and a wrong hash destroys the reversal of ${lostTotal.toLocaleString("en-US")} records`);
field operation reversed secret reversal lost decision meter reading value encoding yes yes 0 - reads with 0 items meter reading value encryption yes yes 1 - fits meter reading value hashing yes yes - 12400 no reversal field crew password encoding no yes 0 - reads with 0 items field crew password encryption no yes 1 - single root opens everything field crew password hashing no yes - - fits device serial digest encoding no no 0 - raw value stays device serial digest encryption no no 1 - single root opens everything device serial digest hashing no no - - fits transfer packet encoding yes no 0 - fits transfer packet encryption yes no 1 - unnecessary root transfer packet hashing yes no - 12400 no reversal 12 matches, 4 fit; 3 matches bring the raw value back with 0 items, and a wrong hash destroys the reversal of 24,800 records
Four of the twelve matches fit; eight carry a cost, and the costs are not of the same kind.
The quietest flaw is in the first row. If the meter reading is encoded, the record looks unreadable and whoever is looking assumes it is protected; yet the reversal item count is zero. Because encoding has no secret item at all, treating an encoded value as protected is the same thing as recording an unprotected value as protected. The same flaw repeats in the password row, where the record count is 340.
The second flaw runs the other way. If the field crew password is encrypted, reversal becomes possible — and that is exactly what is unwanted. Encryption ties all 340 records to a single item, the key. When that item breaks, the number of records that fall is 340; in hashing, there is no shared item to break. This row is the course’s first concrete example of its measurement axis: a wrong choice of operation creates a root, and the cost of creating a root is the number of items that fall if that root breaks.
The third flaw is the irreversible one. If the meter reading or the transfer packet is hashed, the value is not protected — it is lost; across the two fields, 24,800 records total become unreadable again. Unlike the other two, this flaw surfaces not at the moment of a leak but at the moment of first writing, and it cannot be undone.
The Course’s Three Columns
This lesson sets up the three questions that will be asked of every mechanism throughout the course, and answers the first one.
Trust root. What does the mechanism assume to be true from the start? Encoding has no root — it assumes nothing and promises nothing. Encryption’s root is the key staying secret. Hashing’s root is that two different inputs do not produce the same output, and that root is the subject of the next lesson.
How many items must be true at once. From root to use, how many components need to hold together? In encryption, the number measured is two: the key must stay secret and the ciphertext must be uncorrupted. In encoding, it is zero. In hashing, it is one: the function’s collision resistance.
How many items fall if the root breaks. If the key leaks, every record encrypted with that key falls; in the table’s password row, that is 340. In encoding, there is no root to break, because nothing is closed. In hashing, if the root breaks, what falls is not the records’ secrecy but the meaning of the comparison.
Every lesson throughout the course fills in these three columns. “That algorithm is strong” carries no decision. The root’s name, the chain’s length, and the number of items that fall at the moment of breaking do. A fourth question always accompanies the third column: what does renewal cost once the root breaks? When a key leaks, producing a new key is a single operation, but re-encrypting the 12,400 records that key protected and getting the key to every endpoint that needs it are separately counted items. The cost measured in this course is not the break itself, but the number of items touched by hand after the break.
Summary
- The three operations are separated by the number of secret items needed for reversal: encoding 0, encryption 2 (the key and an uncorrupted ciphertext), and for hashing, no item suffices.
- Encoding’s and encryption’s output grow with the input (44→92 and 64→112 characters); hashing’s output is 44 characters for every input, and that fixed length is proof that the input is not carried.
- All three operations are deterministic: the same input always gives the same output.
- Of the twelve matches between four fields and three operations, only four fit; in three matches, the raw value comes back with 0 items, and in two matches, the reversal of 24,800 records is destroyed.
- Every mechanism is measured with three columns: the trust root’s name, the number of items that must be true at once from root to use, and the number of items that fall when the root breaks.
Next Step
In this lesson, hashing’s root was passed over in one sentence: two different inputs do not give the same output. But that sentence, as it stands, cannot be true. Hashing’s output has a fixed length and the number of possible inputs is unlimited; therefore, different inputs that give the same output certainly exist. The root is the assumption not that a collision does not exist, but that it cannot be found. The next lesson turns that assumption into a number: at how many bits of output is the first collision found in how many attempts, how closely does the measured number match the theoretical bound, and what happens to the number of attempts needed when the output length doubles. The same measure also explains why deprecated hashing algorithms are retired step by step.
To keep your progress and take notes, Log in
My notes
Log in to take notes.