Skip to content

Commit

Permalink
Add mkem scheme for pigeonhole mixnet protocols
Browse files Browse the repository at this point in the history
  • Loading branch information
david415 committed Jul 4, 2024
1 parent 2032ebc commit 74d3f98
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ require (

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/katzenpost/chacha20poly1305 v0.0.0-20211026103954-7b6fb2fc0129 // indirect
github.com/mattn/go-pointer v0.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ github.com/henrydcase/nobs v0.0.0-20230313231516-25b66236df73 h1:d3rq/Tz+RJ5h1xk
github.com/henrydcase/nobs v0.0.0-20230313231516-25b66236df73/go.mod h1:ptK2MJqVLVEa/V/oK8n+MEyUDCSjSylW+jeNmCG1DJo=
github.com/katzenpost/chacha20 v0.0.0-20190910113340-7ce890d6a556 h1:9gHByAWH1LydGefFGorN1ZBRZ/Oz9iozdzMvRTWpyRw=
github.com/katzenpost/chacha20 v0.0.0-20190910113340-7ce890d6a556/go.mod h1:d9kxwmGOcutgP6bQwr2xaLInaW5yJsxsoPRyUIG0J/E=
github.com/katzenpost/chacha20poly1305 v0.0.0-20211026103954-7b6fb2fc0129 h1:BkXIK2/3bAtqFbzea4tMVfhu8BQ9j05yCVy/z8iCz3s=
github.com/katzenpost/chacha20poly1305 v0.0.0-20211026103954-7b6fb2fc0129/go.mod h1:moU3dWG1uk4xayUNdUPs2Q3dx2Zsk5i9Ysfx+ujQrwM=
github.com/katzenpost/circl v1.3.9-0.20240222183521-1cd9a34e9a0c h1:FYy03rLIjdyjklBOI6YSCb3q7OubTx0dVDWYOgDsvA8=
github.com/katzenpost/circl v1.3.9-0.20240222183521-1cd9a34e9a0c/go.mod h1:+EBrwiGYs9S+qZqaqxujN1CReTNCMAG6p+31KkEDeeA=
github.com/katzenpost/sntrup4591761 v0.0.0-20231024131303-8755eb1986b8 h1:TsKxH0x2RUwf5rBw67k15bqVM3oVbexA9oaTZQLIy3Y=
Expand All @@ -38,12 +40,19 @@ gitlab.com/elixxir/crypto v0.0.9 h1:vt7+yRvGd/G9VtpQCaXa7EKPmHTgThf2hXbzZ1vlU6I=
gitlab.com/elixxir/crypto v0.0.9/go.mod h1:KV3F+kO0VVusLIKPqSKMk4HZ4yQiS12SaIsOXg9LJb0=
gitlab.com/xx_network/crypto v0.0.6 h1:+C44rBhclcbWrGa5EOic5yDF3NrXAbXScCb/mXmm3Ro=
gitlab.com/xx_network/crypto v0.0.6/go.mod h1:C69/+XTiqJvKkYzcyA47+LdxvAofl9AzR/Nyo36y9hs=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
128 changes: 128 additions & 0 deletions kem/mkem/mkem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-FileCopyrightText: Copyright (C) 2024 David Stainton
// SPDX-License-Identifier: AGPL-3.0-only

// Package mkem provides multiparty KEM construction.
package mkem

import (
"crypto/cipher"
"crypto/rand"
"errors"

"github.com/katzenpost/chacha20poly1305"
"github.com/katzenpost/hpqc/hash"
"github.com/katzenpost/hpqc/nike"
)

type MKEMCiphertext struct {
EphemeralPublicKey *PublicKey
DEKCiphertexts [][]byte
SecretCiphertext []byte
}

// Scheme is an MKEM scheme.
type Scheme struct {
nike nike.Scheme
}

// FromNIKE creates a new KEM adapter Scheme
// using the given NIKE Scheme.
func FromNIKE(nike nike.Scheme) *Scheme {
if nike == nil {
panic("NIKE is nil")
}
return &Scheme{
nike: nike,
}
}

func (s *Scheme) GenerateKeyPair() (*PublicKey, *PrivateKey, error) {
pubkey, privkey, err := s.nike.GenerateKeyPair()
if err != nil {
return nil, nil, err
}
return &PublicKey{
publicKey: pubkey,
}, &PrivateKey{
privateKey: privkey,
}, nil
}

func (s *Scheme) createCipher(key []byte) cipher.AEAD {
aead, err := chacha20poly1305.New(key)
if err != nil {
panic(err)
}
return aead
}

func (s *Scheme) encrypt(key []byte, plaintext []byte) []byte {
aead := s.createCipher(key)
nonce := make([]byte, aead.NonceSize())
_, err := rand.Reader.Read(nonce)
if err != nil {
panic(err)
}
return aead.Seal(nonce, nonce, plaintext, nil)
}

func (s *Scheme) decrypt(key []byte, ciphertext []byte) ([]byte, error) {
aead := s.createCipher(key)
nonce := ciphertext[:aead.NonceSize()]
ciphertext = ciphertext[aead.NonceSize():]
return aead.Open(nil, nonce, ciphertext, nil)
}

func (s *Scheme) Encapsulate(keys []*PublicKey, sharedSecret []byte) *MKEMCiphertext {
ephPub, ephPriv, err := s.nike.GenerateKeyPair()
if err != nil {
panic(err)
}

secrets := make([][hash.HashSize]byte, len(keys))
for i := 0; i < len(keys); i++ {
secrets[i] = hash.Sum256(s.nike.DeriveSecret(ephPriv, keys[i].publicKey))
}

msgKey := make([]byte, 32)
_, err = rand.Reader.Read(msgKey)
if err != nil {
panic(err)
}
secretCiphertext := s.encrypt(msgKey, sharedSecret)

outCiphertexts := make([][]byte, len(secrets))
for i := 0; i < len(secrets); i++ {
outCiphertexts[i] = s.encrypt(secrets[i][:], msgKey)
}

return &MKEMCiphertext{
EphemeralPublicKey: &PublicKey{
publicKey: ephPub,
},
DEKCiphertexts: outCiphertexts,
SecretCiphertext: secretCiphertext,
}
}

func (s *Scheme) Decapsulate(privkey *PrivateKey, c *MKEMCiphertext) ([]byte, error) {
ephSecret := hash.Sum256(s.nike.DeriveSecret(privkey.privateKey, c.EphemeralPublicKey.publicKey))
for i := 0; i < len(c.DEKCiphertexts); i++ {
msgKey, err := s.decrypt(ephSecret[:], c.DEKCiphertexts[i])
if err != nil {
continue
}
return s.decrypt(msgKey, c.SecretCiphertext)
}
return nil, errors.New("failed to trial decrypt")
}

// PrivateKey is an MKEM private key.
type PrivateKey struct {
privateKey nike.PrivateKey
}

// PublicKey is an MKEM public key.
type PublicKey struct {
publicKey nike.PublicKey
}
41 changes: 41 additions & 0 deletions kem/mkem/mkem_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright (C) 2024 David Stainton
// SPDX-License-Identifier: AGPL-3.0-only

// Package mkem provides multiparty KEM construction.
package mkem

import (
"crypto/rand"
"testing"

"github.com/katzenpost/hpqc/nike/schemes"
"github.com/stretchr/testify/require"
)

func TestMKEMCorrectness(t *testing.T) {
nikeName := "x25519"
nike := schemes.ByName(nikeName)
s := FromNIKE(nike)

replica1pub, replica1priv, err := s.GenerateKeyPair()
require.NoError(t, err)

replica2pub, replica2priv, err := s.GenerateKeyPair()
require.NoError(t, err)

secret := make([]byte, 32)
_, err = rand.Reader.Read(secret)
require.NoError(t, err)

ciphertext := s.Encapsulate([]*PublicKey{replica1pub, replica2pub}, secret)

secret1, err := s.Decapsulate(replica1priv, ciphertext)
require.NoError(t, err)

require.Equal(t, secret, secret1)

secret2, err := s.Decapsulate(replica2priv, ciphertext)
require.NoError(t, err)

require.Equal(t, secret, secret2)
}

0 comments on commit 74d3f98

Please # to comment.