Commit 5da6f168 authored by Amirali Sanatinia's avatar Amirali Sanatinia
Browse files

fixes

parent f3925f79
Loading
Loading
Loading
Loading
+20 −0
Changes for notebooks/2. Hashing Algorithms.ipynb: 20 added lines, 0 removed lines.
Original line number Diff line number Diff line
%% Cell type:markdown id: tags:

# Hashing Algorithms

%% Cell type:markdown id: tags:

The goal is to have a long message as input and produce an output which is much shorter, called the hash or message digest. Furthermore, we want it to have certain properties.

- **Input**: long message
- **Output**: short fixed size block (called hash or message digest)

*We want the same input always produces the same output (deterministic)*

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - A *non* security related example of hash functions?
 - A security related example of hash functions?

%% Cell type:markdown id: tags:

*** There is a difference between hash functions and cryptographic hash functions ***

%% Cell type:markdown id: tags:

### Desired properties
  - Pre-image: Given a hash $h$ it is computationally infeasible to find a message $m$ that produces $h$
  - Second preimage: Given message m, it is computationally infeasible to find a message $m’$, ($m ≠ m’$) such that, $h(m) = h(m’)$
  - Collisions: It is computationally difficult to find any two messages $m, m’$ $(m ≠ m’)$ such that, $h(m) = h(m’)$


**Examples**:
 - Recommended Hash Algorithm (SHA-2, SHA-3) by NIST
 - <strike>SHA-1</strike>: output 160 bits being phased out (shattered.io)
 - <strike>MD2, MD4, and MD5</strike> by Ron Rivest [RFC1319, 1320, 1321]

<br>
source: shattered.io
<br>
<img src="include/shattered.png" width="400">
<img src="include/shattered_diagram.png" width="400">

%% Cell type:markdown id: tags:

<br>

%% Cell type:markdown id: tags:

### SHA Family

Secure Hash Algorithm (SHA) family, is a series of hashing algorithms. Ranging from SHA-0 to SHA-3. SHA-0 should never be used, it's advised to move from SHA-1 to SHA-2. SHA-3 is the most recent version, standardized by NIST.

 * SHA-1: Digest size (160), Block size (512)
 * SHA-2: Digest size (224, 256, 384, or 512), Block size (512, 1024)
 * SHA-3: Digest size (224, 256, 384, 512), Block size (1600)

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
import base64 # to produce human readable encoding of the bytes

digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(b"O'Reilly")
digest.update(b"Crypto")
msg_digest = digest.finalize()
# Notice the output size of the digest
print ("msg_digest:", len(msg_digest), len(msg_digest) * 8)
print ("base64 encoding:", base64.b64encode(msg_digest))

print()

digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(b"O'ReillyCrypto")
msg_digest = digest.finalize()
# Notice the output size of the digest
print ("msg_digest:", len(msg_digest), len(msg_digest) * 8)
print ("base64 encoding:", base64.b64encode(msg_digest))

print()
```

%% Cell type:markdown id: tags:

## <font color=" #6495ED">Exercise</font>

 - Let's explore what other hashing algorithms are available and use a different one.
 - Change "O'ReillyCrypto", to "O'Reilly Crypto" and see check the result

%% Cell type:markdown id: tags:

### Python Builtin Support

%% Cell type:markdown id: tags:

*"Additional algorithms may also be available depending upon the OpenSSL library that Python uses on your platform. On most platforms the sha3_224(), sha3_256(), sha3_384(), sha3_512(), shake_128(), shake_256() are also available."*

source: https://docs.python.org/3/library/hashlib.html

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```

%% Cell type:markdown id: tags:

## <font color=" #6495ED">Exercise</font>
 - Use a different hashing algorithm and compare the digest size and speed

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```

%% Cell type:markdown id: tags:

## <font color=" #6495ED">Exercise</font>

 - Let's explore the impact of the size of the message on the speed of our hashing

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```

%% Cell type:markdown id: tags:

### Hash-based message authentication code (HMAC)

HMACs are used for message authentications combined with a secret key. This provide integrity check and authentication. An attacker can not forge the digest without knowing the secret key. Same message produces different digests for different keys.

<img src="include/SHAhmac.png" width="600">

image source: wikipedia

%% Cell type:markdown id: tags:

$ HMAC(K,m) = H((K\oplus opad) || H((K \oplus ipad) || m))$

%% Cell type:markdown id: tags:

### HMAC Calculation

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hmac, hashes
import os
import base64

