Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions calypso/api_v4.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package calypso

import (
"go.dedis.ch/cothority/v3"
"go.dedis.ch/kyber/v3"
"go.dedis.ch/onet/v3"
)

// TODO: add LTSID of type kyber.Point
// TODO: think about authentication
// TODO: add CreateAndAuthorise
// TODO: add REST interface

type LTSID kyber.Point

// ClientV4 is a class to communicate to the calypso service.
type ClientV4 struct {
*onet.Client
}

// NewClientV4 creates a new client to interact with the Calypso Service.
func NewClientV4() *ClientV4 {
return &ClientV4{Client: onet.NewClient(cothority.Suite, ServiceName)}
}

// CreateLTS starts a new Distributed Key Generation with the nodes in the roster and
// returns the collective public key X. This X is also used later to identify the
// LTS instance, as there can be more than one LTS group on a node.
//
// It also sets up an authorisation option for the nodes.
//
// This can only be called from localhost, except if the environment variable
// COTHORITY_ALLOW_INSECURE_ADMIN is set to 'true'.
//
// In case of error, X is nil, and the error indicates what is wrong.
// The `sig` returned is a collective signature on the following hash:
// sha256( X | protobuf.Encode(auth) )
// It can be verified using the aggregate service key from the roster:
// msg := sha256.New()
// Xbuf, err := X.MarshalBinary()
// // Check for errors
// msg.Write(Xbuf)
// authBuf, err := protobuf.Encode(auth)
// // Check for errors
// err = schnorr.Verify(cothority.Suite, roster.ServiceAggregate(calypso.ServiceName),
// msg.Sum(nil), sig)
// // If err == nil, the signature is correct
func (c *ClientV4) CreateLTS(ltsRoster *onet.Roster, auth Auth) (X LTSID, sig []byte, err error) {
return
}

// Reencrypt requests the re-encryption of the secret stored in the grant.
// The grant must also contain the ephemeral key to which the secret will be
// reencrypted to.
// Finally the grant must contain information about how to verify that the
// reencryption request is valid.
//
// This can be called from anywhere.
//
// If the grant is valid, the reencrypted XHat is returned and err is nil. In case
// of error, XHat is nil, and the error will be returned.
func (c *ClientV4) Reencrypt(X kyber.Point, grant Grant) (XHat kyber.Point, err error) {
return
}
64 changes: 64 additions & 0 deletions calypso/proto.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package calypso

import (
"time"

"go.dedis.ch/cothority/v3/byzcoin"
"go.dedis.ch/cothority/v3/skipchain"
"go.dedis.ch/kyber/v3"
Expand All @@ -9,6 +11,7 @@ import (

// PROTOSTART
// type :skipchain.SkipBlockID:bytes
// type :time.Time:uint64
// package calypso;
// import "byzcoin.proto";
// import "onet.proto";
Expand Down Expand Up @@ -126,3 +129,64 @@ type GetLTSReply struct {
type LtsInstanceInfo struct {
Roster onet.Roster
}

//
// V4 proposed extensions
//

// Auth holds all possible authentication structures. When using it to call
// Authorise, only one of the fields must be non-nil.
type Auth struct {
ByzCoin *AuthByzCoin
AuthX509Cert *AuthX509Cert
}

// AuthByzCoin holds the information necessary to authenticate a byzcoin request.
// In the ByzCoin model, all requests are valid as long as they are stored in the
// blockchain with the given ID.
// The TTL is to avoid that too old requests are re-used. If it is 0, it is disabled.
type AuthByzCoin struct {
ByzCoinID skipchain.SkipBlockID
TTL time.Time
}

// AuthX509Cert holds the information necessary to authenticate a HyperLedger/Fabric
// request. In its simplest form, it is simply the CA that will have to sign the
// certificates of the requesters.
// The Threshold indicates how many clients must have signed the request before it
// is accepted.
type AuthX509Cert struct {
// Slice of ASN.1 encoded X509 certificates.
CA [][]byte
Threshold int
}

// Grant holds one of the possible grant proofs for a reencryption request. Each
// grant proof must hold the secret to be reencrypted, the ephemeral key, as well
// as the proof itself that the request is valid. For each of the authentication
// schemes, this proof will be different.
type Grant struct {
ByzCoin *GrantByzCoin
X509Cert *GrantX509Cert
}

// GrantByzCoin holds the proof of the write instance, holding the secret itself.
// The proof of the read instance holds the ephemeral key. Both proofs can be
// verified using one of the stored ByzCoinIDs.
type GrantByzCoin struct {
// Write is the proof containing the write request.
Write byzcoin.Proof
// Read is the proof that he has been accepted to read the secret.
Read byzcoin.Proof
}

// GrantX509Cert holds the proof that at least a threshold number of clients
// accepted the reencryption.
// For each client, there must exist a certificate that can be verified by the
// CA certificate from AuthX509Cert. Additionally, each client must sign the
// following message:
// sha256( Secret | Ephemeral | Time )
type GrantX509Cert struct {
Secret kyber.Point
Certificates [][]byte
}
64 changes: 64 additions & 0 deletions calypso/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package calypso

import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"

"golang.org/x/crypto/ed25519"
)

var (
// selection of OID numbers is not random See documents
// https://tools.ietf.org/html/rfc5280#page-49
// https://tools.ietf.org/html/rfc7229
WriteIdOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 13, 1}
EphemeralKeyOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 13, 2}
)

