A Social Distanced Santa Sends Secure Presents

It seems like a lifetime away, but I remember the time when there was great debate about the track-and-trace method. So while most of the…

Photo by Markus Spiske on Unsplash

A Social Distanced Santa Sends Secure Presents

It seems like a lifetime away, but I remember the time when there was great debate about the track-and-trace method. So while most of the world went for a decentralised approach, the English system used a centralised method — Integrated Encryption Scheme (IES). It made sense for the as the NHS as it used a public key for the track and trace system and a random value on the phone, and then encrypted a contact. The NHS — with their private key — were then able to decrypt this. It thus used good old public key and secret key encrytion — a hybrid encryption scheme. So, as it’s as it’s nearly Christmas, let’s have a crypto Christmas.

Santa needs to send presents securely

Well, because of social distancing, Santa can’t deliver presents this year, so he’ll have to send them electronically. So how does Santa (Alice) send a secret present to a child (Bob)? Well, Santa’s little Elves have been prototyping two forms of IES: discrete logs and elliptic curve.

As part of the Elve’s research they found that IES is a hybrid encryption scheme which allows Santa (Alice) to get Bob’s public key, and then generate an encryption key based on Bob’s public key and Santa’s private key. Initially Bob and Santa (Alice) agree on a generate value (g) and a prime number (p). The elves setup each person with a Christmas wallet, and with a public key (B) and a private key (b). If Bob has a private key of b, he can share his public key with Santa (Alice):

Santa (Alice) receives this public key, and generates a private key (a) specially for Bob. Santa (Alice) then computes:

and which is:

Santa (Alice) will then take a present (m) and encrypt with this key, and send to Bob along with Santa’s public key:

Bob can then construct his present by recreating the secret key with:

and can then decrypt it, to find out the present. In the following example we use PBKDF2 as the Key Derivation Function (KDF), so that the present can be hacked by Eve:

The following is the code [here]:

import sys
import random
import Padding
import binascii
from Crypto.Util.number import getPrime
from Crypto.Random import get_random_bytes
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256
from Crypto.Cipher import AES

primebits=64
msg = "Hello123"
def encrypt(plaintext,key, mode):
encobj = AES.new(key,mode)
return(encobj.encrypt(plaintext))
def decrypt(ciphertext,key, mode):
encobj = AES.new(key,mode)
return(encobj.decrypt(ciphertext))
if (len(sys.argv)>2):
primebits=int(sys.argv[2])
if (len(sys.argv)>2):
msg=(sys.argv[1])
p = getPrime(primebits, randfunc=get_random_bytes)
g=3
a = random.randint(0, p-1)
b = random.randint(0, p-1)
A = pow(g,a,p)
B = pow(g,b,p)
Key=pow(B,a,p)

salt = get_random_bytes(16)
key = PBKDF2(str(Key), salt, 32, count=1000, hmac_hash_module=SHA256)
plaintext = Padding.appendPadding(msg,blocksize=Padding.AES_blocksize,mode=0)

ciphertext = encrypt(plaintext.encode(),key,AES.MODE_ECB)
print ("Prime: ",p)
print ("g: ",g)
print ("\nBob's private (b): ",b)
print ("Bob's public B (g^b mod p): ",B)
print ("\nAlice private (a): ",a)
print ("Alice public A (g^a mod p): ",A)

print ("\nMessage: ",msg)
print ("Key: KDF(g^{ab}) ",binascii.hexlify(bytearray(key)).decode())
print ("Ciphertext: ",binascii.hexlify(bytearray(ciphertext)).decode())
plaintext = decrypt(ciphertext,key,AES.MODE_ECB)
plaintext = Padding.removePadding(plaintext.decode(),mode=0)
print ("\nBob computes plaintext: ",plaintext)

And a sample run [here]:

Prime:  8416478506687054991102209043232527279335758847975538292195117491567195312725319134563640953333329360142705851747113206242177426116248529707875011852278199
g: 3

Bob's private (b): 6699584622415965947537283709253744817495683102417168746967532742027937753809185333796701050955426326286356161480050736529344284909076840621563104787728349
Bob's public B (g^b mod p): 3768178837015555886507885507893372126914487884847399652285032502857660450270723507666418753164980630598492186176001152503910458321394478211534478263220082

