Cryptography for newcomers

The hitchhiker's guide to cipher in devs

@m4d_z
alwaysdata
Users do not know how to protect themselves

Any sensitive data must
be transferred and stored
in an encrypted form

We Need Cryptography!

Cryptography is a matter of:

  • Hash
  • Encryption
  • Key Exchange
  • Signature

Crypto, History, and main flaws

Crypto is as old as war

Cryptanalysis
is as old as Encryption!

Flaw #1: Too Simple Key

Shift Cipher Wheel

Fix?

  • Long-length keys
  • Make them hard to forge

Flaw #2: Repetitions

Vigenère Cipher

Plain text:  ATTACK AT DAWN
Cipher key:  LEMONL EM ONLE
Cipher text: LXFOPV EF RNHR

Fix?

  • Avoid Common Denominator
  • Use Unique Keys

Flaw #3: Brute Force Attack

xkcd://538
Enigma, virtually unbreakable

No pattern is strong enough to resist to an endless attack

Hashing, Salting, Cooking

Hashing is
Data Obfuscation

Failed!

Simple Rainbow Table - Wikimedia

Salting

  • Entropy
  • Avoid repetitions
  • Must be unique and random
from base64 import b64encode
import secrets
import argon2
import hmac
from haslib import sha256

salt = secrets.token_bytes(64)
hash = argon2.low_level.hash_secret_raw('b'*password, salt,
    time_cost=1, memory_cost=8, parallelism=1, hash_len=8,
    type=argon2.low_level.Type.D)

record = "$argon2$t=1,m=8,p=1$%s%s" % (b64encode(salt), b64encode(hash))
ph = argon2.PasswordHasher()
record = ph.hash(password)
digest = hmac.new(server_key, 'b'*record, digestmod=sha256).digest()

A properly hashed password with
no repetition and a time-controlled execution
decrease the risk of brute-force hacking

Key-based encryption

How Encryption Works?

  • Based on Maths
  • Use a Key as input
  • Compute Substitutions
  • Rely on Randomness

Block Ciphers

  • DES (Data Encryption Standard)
  • AES (Advanced Encryption Standard)
  • IDEA
  • BlowFish

Block modes of operation

  • ECB (Electronic Code Book)
  • CBC (Cipher Block Chaining)
  • AEAD (Authenticated Encryption with Associated Data)
    (OCB, EAX, …)

Computers aren’t
truly random!

Padding, Randomness, IV

  • Unpredictible, non-deterministic values
  • CSPRNG functions
    rather than direct /dev/urandom access
  • IV (Initialization Vector)
    blocks used to init a cipher function and put it in a unique state

Stream Ciphers

  • RC4
  • ChaCha20 ?
  • Panama ?

Dealing with Keys

Symmetric Keys

  • Same key used for all tasks
  • Fast
  • Suitable for huge amount off data

A symmetric key
must be shared,
which means it could leak

Diffie-Hellman Key Exchange

Asymmetric Keys

  • One private key
  • One (or more) public key(s)
  • “Public” is used for encryption
  • Very strong and very slow

RSA or Eliptic Curves?

RSA

  • Prime Number Factors
  • Widely deployed
  • Very Fast on encryption and Signature
  • Easy to implement / understand
  • Very slow in decryption

ECC

  • Eliptic Curves Discrete Logarithm
  • Small keys, ciphertexts, signatures
  • Fast
  • Complicated to implement
  • The strongest, the slowest

→ Better rely on ECC when available

Key Wrapping

import secrets
from struct import pack
from Crypto.Cipher import Blowfish
from Crypto.PublicKey import RSA

iv = secrets.randbits(Blowfish.block_size)
plen = Blowfish.block_size - divmod(len(msg), Blowfish.block_size)[1]
padding = [plen]*plen

token = secrets.token_bytes(32)
cipher = Blowfish.new(token, Blowfish.MODE_CBC, iv)

key = RSA.importKey(recipient_key)
encToken = key.public_key.encrypt(token, 32)

wrap = pack(b'---', encToken, iv + cipher.encrypt(msg + pack('b'*plen, *padding)))

