Computer Networks

Network Security

Cryptography for Networks

ONE key, used to both encrypt and decrypt.

JrCodex·9 min read

Jr Codex Computer Networks Notes

Level: Intermediate–Advanced Prerequisites: Chapter 1: The Threat Model Time to complete: ~25 minutes


Table of Contents

  1. Symmetric Encryption
  2. The Key Distribution Problem
  3. Asymmetric Encryption
  4. Hashing and MACs
  5. Digital Signatures
  6. Key Exchange and Forward Secrecy
  7. Summary & Next Steps

1. Symmetric Encryption

The Model
─────────────────────────────────────────
  ONE key, used to both encrypt and decrypt.

    ciphertext = E(key, plaintext)
    plaintext  = D(key, ciphertext)

  Both parties must already share the key.

  AES is the standard: AES-128 and AES-256, in a
  mode of operation.
─────────────────────────────────────────
MODES MATTER MORE THAN KEY SIZE
─────────────────────────────────────────
  ECB   encrypts each block independently.
        IDENTICAL PLAINTEXT BLOCKS PRODUCE
        IDENTICAL CIPHERTEXT — so structure in the
        data remains visible.
        NEVER USE IT.

  CBC   chains blocks together with an IV. Better,
        and needs a separate MAC for integrity, and
        is vulnerable to padding oracle attacks if
        implemented carelessly.

  GCM   AEAD — Authenticated Encryption with
        Associated Data. Provides confidentiality
        AND integrity in one construction, and is
        parallelisable.
        ── the modern default

  ChaCha20-Poly1305
        AEAD, fast in SOFTWARE. Preferred on
        devices without AES hardware acceleration.
─────────────────────────────────────────
Why AEAD Is the Right Default
─────────────────────────────────────────
  Encryption alone gives CONFIDENTIALITY, not
  INTEGRITY (Chapter 1). An attacker can flip bits
  in ciphertext, producing predictable changes in
  plaintext.

  Combining encryption with a MAC by hand is
  error-prone, and the order matters
  (encrypt-then-MAC is correct; the alternatives
  have been broken).

  AEAD modes do both, correctly, in one primitive —
  removing an entire class of implementation
  mistakes.
─────────────────────────────────────────
Its Strength and Its Problem
─────────────────────────────────────────
  FAST. AES with hardware acceleration runs at
  gigabytes per second — so all BULK DATA is
  encrypted symmetrically.

  But it requires a SHARED KEY. Which is Section 2.
─────────────────────────────────────────

2. The Key Distribution Problem

The Difficulty
─────────────────────────────────────────
  To communicate securely with a stranger, you must
  first share a secret key.

  To share it securely, you need a secure channel.
  To have a secure channel, you need a shared key.

  Circular.
─────────────────────────────────────────
And the Scaling Problem
─────────────────────────────────────────
  N parties, each pair needing its own key:

    N(N-1)/2 keys

    10 parties ──►      45 keys
   100 parties ──►   4,950
  1,000 parties ──► 499,500

  Every website you visit would need a pre-shared
  key with you, arranged in advance.

  Asymmetric cryptography solves both problems.
─────────────────────────────────────────

3. Asymmetric Encryption

The Model
─────────────────────────────────────────
  TWO mathematically related keys:

    PUBLIC KEY   given to everyone
    PRIVATE KEY  never shared

  Encrypt with the public key ──► only the private
  key decrypts.
  Sign with the private key ──► anyone verifies
  with the public key.

  The private key cannot feasibly be derived from
  the public one — that is the whole security
  assumption.
─────────────────────────────────────────
What Solves What
─────────────────────────────────────────
  KEY DISTRIBUTION  publish your public key
                    openly; no secure channel
                    needed

  SCALING           N parties need N key PAIRS, not
                    N²/2 shared keys
─────────────────────────────────────────
The Cost
─────────────────────────────────────────
  Asymmetric operations are roughly 100-1000x
  SLOWER than symmetric ones, and can only encrypt
  small amounts of data.

  THE HYBRID SOLUTION — used by essentially every
  real system:

    1. Use ASYMMETRIC crypto to establish a shared
       SYMMETRIC key
    2. Use SYMMETRIC crypto for all the actual data

  You get asymmetric's key distribution and
  symmetric's speed. This is exactly what TLS does
  (Chapter 3).
─────────────────────────────────────────
The Algorithms
─────────────────────────────────────────
  RSA      based on the difficulty of factoring
           large numbers. Needs 2048+ bit keys.
           Well understood, slower, larger.

  ECC      elliptic curve. 256-bit ECC ≈ 3072-bit
           RSA in strength.
           Smaller keys, faster operations, less
           bandwidth ── the modern default.
           X25519 for key exchange, Ed25519 for
           signatures.
─────────────────────────────────────────

4. Hashing and MACs

CRYPTOGRAPHIC HASH FUNCTIONS
─────────────────────────────────────────
  Arbitrary input ──► fixed-size output.

  REQUIRED PROPERTIES:
    DETERMINISTIC     same input, same output
    FAST to compute
    PREIMAGE          given h(x), cannot find x
    RESISTANT
    SECOND PREIMAGE   given x, cannot find y with
    RESISTANT         h(y) = h(x)
    COLLISION         cannot find ANY x, y with
    RESISTANT         h(x) = h(y)
    AVALANCHE         one bit changed ──► ~half the
                      output bits change

  USE: SHA-256, SHA-3, BLAKE2.
  BROKEN, DO NOT USE: MD5, SHA-1.
─────────────────────────────────────────
import hashlib, hmac, secrets
 
data = b"transfer 100 to account B"
print(hashlib.sha256(data).hexdigest())
 
# AVALANCHE: one character changes everything.
print(hashlib.sha256(b"transfer 100 to account C").hexdigest())
A HASH IS NOT INTEGRITY PROTECTION
─────────────────────────────────────────
  An on-path attacker who modifies your message can
  simply RECOMPUTE the hash. Anyone can compute a
  hash — that is the point.

  To prove a message came from someone holding a
  SECRET, you need a MAC.
─────────────────────────────────────────
# HMAC — a hash keyed with a shared secret.
key = secrets.token_bytes(32)
tag = hmac.new(key, data, hashlib.sha256).hexdigest()
 
def verify(key, data, tag):
    expected = hmac.new(key, data, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, tag)      # CONSTANT TIME — see below
 
print(verify(key, data, tag))                                  # True
print(verify(key, b"transfer 100 to account C", tag))          # False
WHY compare_digest RATHER THAN ==
─────────────────────────────────────────
  A normal string comparison returns as soon as it
  finds a mismatched byte.

  So comparing an attacker's guess takes measurably
  longer when more leading bytes are correct. By
  timing many attempts, an attacker recovers the
  tag one byte at a time — a TIMING ATTACK.

  compare_digest always examines every byte, taking
  constant time.

  This applies to comparing ANY secret: tokens,
  password hashes, signatures. It is a small habit
  that closes a real hole.
─────────────────────────────────────────

5. Digital Signatures

The Difference From a MAC
─────────────────────────────────────────
  MAC        a SHARED key. Either party could have
             produced the tag, so it proves
             authenticity between them — and
             neither can prove it to a third party.

  SIGNATURE  the PRIVATE key signs; the PUBLIC key
             verifies. Only the key holder could
             have produced it.
             ──► NON-REPUDIATION (Chapter 1)
─────────────────────────────────────────
How It Works
─────────────────────────────────────────
  SIGNING
    1. hash the message
    2. encrypt the hash with the PRIVATE key

  VERIFYING
    1. hash the received message
    2. decrypt the signature with the PUBLIC key
    3. compare

  Hashing first means signatures are small and fast
  regardless of message size — and it is why hash
  collision resistance matters so much: two
  messages with the same hash would share a valid
  signature.
─────────────────────────────────────────
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.exceptions import InvalidSignature
 
private = ed25519.Ed25519PrivateKey.generate()
public  = private.public_key()
 
message = b"transfer 100 to account B"
signature = private.sign(message)
 
try:
    public.verify(signature, message)
    print("valid")
except InvalidSignature:
    print("INVALID")
 
try:
    public.verify(signature, b"transfer 900 to account B")   # tampered
except InvalidSignature:
    print("tampering detected")

Signatures are what make certificates work — a certificate is a signed statement binding a name to a public key, which is Chapter 3.


6. Key Exchange and Forward Secrecy

DIFFIE-HELLMAN
─────────────────────────────────────────
  Two parties derive a SHARED SECRET over a PUBLIC
  channel, without ever transmitting it.

    Public: a large prime p, a generator g

    Alice picks secret a, sends  A = g^a mod p
    Bob   picks secret b, sends  B = g^b mod p

    Alice computes  B^a mod p = g^(ab) mod p
    Bob   computes  A^b mod p = g^(ab) mod p

    Both hold g^(ab) mod p. An eavesdropper saw
    only g^a and g^b, and cannot compute g^(ab)
    without solving the discrete logarithm problem.
─────────────────────────────────────────
import secrets, hashlib
 
p = int("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
        "29024E088A67CC74020BBEA63B139B22514A08798E3404DD", 16)   # a 1536-bit prime
g = 2
 
a = secrets.randbelow(p - 2) + 1            # Alice's secret, never transmitted
b = secrets.randbelow(p - 2) + 1            # Bob's secret, never transmitted
 
A = pow(g, a, p)                            # sent in the clear
B = pow(g, b, p)                            # sent in the clear
 
alice_secret = pow(B, a, p)
bob_secret   = pow(A, b, p)
assert alice_secret == bob_secret           # the same value, never transmitted
 
session_key = hashlib.sha256(str(alice_secret).encode()).digest()
DH ALONE IS VULNERABLE
─────────────────────────────────────────
  Diffie-Hellman gives you a shared secret with
  SOMEBODY. It does not tell you WHO.

  A machine-in-the-middle performs DH separately
  with each party and relays between them, reading
  everything.

  So DH must be AUTHENTICATED — each side signs its
  DH parameters with a key you can verify.

  This is Chapter 1's point once more:
  confidentiality without authentication is
  worthless. TLS combines them (Chapter 3).
─────────────────────────────────────────
PERFECT FORWARD SECRECY
─────────────────────────────────────────
  Use an EPHEMERAL DH key pair, discarded after the
  session (ECDHE — the E is ephemeral).

  WHY IT MATTERS:
    Without PFS, an attacker who records encrypted
    traffic today and obtains the server's private
    key in five years can decrypt ALL of it
    retroactively.

    With PFS, each session's key was derived from
    ephemeral values that no longer exist anywhere.
    Compromising the long-term key lets an attacker
    IMPERSONATE the server in future — but not read
    the past.

  TLS 1.3 makes PFS MANDATORY. Non-PFS key exchange
  was removed from the protocol entirely, which is
  one of its most important changes.
─────────────────────────────────────────

7. Summary & Next Steps

Key Takeaways

  • Symmetric encryption is fast and needs a shared key; asymmetric solves key distribution and scaling but is far slower, so every real system is a hybrid.
  • AEAD modes provide confidentiality and integrity in one primitive, removing a whole class of errors from combining encryption and MACs by hand.
  • A hash proves nothing about origin because anyone can compute one; a MAC requires a shared secret, and a signature additionally provides non-repudiation.
  • Diffie-Hellman establishes a shared secret with somebody and must be authenticated; ephemeral keys give forward secrecy, which TLS 1.3 made mandatory.

Concept Check

  1. Why is ECB mode unusable even though it uses the same AES cipher as GCM?
  2. Why does comparing a MAC with == create a vulnerability that compare_digest avoids?
  3. What exactly does perfect forward secrecy protect against that ordinary encryption does not?

Next Chapter

Chapter 3: TLS and HTTPS


Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index | Back to Computer Networks Index