This shows you the differences between two versions of the page.
|
ac:laboratoare:11 [2025/11/06 10:19] marios.choudary |
ac:laboratoare:11 [2025/11/06 10:36] (current) marios.choudary |
||
|---|---|---|---|
| Line 83: | Line 83: | ||
| For this you can either modify the work you did above to use a post-quantum key exchange method such as ML-KEM in Python. | For this you can either modify the work you did above to use a post-quantum key exchange method such as ML-KEM in Python. | ||
| + | To do this in Python you can use a library such as [[https://pypi.org/project/mlkem/|ml-kem]]. | ||
| + | You can install this as follows: | ||
| + | <code> | ||
| + | pip install ml-kem | ||
| + | </code> | ||
| + | |||
| + | Below is an example of how to use this library in a simple client-server scenario. Note: this example is provided by Gemini AI, so use it with caution and double-check it: | ||
| + | <code> | ||
| + | # Install the library first: pip install ml-kem | ||
| + | |||
| + | from mlkem.ml_kem import ML_KEM | ||
| + | from mlkem.parameter_set import ML_KEM_768 # Recommended security level | ||
| + | import secrets | ||
| + | |||
| + | # --- Server (Alice) Side --- | ||
| + | |||
| + | def server_keygen(): | ||
| + | """Alice generates her ML-KEM key pair.""" | ||
| + | # Initialize ML-KEM with the desired security level | ||
| + | ml_kem = ML_KEM(parameters=ML_KEM_768, randomness=secrets.token_bytes) | ||
| + | | ||
| + | # Generate the encapsulation key (ek, public) and decapsulation key (dk, private) | ||
| + | ek, dk = ml_kem.key_gen() | ||
| + | | ||
| + | print("Alice: Generated Public Key (ek) and Private Key (dk).") | ||
| + | return ek, dk, ml_kem | ||
| + | |||
| + | def server_decapsulate(dk, c, ml_kem): | ||
| + | """Alice decapsulates the ciphertext to get the shared secret.""" | ||
| + | try: | ||
| + | K_prime = ml_kem.decaps(dk, c) | ||
| + | print("Alice: Successfully decapsulated the Shared Secret (K').") | ||
| + | return K_prime | ||
| + | except ValueError as e: | ||
| + | print(f"Alice: Decapsulation failed! {e}") | ||
| + | return None | ||
| + | |||
| + | # --- Client (Bob) Side --- | ||
| + | |||
| + | def client_encapsulate(ek): | ||
| + | """Bob encapsulates a shared secret using Alice's public key.""" | ||
| + | ml_kem = ML_KEM(parameters=ML_KEM_768, randomness=secrets.token_bytes) | ||
| + | | ||
| + | # Encapsulate to get the shared secret (K) and the ciphertext (c) | ||
| + | K, c = ml_kem.encaps(ek) | ||
| + | | ||
| + | print("Bob: Encapsulated a Shared Secret (K) and created Ciphertext (c).") | ||
| + | return K, c | ||
| + | |||
| + | # --- Communication Flow Simulation --- | ||
| + | |||
| + | # 1. Server (Alice) Key Generation | ||
| + | ek_server, dk_server, ml_kem_instance = server_keygen() | ||
| + | |||
| + | # 2. Public Key Transmission (ek_server is sent to the client) | ||
| + | print("\n--- Network Transmission: ek sent to Bob ---") | ||
| + | |||
| + | # 3. Client (Bob) Encapsulation | ||
| + | K_client, c_client = client_encapsulate(ek_server) | ||
| + | |||
| + | # 4. Ciphertext Transmission (c_client is sent back to the server) | ||
| + | print("\n--- Network Transmission: c sent to Alice ---") | ||
| + | |||
| + | # 5. Server (Alice) Decapsulation | ||
| + | K_server = server_decapsulate(dk_server, c_client, ml_kem_instance) | ||
| + | |||
| + | # 6. Verification | ||
| + | print("\n--- Verification ---") | ||
| + | if K_client is not None and K_server is not None: | ||
| + | if K_client == K_server: | ||
| + | print("Success! Alice's and Bob's shared secrets match.") | ||
| + | # The shared secret can now be used as an AES key, e.g., K_client | ||
| + | # The shared secret is bytes: | ||
| + | # print(f"Shared Secret: {K_client.hex()}") | ||
| + | else: | ||
| + | print("Failure! Shared secrets do not match.") | ||
| + | else: | ||
| + | print("Failure in key exchange process.") | ||
| + | </code> | ||
| + | |||
| Otherwise, you can start from the Diffie-Hellman key exchange lab we did in OpenSSL/C. You may start from {{:ac:laboratoare:lab_dhe_solved.zip|this}} code, that provides a working solution for the Diffie-Hellman lab in OpenSSL. | Otherwise, you can start from the Diffie-Hellman key exchange lab we did in OpenSSL/C. You may start from {{:ac:laboratoare:lab_dhe_solved.zip|this}} code, that provides a working solution for the Diffie-Hellman lab in OpenSSL. | ||
| Line 230: | Line 310: | ||
| * Extract the raw public key bytes to replace the placeholder in the C code. A common way to get the raw bytes is to convert the PEM file to a DER format and extract the key component. For ML-KEM, the raw key is often found within the ASN.1 structure. | * Extract the raw public key bytes to replace the placeholder in the C code. A common way to get the raw bytes is to convert the PEM file to a DER format and extract the key component. For ML-KEM, the raw key is often found within the ASN.1 structure. | ||
| <note> | <note> | ||
| - | See the DH lab for information about this process | + | See the DH lab for information about this process. But see also below more details for this process, as provided by Gemini AI. |
| </note> | </note> | ||
| * Compile the code (adjusting paths to your OpenSSL 3.5 installation), using a command like this (check again also the DH lab for more details): | * Compile the code (adjusting paths to your OpenSSL 3.5 installation), using a command like this (check again also the DH lab for more details): | ||
| Line 236: | Line 316: | ||
| gcc -o mlkem_client mlkem_client.c -I/path/to/openssl/include -L/path/to/openssl/lib -lssl -lcrypto | gcc -o mlkem_client mlkem_client.c -I/path/to/openssl/include -L/path/to/openssl/lib -lssl -lcrypto | ||
| </code> | </code> | ||
| + | |||
| + | The following GeminiAI-generated scripts (might have some bugs, please double-check, they are only given as reference/helper tools) generate an ML-KEM-768 key pair and, importantly, forces OpenSSL to save the private key in the FIPS 203 'dk' format (the one the C code expects for decapsulation) rather than the default 'seed' format, which is not what the raw C code imports: | ||
| + | <code> | ||
| + | #!/bin/bash | ||
| + | KEY_ALG="ML-KEM-768" | ||
| + | PRIV_FILE="server_private_dk.pem" | ||
| + | PUB_FILE="server_public_ek.pem" | ||
| + | RAW_PRIV_FILE="server_private_raw.bin" | ||
| + | RAW_PUB_FILE="server_public_raw.bin" | ||
| + | |||
| + | echo "--- 1. Generating $KEY_ALG Key Pair (forcing dk-only private key) ---" | ||
| + | |||
| + | # Generate the private key, using '-provparam ml-kem.retain_seed=no' | ||
| + | # to ensure it saves the full FIPS 203 'dk' decapsulation key, | ||
| + | # NOT just the seed. | ||
| + | openssl genpkey -algorithm "$KEY_ALG" \ | ||
| + | -provparam ml-kem.retain_seed=no \ | ||
| + | -out "$PRIV_FILE" | ||
| + | |||
| + | # Extract the public key from the private key file | ||
| + | openssl pkey -in "$PRIV_FILE" -pubout -out "$PUB_FILE" | ||
| + | |||
| + | echo "Keys generated: $PRIV_FILE and $PUB_FILE" | ||
| + | echo "----------------------------------------------------------------" | ||
| + | </code> | ||
| + | |||
| + | The ML-KEM public key is in the SubjectPublicKeyInfo structure in the PEM file. We use openssl pkey to output the key in DER format and then remove the ASN.1 header/wrapper to get the raw bytes: | ||
| + | <code> | ||
| + | echo "--- 2. Extracting RAW Public Key (608 bytes) ---" | ||
| + | |||
| + | # The ML-KEM public key is 608 bytes long. | ||
| + | # The 'pubout' option converts to DER, and 'tail' removes the ASN.1 wrapper. | ||
| + | # NOTE: The ASN.1 wrapper size may vary by OpenSSL version/config. We must | ||
| + | # determine the exact offset to trim off the SubjectPublicKeyInfo header. | ||
| + | # For standard ML-KEM-768 PKCS#8 output, the header is typically 24 bytes. | ||
| + | HEADER_BYTES=24 # Common offset for ML-KEM-768 PKCS#8 SubjectPublicKeyInfo header | ||
| + | |||
| + | openssl pkey -in "$PUB_FILE" -pubin -outform DER -out "$RAW_PUB_FILE" | ||
| + | # Trim the ASN.1 header to get the raw 608-byte key | ||
| + | # (Skip first $HEADER_BYTES, save the next 608 bytes) | ||
| + | dd if="$RAW_PUB_FILE" of="client_public_key_raw_final.bin" bs=1 skip=$HEADER_BYTES count=608 status=none | ||
| + | |||
| + | echo "Raw public key saved to client_public_key_raw_final.bin" | ||
| + | # Print the hex array for C code | ||
| + | echo "Public Key Hex Array:" | ||
| + | xxd -p "client_public_key_raw_final.bin" | tr -d '\n' | sed 's/../&, 0x/g' | sed 's/^, 0x/0x/' | sed 's/, $//' | ||
| + | echo | ||
| + | echo "----------------------------------------------------------------" | ||
| + | </code> | ||
| + | |||
| + | Similarly, the private key (the FIPS 203 'dk' component) is inside the PKCS#8 private key structure. We again convert to DER and trim the ASN.1 wrappers: | ||
| + | <code> | ||
| + | echo "--- 3. Extracting RAW Private Key (1184 bytes) ---" | ||
| + | |||
| + | # The ML-KEM-768 private key (dk) is 1184 bytes long. | ||
| + | # We convert to DER and again skip the ASN.1 header bytes. | ||
| + | # For PKCS#8 'dk' private key, the header is typically 50 bytes. | ||
| + | HEADER_BYTES=50 # Common offset for ML-KEM-768 PKCS#8 PrivateKeyInfo header | ||
| + | |||
| + | openssl pkey -in "$PRIV_FILE" -outform DER -out "$RAW_PRIV_FILE" | ||
| + | # Trim the ASN.1 header to get the raw 1184-byte key | ||
| + | # (Skip first $HEADER_BYTES, save the next 1184 bytes) | ||
| + | dd if="$RAW_PRIV_FILE" of="server_private_key_raw_final.bin" bs=1 skip=$HEADER_BYTES count=1184 status=none | ||
| + | |||
| + | echo "Raw private key saved to server_private_key_raw_final.bin" | ||
| + | # Print the hex array for C code | ||
| + | echo "Private Key Hex Array:" | ||
| + | xxd -p "server_private_key_raw_final.bin" | tr -d '\n' | sed 's/../&, 0x/g' | sed 's/^, 0x/0x/' | sed 's/, $//' | ||
| + | echo | ||
| + | echo "----------------------------------------------------------------" | ||
| + | </code> | ||
| + | |||
| + | You can now use the hex output from these scripts to reliably replace the placeholder arrays in your C client and server code. | ||
| + | See also [[https://www.youtube.com/watch?v=FzLzH5W-mi8|this]] video about Post-Quantum Cryptography support in OpenSSL. | ||
| + | |||
| Below is also an example code from Gemini AI as starting point for the server, to pair with the code above for the client. | Below is also an example code from Gemini AI as starting point for the server, to pair with the code above for the client. | ||