Alice private (a): 5470739281051810814512512335841627840803474931459249369015684012926827768872989239380996381432100280212380466568179218753070408955187854714762085616917428
Alice public A (g^a mod p): 3384022938591466480809728318548388343778881806887436186151966683799645697603324909174016583615809818161251197747511272930973501489135539408911913106649100

Message: Buzz Lightyear
Key: KDF(g^{ab}) af772eaa371b7fdea5d2f6686ba50390561de1fa552777e49a7bffe4f1774168
Ciphertext: 911108d83905c93548e2d01061655913

Bob computes plaintext: Buzz Lightyear

But Santa (Alice) wants to use something more modern than discrete logarithms to send presents. So the elves have implemented elliptic curve methods. Initially they have used secp256k1 elliptic curve and which has a base point (G), an order (n) and a prime number (p). If Bob has a private key of b, he can share his public key with Santa (Alice):

B=bG

Santa (Alice) receives this public key, and generates a private key (a). Santa then computes:

Key=bA

and which is:

Key=abG

Santa (Alice) will then take a present (m) and encrypt with this key, and send to Bob with Santa’s public key:

A=bG

Bob can then construct this present with:

Key=KDF(bA)

and can then decrypt the message. In the following example we use PBKDF2 as the Key Derivation Function (KDF).

The following is the code [here]:

from secp256k1 import curve,scalar_mult,is_on_curve
import random
import sys
from Crypto.Hash import SHA256
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Protocol.KDF import PBKDF2
import Padding
import binascii

def encrypt(plaintext,key, mode):
encobj = AES.new(key,mode)
return(encobj.encrypt(plaintext))
def decrypt(ciphertext,key, mode):
encobj = AES.new(key,mode)
return(encobj.decrypt(ciphertext))
print("Basepoint:\t", curve.g)
msg="Hello12"
if (len(sys.argv)>1):
msg=(sys.argv[1])
a  = random.randrange(1, curve.n)
b = random.randrange(1, curve.n)

A = scalar_mult(a,curve.g)
B= scalar_mult(b,curve.g)
K1= scalar_mult(a,B)
print ("Message: ",msg)
print ("\na (Alice):\t",a)
print ("b (Bob):\t",b)
print ("\nA (Alice) aG:\t",A)
print ("B (Bob) bG:\t",B)
salt = get_random_bytes(16)
key = PBKDF2(str(K1[0]), salt, 32, count=1000, hmac_hash_module=SHA256)
plaintext = Padding.appendPadding(msg,blocksize=Padding.AES_blocksize,mode=0)
ciphertext = encrypt(plaintext.encode(),key,AES.MODE_ECB)

print ("\nKey: KDF(abG) ",binascii.hexlify(bytearray(key)).decode())
print ("Ciphertext: ",binascii.hexlify(bytearray(ciphertext)).decode())

plaintext = decrypt(ciphertext,key,AES.MODE_ECB)
plaintext = Padding.removePadding(plaintext.decode(),mode=0)
print ("\nBob computes plaintext: ",plaintext)

And a sample run with Buzz Lightyear [here]:

Basepoint:	 (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)
Message: Buzz Lightyear
a (Alice):	 40979724007234306723700858798671990712800660819077943610005341565220696004702
b (Bob): 19083761352740274642098894747370491425661480826886682521773091829562624498116
A (Alice) aG:	 (7280348578124714486783960932049032669908641450989528081168193504163245402981, 13680724886278489609349717858347319621071299939224593447568792992941017817164)
B (Bob) bG: (10092239408946548870449312112249181646672301560401695092883288951297934400850, 105510569139779171061042697129294179159643291210029455380160616726616896378520)
Key: KDF(abG)  eb323a6f8660775814f900af1826fab2f3ce1a22b41a7d7eeacc3223a1c1a1aa
Ciphertext: bf2d9636a28aef3a08f23f9c2263aa57
Bob computes plaintext:  Buzz Lightyear

And there you go! Happy Crypto Christmas!