hmc_key = k = os.urandom(16)
hmc = hmac.HMAC(hmc_key, hashes.SHA1(), default_backend())
hmc.update(b"O'ReillyCrypto")
hmc_sig = hmc.finalize()
print (base64.b64encode(hmc_sig))
```

%% Cell type:markdown id: tags:

### HMAC Verification

%% Cell type:code id: tags:

``` python
# Verification Successufl
hmc = hmac.HMAC(hmc_key, hashes.SHA1(), default_backend())
hmc.update(b"O'ReillyCrypto")
hmc.verify(hmc_sig)
```

%% Cell type:markdown id: tags:

## <font color=" #6495ED">Exercise</font>

 - Let's change the calculated HMAC and see what happens when the two HMACs don't match

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```
+15 −0
Changes for notebooks/3. Symmetric Encryption.ipynb: 15 added lines, 0 removed lines.
Original line number Diff line number Diff line
%% Cell type:markdown id: tags:

# Modern Cryptography

Generally speaking there are two *kinds* of encryptions. Symmetric and Asymmetric.

In the symmetric encryption the parties involved share the ***same*** key.

In the asymmetric encryption schemes the parties use ***different*** keys, that are mathematically ***related*** to each other.

<img src="include/sym_vs_asym.png" width="700">

%% Cell type:markdown id: tags:

## Symmetric Encryption

In the following we look at the symmetric encryption algorithms. In symmetric crypto, we use the same key for encryption and decryption. **Therefore, the two parties needs to establish a secret key between them.** Symmetric encryption can be up to 1000 times faster than asymmetric encryption. Given the support of some crypto algorithm in the CPU and at hardware level, even faster.

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - How should we share this secret key? securely, of course!

%% Cell type:markdown id: tags:

### Advanced Encryption Algorithm (AES)

AES is based on Rijndael encryption algorithm, designed by Joan Daemen and Vincent Rijmen. It was one of the algorithms submitted to U.S. National Institute of Standards and Technology (NIST) to replace DES and 3DES. It was published in 1998 and accepted and standardized in 2001.

 * AES supports key sizes of 128/192/256 bits
 * Block size: 128 bit
 * It's iterative rather than Feistel cipher
 * Treats data in 4 groups of 4 bytes
 * Operates on an entire block in every round
 * Resistant against known attacks
 * Speed and code compactness on many CPUs
 * Rijndael block and key size vary between 128, 192, 256
 * However, in AES block size in 128
 * Number of rounds a function of key size
  * 128 bits     10 rounds
  * 192 bits     12 rounds
  * 256 bits     14 rounds

 * Today most implementations use the CPU support (Intel AES-NI)

%% Cell type:markdown id: tags:

### Block cipher mode of operation

To encrypt messages of arbitrary size with block ciphers, we use the following algorithms, called the modes of operation. They define how to encrypt each block of the plaintext to produce the corresponding cipher text block. Some of these are completely insecure (ECB) and should not be used.

 * Electronic Codebook (ECB)
 * Cipher Block Chaining (CBC)
 * Counter (CTR)


### Electronic Codebook (ECB)

<img src="include/ECB_enc.png">
<img src="include/ECB_dec.png">



### Cipher Block Chaining (CBC)

<img src="include/CBC_enc.png">
<img src="include/CBC_dec.png">



### Counter (CTR)

<img src="include/CTR_enc.png">
<img src="include/CTR_dec.png">

%% Cell type:code id: tags:

``` python
import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
key = os.urandom(16) # in bytes, 128 bits
iv = os.urandom(16)
```

%% Cell type:code id: tags:

``` python
len(b"O'ReillyCrypto19")
```

%% Cell type:code id: tags:

``` python
# ECB Mode, we only need a key
### *** DO NOT USE ECB. IT IS INSECURE *** ###

cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend())
encryptor = cipher.encryptor()
# note that we don't need padding here, since len(b"O'ReillyCrypto19") = 16
cipher_text = encryptor.update(b"O'ReillyCrypto19") + encryptor.finalize()
```

%% Cell type:code id: tags:

``` python
cipher_text
```

%% Cell type:code id: tags:

``` python
decryptor = cipher.decryptor()
decryptor.update(cipher_text) + decryptor.finalize()
```

%% Cell type:code id: tags:

``` python
# CBC Mode, we also need an IV
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
# note that we don't need padding here, since len(b"O'ReillyCrypto19") = 16
cipher_text = encryptor.update(b"O'ReillyCrypto19") + encryptor.finalize()
```

%% Cell type:code id: tags:

``` python
cipher_text
```

%% Cell type:code id: tags:

``` python
decryptor = cipher.decryptor()
decryptor.update(cipher_text) + decryptor.finalize()
```

%% Cell type:code id: tags:

``` python
# CTR Mode, we don't need padding in CTR mode. In transforms a block cipher into a stream cipher
# we only need to introduce the nonce
cipher = Cipher(algorithms.AES(key), modes.CTR(os.urandom(16)), backend=default_backend())
encryptor = cipher.encryptor()
cipher_text = encryptor.update(b"O'Reilly Crypto 2019") + encryptor.finalize()
print (cipher_text)
```

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Encrypt the file following text using the ECB, and CBC or CTR mode and compare the results.

%% Cell type:code id: tags:

``` python
plain_text = b"O'ReillyCrypto19" * 128
```

%% Cell type:code id: tags:

``` python
import base64
def print_text(text, b64=True):
    for i in range(0, 128, 16):
        if b64:
            pt = base64.b64encode(text[i:i+16])
        else:
            pt = text[i:i+16]
        print (pt)
```

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise (Extra)</font>*

Encrypt the file "include/tux.png" using the ECB, and CBC or CTR mode and compare the results.
 - **You need to install the [pillow](https://python-pillow.org/) library**
     - read the image file and get all the pixel values
     - encrypt the pixel values
     - create a new image with new (encrypted) pixel values
     - write your newly crafted image to disk


%% Cell type:markdown id: tags:

The origianl image from Wikipedia. <img src="include/tux.png">

%% Cell type:markdown id: tags:

<br>
ECB Encryption of the image with two different keys. The results (colors) are different, because we are using two different keys. However, the patterns inside the data (image) is not hidden.
<br>
<br>

<img src="include/ECB1.png" width="200" align="left" > <img src="include/ECB2.png" width="200" >

%% Cell type:markdown id: tags:

<br>
As compared to when we are using the CBC (or CTR) mode. Because we introduce the randomness at the beginning (IV), and we carry this randomness (noise) throughout the encryption the patterns are diminished.
<br>
<br>

<img src="include/CBC1.png" width = 200>

%% Cell type:markdown id: tags:

## Encryption alone is not good enough

Encrypting you data alone will not protect you from data tampering. Meaning an adversary can change the results of your decryption without having access to the key. All, without you noticing. That's when are HMACs from previously become handy.

%% Cell type:markdown id: tags:

### Bit flipping attack

%% Cell type:markdown id: tags:

Since the IV is sent in clear we can change the IV value and change the corresponding plaintext, when using CBC mode.

***Encryption***

 - $C_{i}=E_{K}(P_{i}\oplus C_{i-1})$
 - $ C_{0}=IV$

***Decryption***
 - $P_{i}=D_{K}(C_{i})\oplus C_{i-1}$
 - $C_{0}=IV$


Therefore to change the plaintext value we just need to xor the old plaintext(p), and the new value (t), with the IV:

$IV = IV \oplus p \oplus t$

Meaning if the first 4 bytes of the plaint text are: "1234" and we want to change it to "6789" all we have to do is

$IV[0:4] = IV[0:4] \oplus 1234 \oplus 6789$

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Imagine the message is only 16 bytes, "O'ReillyCrypto19". Change the year from 19 to 20 by modifying the value of IV.

%% Cell type:code id: tags:

``` python
def xor(s1, s2):
    return bytes([a ^ b for a,b in zip(s1,s2)])
```

%% Cell type:code id: tags:

``` python
iv = os.urandom(16)
key = os.urandom(16)
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
cipher_text = encryptor.update(b"O'ReillyCrypto19") + encryptor.finalize()
```

%% Cell type:code id: tags:

``` python
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
decryptor.update(cipher_text) + decryptor.finalize()
```

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```

%% Cell type:markdown id: tags:

<br>

%% Cell type:markdown id: tags:

## Authenticated Encryption with Associated Data (AEAD)

AEAD provides confidentiality, integrity, and authenticity at once. Such schemes help to mitigate against the bit flipping attacks that we just did. The Galois/Counter Mode (GCM) mode of operation is the recommended schemes to be used. Fortunately, the *cryptography* library already has it implemented.

%% Cell type:code id: tags:

``` python
# GCM Mode, we also need an IV
cipher = Cipher(algorithms.AES(key), modes.GCM(iv), backend=default_backend())
encryptor = cipher.encryptor()
# note that we don't need padding here, since len("O'ReillyCrypto19") = 16
encryptor.authenticate_additional_data(b"SOME ADDITIONAL DATA")
cipher_text = encryptor.update(b"O'ReillyCrypto19") + encryptor.finalize()
tag = encryptor.tag
```

%% Cell type:code id: tags:

``` python
decryptor = Cipher(algorithms.AES(key), modes.GCM(iv,tag), backend=default_backend()).decryptor()
decryptor.authenticate_additional_data(b"SOME ADDITIONAL DATA")
decryptor.update(cipher_text) + decryptor.finalize()
```

%% Cell type:markdown id: tags:

## Padding

With some block cipher mode of operations (e.g., CBC) we need to pad the data to the block size. Otherwise, if would throw exception.

%% Cell type:code id: tags:

``` python
import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
key = os.urandom(16) # in bytes, 128 bits

#CTR
cipher = Cipher(algorithms.AES(key), modes.CTR(os.urandom(16)), backend=default_backend())
encryptor = cipher.encryptor()
# len("b"O'Reilly Crypto 2019") =20, but we don't need padding in CTR
ctr_ct = encryptor.update(b"O'Reilly Crypto 2019") + encryptor.finalize()
```

%% Cell type:code id: tags:

``` python
#CBC
cipher = Cipher(algorithms.AES(key), modes.CBC(os.urandom(16)), backend=default_backend())
encryptor = cipher.encryptor()
# len("b"O'Reilly Crypto 2019") = 20, throws an exception
cbc_ct = encryptor.update(b"O'Reilly Crypto 2019") + encryptor.finalize()
```

%% Cell type:markdown id: tags:

### Public-Key Cryptography Standards (PKCS)

PKCS7 padding is described in RFC 5652. The number of missing bytes (n) to the whole block size is repeated n times.

 - For example if the block size is 16,
   - The data is of size 13, the data is padding with 3, 3 times. 03 03 03.
   - The data is of size 14, the data is padded with 2, 2 times. 02 02

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Implement PKCS7 padding

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```
+30 −64
Changes for notebooks/4. Asymmetric Encryption.ipynb: 30 added lines, 64 removed lines.
Original line number Diff line number Diff line
%% Cell type:markdown id: tags:

# Asymmetric Encryption

Reminder: In the asymmetric encryption schemes the parties use ***different*** keys, that are mathematically ***related*** to each other.

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Why asymmetric encryption is useful?
 - Give a few examples where it can be used?

%% Cell type:markdown id: tags:

## RSA

RSA, is an asymmetric encryption algorithm by Ron Rivest, Adi Shamir, and Leonard Adleman. It was published in 1977. It's security is based on the hardness of factorization problem. However, now it has its own problem, called the RSA problem. RSA is slow, and is not used for encrypting large data, but it's mostly used to encrypt the symmetric key that is used for encryption.


 * $p, q$, two big prime numbers (private, chosen)
 * $n = pq$, $φ(n) = (p-1)(q-1)$   (public, calculated)
 * $e$, with $gcd(φ(n), e) = 1,  1 < e < φ(n)$	(public, chosen)
 * $d = e - 1$ mod $φ(n)$	(private, calculated)
 * $E(M) = M^e \mod n$
 * $D(M) = M^d \mod n$
 * $D(E(M)) = M^{ed} \mod n = M$

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - How to test if a number is prime?

%% Cell type:markdown id: tags:

## RSA EXAMPLE

 - p = 5; q = 11 => n = 55
 - φ(n) = 40
 - e = 3 => d = 27
  - Because ed = 1 mod φ(n)
 - Public key: (e, n)
 - Private key: (d, n)
 - Encryption
  - M = 2
 - Encryption(M) = $ M^e\mod n$  = $2^3\mod n$ = 8
 - Decryption(8) = $ M^d\mod n$  = $8^{27} \mod n$ = 2

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Perform textbook RSA encryption and decryption using the values from the example

%% Cell type:code id: tags:

``` python
p = 5
q = 11
n = p * q
e = 3
d = 27
m = 2
```

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```

%% Cell type:markdown id: tags:

### Signing

The purpose of signatures is to prove the "creator" of a message. To perform this task we use the private key, since only the owner of the keypair is aware of the private key. Everyone with the knowledge of the public key can verify the signature.

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Create textbook RSA signature on message (m=2). Use the private key (d), instead of the public key (e) to generate the signature. Use the public key (e) to verify the signature.

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
p =5
q = 11
n = p * q
e = 3
d = 27
m = 2
```

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```

%% Cell type:markdown id: tags:

### Generating RSA keypairs using OpenSSL

To generate keys, use the following instructions:

```bash
 openssl genrsa -out private_key.pem 2048
 openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
 openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
 ```

%% Cell type:code id: tags:

``` python
# import key from a file. E.g., previously generated by OpenSSL
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization

with open("private_key.pem", "rb") as key_file:
     private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None,
            backend=default_backend())
