kyber

package
v0.0.0-...-642df0c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 14, 2024 License: BSD-3-Clause, CC0-1.0 Imports: 4 Imported by: 0

README

Go Post Quantum Safe Lib

This library offers a fast, secure, and easy to use implementation of the post-quantum candidates of the CRYSTALS suite. It contains Kyber, a key-encapsulation mechanism whose goal is to securely transmit symmetric key material over an insecure channel, and Dilithium, a digital signature algorithm that produces a signature that can be verified against a key, and can be used towards authentication or integrity.

API

To begin with, the crystal-go module can be installed via:

go get -u github.com/kudelskisecurity/crystals-go

The API of Kyber and Dilihtium is very similar, and can be divided in two steps:

The user first has to define which level of security they want to work with by creating an instance of Kyber or Dilithium among (in increasing level of security) Kyber512, Kyber768, Kyber1024 and Dilithium2, Dilithium3, Dilithium5. For example:

k := NewKyber512() //Creates a Kyber instance with light security level 
d := Dilithium3() //Creates a Dilithium instance with recommended/medium security level

The newly created instance defines all parameters used internally. In a second step, the user can now invoke our generic methods on an instance of Kyber or Dilithium.

Kyber

The core functions of Kyber, a KEM, are a tuple KeyGen, Encaps, and Decaps. The key generation function returns a public key that can be openly disclosed, and a secret key that should remain private. The encapsulation function is used to generate and encrypt a shared secret given a public key. Secret that can be recovered using the associated secret key. No one except the secret key holder can recover the value of the shared secret.

Translated to code, a KEM protocol between Alice and Bob using our API looks like this:

Alice and Bob agreed on using the recommended security level. Alice can now generate a public and private key pair by calling:

k := NewKyber768()
pk, sk := k.KeyGen(seed)

Once the keys are generated, Alice can send her public key to Bob, who encapsulates a shared secret using:

k := NewKyber768()
c, ss := k.Encaps(pk, coins)

The ciphertext is transmitted to Alice for her to recover the value of ss with:

ss := k.Decaps(sk, c) //Matches the value held by Bob
Dilithium

For Dilithium, the DSA, the main methods are KeyGen, Sign, and Verify, which very intuitively, correspond to the verification key (public) and signing key (secret) generation, the signature algorithm, and the verification algorithm. The signature, given a message and a signing key, produces a signature that is verifiable against the associated public verification key. Dilithium signatures are said to be unforgeable, meaning that it is extremely hard to create a valid signature without actually holding the signing key. In that case, Dilithium can be used as an authentication mechanism, as a valid signature is the proof that the signer is the secret key holder. If the message is tampered, the signature will not verify anymore, so Dilithium can also be used to enforce message integrity.

Similarly, we can translate the Dilithium protocol to code. W.L.O.G, we choose Alice to be the signer, and Bob the verifier, and assume that they agreed on using the light security level.

Alice starts by generating a key pair:

d := Dilithium2() //Creates a Dilithium instance with recommended security level
pk, sk := d.KeyGen()

She can then sign a message of her choice using:

msg := []byte("This is a message.")
sig := d.Sign(sk, msg)

Then transmit her public key, message, and signature to Bob for him to verify it with:

d := Dilithium2()
verified := d.Verify(pk, sig, msg) //verified is true for honest executions

A feature of Dilithium is to be available both in randomized or deterministic mode. When creating a Dilithium instance, a boolean is given as parameter to indicate which one to use. By default, the boolean is set to true, setting Dilithium to the randomized mode, but passing false as parameter will choose the deterministic mode. For example, d := NewDilithium3(false) will create a Dilithium instance with parameters set to the security level 3, and a deterministic signature. The signing and verification procedure is the same for both and follows the aforementioned flow.

Random inputs

This leads us to the final feature of the API regarding randomization. Both Kyber and Dilithium use random numbers. The concerned methods accept as argument seed or coins of 32 bytes to be used as random material, which allows for reproducibility for example, or is useful if the user does not trust the environment to generate good randomness and wants to use randomness from their own source. They can also be nil, in which case the randomness will be generated during using Go's official crypto/rand library.

Helpers

The output sizes of Kyber and Dilithium (keys, signature,...) vary based on the security level. We provide for both schemes getter functions that return the size of the keys and ciphertext or signature structures based on the security level of the instance used. They can be called as follows:

sizePk := k.SizePk()
sizeSk := k.SizeSk()
sizeC := k.SizeC()

for Kyber, or

sizePk := k.SizePk()
sizeSk := k.SizeSk()
sizeSig := d.SizeSig()

for Dilithium.