func Verify(rootCert *x509.Certificate, toVerify *x509.Certificate) (writeId []byte, key ed25519.PublicKey, err error) {
roots := x509.NewCertPool()
roots.AddCert(rootCert)

cert, err := x509.ParseCertificate(toVerify.Raw)
if err != nil {
return nil, nil, err
}

opts := x509.VerifyOptions{
Roots: roots,
}

writeIdExt := getExtension(cert, WriteIdOID)
ephemeralKeyExt := getExtension(cert, EphemeralKeyOID)

unmarkUnhandledCriticalExtension(cert, WriteIdOID)
unmarkUnhandledCriticalExtension(cert, EphemeralKeyOID)

if _, err := cert.Verify(opts); err != nil {
return nil, nil, err
}

return writeIdExt.Value, ephemeralKeyExt.Value, nil
}

func unmarkUnhandledCriticalExtension(cert *x509.Certificate, id asn1.ObjectIdentifier) {
for i, extension := range cert.UnhandledCriticalExtensions {
if id.Equal(extension) {
cert.UnhandledCriticalExtensions = append(cert.UnhandledCriticalExtensions[0:i],
cert.UnhandledCriticalExtensions[i+1:]...)
return
}
}
}

func getExtension(certificate *x509.Certificate, id asn1.ObjectIdentifier) *pkix.Extension {

for _, ext := range certificate.Extensions {
if ext.Id.Equal(id) {
return &ext
}
}

return nil
}
60 changes: 60 additions & 0 deletions calypso/verify_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package calypso

import (
"crypto/x509"
"encoding/hex"
"encoding/pem"
"errors"
"testing"
)

const (
rootCert1 = `-----BEGIN CERTIFICATE-----
MIIB1jCCATigAwIBAgIBATAKBggqhkjOPQQDBDAdMRswGQYDVQQDExJCeXpHZW4g
c2lnbmVyIG9yZzEwHhcNMTkwMzI4MjEwNzUxWhcNNDQwMzIxMjEwNzUxWjAdMRsw
GQYDVQQDExJCeXpHZW4gc2lnbmVyIG9yZzEwgZswEAYHKoZIzj0CAQYFK4EEACMD
gYYABABqdo+aDVte5Fz/xG5Z2GYmIbcVJdXxrMJrTBYgHQafSw0BBKrAyeMcZ534
/V6eNfkiZa3kuflo6Y2E/NtVxyl7dgFBYTdqvLtPdg7+K7pdj8eKFrAQ0DDi5S0x
aM96oR3S0bU4MIbfMqW1fAsLPw3476Gvju73bfJhEJ3ukx6W2olq+KMmMCQwDgYD
VR0PAQH/BAQDAgIEMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYIKoZIzj0EAwQDgYsA
MIGHAkEcvPgm0qnXMgpJiOD52VUL3qTwU6uzRYhwIWa3sWCP471/muzsq6PctAEu
CHkpnAlH3DuS2MBBql8ifwwK2PdOGQJCAQcE3+qdiyrABJ315INCTu6HAjpGv0cR
VQWcCmSs80tS9gzvQJ8+peWRuzGvy1Uoyj0qHTSJOHx6z86oOIVbXAIj
-----END CERTIFICATE-----`

validPem = `-----BEGIN CERTIFICATE-----
MIICKTCCAYqgAwIBAgIQYNsgS2KrQ1ptA7E+cRfiUjAKBggqhkjOPQQDBDAdMRsw
GQYDVQQDExJCeXpHZW4gc2lnbmVyIG9yZzEwHhcNMTkwMzI4MjEwNzUxWhcNMTkw
NDExMjEwNzUxWjAoMSYwJAYDVQQDDB1FcGhlbWVyYWwgcmVhZCBvcGVyYXRpb24g
JiBDbzB2MBAGByqGSM49AgEGBSuBBAAiA2IABPEbevkxsAu3BqZjMBzl+ppSLX1F
4oqnAUxmXx+Yw9mgyunTWzHKPAgHoYmaVDL2a+MDVngmbJI+BiXaZBE00gW854pz
ROa1Z7KxjYGgbRINavXX5nSTbs+xH3w76d3ppKOBgzCBgDAOBgNVHQ8BAf8EBAMC
BSAwDAYDVR0TAQH/BAIwADAvBggrBgEFBQcNAQEB/wQg7PBd8YGomyUmjZpqOy9h
gdAdKEfArphKLRkkozsRRvIwLwYIKwYBBQUHDQIBAf8EIBuJzdwW5DfOVymjPvBM
YXsz+apB9URZnhN1jZy2wrixMAoGCCqGSM49BAMEA4GMADCBiAJCAYwxRrOwCydO
r5KoAndH8/U9nIaM4BWcx1pwYFMM44P0BzXDQgDSYwIAhAQ5hvOpaMPB4IMKI37C
G1lsOKivZEboAkIA90UbyVD7ahZdbpCDKUYAoVejKgA5JAsm8kUGPWt+siw2hsT9
V/NTETY3evBjoX8kkWs/E5pWpwEGKPQaS25gw1s=
-----END CERTIFICATE-----`
)

func Test_VerifyCertificateHappyDayScenario(t *testing.T) {
caCert, _ := certFromPem([]byte(rootCert1))
cert, _ := certFromPem([]byte(validPem))

writeId, key, _ := Verify(caCert, cert)
t.Log("writeId", hex.EncodeToString(writeId))
t.Log("key", hex.EncodeToString(key))
}

func certFromPem(pemCerts []byte) (cert *x509.Certificate, err error) {
var block *pem.Block

block, pemCerts = pem.Decode(pemCerts)

if block.Type != "CERTIFICATE" {
return nil, errors.New("expected a certificate")
}

return x509.ParseCertificate(block.Bytes)
}
Loading