public_key = private_key.public_key()
```

%% Cell type:code id: tags:

``` python
# Generate a 2048 bit private key
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa

private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend())
# to get the public key
public_key = private_key.public_key()
```

%% Cell type:code id: tags:

``` python
2 ** 16 +1
```

%% Cell type:code id: tags:

``` python
print(bin(2**16 + 1))
print(bin(2**1 + 1))
```

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - What's wrong with textbook RSA?

%% Cell type:markdown id: tags:

### It's all about padding and randomness

%% Cell type:markdown id: tags:

### Optimal Asymmetric Encryption Padding (OAEP)

Textbook RSA is not IND-CPA secure, therefore we use Optimal Asymmetric Encryption Padding (OAEP).

<img src="include/RSA_OAEP.png">

image souce: wikipedia

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

message = b"Y"*127
ciphertext = public_key.encrypt(
    message,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA1()),
        algorithm=hashes.SHA1(),
        label=None))
```

%% Cell type:code id: tags:

``` python
print(ciphertext)
```

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

plaintext = private_key.decrypt(
    ciphertext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA1()),
        algorithm=hashes.SHA1(),
        label=None))
```

%% Cell type:code id: tags:

``` python
print(plaintext)
```

%% Cell type:markdown id: tags:

### Probabilistic Signature Scheme (PSS)

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

message = b"Message to be signed"
```

%% Cell type:code id: tags:

``` python
# signature creation
signature = private_key.sign(
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256())
```

%% Cell type:code id: tags:

``` python
# signature verification
public_key.verify(
    signature,
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256())
```

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Compare the speed of encryption using RSA-2048 and AES-128.

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```

%% Cell type:markdown id: tags:

These tests give an idea of the speed difference between RSA and AES. However, our benchmark does not reflect the whole picture. To get a better view run:

``` bash
 $ openssl speed rsa aes
```

%% Cell type:markdown id: tags:

## Diffie–Hellman key exchange

%% Cell type:markdown id: tags:

Diffie-Hellman is a key exchange protocol and an example of an asymmetric cryptography. Named after it's inventors  Whitfield Diffie and Martin Hellman in 1976. The security of DH key exchange relies on the hardness of discrete logarithm.

%% Cell type:markdown id: tags:

 - Alice: generate private key ($a$). Calculate public key $A = (g ^ a)$ mod $p$
 - Bob: generate private key ($b$). Calculate public key $B = (g ^ b)$ mod $p$


 - Alice -> Bob: Alice sends her public key ($A$) to Bob
 - Bob -> Alice: Bob send his public key ($B$) to Alice


 - Alice and Bob: Calculate shared key K = $g ^ {ab}$ mod $p$
 - $(g ^ a) ^ b = (g ^ b) ^ a = g ^ {ab}$

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Perform basic DH key exchange using the values below to create a shared key ($g^{ab}$)

%% Cell type:code id: tags:

``` python
g = 2
p = 19
a = 7
b = 8
```

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```

%% Cell type:markdown id: tags:

### Performing DH key exchange with key derivation

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives.kdf.hkdf import HKDF

parameters = dh.generate_parameters(generator=2, key_size=2048,
                                    backend=default_backend())

private_key_a = parameters.generate_private_key()  # a
private_key_b = parameters.generate_private_key()  # b
```

%% Cell type:code id: tags:

``` python
public_key_a = private_key_a.public_key() # g^a
public_key_b = private_key_b.public_key() # g^b
```

%% Cell type:code id: tags:

``` python
shared_key_b = private_key_b.exchange(public_key_a) # (g^a) ^ b = g ^ ab
```

%% Cell type:code id: tags:

``` python
shared_key_a = private_key_a.exchange(public_key_b) # (g^a) ^ b = g ^ ab
```

%% Cell type:code id: tags:

``` python
assert shared_key_a == shared_key_b # (g^a)^b == (g^b)^a == g^ab == g^ba
```

%% Cell type:code id: tags:

``` python
derived_key = HKDF(
    algorithm=hashes.SHA256(),
    length=32,
    salt=None,
    info=b'shared DH key',
    backend=default_backend()).derive(shared_key_a)
```

%% Cell type:code id: tags:

``` python
len(derived_key)
```

%% Cell type:markdown id: tags:

## Elliptic-curve cryptography (ECC)

%% Cell type:markdown id: tags:

ECC is another asymmetric approach that is based on elliptic curves over finite fields. ECC is a more recent approach compared to RSA. They key sizes in ECC are smaller than RSA. As a result it can consume less resources and is popular in environments with constrained resources such as IoT devices and sensors.

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.asymmetric import ec

private_key = ec.generate_private_key(ec.SECP256K1(), default_backend())

private_key = Ed25519PrivateKey.generate()
data = b"Some data to be signed"
signature = private_key.sign(data)
signature = private_key.sign(data, ec.ECDSA(hashes.SHA256()))
```

%% Cell type:code id: tags:

``` python
public_key = private_key.public_key()
```

%% Cell type:code id: tags:

``` python
public_key.verify(signature, data)
public_key.verify(signature, data, ec.ECDSA(hashes.SHA256()))
```

%% Cell type:code id: tags:

``` python
public_key.verify(signature+b"1", data)
public_key.verify(signature+b"1", data, ec.ECDSA(hashes.SHA256()))
```

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Compare speed of signature generation and verification using ECC and RSA.

%% Cell type:code id: tags:

``` python
with open("private_key.pem", "rb") as key_file:
     rsa_private_key = serialization.load_pem_private_key(
         key_file.read(),
         password=None,
         backend=default_backend())

rsa_public_key = rsa_private_key.public_key()

ec_private_key = Ed25519PrivateKey.generate()
ec_public_key = ec_private_key.public_key()

msg = b"Message to be signed (very important)"

def rsa_gen_sig(msg):
    # signature creation
    return rsa_private_key.sign(
        msg,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH),
        hashes.SHA256())


def rsa_verify_sig(msg, sig):
    #verify signature
    return rsa_public_key.verify(
        sig,
        msg,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH),
        hashes.SHA256())


def ec_gen_sig(msg):
    return ec_private_key.sign(msg)


def ec_verify_sig(msg, sig):
    return ec_public_key.verify(sig, msg)
```

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*
+5 −0
Changes for notebooks/6. Full Cryptographic Protocols.ipynb: 5 added lines, 0 removed lines.
Original line number Diff line number Diff line
%% Cell type:markdown id: tags:

# Cryptographic Protocols

%% Cell type:markdown id: tags:

Transport Layer Security (TLS) and Pretty Good Privacy (PGP) are examples of cryptographic protocols that combine the cryptographic primitives that we learned about in previous sections.

%% Cell type:markdown id: tags:

# Transport Layer Security (TLS)

TLS Formerly known as Secure Sockets Layer (SSL) is a cryptographic protocol suite to secure data in transfer. It is widely used over internet with different protocols such as HTTP, FTP, SMTP, etc. Different cipher suites provides variety of security protection services based on the users' needs.

<img src="include/tls_http.png" width=800>

%% Cell type:markdown id: tags:

# Pretty Good Privacy (PGP)

 - A data encryption/decryption tool
 - Can be used to encrypt and authenticat email, files, etc.
 - Created by Phil Zimmermann in 1991
 - A practical hybrid system that uses symmetric and asymmetric crypto

<img src="include/PGP_diagram.png" width=500>

image source: wikipedia

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Implement PGP to encrypt some content as shown in the diagram above. Use AES in GCM mode.

%% Cell type:markdown id: tags:

 - def enc_key(public_key, message)
 - def dec_key(private_key, ciphertext)
 - def enc_msg(key, iv, msg)
 - def dec_msg(key, iv, tag, ciphertext)

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
```
+6 −102
Changes for solutions/4. Asymmetric Encryption [Solution].ipynb: 6 added lines, 102 removed lines.
Original line number Diff line number Diff line
%% Cell type:markdown id: tags:

# Asymmetric Encryption

Reminder: In the asymmetric encryption schemes the parties use ***different*** keys, that are mathematically ***related*** to each other.

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Why asymmetric encryption is useful?
 - Give a few examples where it can be used?

%% Cell type:markdown id: tags:

## RSA

RSA, is an asymmetric encryption algorithm by Ron Rivest, Adi Shamir, and Leonard Adleman. It was published in 1977. It's security is based on the hardness of factorization problem. However, now it has its own problem, called the RSA problem. RSA is slow, and is not used for encrypting large data, but it's mostly used to encrypt the symmetric key that is used for encryption.


 * $p, q$, two big prime numbers (private, chosen)
 * $n = pq$, $φ(n) = (p-1)(q-1)$   (public, calculated)
 * $e$, with $gcd(φ(n), e) = 1,  1 < e < φ(n)$	(public, chosen)
 * $d = e - 1$ mod $φ(n)$	(private, calculated)
 * $E(M) = M^e \mod n$
 * $D(M) = M^d \mod n$
 * $D(E(M)) = M^{ed} \mod n = M$

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - How to test if a number is prime?

%% Cell type:markdown id: tags:

## RSA EXAMPLE

 - p = 5; q = 11 => n = 55
 - φ(n) = 40
 - e = 3 => d = 27
  - Because ed = 1 mod φ(n)
 - Public key: (e, n)
 - Private key: (d, n)
 - Encryption
  - M = 2
 - Encryption(M) = $ M^e\mod n$  = $2^3\mod n$ = 8
 - Decryption(8) = $ M^d\mod n$  = $8^{27} \mod n$ = 2

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Perform textbook RSA encryption and decryption using the values from the example

%% Cell type:code id: tags:

``` python
p = 5
q = 11
n = p * q
e = 3
d = 27
m = 2
```

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
e * d
```

%% Cell type:code id: tags:

``` python
e * d % ((p-1)*(q-1))
```

%% Cell type:code id: tags:

``` python
c = m ** e
print(c)
```

%% Cell type:code id: tags:

``` python
c = m ** e % n
print(c)
```

%% Cell type:code id: tags:

``` python
c = pow(m, e, n)
print(c)
```

%% Cell type:code id: tags:

``` python
c ** d
```

%% Cell type:code id: tags:

``` python
m_dec = c ** d % n
print(m_dec)
```

%% Cell type:code id: tags:

``` python
m_dec = pow(c, d, n)
print(m_dec)
```

%% Cell type:markdown id: tags:

### Signing

