Files
appliedcrypto/z_jupyter/04_key_exchange.ipynb
billbuchanan bd13a9d0cb Labs
2026-02-04 09:47:31 +00:00

395 lines
13 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "markdown",
"id": "a7782a35",
"metadata": {},
"source": [
"\n",
"# Diffie Hellman\n",
"With key exchange, Bob and Alice end up with the same secret key, even though Eve is listening to their communications:\n",
"\n",
"<img src='graphics/g_key_02.png' width=\"800px\">\n",
"\n",
"The Diffie-Hellman method allows Bob and Alice to pass public values to each other and then derive the same key:\n",
"\n",
"<img src='graphics/g_key_01.png' width=\"800px\">\n",
"\n",
"For this Bob generates a secret value (b) and Alice generates a secret value (a). Bob passes g^b mod p, and Alice passes g^a mod p.\n"
]
},
{
"cell_type": "markdown",
"id": "1265ed12",
"metadata": {},
"source": [
"> Bob and Alice have agreed on the values:\n",
"\n",
"g=2879, N= 9929\n",
"\n",
"Bob Select x=6, Alice selects y=9\n",
"\n",
"Now calculate (using a calculator or Python:\n",
"\n",
"Alices A value (g^x mod N):\n",
"\n",
"Bobs B value (g^y mod N):\n",
"\n",
"Now they exchange the values. Next calculate the shared key:\n",
"\n",
"Alices value (B^x mod N):\n",
"\n",
"Bobss value (A^y mod N):\n",
"\n",
"> Do they match? Yes/No"
]
},
{
"cell_type": "markdown",
"id": "3ed130a0",
"metadata": {},
"source": [
"We can use Python to implement a simple example with a given base generator (g) and prime number (p):"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "80911155",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"g: 5 (a shared value), n: 1001 (a prime number)\n",
"\n",
"Alice calculates:\n",
"a (Alice random): 410\n",
"Alice value (A): 298 (g^a) mod p\n",
"\n",
"Bob calculates:\n",
"b (Bob random): 105\n",
"Bob value (B): 265 (g^b) mod p\n",
"\n",
"Alice calculates:\n",
"Key: 155 (B^a) mod p\n",
"Key: 210e3b160c355818509425b9d9e9fd3ea2e287f2c43a13e5be8817140db0b9e6\n",
"\n",
"Bob calculates:\n",
"Key: 155 (A^b) mod p\n",
"Key: 210e3b160c355818509425b9d9e9fd3ea2e287f2c43a13e5be8817140db0b9e6\n"
]
}
],
"source": [
"# 04_01.py\n",
"# https://asecuritysite.com/keyexchange/diffie_py\n",
"import random\n",
"import hashlib\n",
"\n",
"g=5\n",
"p=1001\n",
"\n",
"a=random.randint(1, p)\n",
"\n",
"b=random.randint(1,p)\n",
"\n",
"A = (g**a) % p\n",
"B = (g**b) % p\n",
"\n",
"print('g: ',g,' (a shared value), n: ',p, ' (a prime number)')\n",
"\n",
"print('\\nAlice calculates:')\n",
"print('a (Alice random): ',a)\n",
"print('Alice value (A): ',A,' (g^a) mod p')\n",
"\n",
"print('\\nBob calculates:')\n",
"print('b (Bob random): ',b)\n",
"print('Bob value (B): ',B,' (g^b) mod p')\n",
"\n",
"print('\\nAlice calculates:')\n",
"keyA=(B**a) % p\n",
"print('Key: ',keyA,' (B^a) mod p')\n",
"print('Key: ',hashlib.sha256(str(keyA).encode()).hexdigest())\n",
"\n",
"print('\\nBob calculates:')\n",
"keyB=(A**b) % p\n",
"print('Key: ',keyB,' (A^b) mod p')\n",
"print('Key: ',hashlib.sha256(str(keyB).encode()).hexdigest())"
]
},
{
"cell_type": "markdown",
"id": "6d22b5c8",
"metadata": {},
"source": [
"> Run the code and verify that Bob and Alice compute the same shared secret.\n",
"\n",
"> The (g**b) % p operation can be computationally intensive. Replace this operation with pow(g,b,p), and verify that the program still works.\n",
"\n",
"> Use a prime number of 997, and verify the operation of the code.\n",
"\n",
"> Use a prime number of 2^19-1, and verify the operation of the code."
]
},
{
"cell_type": "markdown",
"id": "b8e14951",
"metadata": {},
"source": [
"## Selecting generator for discrete logs\n",
"With a discrete log method, we have the form of:\n",
"\n",
"Y=g^x (mod p)\n",
"\n",
"For the base, we must select a base generator that produces all the possible values of Y for x, with a given prime number of p.\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "548c6405",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"p: 11\n",
"Values for g^x mod p: \n",
"2 6 7 8 None\n"
]
}
],
"source": [
"# 04_02.py\n",
"# https://asecuritysite.com/dh/pickg2\n",
"p=11\n",
"\n",
"def getG(p):\n",
"\n",
" for x in range (1,p):\n",
"\t rand = x\n",
"\t exp=1\n",
"\t next = rand % p\n",
"\n",
"\t while (next != 1 ):\n",
"\t\t next = (next*rand) % p\n",
"\t\t exp = exp+1\n",
"\n",
"\n",
"\t if (exp==p-1):\n",
"\t\t print (rand,end=' ')\n",
"\n",
"print (\"p: \",p)\n",
"print (\"Values for g^x mod p: \")\n",
"print (getG(p))"
]
},
{
"cell_type": "markdown",
"id": "222bec32",
"metadata": {},
"source": [
"> For a prime number of p=11, what are the possible base generator values?\n",
"> For a prime number of p=97, what are the possible base generator values?\n",
"> If we pick a value of p=11, and take a value of g=5, show that we do not output all the possible values for Y=g^x (mod p).\n",
"> If we pick a value of p=11, and take a value of g=6, show that we do output all the possible values for Y=g^x (mod p)."
]
},
{
"cell_type": "markdown",
"id": "8c457f5e",
"metadata": {},
"source": [
"# Elliptic Curve Diffie Hellman (ECDH)\n",
"The most popular key exchange method is ECDH (Elliptic Curve Diffie Hellman). This uses an elliptic curve method, of which the most popular curve is P256 (SECP256R1). \n",
"\n",
"<img src='graphics/g_key_12.png' width=\"800px\">\n",
"\n",
"The following is the some code:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "73037ef4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Name of curve: secp256r1\n",
"Generated key size: 32 bytes (256 bits)\n",
"\n",
"Bob private key value: 115386775143760985994606199743124500698563324842662591622007624940178885352350\n",
"Bob's public key: 3059301306072a8648ce3d020106082a8648ce3d0301070342000453c9db700d76051a285ac6ecf2a27bc82056faca157b52d9744d1f781577e2a8bd0011523ad95fc5012b2c1d5bbae06a51f4a64e34e5397e4c282fe595bf8037\n",
"\n",
"Alice private key value: 73556695653589803082300318169887127685187875212044508782243551530526730975658\n",
"Alice's public key: 3059301306072a8648ce3d020106082a8648ce3d03010703420004aa8ac00f7a33ab033eb047d9fe2892a9da3ad8381b8bd1f4e0007f89b13d5b6a243a3ec52af2c48903cdf662c6cf79acc05292bce510f4fcb11071cb6be338f2\n",
"\n",
"Bob's derived key: c1c76e98cb3ef84986fa5395b8da34bff86c180005ff3de1ea088807542ad517\n",
"Alice's derived key: c1c76e98cb3ef84986fa5395b8da34bff86c180005ff3de1ea088807542ad517\n"
]
}
],
"source": [
"# 04_03.py\n",
"# https://asecuritysite.com/hazmat/hashnew13\n",
"from cryptography.hazmat.primitives import hashes\n",
"from cryptography.hazmat.primitives.asymmetric import ec\n",
"from cryptography.hazmat.primitives.kdf.hkdf import HKDF\n",
"from cryptography.hazmat.primitives import serialization\n",
"from cryptography.hazmat.backends import default_backend\n",
"import binascii\n",
"import sys\n",
"\n",
"Bob_private_key = ec.generate_private_key(ec.SECP384R1(),default_backend())\n",
"\n",
"Alice_private_key = ec.generate_private_key(ec.SECP384R1(),default_backend())\n",
"size=32 # 256 bit key\n",
"\n",
"\n",
"\n",
"Bob_private_key = ec.generate_private_key(ec.SECP256R1(),default_backend())\n",
"Alice_private_key = ec.generate_private_key(ec.SECP256R1(),default_backend()) \n",
"\n",
"Bob_shared_key = Bob_private_key.exchange(ec.ECDH(), Alice_private_key.public_key())\n",
"\n",
"Bob_derived_key = HKDF(algorithm=hashes.SHA256(),length=size,salt=None,info=b'',backend=default_backend()).derive(Bob_shared_key)\n",
"\n",
"Alice_shared_key = Alice_private_key.exchange(ec.ECDH(), Bob_private_key.public_key())\n",
"\n",
"Alice_derived_key = HKDF(algorithm=hashes.SHA256(),length=size,salt=None,info=b'',backend=default_backend()).derive(Alice_shared_key)\n",
"\n",
"print (\"Name of curve: \",Bob_private_key.public_key().curve.name)\n",
"print (f\"Generated key size: {size} bytes ({size*8} bits)\")\n",
"\n",
"vals = Bob_private_key.private_numbers()\n",
"print (f\"\\nBob private key value: {vals.private_value}\")\n",
"vals=Bob_private_key.public_key()\n",
"enc_point=binascii.b2a_hex(vals.public_bytes(encoding=serialization.Encoding.DER,format=serialization.PublicFormat.SubjectPublicKeyInfo)).decode()\n",
"print(\"Bob's public key: \",enc_point)\n",
"\n",
"vals = Alice_private_key.private_numbers()\n",
"print (f\"\\nAlice private key value: {vals.private_value}\")\n",
"vals=Alice_private_key.public_key()\n",
"enc_point=binascii.b2a_hex(vals.public_bytes(encoding=serialization.Encoding.DER,format=serialization.PublicFormat.SubjectPublicKeyInfo)).decode()\n",
"print(\"Alice's public key: \",enc_point)\n",
"\n",
"print (\"\\nBob's derived key: \",binascii.b2a_hex(Bob_derived_key).decode())\n",
"print(\"Alice's derived key: \",binascii.b2a_hex(Alice_derived_key).decode())"
]
},
{
"cell_type": "markdown",
"id": "4a3e413f",
"metadata": {},
"source": [
"> The program uses SECP256R1, and which is also known as the NIST P-256 curve. The Bitcoin curve is SECP256k1. Modify the program so that it uses this curve. Verify that it still works of the shared key.\n",
"\n",
"> An enhanced curve is SECP521R1. Modify the program so that it uses this curve. Verify that it still works of the shared key.\n",
"\n",
"> The Brainpool curves have some advantages over SECP2561. Modify the program so that it uses the P256R1 curve, and verify that it still works for the shared key.\n",
"\n",
"> The program creates a 256-bit key. Modify the program so that it generated a 128-bit key.\n",
"\n",
"> Modify the program so that is displays the shared key in a Base64 format.\n"
]
},
{
"cell_type": "markdown",
"id": "1c0e6351",
"metadata": {},
"source": [
"## DH Parameters \n",
"In the DH key generation we have two base parameters: g and p. We can use OpenSSL to generate these parameters. Such as:"
]
},
{
"cell_type": "markdown",
"id": "ead31dbd",
"metadata": {},
"source": [
">> Generate 768-bit Diffie-Hellman parameters:\n",
"\n",
"openssl dhparam -out dhparams.pem -text 768 \n",
"\n",
">> View your key exchange parameters with:\n",
"\n",
"cat dhparams.pem\t\n",
"\n",
">> What is the value of g:\n",
"\n",
">> How many bits does the prime number have?\n",
"\n",
">> How long does it take to produce the parameters for 1,024 bits (Group 2)?\n",
"\n",
">> How long does it take to produce the parameters for 1536 bits (Group 5)?\n",
"\n",
">> How would we change the g value?"
]
},
{
"cell_type": "markdown",
"id": "a4266944",
"metadata": {},
"source": [
"## EC Paramters\n",
"With elliptic curves, we can use OpenSSL to generate a curve:"
]
},
{
"cell_type": "markdown",
"id": "f844cbaf",
"metadata": {},
"source": [
">> Lets look at the Elliptic curves we can create:\n",
"\n",
"openssl ecparam -list_curves\n",
"\n",
">> We can create our elliptic parameter file with:\n",
"\n",
"openssl ecparam -name secp256k1 -out secp256k1.pem\n",
"\n",
">> Now view the details with:\n",
"\n",
"openssl ecparam -in secp256k1.pem -text -param_enc explicit -noout\n",
"\n",
">> What are the details of the key?\n",
"\n",
">> Now we can create our key pair:\n",
"\n",
"openssl ecparam -in secp256k1.pem -genkey -noout -out mykey.pem\n",
"\n",
">> Name three 160-bit curves:"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}