Signature

  • Reversed Asymmetric Cipher
  • For trusting purposes only
x.509 Certificates

Use it day to day

Protect

  • Network SSL/TLS, chain of trust
  • Passwords → Hashing functions
  • Data → Asymmetric Cipher w/ Key Wrapping, HSM

Low-level languages

  • OS modules
  • Librairies (OpenSSL)
  • Hardware (AES-NI, Co-processor)

Desktop/Server

  • Python: Cryptography / PyCrypto
  • Ruby: RbNaCl
  • Node.js: Crypto built-in module
  • PHP: Mcrypt
  • Java: JCE Framework
  • etc.

Multi-platform libraries

  • Low-level for fast perfomances
  • Agnostic
  • Portable
  • Available using bindings

Encrypt before sending to the client

import nacl
from nacl.public import PrivateKey, Box
import User

@app.route("/secret/<username>/<nonce>")
def get_secret(username, nonce):
    skfile = open("sk_server.key", "r")
    skserver = PrivateKey(skfile, nacl.encoding.RawEncoder)

    user = User.query.filter_by(username=username).first()

    box = Box(skserver, user.pk)

    message = b"Never Gonna Give You Up!"
    encrypted = box.encrypt(message, nonce)

    return encrypted

Mobile/Web

  • WebCrypto
  • Browser libs
  • iOS: CryptoKit
  • Libsodium!

Decrypt a blob from the server

const _sodium = require('libsodium-wrapper')

(async () => {
  await _sodium.ready
  const sodium = _sodium

  const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES)

  await res = fetch(`/secret/${user.name}/${sodium.to_string(nonce)}`)
  const encrypted = res.text()

  const decrypted = sodium.crypto_box_open_easy(encrypted, nonce, SERVER_PK, user.sk)
  const message = sodium.to_string(decrypted)
  // Never Gonna Give You Up!
})

Auth High-level API

A public-key cryptography based API that allow
passwordless authentication on platforms

WebAuthn: register

@app.route('/webauthn_begin_activate', methods=['POST'])
def webauthn_begin_activate():
    # ...
    rp_name = 'localhost'
    challenge = util.generate_challenge(32)

    make_credential_options = webauthn.WebAuthnMakeCredentialOptions(
        challenge, rp_name, RP_ID, ukey, username, display_name,
        'https://example.com')

    return jsonify(make_credential_options.registration_dict)

WebAuthn: challenge

@app.route('/webauthn_begin_assertion', methods=['POST'])
def webauthn_begin_assertion():
    webauthn_user = webauthn.WebAuthnUser(
        user.ukey, user.username, user.display_name, user.icon_url,
        user.credential_id, user.pub_key, user.sign_count, user.rp_id)

    webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
        webauthn_user, challenge)

    return jsonify(webauthn_assertion_options.assertion_dict)

WebAuthn: login

@app.route('/verify_assertion', methods=['POST'])
def verify_assertion():
    # ...
    webauthn_user = webauthn.WebAuthnUser(
        user.ukey, user.username, user.display_name, user.icon_url,
        user.credential_id, user.pub_key, user.sign_count, user.rp_id)

    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
        webauthn_user, assertion_response, challenge,
        ORIGIN, uv_required=False)  # User Verification

    try:
        sign_count = webauthn_assertion_response.verify()
    except Exception as e:
        return jsonify({'fail': 'Assertion failed. Error: {}'.format(e)})

Think Cryptographically 😎

Never be a Sorcerers Apprentice

Never forget that

  1. Security has an inverse relation to the ease of use
  2. Security has a cost
m4dz's avatar
m4dz

Paranoïd Web Dino · Tech Evangelist

alwaysdata logo
https://www.alwaysdata.com

Questions?

Illustrations

m4dz, CC BY-SA 4.0

Interleaf images

Courtesy of Unsplash and Pexels contributors

Icons

  • Layout icons are from Entypo+
  • Content icons are from FontAwesome

Fonts

  • Cover Title: Sinzano
  • Titles: Argentoratum
  • Body: Mohave
  • Code: Fira Code

Tools

Powered by Reveal.js

Source code available at
https://git.madslab.net/talks