Commit db143b0b authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Merge branch 'paillier'

parents 2c58b9be d148654b
......@@ -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"
......
......@@ -57,6 +57,7 @@ type CredentialType struct {
AttributeTypes []*AttributeType `xml:"Attributes>Attribute" json:"-"`
XMLVersion int `xml:"version,attr"`
XMLName xml.Name `xml:"IssueSpecification"`
IssueURL TranslatedString `xml:'IssueURL'`
Valid bool `xml:"-"`
}
......
package irmaclient
import (
"crypto/rand"
"sort"
"strconv"
"time"
"github.com/credentials/go-go-gadget-paillier"
raven "github.com/getsentry/raven-go"
"github.com/go-errors/errors"
"github.com/privacybydesign/gabi"
......@@ -45,7 +42,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
......@@ -174,12 +170,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")
......@@ -202,7 +192,6 @@ func (client *Client) CredentialInfoList() irma.CredentialInfoList {
}
}
sort.Sort(list)
return list
}
......@@ -687,32 +676,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 {
......@@ -754,15 +717,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{}
......@@ -786,6 +748,22 @@ func (client *Client) keyshareEnrollWorker(managerID irma.SchemeManagerIdentifie
return nil
}
// KeyshareVerifyPin verifies the specified PIN at the keyshare server, returning if it succeeded;
// if not, how many tries are left, or for how long the user is blocked. If an error is returned
// it is of type *irma.SessionError.
func (client *Client) KeyshareVerifyPin(pin string, schemeid irma.SchemeManagerIdentifier) (bool, int, int, error) {
scheme := client.Configuration.SchemeManagers[schemeid]
if scheme == nil || !scheme.Distributed() {
return false, 0, 0, &irma.SessionError{
Err: errors.Errorf("Can't verify pin of scheme %s", schemeid.String()),
ErrorType: irma.ErrorUnknownSchemeManager,
Info: schemeid.String(),
}
}
kss := client.keyshareServers[schemeid]
return verifyPinWorker(pin, kss, irma.NewHTTPTransport(kss.URL))
}
func (client *Client) KeyshareChangePin(manager irma.SchemeManagerIdentifier, oldPin string, newPin string) {
go func() {
err := client.keyshareChangePinWorker(manager, oldPin, newPin)
......
......@@ -3,12 +3,11 @@ package irmaclient
import (
"encoding/json"
"errors"
gobig "math/big"
"os"
"testing"
"github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/big"
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/fs"
"github.com/privacybydesign/irmago/internal/test"
......@@ -80,33 +79,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, gobig.NewInt(1), new(gobig.Int).Exp(gobig.NewInt(2), PrivateKey.L, PrivateKey.N))
require.Equal(t, PrivateKey.NSquared, new(gobig.Int).Exp(PrivateKey.N, gobig.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) {
......@@ -184,36 +162,6 @@ func TestCandidates(t *testing.T) {
require.Empty(t, attrs)
}
func TestPaillier(t *testing.T) {
client := parseStorage(t)
defer test.ClearTestStorage(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 ]]
nsquared := big.Convert(sk.NSquared)
cipher.Exp(cipher, resp, nsquared).Mul(cipher, commcipher).Mod(cipher, 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)
}
func TestCredentialRemoval(t *testing.T) {
client := parseStorage(t)
defer test.ClearTestStorage(t)
......
......@@ -52,20 +52,18 @@ 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 {
......@@ -129,13 +127,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)
......@@ -279,6 +275,37 @@ func (ks *keyshareSession) VerifyPin(attempts int) {
}))
}
func verifyPinWorker(pin string, kss *keyshareServer, transport *irma.HTTPTransport) (
success bool, tries int, blocked int, err error) {
pinmsg := keysharePinMessage{Username: kss.Username, Pin: kss.HashedPin(pin)}
pinresult := &keysharePinStatus{}
err = transport.Post("users/verify/pin", pinresult, pinmsg)
if err != nil {
return
}
switch pinresult.Status {
case kssPinSuccess:
success = true
kss.token = pinresult.Message
transport.SetHeader(kssAuthHeader, kss.token)
return
case kssPinFailure:
tries, err = strconv.Atoi(pinresult.Message)
return
case kssPinError:
blocked, err = strconv.Atoi(pinresult.Message)
return
default:
err = &irma.SessionError{
Err: errors.New("Keyshare server returned unrecognized PIN status"),
ErrorType: irma.ErrorServerResponse,
Info: "Keyshare server returned unrecognized PIN status",
}
return
}
}
// Verify the specified pin at each of the keyshare servers involved in the specified session.
// - If the pin did not verify at one of the keyshare servers but there are attempts remaining,
// the amount of remaining attempts is returned as the second return value.
......@@ -296,30 +323,11 @@ func (ks *keyshareSession) verifyPinAttempt(pin string) (
kss := ks.keyshareServers[manager]
transport := ks.transports[manager]
pinmsg := keysharePinMessage{Username: kss.Username, Pin: kss.HashedPin(pin)}
pinresult := &keysharePinStatus{}
err = transport.Post("users/verify/pin", pinresult, pinmsg)
if err != nil {
return
}
switch pinresult.Status {
case kssPinSuccess:
kss.token = pinresult.Message
transport.SetHeader(kssAuthHeader, "Bearer "+kss.token)
case kssPinFailure:
tries, err = strconv.Atoi(pinresult.Message)
return
case kssPinError:
blocked, err = strconv.Atoi(pinresult.Message)
return
default:
err = errors.New("Keyshare server returned unrecognized PIN status")
success, tries, blocked, err = verifyPinWorker(pin, kss, transport)
if !success {
return
}
}
success = true
return
}
......@@ -390,18 +398,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{}
......@@ -411,7 +408,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
......@@ -465,15 +462,7 @@ func (ks *keyshareSession) finishDisclosureOrSigning(challenge *big.Int, respons
ks.sessionHandler.KeyshareError(&managerID, err)
return
}
// Decrypt the responses and populate a slice of ProofP's
proofPs[i] = claims.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) {
sk := new(paillier.PrivateKey)
if err = json.Unmarshal(bytes, sk); err != nil {
return
}
*psk = paillierPrivateKey(*sk)
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
}
return key, nil
}
func (s *storage) LoadLogs() (logs []*LogEntry, err error) {
logs = []*LogEntry{}
if err := s.load(&logs, logsFile); err != nil {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment