Chapter 4 — Programming I
Lesson map
Lesson 04 published. Map, example, EX010.
flowchart TD
N0["Learning outcomes"]
N1["4.1 Bytes are not text"]
N2["4.2 Using hashlib"]
N3["4.3 Canonical formats"]
N4["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4
N4 --> EX["Worked example"]
EX --> Q["Exercise EX010"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["4.1 Bytes are not text"]
N2["4.2 Using hashlib"]
N3["4.3 Canonical formats"]
N4["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> Q["EX010"]
[Learning outcomes]
|
v
[4.1 Bytes are not text]
|
v
[4.2 Using hashlib]
|
v
[4.3 Canonical formats]
|
v
[Synthesis]
|
v
[Worked example] --> [Exercise EX010]
Learning outcomes
You will (1) represent bytes, text and hexadecimal correctly; (2) use the standard library's hash functions; (3) avoid common encoding errors that break cryptographic checks.
4.1 Bytes are not text
Cryptographic functions operate on bytes, not characters. In Python, "litres" is text and b"litres" is bytes; converting between them requires an explicit encoding such as UTF-8. Aiyana's first bug comes from hashing a reading on one system with a different encoding from another, so equal readings produced different digests. She learns to fix one canonical encoding in the specification, to convert once at the boundary and to keep bytes everywhere inside. Hexadecimal is a display format for bytes: two hex characters represent one byte, which is why a 32-byte SHA-256 digest prints as 64 characters.
4.2 Using hashlib
The hashlib module provides SHA-256 and other functions from FIPS 180-4. A hash maps input of any length to a fixed-length digest; changing one bit changes about half the output bits. Aiyana writes small functions that hash a reading string and print the hexadecimal digest, then checks published test vectors from the standard to confirm she is calling the function correctly. She learns the difference between calling update repeatedly on a hash object, which hashes the concatenation, and hashing each piece separately, which gives different results that confused her first test.
4.3 Canonical formats
Two programs must agree byte for byte before hashes or signatures can match. Aiyana defines a canonical reading format for Ridgeway: fixed field order, semicolon separators, integer litres and timestamps in UTC with a trailing Z. She avoids floating-point litres because 18342.0 and 18342 would hash differently. She also avoids letting a dictionary's printing order define the bytes. This discipline anticipates later chapters, where readings are authenticated with HMAC and firmware with signatures; both fail, harmlessly but confusingly, if sender and receiver disagree about the exact bytes.
Worked example
Aiyana fixes a failing test by encoding readings as UTF-8 once, using integer litres and UTC timestamps, then confirms her SHA-256 calls against a FIPS 180-4 test vector.
Synthetic case. The reading format is invented for Ridgeway; FIPS 180-4 is real.
Errors and limits
Hashing is not encryption: anyone can compute a digest. Mixing text and bytes silently changes results, so specify encodings explicitly.
Exercises
- EX010 — Hex length. Why does a SHA-256 digest print as 64 hex characters? Rubric: 3 = 32 bytes, two hex characters per byte;; 2 = 64 bytes;; 1 = random;; 0 = none.
- EX011 — Encoding bug. What caused Aiyana's first mismatched digests? Rubric: 3 = different text encodings on two systems for equal readings;; 2 = encoding mentioned;; 1 = wrong algorithm;; 0 = none.
- EX012 — Integer litres. Why does the canonical format use integer litres? Rubric: 3 = 18342.0 and 18342 produce different bytes and digests;; 2 = floats imprecise;; 1 = style;; 0 = none.
Synthesis
Correct cryptographic programming starts with bytes, explicit encodings, verified test vectors and a canonical format both sides agree on.
Continue studying — chapter 04 of 50
← Chapter 03 (draft) · Course chapter index · Chapter 05 (draft) →