Commit 91b560a1 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Remove Paillier encryption of challenge in keyshare protocol

parent a5eb23a3
......@@ -3,200 +3,263 @@
[[projects]]
branch = "master"
digest = "1:e730c8372514c662e9ed97228c2e2023f1ec99eb68f7bb56a44c1084733d85f5"
name = "github.com/bwesterb/byteswriter"
packages = ["."]
pruneopts = "UT"
revision = "c31a76b641f8b94ffccad99eeeabf795e53b1e4a"
[[projects]]
branch = "master"
digest = "1:670b0da164df747fbf79fb31921355d9e5c81a54bfe4c3ea7b2d4ec734f088c3"
name = "github.com/bwesterb/go-atum"
packages = ["."]
pruneopts = "UT"
revision = "b79a6b33c09b7b8d49d2454bbde06162fdc0cd70"
[[projects]]
branch = "master"
digest = "1:e2c5caf65b79d6a5b246690148a6ae89c4bd2cf1af0d8dd2370c5bb4315ef155"
name = "github.com/bwesterb/go-pow"
packages = ["."]
pruneopts = "UT"
revision = "b53ca488a9ca0a21ee44c6a6b0ad792693214e1a"
[[projects]]
branch = "master"
digest = "1:bde789a11b20e103918cf1d67fe45d53fc32d2f10b31f0f571e9a2358489e29b"
name = "github.com/bwesterb/go-xmssmt"
packages = ["."]
pruneopts = "UT"
revision = "58241c99638a738580f8258ac438f0b7f91d346d"
[[projects]]
digest = "1:fed1f537c2f1269fe475a8556c393fe466641682d73ef8fd0491cd3aa1e47bad"
name = "github.com/certifi/gocertifi"
packages = ["."]
pruneopts = "UT"
revision = "deb3ae2ef2610fde3330947281941c562861188b"
version = "2018.01.18"
[[projects]]
digest = "1:3535f00c607f3993a1a2f1cbb1b3faa3bf8903f9edc77c1386491882d3b2754c"
name = "github.com/cespare/xxhash"
packages = ["."]
pruneopts = "UT"
revision = "5c37fe3735342a2e0d01c87a907579987c8936cc"
version = "v1.0.0"
[[projects]]
digest = "1:c28625428387b63dd7154eb857f51e700465cfbf7c06f619e71f2da33cefe47e"
name = "github.com/coreos/bbolt"
packages = ["."]
pruneopts = "UT"
revision = "583e8937c61f1af6513608ccc75c97b6abdf4ff9"
version = "v1.3.0"
[[projects]]
branch = "master"
name = "github.com/credentials/go-go-gadget-paillier"
packages = ["."]
revision = "720887075201f3105c335f837431426d54891b7f"
[[projects]]
branch = "master"
digest = "1:037949d323e9e831fd1be12164b6f17ab42dd963ab7594c1643352bc1186754b"
name = "github.com/credentials/safeprime"
packages = ["."]
pruneopts = "UT"
revision = "424faf641b4987fde58280730486e1035e057abc"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:67d0b50be0549e610017cb91e0b0b745ec0cad7c613bc8e18ff2d1c1fc8825a7"
name = "github.com/edsrzf/mmap-go"
packages = ["."]
pruneopts = "UT"
revision = "0bce6a6887123b67a60366d2c9fe2dfb74289d2e"
[[projects]]
branch = "master"
digest = "1:9c6a4ebc26a2b26c5c8d6989296e05713b600e43de296b4dec42d0b464f5d823"
name = "github.com/getsentry/raven-go"
packages = ["."]
pruneopts = "UT"
revision = "563b81fc02b75d664e54da31f787c2cc2186780b"
[[projects]]
digest = "1:d673f2fdcd54db695a2e562d82ebd6d361926cd5624ce28a7d7c4fdb202803cb"
name = "github.com/go-errors/errors"
packages = ["."]
pruneopts = "UT"
revision = "3afebba5a48dbc89b574d890b6b34d9ee10b4785"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:07671f8997086ed115824d1974507d2b147d1e0463675ea5dbf3be89b1c2c563"
name = "github.com/hashicorp/errwrap"
packages = ["."]
pruneopts = "UT"
revision = "7554cd9344cec97297fa6649b055a8c98c2a1e55"
[[projects]]
branch = "master"
digest = "1:77cb3be9b21ba7f1a4701e870c84ea8b66e7d74c7c8951c58155fdadae9414ec"
name = "github.com/hashicorp/go-cleanhttp"
packages = ["."]
pruneopts = "UT"
revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d"
[[projects]]
branch = "master"
digest = "1:e5048c5da80697be2fcdecc944e29d2999e01fd7f48b643168443209779f3463"
name = "github.com/hashicorp/go-multierror"
packages = ["."]
pruneopts = "UT"
revision = "b7773ae218740a7be65057fc60b366a49b538a44"
[[projects]]
branch = "master"
digest = "1:27d0d41550b480f5a217f6ad9f82491248d092e9cea775845d8784e0fc7c7d1b"
name = "github.com/hashicorp/go-retryablehttp"
packages = ["."]
pruneopts = "UT"
revision = "3b087ef2d313afe6c55b2f511d20db04ca767075"
[[projects]]
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
pruneopts = "UT"
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
[[projects]]
branch = "master"
digest = "1:df1893e9003421fe56fe6b1d9b2650542712a33b978bc825bf4ce940960d4edc"
name = "github.com/mhe/gabi"
packages = ["."]
pruneopts = "UT"
revision = "4f733a839b2b9234ef7c2610fbc8270bafe250c7"
[[projects]]
branch = "master"
digest = "1:16886567e49201f2bb97fc738dfe8097494764135a83b533fc020fcefe37d8fe"
name = "github.com/nightlyone/lockfile"
packages = ["."]
pruneopts = "UT"
revision = "6a197d5ea61168f2ac821de2b7f011b250904900"
[[projects]]
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "UT"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:abab2c85a12689a4218964335aa990ff33b4f3c7ba48e3283861c4714d2e93b1"
name = "github.com/rainycape/dl"
packages = ["."]
pruneopts = "UT"
revision = "1b01514224a1a60a6bcbb0b6b9d3a00ec14ae17f"
[[projects]]
digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e"
name = "github.com/spf13/cobra"
packages = ["."]
pruneopts = "UT"
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
version = "v0.0.1"
[[projects]]
digest = "1:1b21a2b4058a779f290c7341cd93267492e0ecea6c8b54f64a4a5fd7ff131034"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = "UT"
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
version = "v1.0.0"
[[projects]]
digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6"
name = "github.com/stretchr/testify"
packages = [
"assert",
"require"
"require",
]
pruneopts = "UT"
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
digest = "1:710ccf83337a9ca27abe968c3e58fdf16bd69d76b9870dadafc511e94fc33d7f"
name = "github.com/templexxx/cpufeat"
packages = ["."]
pruneopts = "UT"
revision = "3794dfbfb04749f896b521032f69383f24c3687e"
[[projects]]
digest = "1:a0a269bea865974fc4d583373c984a5aa60cf98b5aa4f3e1b5de527891d37845"
name = "github.com/templexxx/xor"
packages = ["."]
pruneopts = "UT"
revision = "0af8e873c554da75f37f2049cdffda804533d44c"
version = "0.1.2"
[[projects]]
branch = "master"
digest = "1:f1bcadef588162241008b4806b1f2a9c92153cf442fd3d9ad3bc879518a5a824"
name = "github.com/timshannon/bolthold"
packages = ["."]
pruneopts = "UT"
revision = "525de816b1a2c1cfbb67b5e9936a6b76e0c7f3e6"
[[projects]]
branch = "master"
digest = "1:b734bbd83047ffac93d12295ef569c9da40411e2228ca94761e6df5b2242db4e"
name = "golang.org/x/crypto"
packages = [
"ed25519",
"ed25519/internal/edwards25519",
"sha3"
"sha3",
]
pruneopts = "UT"
revision = "159ae71589f303f9fbfd7528413e0fe944b9c1cb"
[[projects]]
branch = "master"
digest = "1:d773e525476aefa22ea944a5425a9bfb99819b2e67eeb9b1966454fd57522bbf"
name = "golang.org/x/sys"
packages = ["unix"]
pruneopts = "UT"
revision = "1b2967e3c290b7c545b3db0deeda16e9be4f98a2"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "edc6335a88ff851653a42b9198c2f0f2e7f27502e18a9b48d2641de3b0bd69be"
input-imports = [
"github.com/bwesterb/go-atum",
"github.com/getsentry/raven-go",
"github.com/go-errors/errors",
"github.com/hashicorp/go-retryablehttp",
"github.com/mhe/gabi",
"github.com/pkg/errors",
"github.com/spf13/cobra",
"github.com/stretchr/testify/require",
]
solver-name = "gps-cdcl"
solver-version = 1
......@@ -24,11 +24,6 @@
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/credentials/go-go-gadget-paillier"
[[constraint]]
branch = "master"
name = "github.com/getsentry/raven-go"
......
package irmaclient
import (
"crypto/rand"
"math/big"
"sort"
"time"
"strconv"
"time"
"github.com/credentials/go-go-gadget-paillier"
raven "github.com/getsentry/raven-go"
"github.com/go-errors/errors"
"github.com/mhe/gabi"
......@@ -45,7 +43,6 @@ type Client struct {
attributes map[irma.CredentialTypeIdentifier][]*irma.AttributeList
credentialsCache map[irma.CredentialTypeIdentifier]map[int]*credential
keyshareServers map[irma.SchemeManagerIdentifier]*keyshareServer
paillierKeyCache *paillierPrivateKey
logs []*LogEntry
updates []update
......@@ -175,12 +172,6 @@ func New(
if cm.keyshareServers, err = cm.storage.LoadKeyshareServers(); err != nil {
return nil, err
}
if cm.paillierKeyCache, err = cm.storage.LoadPaillierKeys(); err != nil {
return nil, err
}
if cm.paillierKeyCache == nil {
cm.paillierKey(false)
}
if len(cm.UnenrolledSchemeManagers()) > 1 {
return nil, errors.New("Too many keyshare servers")
......@@ -656,32 +647,6 @@ func (client *Client) ConstructCredentials(msg []*gabi.IssueSignatureMessage, re
// Keyshare server handling
// PaillierKey returns a new Paillier key (and generates a new one in a goroutine).
func (client *Client) paillierKey(wait bool) *paillierPrivateKey {
cached := client.paillierKeyCache
ch := make(chan bool)
// Would just write client.paillierKeyCache instead of cached here, but the worker
// modifies client.paillierKeyCache, and we must be sure that the boolean here and
// the if-clause below match.
go client.paillierKeyWorker(cached == nil && wait, ch)
if cached == nil && wait {
<-ch
// generate yet another one for future calls, but no need to wait now
go client.paillierKeyWorker(false, ch)
}
return client.paillierKeyCache
}
func (client *Client) paillierKeyWorker(wait bool, ch chan bool) {
newkey, _ := paillier.GenerateKey(rand.Reader, 2048)
client.paillierKeyCache = (*paillierPrivateKey)(newkey)
client.storage.StorePaillierKeys(client.paillierKeyCache)
if wait {
ch <- true
}
}
func (client *Client) genSchemeManagersList(enrolled bool) []irma.SchemeManagerIdentifier {
list := []irma.SchemeManagerIdentifier{}
for name, manager := range client.Configuration.SchemeManagers {
......@@ -723,15 +688,14 @@ func (client *Client) keyshareEnrollWorker(managerID irma.SchemeManagerIdentifie
}
transport := irma.NewHTTPTransport(manager.KeyshareServer)
kss, err := newKeyshareServer(managerID, client.paillierKey(true), manager.KeyshareServer)
kss, err := newKeyshareServer(managerID, manager.KeyshareServer)
if err != nil {
return err
}
message := keyshareEnrollment{
Email: email,
Pin: kss.HashedPin(pin),
Language: lang,
PublicKey: (*paillierPublicKey)(&kss.PrivateKey.PublicKey),
Email: email,
Pin: kss.HashedPin(pin),
Language: lang,
}
qr := &irma.Qr{}
......
......@@ -3,7 +3,6 @@ package irmaclient
import (
"encoding/json"
"errors"
"math/big"
"os"
"testing"
......@@ -130,33 +129,12 @@ func verifyCredentials(t *testing.T, client *Client) {
}
}
func verifyPaillierKey(t *testing.T, PrivateKey *paillierPrivateKey) {
require.NotNil(t, PrivateKey)
require.NotNil(t, PrivateKey.L)
require.NotNil(t, PrivateKey.U)
require.NotNil(t, PrivateKey.PublicKey.N)
require.Equal(t, big.NewInt(1), new(big.Int).Exp(big.NewInt(2), PrivateKey.L, PrivateKey.N))
require.Equal(t, PrivateKey.NSquared, new(big.Int).Exp(PrivateKey.N, big.NewInt(2), nil))
plaintext := "Hello Paillier!"
ciphertext, err := PrivateKey.Encrypt([]byte(plaintext))
require.NoError(t, err)
decrypted, err := PrivateKey.Decrypt(ciphertext)
require.NoError(t, err)
require.Equal(t, plaintext, string(decrypted))
}
func verifyKeyshareIsUnmarshaled(t *testing.T, client *Client) {
require.NotNil(t, client.paillierKeyCache)
require.NotNil(t, client.keyshareServers)
testManager := irma.NewSchemeManagerIdentifier("test")
require.Contains(t, client.keyshareServers, testManager)
kss := client.keyshareServers[testManager]
require.NotEmpty(t, kss.Nonce)
verifyPaillierKey(t, kss.PrivateKey)
verifyPaillierKey(t, client.paillierKeyCache)
}
func TestStorageDeserialization(t *testing.T) {
......@@ -266,36 +244,6 @@ func TestCandidates(t *testing.T) {
test.ClearTestStorage(t)
}
func TestPaillier(t *testing.T) {
client := parseStorage(t)
challenge, _ := gabi.RandomBigInt(256)
comm, _ := gabi.RandomBigInt(1000)
resp, _ := gabi.RandomBigInt(1000)
sk := client.paillierKey(true)
bytes, err := sk.Encrypt(challenge.Bytes())
require.NoError(t, err)
cipher := new(big.Int).SetBytes(bytes)
bytes, err = sk.Encrypt(comm.Bytes())
require.NoError(t, err)
commcipher := new(big.Int).SetBytes(bytes)
// [[ c ]]^resp * [[ comm ]]
cipher.Exp(cipher, resp, sk.NSquared).Mul(cipher, commcipher).Mod(cipher, sk.NSquared)
bytes, err = sk.Decrypt(cipher.Bytes())
require.NoError(t, err)
plaintext := new(big.Int).SetBytes(bytes)
expected := new(big.Int).Set(challenge)
expected.Mul(expected, resp).Add(expected, comm)
require.Equal(t, plaintext, expected)
test.ClearTestStorage(t)
}
func TestCredentialRemoval(t *testing.T) {
client := parseStorage(t)
......
......@@ -46,26 +46,24 @@ type keyshareSession struct {
}
type keyshareServer struct {
URL string `json:"url"`
Username string `json:"username"`
Nonce []byte `json:"nonce"`
PrivateKey *paillierPrivateKey `json:"keyPair"`
URL string `json:"url"`
Username string `json:"username"`
Nonce []byte `json:"nonce"`
SchemeManagerIdentifier irma.SchemeManagerIdentifier
token string
}
type keyshareEnrollment struct {
Username string `json:"username"`
Pin string `json:"pin"`
PublicKey *paillierPublicKey `json:"publicKey"`
Email *string `json:"email"`
Language string `json:"language"`
Username string `json:"username"`
Pin string `json:"pin"`
Email *string `json:"email"`
Language string `json:"language"`
}
type keyshareChangepin struct {
Username string `json:"id"`
OldPin string `json:"oldpin"`
NewPin string `json:"newpin"`
OldPin string `json:"oldpin"`
NewPin string `json:"newpin"`
}
type keyshareAuthorization struct {
......@@ -123,13 +121,11 @@ const (
func newKeyshareServer(
schemeManagerIdentifier irma.SchemeManagerIdentifier,
privatekey *paillierPrivateKey,
url string,
) (ks *keyshareServer, err error) {
ks = &keyshareServer{
Nonce: make([]byte, 32),
URL: url,
PrivateKey: privatekey,
Nonce: make([]byte, 32),
URL: url,
SchemeManagerIdentifier: schemeManagerIdentifier,
}
_, err = rand.Read(ks.Nonce)
......@@ -377,18 +373,7 @@ func (ks *keyshareSession) GetCommitments() {
// receive their responses (2nd and 3rd message in Schnorr zero-knowledge protocol).
func (ks *keyshareSession) GetProofPs() {
_, issig := ks.session.(*irma.SignatureRequest)
_, issuing := ks.session.(*irma.IssuanceRequest)
challenge := ks.builders.Challenge(ks.session.GetContext(), ks.session.GetNonce(), issig)
kssChallenge := challenge
// In disclosure or signature sessions the challenge is Paillier encrypted.
if !issuing {
bytes, err := ks.keyshareServer.PrivateKey.Encrypt(challenge.Bytes())
if err != nil {
ks.sessionHandler.KeyshareError(&ks.keyshareServer.SchemeManagerIdentifier, err)
}
kssChallenge = new(big.Int).SetBytes(bytes)
}
// Post the challenge, obtaining JWT's containing the ProofP's
responses := map[irma.SchemeManagerIdentifier]string{}
......@@ -398,7 +383,7 @@ func (ks *keyshareSession) GetProofPs() {
continue
}
var jwt string
err := transport.Post("prove/getResponse", &jwt, kssChallenge)
err := transport.Post("prove/getResponse", &jwt, challenge)
if err != nil {
ks.sessionHandler.KeyshareError(&managerID, err)
return
......@@ -457,14 +442,7 @@ func (ks *keyshareSession) finishDisclosureOrSigning(challenge *big.Int, respons
return
}
// Decrypt the responses and populate a slice of ProofP's
proofPs[i] = msg.ProofP
bytes, err := ks.keyshareServer.PrivateKey.Decrypt(proofPs[i].SResponse.Bytes())
if err != nil {
ks.sessionHandler.KeyshareError(&managerID, err)
return
}
proofPs[i].SResponse = new(big.Int).SetBytes(bytes)
}
// Create merged proofs and finish protocol
......
package irmaclient
import (
"encoding/json"
"math/big"
"github.com/credentials/go-go-gadget-paillier"
)
// paillierPrivateKey is an alias for paillier.PrivateKey so that we can add a custom unmarshaler to it.
type paillierPrivateKey paillier.PrivateKey
type paillierPublicKey paillier.PublicKey
func (psk *paillierPrivateKey) UnmarshalJSON(bytes []byte) (err error) {
// First try to unmarshal it as a keypair serialized in the old Android format
oldFormat := &struct {
PrivateKey struct {
L *big.Int `json:"lambda"`
U *big.Int `json:"preCalculatedDenominator"`
} `json:"privateKey"`
PublicKey struct {
N *big.Int `json:"n"`
G *big.Int `json:"g"`
NSquared *big.Int `json:"nSquared"`
} `json:"publicKey"`
}{}
if err = json.Unmarshal(bytes, oldFormat); err != nil {
return
}
if oldFormat.PrivateKey.L != nil {
psk.L = oldFormat.PrivateKey.L
psk.U = oldFormat.PrivateKey.U
psk.PublicKey.G = oldFormat.PublicKey.G
psk.PublicKey.N = oldFormat.PublicKey.N
psk.PublicKey.NSquared = oldFormat.PublicKey.NSquared
return nil
}
newFormat := new(paillier.PrivateKey)
if err = json.Unmarshal(bytes, newFormat); err != nil {
return
}
*psk = paillierPrivateKey(*newFormat)
return
}
func (psk *paillierPrivateKey) MarshalJSON() ([]byte, error) {
return json.Marshal(paillier.PrivateKey(*psk))
}
func (psk *paillierPrivateKey) Encrypt(bytes []byte) ([]byte, error) {
return paillier.Encrypt(&psk.PublicKey, bytes)
}
func (psk *paillierPrivateKey) Decrypt(bytes []byte) ([]byte, error) {
return paillier.Decrypt((*paillier.PrivateKey)(psk), bytes)
}
func (ppk *paillierPublicKey) MarshalJSON() ([]byte, error) {
temp := struct {
N *big.Int `json:"n"`
G *big.Int `json:"g"`
NSquared *big.Int `json:"nSquared"`
Bits int `json:"bits"`
}{ppk.N, ppk.G, ppk.NSquared, ppk.N.BitLen()}
return json.Marshal(temp)
}
......@@ -24,7 +24,6 @@ const (
skFile = "sk"
attributesFile = "attrs"
kssFile = "kss"
paillierFile = "paillier"
updatesFile = "updates"
logsFile = "logs"
preferencesFile = "preferences"
......@@ -103,10 +102,6 @@ func (s *storage) StoreKeyshareServers(keyshareServers map[irma.SchemeManagerIde
return s.store(keyshareServers, kssFile)
}
func (s *storage) StorePaillierKeys(key *paillierPrivateKey) error {
return s.store(key, paillierFile)
}
func (s *storage) StoreLogs(logs []*LogEntry) error {
return s.store(logs, logsFile)
}
......@@ -184,17 +179,6 @@ func (s *storage) LoadKeyshareServers() (ksses map[irma.SchemeManagerIdentifier]
return ksses, nil
}
func (s *storage) LoadPaillierKeys() (key *paillierPrivateKey, err error) {
key = new(paillierPrivateKey)
if err := s.load(key, paillierFile); err != nil {
return nil, err
}
if key.N == nil { // TODO this is ugly
return nil, nil
}