The purpose of signatures is to prove the "creator" of a message. To perform this task we use the private key, since only the owner of the keypair is aware of the private key. Everyone with the knowledge of the public key can verify the signature.

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Create textbook RSA signature on message (m=2). Use the private key (d), instead of the public key (e) to generate the signature. Use the public key (e) to verify the signature.

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
p =5
q = 11
n = p * q
e = 3
d = 27
m = 2
```

%% Cell type:code id: tags:

``` python
sig = pow(m, d, n)
print(sig)
```

%% Cell type:code id: tags:

``` python
m = pow(sig, e, n)
print(m)
```

%% Cell type:markdown id: tags:

### Generating RSA keypairs using OpenSSL

To generate keys, use the following instructions:

```bash
 openssl genrsa -out private_key.pem 2048
 openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
 openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
 ```

%% Cell type:code id: tags:

``` python
# import key from a file. E.g., previously generated by OpenSSL
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization

with open("private_key.pem", "rb") as key_file:
     private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None,
            backend=default_backend())
public_key = private_key.public_key()
```

%% Cell type:code id: tags:

``` python
# Generate a 2048 bit private key
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa

private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend())
# to get the public key
public_key = private_key.public_key()
```

%% Cell type:code id: tags:

``` python
2 ** 16 +1
```

%% Cell type:code id: tags:

``` python
print(bin(2**16 + 1))
print(bin(2**1 + 1))
```

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - What's wrong with textbook RSA?

%% Cell type:markdown id: tags:

### It's all about padding and randomness

%% Cell type:markdown id: tags:

### Optimal Asymmetric Encryption Padding (OAEP)

Textbook RSA is not IND-CPA secure, therefore we use Optimal Asymmetric Encryption Padding (OAEP).

<img src="include/RSA_OAEP.png">

image souce: wikipedia

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

message = b"Y"*127
ciphertext = public_key.encrypt(
    message,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA1()),
        algorithm=hashes.SHA1(),
        label=None))
```

%% Cell type:code id: tags:

``` python
print(ciphertext)
```

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

plaintext = private_key.decrypt(
    ciphertext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA1()),
        algorithm=hashes.SHA1(),
        label=None))
```

%% Cell type:code id: tags:

``` python
print(plaintext)
```

%% Cell type:markdown id: tags:

### Probabilistic Signature Scheme (PSS)

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

message = b"Message to be signed"
```

%% Cell type:code id: tags:

``` python
# signature creation
signature = private_key.sign(
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256())
```

%% Cell type:code id: tags:

``` python
# signature verification
public_key.verify(
    signature,
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256())
```

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Compare the speed of encryption using RSA-2048 and AES-128.

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
import os
import time
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

message = b"Y"*200
aes_key = os.urandom(16)
ctr = os.urandom(16)

def rsa_2048_enc(message):
    public_key.encrypt(
        message,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA1()),
            algorithm=hashes.SHA1(),
            label=None))


def aes_128_enc(message):
    cipher = Cipher(algorithms.AES(aes_key), modes.CTR(ctr), backend=default_backend())
    encryptor = cipher.encryptor()
    ctr_ct = encryptor.update(message) + encryptor.finalize()
```

%% Cell type:code id: tags:

``` python
loops = 50000

s = time.time()

for i in range(loops):
    rsa_2048_enc(message)

e = time.time()
print("RSA: Encrypted {} messages in {} seconds".format(loops, e-s))


s = time.time()

for i in range(loops):
    aes_128_enc(message)

e = time.time()
print("AES: Encrypted {} messages in {} seconds".format(loops, e-s))
```

%% Cell type:markdown id: tags:

These tests give an idea of the speed difference between RSA and AES. However, our benchmark does not reflect the whole picture. To get a better view run:

``` bash
 $ openssl speed rsa aes
```

%% Cell type:markdown id: tags:

## Diffie–Hellman key exchange

%% Cell type:markdown id: tags:

Diffie-Hellman is a key exchange protocol and an example of an asymmetric cryptography. Named after it's inventors  Whitfield Diffie and Martin Hellman in 1976. The security of DH key exchange relies on the hardness of discrete logarithm.

%% Cell type:markdown id: tags:

 - Alice: generate private key ($a$). Calculate public key $A = (g ^ a)$ mod $p$
 - Bob: generate private key ($b$). Calculate public key $B = (g ^ b)$ mod $p$


 - Alice -> Bob: Alice sends her public key ($A$) to Bob
 - Bob -> Alice: Bob send his public key ($B$) to Alice


 - Alice and Bob: Calculate shared key K = $g ^ {ab}$ mod $p$
 - $(g ^ a) ^ b = (g ^ b) ^ a = g ^ {ab}$

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Perform basic DH key exchange using the values below to create a shared key ($g^{ab}$)

%% Cell type:code id: tags:

``` python
g = 2
p = 19
a = 7
b = 8
```

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
gab = pow(pow(g, a, p), b, p) # (g^a) ^ b
print(gab)
```

