Alice Does a Secure Lucky Dip

And so Bob sets up a Lucky Dip competiton, and Alice wants to play, but doesn’t trust anyone to pick for her. How can she do it? Well, one…

Photo by Waldemar Brandt on Unsplash

Alice Does a Secure Lucky Dip

And so Bob sets up a Lucky Dip competition, and Alice wants to play but doesn’t trust anyone to pick for her. How can she do it? Well, one way is with Commutative Encryption. For this, we create encryption, where we can apply the symmetric keys in any order, and then decrypt in any order. Thus the cipher might be created by encrypting with Bob’s Key (KB), and then by Alice’s key (KA):

C = KA(KB(M))

We can then decrypt either by decrypting with Alice’s key first, and then with Bob’s key, or vice versa. So let’s look at the lucky dip. Bob first creates four prizes (LOSE, $1, $10, and $100), and Alice pays $50. Bob then encrypts each of the prizes with his encryption key (KB). Next Alice selects one, and applies her own key to it, and then returns them all back to Bob. Bob then detects the one that has been selected and then decrypts the one that Alice has picked. Bob cannot determine if Alice has won a prize, as it will still be encrypted with Alice’s key. He then sends this one back to Alice, and she decrypts with her key and reveals the prize:

Commutative encryption using ChaCha20

ChaCha20 is wiping the floor with AES just now, and showing how out-of-date block ciphers look in a world focused on the speed of processing of data.

With commutative encryption, we can decrypt with the keys in any order. Normally we would encrypt with Bob’s key and then encrypt with Alice’s key, and then we must decrypt with Alice’s key and then Bob’s. In commutative encryption, we can decrypt in any order.

With a stream cipher, we can automatically apply commutative as we basically just EX-OR with the keystream. In the following we use Go code, and where Bob encrypts, Alice encrypts, Bob decrypts, and then Alice decrypts [here]:

package main

import (
"fmt"
"github.com/Yawning/chacha20"
"crypto/sha256"
"golang.org/x/crypto/chacha20poly1305"
"os"
)

func main() {

pass_bob:="pass1"
pass_alice:="pass2"
msg:="Hello"

argCount := len(os.Args[1:])

if (argCount>0) {msg= string(os.Args[1])}
if (argCount>1) {pass_bob= string(os.Args[2])}
if (argCount>2) {pass_alice= string(os.Args[3])}



key_bob := sha256.Sum256([]byte(pass_bob))
key_alice := sha256.Sum256([]byte(pass_alice))
nonce := make([]byte, chacha20poly1305.NonceSizeX)


c_bob, _ := chacha20.NewCipher(key_bob[:32], nonce)
c_alice, _ := chacha20.NewCipher(key_alice[:32], nonce)



out := []byte(msg)

fmt.Printf("Input text:\t%s\nBob passphrase: %s\nAlice passphrase: %s\n\n", out,pass_bob,pass_alice)
fmt.Printf("Input text:\t%s\nBob keygen: %x\nAlice keygen: %x\n\n", out,key_bob,key_alice)

c_bob.XORKeyStream(out, out)

fmt.Printf("Cipher after Bob encrypt: %x\n",out)

c_alice.XORKeyStream(out, out)


fmt.Printf("Cipher after Alice encrypt: %x\n",out)


c_bob, _ = chacha20.NewCipher(key_bob[:32], nonce)
c_alice, _ = chacha20.NewCipher(key_alice[:32], nonce)

c_bob.XORKeyStream(out, out)
fmt.Printf("Cipher after Bob decrypt: %x\n",out)
c_alice.XORKeyStream(out, out)
fmt.Printf("Cipher after Alice decrypt: %x\n",out)




fmt.Printf("Decrypted text:\t%s\n", out)

}

And a sample run [here]:

Input text: Hello
Bob passphrase: qwerty
Alice passphrase: 123456Input text: Hello
Bob keygen: 65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5
Alice keygen: 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92Cipher after Bob encrypt: d9eef8ecdc
Cipher after Alice encrypt: 7a5dcd0f43
Cipher after Bob decrypt: ebd6598ff0
Cipher after Alice decrypt: 48656c6c6f
Decrypted text: Hello

We can easily extend the method to Carol, Trent, and so on. In my simple example, I have used the same nonce for Bob and Alice, but, in real life, they would use different values, and these would be random for every transaction.