Love P256, but also want Kyber?

--

P256 rules in key exchange, and is, by far, the most popular method used with ECDH (Elliptic Curve Diffie Hellman). But, elliptic curve methods will be cracked by quantum computers. At the current time, Kyber is the method that is most likely to replace P256, but many systems do not currently support it. So, one solution is to use a hybrid method, and which sends the P256 public key and the Kyber public key in the key exchange.

The following is an outline of the code [here]:

package main

// Based on examples at https://github.com/cloudflare/circl/tree/master/kem/kyber
import (
"fmt"
"math/rand"
"os"
"time"
"github.com/cloudflare/circl/kem/schemes"
"github.com/cloudflare/circl/kem/sike/sikep434"
"github.com/cloudflare/circl/kem/sike/sikep503"
"github.com/cloudflare/circl/kem/sike/sikep751"
"github.com/cloudflare/circl/kem/hybrid"
)
func main() {
meth := "SIKEp434" // SIKEp424 SIKEp751

argCount := len(os.Args[1:])
if argCount > 0 {
meth = os.Args[1]
}

scheme := sikep434.Scheme()
if (meth=="SIKEp434") { scheme= sikep434.Scheme()
} else if (meth=="SIKEp503") { scheme= sikep503.Scheme()
} else if (meth=="SIKEp751") { scheme= sikep751.Scheme()
} else if (meth=="P256Kyber768Draft00") { scheme= hybrid.P256Kyber768Draft00()
} else { scheme = schemes.ByName(meth) }
rand.Seed(time.Now().Unix())

var seed [48]byte
kseed := make([]byte, scheme.SeedSize())
eseed := make([]byte, scheme.EncapsulationSeedSize())

for i := 0; i < 48; i++ {
seed[i] = byte(rand.Intn(255))
}
for i := 0; i < scheme.SeedSize(); i++ {
kseed[i] = byte(rand.Intn(255))
}

for i := 0; i < scheme.EncapsulationSeedSize(); i++ {
eseed[i] = byte(rand.Intn(255))
}
pk, sk := scheme.DeriveKeyPair(kseed)
ppk, _ := pk.MarshalBinary()
psk, _ := sk.MarshalBinary()
ct, ss, _ := scheme.EncapsulateDeterministically(pk, eseed)
ss2, _ := scheme.Decapsulate(sk, ct)
fmt.Printf("Method: %s \n", meth)
fmt.Printf("Seed for key exchange: %X\n", seed)
fmt.Printf("Public Key (pk) = %X (first 32 bytes)\n", ppk[:32])
fmt.Printf("Private key (sk) = %X (first 32 bytes)\n", psk[:32])
fmt.Printf("Cipher text (ct) = %X (first 32 bytes)\n", ct[:32])
fmt.Printf("\nShared key (Bob):\t%X\n", ss)
fmt.Printf("Shared key (Alice):\t%X", ss2)
fmt.Printf("\n\nLength of Public Key (pk) = %d bytes \n", len(ppk))
fmt.Printf("Length of Secret Key (sk) = %d bytes\n", len(psk))
fmt.Printf("Length of Cipher text (ct) = %d bytes\n", len(ct))
}

And for P256Kyber768Draft00 [here]:

Method: P256Kyber768Draft00 
Seed for key exchange: AA8059DB969113C27942C37DA94864AF773B02B1FDE7D07B0E72C6B72080CD113A8421D4E36A171BCC78E51AE167C200
Public Key (pk) = 04CC9515E938C05E7E3A3AC21BC8BF38E9A81007C3FABA665BD5AA357C218D96 (first 32 bytes)
Private key (sk) = 8CE1B78B2A97F4E474BAD466D2EF1F3BAF60DE57708BB24FC6F316E11F4D0C4C (first 32 bytes)
Cipher text (ct) = 04113041E6C1B692203455F32A2B360352C5405BBE3E48C0AEEAADAA446A37F2 (first 32 bytes)

Shared key (Bob): 7C0987B78F37EC5335DA05A1951D65DDAFAF9E8B1756E0A274B331B629E816E33FE89DF0366E2D9C810175CAF038FC5CA8251ECA20A1471AB2829AFB557EDEF8
Shared key (Alice): 7C0987B78F37EC5335DA05A1951D65DDAFAF9E8B1756E0A274B331B629E816E33FE89DF0366E2D9C810175CAF038FC5CA8251ECA20A1471AB2829AFB557EDEF8

Length of Public Key (pk) = 1249 bytes
Length of Secret Key (sk) = 2432 bytes
Length of Cipher text (ct) = 1153 bytes

We can see that the public key is 1,249 bytes. This is because the public key for P256 is 65 bytes, and the public key for Kyber768 is 1,184. Overall the key sizes for Kyber are:

Type      Public key size (B)   Secret key size (B)  Ciphertext size (B)
------------------------------------------------------------------------
Kyber512 800 1,632 768
Kyber738 1,184 2,400 1,088
Kyber1024 1,568 3,168 1,568

Normally Kyber512 is not used, as many worry about its security strength, thus Kyber738 is used. This gives 192 bit equivalent security.

We can also use X25519 as an alternative to P256. A sample run for Kyber512-X25519 is [here]:

Method: Kyber512-X25519 
Public Key (pk) = 0D584135D3242392B5E6EC06B3990CEEC43CB3A35875F3CDECC53FEADA33E047 (first 32 bytes)
Private key (sk) = 7B558F88CA046A7C1A83431ACAD94629B4A72E8BBEFFF40AD1A7024A2A6DC8A5 (first 32 bytes)
Cipher text (ct) = 170F2B8C341B603A38520E16404C3D507ACB5A45BC582F4CCA1AD807196D071B (first 32 bytes)
Shared key (Bob): 82B7AECBC7A5C381C712FBA258A38984DA32B9D2B39B2D118B7978371402CD9E413574B7684518ECC484D4DFDCFA0EBFE377FEF3AB982F60F47AD666C56EC196
Shared key (Alice): 82B7AECBC7A5C381C712FBA258A38984DA32B9D2B39B2D118B7978371402CD9E413574B7684518ECC484D4DFDCFA0EBFE377FEF3AB982F60F47AD666C56EC196

Length of Public Key (pk) = 832 bytes
Length of Secret Key (sk) = 1664 bytes
Length of Cipher text (ct) = 800 bytes

We can see that the public key is 832 bytes. This is because the public key for X25519 is 32 bytes, and the public key for Kyber768 is 800. Overall the key sizes for Kyber are:

A sample run for Kyber768-X448 is [here]:

Method: Kyber768-X448 
Public Key (pk) = 0F3BC5031BB49ED1184E378C39E8BB1696442290329B716A97E79FF100B73599 (first 32 bytes)
Private key (sk) = 7B86917D307381BC146125709BD2A39EF6623F8A05F264416E26660F04CA4D20 (first 32 bytes)
Cipher text (ct) = 6575C5E916830D96D36C204307284AF89587BEFFC3F579EFEBE352198709FCE5 (first 32 bytes)

Shared key (Bob): 5FC20E38B2267E042BCB36319732E2BD5C2D57A9C78D4955FB9423D3D982CA5A1FF791AD7707E18942505334C02FB36F30BD55ED660A781B1101B0CF205C30D767D8718E752107C3B82145330DC0B5817B5788F4A25FC4FB51BF71477B03346A
Shared key (Alice): 5FC20E38B2267E042BCB36319732E2BD5C2D57A9C78D4955FB9423D3D982CA5A1FF791AD7707E18942505334C02FB36F30BD55ED660A781B1101B0CF205C30D767D8718E752107C3B82145330DC0B5817B5788F4A25FC4FB51BF71477B03346A
Length of Public Key (pk) = 1240 bytes
Length of Secret Key (sk) = 2456 bytes
Length of Cipher text (ct) = 1144 bytes

A sample run for Kyber1024-X448:

Method: Kyber1024-X448 
Public Key (pk) = ACDC644291611A014B41408A16D52479B513FBE15906B259660BB0579B1D9508 (first 32 bytes)
Private key (sk) = 690133C140927B6212D539458DB18DC0F28B7CEB549BF32C2FB03229A109C705 (first 32 bytes)
Cipher text (ct) = 488232A8878C312FE5988DD19EA04CBC892F25036569423E83061CAEF2C32FC7 (first 32 bytes)
Shared key (Bob): FFE3DA3744D79EEBAC20C6224B9EAE6B9622F5B8202C4227A7500984DC0943842A1EE9A775E5CD699CAE5A3134925F666F0F1C78B9B3237F4EFA50EE7632F47485A9D402238AAB802E1100A1DD037EEBC781889E335328D25E536B04528D343A
Shared key (Alice): FFE3DA3744D79EEBAC20C6224B9EAE6B9622F5B8202C4227A7500984DC0943842A1EE9A775E5CD699CAE5A3134925F666F0F1C78B9B3237F4EFA50EE7632F47485A9D402238AAB802E1100A1DD037EEBC781889E335328D25E536B04528D343A
Length of Public Key (pk) = 1624 bytes
Length of Secret Key (sk) = 3224 bytes
Length of Cipher text (ct) = 1624 bytes

--

--

Prof Bill Buchanan OBE FRSE
ASecuritySite: When Bob Met Alice

Professor of Cryptography. Serial innovator. Believer in fairness, justice & freedom. Based in Edinburgh. Old World Breaker. New World Creator. Building trust.