%% Cell type:code id: tags:

``` python
gba = pow(pow(g, b, p), a, p) # (g^b) ^ a
print(gba)
```

%% Cell type:code id: tags:

``` python
assert gab == gba
```

%% Cell type:markdown id: tags:

### Performing DH key exchange with key derivation

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives.kdf.hkdf import HKDF

parameters = dh.generate_parameters(generator=2, key_size=2048,
                                    backend=default_backend())

private_key_a = parameters.generate_private_key()  # a
private_key_b = parameters.generate_private_key()  # b
```

%% Cell type:code id: tags:

``` python
public_key_a = private_key_a.public_key() # g^a
public_key_b = private_key_b.public_key() # g^b
```

%% Cell type:code id: tags:

``` python
shared_key_b = private_key_b.exchange(public_key_a) # (g^a) ^ b = g ^ ab
```

%% Cell type:code id: tags:

``` python
shared_key_a = private_key_a.exchange(public_key_b) # (g^a) ^ b = g ^ ab
```

%% Cell type:code id: tags:

``` python
assert shared_key_a == shared_key_b # (g^a)^b == (g^b)^a == g^ab == g^ba
```

%% Cell type:code id: tags:

``` python
derived_key = HKDF(
    algorithm=hashes.SHA256(),
    length=32,
    salt=None,
    info=b'shared DH key',
    backend=default_backend()).derive(shared_key_a)
```

%% Cell type:code id: tags:

``` python
len(derived_key)
```

%% Cell type:markdown id: tags:

## Elliptic-curve cryptography (ECC)

%% Cell type:markdown id: tags:

ECC is another asymmetric approach that is based on elliptic curves over finite fields. ECC is a more recent approach compared to RSA. They key sizes in ECC are smaller than RSA. As a result it can consume less resources and is popular in environments with constrained resources such as IoT devices and sensors.

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.asymmetric import ec

private_key = ec.generate_private_key(ec.SECP256K1(), default_backend())

private_key = Ed25519PrivateKey.generate()
data = b"Some data to be signed"
signature = private_key.sign(data)
signature = private_key.sign(data, ec.ECDSA(hashes.SHA256()))
```

%% Cell type:code id: tags:

``` python
public_key = private_key.public_key()
```

%% Cell type:code id: tags:

``` python
public_key.verify(signature, data)
```

%% Cell type:code id: tags:

``` python
public_key.verify(signature+b"1", data)
```

%% Cell type:markdown id: tags:

## *<font color=" #6495ED">Exercise</font>*

 - Compare speed of signature generation and verification using ECC and RSA.

%% Cell type:code id: tags:

``` python
with open("private_key.pem", "rb") as key_file:
     rsa_private_key = serialization.load_pem_private_key(
         key_file.read(),
         password=None,
         backend=default_backend())

rsa_public_key = rsa_private_key.public_key()

ec_private_key = Ed25519PrivateKey.generate()
ec_public_key = ec_private_key.public_key()

msg = b"Message to be signed (very important)"

def rsa_gen_sig(msg):
    # signature creation
    return rsa_private_key.sign(
        msg,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH),
        hashes.SHA256())


def rsa_verify_sig(msg, sig):
    #verify signature
    return rsa_public_key.verify(
        sig,
        msg,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH),
        hashes.SHA256())


def ec_gen_sig(msg):
    return ec_private_key.sign(msg)


def ec_verify_sig(msg, sig):
    return ec_public_key.verify(sig, msg)
```

%% Cell type:markdown id: tags:

## *<font color=" #b74138">Solution</font>*

%% Cell type:code id: tags:

``` python
import time
loops = 5000

s = time.time()
for i in range(loops):
    sig = rsa_gen_sig(msg)
e = time.time()

print("Generated {} RSA PSS signatures in {} seconds".format(loops, e-s))


sig = rsa_gen_sig(msg)
s = time.time()
for i in range(loops):
    rsa_verify_sig(msg, sig)
e = time.time()

print("Verified {} RSA PSS signatures in {} seconds".format(loops, e-s))
public_key.verify(signature, data, ec.ECDSA(hashes.SHA256()))
```

%% Cell type:code id: tags:

``` python
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
private_key = Ed25519PrivateKey.generate()

s = time.time()
for i in range(loops):
    sig = ec_gen_sig(msg)
e = time.time()
print("Generated {} Ed25519 signatures in {} seconds".format(loops, e-s))

s = time.time()
for i in range(loops):
    ec_verify_sig(msg, sig)
e = time.time()
print("Verified {} Ed25519 signatures in {} seconds".format(loops, e-s))
public_key.verify(signature+b"1", data, ec.ECDSA(hashes.SHA256()))
```

%% Cell type:code id: tags:

``` python
```