Finally, we noticed that some packages (for example: binary are only compatible with constant-size objects. Our API outputs slices, which are variable-sized arrays, and function calls in Go return non-constant values, breaking the compatibility with such packages. For applications where resources need to be allocated using constant-size structures, we hardcode the size of our scheme's outputs for each security level, and expose them as constants as part of the Kyber/Dilithium packages. Have a look at the param.go file for an example.

Errors

In order to keep the API pretty simple, any error will result in a nil output (false is the case or Verify). For now the error is printed, but we are working on Log Levels.

Security

Our library stands out because of its security properties. Among the vulnerabilities reported on the original implementation, we integrate countermeasures for most of them, providing a library that is both theoretically and practically secure. We predict that new attacks will be published as the candidates are refined, and expect changes in the code to occur as the security of our library is treated as a continuous process.

We recall that side-channel attacks are high-risk threats and encourage users to prefer libraries with strong implementation security, such as our library, over implementations that lack these guarantees.

Dashboard SCA (work in progress)
Alg Attack Paper
TA
✔️ D Timing of decryption 🔗
✔️ D Timing of re-encryption check 🔗
CM
✖️ KG Cache access monitoring 🔗
✖️ S Cache access monitoring 🔗
✔️ D Cache access monitoring 🔗
FA
✔️ KG Skip of secret addition 🔗
✔️ S Skip of mask addition 🔗
✖️ D Skip of decryption check 🔗
✖️ D Skip of +Q/2 instruction 🔗
✖️ KG Zero of secret 🔗
✖️ KG Zero of noise 🔗
✖️ KG Zero of A 🔗
✖️ S Zero of randomness 🔗
✖️ KG Zero of noise 🔗
✔️ KG Zero of nonce 🔗
✔️ E Zero of nonce 🔗
✔️ S Zero of mask 🔗
✔️ S Loop-abort of mask addition 🔗
✔️ KG Loop abort of noise addition 🔗
✔️ S Err. in hash polynomial 🔗
✔️ S Err. in expand function 🔗

Attacks marked with a gray cross are the ones left, a green checkmark implies that a defense is implemented.

Documentation

Index

Constants

View Source
const (
	SIZEZ             = 32
	SEEDBYTES         = 32
	Kyber512SizePK    = 800
	Kyber512SizeSK    = 1632
	Kyber512SizePKESK = 768
	Kyber512SizeC     = 768 //2*320 + 128

	Kyber768SizePK    = 1184
	Kyber768SizeSK    = 2400
	Kyber768SizePKESK = 1152
	Kyber768SizeC     = 1088 //3*320 + 128

	Kyber1024SizePK    = 1568
	Kyber1024SizeSK    = 3168
	Kyber1024SizePKESK = 1536
	Kyber1024SizeC     = 1568 //4*352 + 160
)

The first block of constants define internal parameters. SEEDBYTES holds the lenght in byte of the random number to give as input, if wanted. The remaining constants are exported to allow for fixed-lenght array instantiation. For a given security level, the consts are the same as the output of the k.SIZEX() functions defined in keys.go

Variables

This section is empty.

Functions

This section is empty.

Types

type Kyber

type Kyber struct {
	Name string
	// contains filtered or unexported fields
}

Kyber struct defines the internal parameters to be used given a security level

func NewKyber1024

func NewKyber1024() *Kyber

NewKyber1024 defines a kyber instance with a very high security level.

func NewKyber512

func NewKyber512() *Kyber

NewKyber512 defines a kyber instance with a light security level.

func NewKyber768

func NewKyber768() *Kyber

NewKyber768 defines a kyber instance with a medium security level.

func NewKyberUnsafe

func NewKyberUnsafe(n, k, q, eta1, et2, du, dv int) *Kyber

NewKyberUnsafe is a skeleton function to be used for research purposes when wanting to use a kyber instance with parameters that differ from the recommended ones.

func (*Kyber) Decaps

func (k *Kyber) Decaps(packedSK, c []byte) (error, []byte)

Decaps decryps a ciphertext given a secret key and checks its validity. The secret key and ciphertext must be give as packed byte array. The recovered shared secret is returned as byte array. If an error occurs durirng the decapsulation process, a nil shared secret is returned. func (k *Kyber) Decaps(packedSK, c []byte) []byte { // modified by unixman

func (*Kyber) Decrypt

func (k *Kyber) Decrypt(packedSK, c []byte) (error, []byte)

Decrypt decrypts a ciphertext given a secret key. The secret key and ciphertext must be give as packed byte array. The recovered message is returned as byte array. If an error occurs durirng the decryption process (wrong key format for example), a nil message is returned. unixman: this decrypts just one block of 32 bytes func (k *Kyber) Decrypt(packedSK, c []byte) []byte { // modified by unixman

func (*Kyber) Encaps

func (k *Kyber) Encaps(packedPK, coins []byte) (error, []byte, []byte)

Encaps generates a shared secret and the encryption of said shared secret using a given public key. A 32 byte long seed can be given as argument (coins). If a nil seed is given, the seed is generated using Go crypto's random number generator. The shared secret and ciphertext returned are packed into byte arrays. If an error occurs during the encaps process, nil arrays are returned. func (k *Kyber) Encaps(packedPK, coins []byte) ([]byte, []byte) { // modified by unixman

func (*Kyber) Encrypt

func (k *Kyber) Encrypt(packedPK, msg, r []byte) (error, []byte)

Encrypt generates the encryption of a message using a public key. A 32 byte long seed can be given as argument (r). If a nil seed is given, the seed is generated using Go crypto's random number generator. The ciphertext returned is packed into a byte array. If an error occurs during the encrpytion process, a nil array is returned. unixman: this encrypts just one block of 32 bytes func (k *Kyber) Encrypt(packedPK, msg, r []byte) []byte { // modified by unixman

func (*Kyber) KeyGen

func (k *Kyber) KeyGen(seed []byte) (error, []byte, []byte)

KeyGen creates a public and private key pair. A 64 byte long seed can be given as argument. If a nil seed is given, the seed is generated using Go crypto's random number generator. The keys returned are packed into byte arrays. unixman: returns err, publicKey, privateKey func (k *Kyber) KeyGen(seed []byte) ([]byte, []byte) {

func (*Kyber) PKEKeyGen

func (k *Kyber) PKEKeyGen(seed []byte) (error, []byte, []byte)

PKEKeyGen creates a public and private key pair. A 32 byte long seed can be given as argument. If a nil seed is given, the seed is generated using Go crypto's random number generator. The keys returned are packed into byte arrays. unixman: returns err, publicKey, privateKey func (k *Kyber) PKEKeyGen(seed []byte) ([]byte, []byte) { // modified by unixman

func (*Kyber) PackPK

func (k *Kyber) PackPK(pk *PublicKey) []byte

PackPK packs a PublicKey into an array of bytes

func (*Kyber) PackPKESK

func (k *Kyber) PackPKESK(sk *PKEPrivateKey) []byte

PackPKESK packs a PKE PrivateKey into a byte array

func (*Kyber) PackSK

func (k *Kyber) PackSK(sk *PrivateKey) []byte

PackSK packs a PrivateKey into a byte array

func (*Kyber) SIZEC

func (k *Kyber) SIZEC() int

SIZEC returns the size in bytes of the ciphertext of a kyber instance

func (*Kyber) SIZEPK

func (k *Kyber) SIZEPK() int

SIZEPK returns the size in bytes of the public key of a kyber instance

func (*Kyber) SIZEPKESK

func (k *Kyber) SIZEPKESK() int

SIZEPKESK returns the size in bytes of the PKE secret key of a kyber instance

func (*Kyber) SIZESK

func (k *Kyber) SIZESK() int

SIZESK returns the size in bytes of the secret key of a kyber instance

func (*Kyber) UnpackPK

func (k *Kyber) UnpackPK(packedPK []byte) (error, *PublicKey)

UnpackPK reverses the packing operation and outputs a PublicKey struct func (k *Kyber) UnpackPK(packedPK []byte) *PublicKey { // modified by unixman

func (*Kyber) UnpackPKESK

func (k *Kyber) UnpackPKESK(psk []byte) (error, *PKEPrivateKey)

UnpackPKESK reverses the packing operation and outputs a PKEPrivateKey struct func (k *Kyber) UnpackPKESK(psk []byte) *PKEPrivateKey { // modified by unixman

func (*Kyber) UnpackSK

func (k *Kyber) UnpackSK(psk []byte) (error, *PrivateKey)

UnpackSK reverses the packing operation and outputs a PrivateKey struct func (k *Kyber) UnpackSK(psk []byte) *PrivateKey { // modified by unixman

type Mat

type Mat []Vec

Mat is used to hold A

type PKEPrivateKey

type PKEPrivateKey struct {
	S Vec //NTT(s)
}

PKEPrivateKey holds the ak strct for Kyber's PKE scheme

type Poly

type Poly [n]int16

Poly represents a polynomial of deg n with coefs in [0, Q)

type PrivateKey

type PrivateKey struct {
	Z   []byte
	SkP []byte
	Pk  []byte
}

PrivateKey holds the sk struct

type PublicKey

type PublicKey struct {
	T   Vec    //NTT(t)
	Rho []byte //32
}

PublicKey holds the pk strct

type Vec

type Vec []Poly

Vec is an array of K polynomials

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL