Commit f0c355d5 authored by Tristan Claverie's avatar Tristan Claverie

Merge branch '190_platform_sign_init' into 'master'

190 platform sign init

This MR is huge because its tests cannot be divided.

This is global MR for the following taiga stories:
- 190
- 191
- 325

See merge request !41
parents 63491f79 7f65e9f7
Pipeline #372 passed with stages
......@@ -31,6 +31,7 @@ Unit tests:
- "go test -coverprofile dfssp_user.part -v dfss/dfssp/user"
- "go test -coverprofile dfssp_contract.part -v dfss/dfssp/contract"
- "go test -coverprofile dfssp_templates.part -v dfss/dfssp/templates"
- "go test -coverprofile dfssp_common.part -v dfss/dfssp/common"
- "go test -coverprofile dfssd.part -v dfss/dfssd"
- "go test -coverprofile dfssc_common.part -v dfss/dfssc/common"
- "go test -coverprofile dfssc_security.part -v dfss/dfssc/security"
......
......@@ -23,3 +23,10 @@ package:
deploy:
mkdir -p /deploy/$(VERSION)
cp release/*.tar.gz /deploy/$(VERSION)/
protobuf:
cd .. && \
protoc --go_out=plugins=grpc:. dfss/dfssc/api/client.proto && \
protoc --go_out=plugins=grpc:. dfss/dfssd/api/demonstrator.proto && \
protoc --go_out=plugins=grpc:. dfss/dfssp/api/platform.proto && \
protoc --go_out=plugins=grpc:. dfss/dfsst/api/resolution.proto
// Code generated by protoc-gen-go.
// source: dfss/dfssc/api/client.proto
// DO NOT EDIT!
/*
Package api is a generated protocol buffer package.
It is generated from these files:
dfss/dfssc/api/client.proto
It has these top-level messages:
Promise
Signature
Hello
*/
package api
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import api1 "dfss/dfssp/api"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
const _ = proto.ProtoPackageIsVersion1
// Promise message contains all the required information to verify
// the identity of the sender and reciever, and the actual promise
type Promise struct {
RecipientKeyHash []byte `protobuf:"bytes,1,opt,name=recipientKeyHash,proto3" json:"recipientKeyHash,omitempty"`
SenderKeyHash []byte `protobuf:"bytes,2,opt,name=senderKeyHash,proto3" json:"senderKeyHash,omitempty"`
SignatureUuid string `protobuf:"bytes,3,opt,name=signatureUuid" json:"signatureUuid,omitempty"`
ContractUuid string `protobuf:"bytes,4,opt,name=contractUuid" json:"contractUuid,omitempty"`
}
func (m *Promise) Reset() { *m = Promise{} }
func (m *Promise) String() string { return proto.CompactTextString(m) }
func (*Promise) ProtoMessage() {}
func (*Promise) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// Signature message contains all the required information to verify
// the identity of the sender and reciever, and the actual signature
type Signature struct {
RecipientKeyHash []byte `protobuf:"bytes,1,opt,name=recipientKeyHash,proto3" json:"recipientKeyHash,omitempty"`
SenderKeyHash []byte `protobuf:"bytes,2,opt,name=senderKeyHash,proto3" json:"senderKeyHash,omitempty"`
Signature string `protobuf:"bytes,3,opt,name=signature" json:"signature,omitempty"`
SignatureUuid string `protobuf:"bytes,4,opt,name=signatureUuid" json:"signatureUuid,omitempty"`
ContractUuid string `protobuf:"bytes,5,opt,name=contractUuid" json:"contractUuid,omitempty"`
}
func (m *Signature) Reset() { *m = Signature{} }
func (m *Signature) String() string { return proto.CompactTextString(m) }
func (*Signature) ProtoMessage() {}
func (*Signature) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
// Hello message is used when discovering peers. It contains the current version of the software.
type Hello struct {
Version string `protobuf:"bytes,1,opt,name=version" json:"version,omitempty"`
}
func (m *Hello) Reset() { *m = Hello{} }
func (m *Hello) String() string { return proto.CompactTextString(m) }
func (*Hello) ProtoMessage() {}
func (*Hello) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func init() {
proto.RegisterType((*Promise)(nil), "api.Promise")
proto.RegisterType((*Signature)(nil), "api.Signature")
proto.RegisterType((*Hello)(nil), "api.Hello")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// Client API for Client service
type ClientClient interface {
TreatPromise(ctx context.Context, in *Promise, opts ...grpc.CallOption) (*api1.ErrorCode, error)
TreatSignature(ctx context.Context, in *Signature, opts ...grpc.CallOption) (*api1.ErrorCode, error)
Discover(ctx context.Context, in *Hello, opts ...grpc.CallOption) (*Hello, error)
}
type clientClient struct {
cc *grpc.ClientConn
}
func NewClientClient(cc *grpc.ClientConn) ClientClient {
return &clientClient{cc}
}
func (c *clientClient) TreatPromise(ctx context.Context, in *Promise, opts ...grpc.CallOption) (*api1.ErrorCode, error) {
out := new(api1.ErrorCode)
err := grpc.Invoke(ctx, "/api.Client/TreatPromise", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *clientClient) TreatSignature(ctx context.Context, in *Signature, opts ...grpc.CallOption) (*api1.ErrorCode, error) {
out := new(api1.ErrorCode)
err := grpc.Invoke(ctx, "/api.Client/TreatSignature", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *clientClient) Discover(ctx context.Context, in *Hello, opts ...grpc.CallOption) (*Hello, error) {
out := new(Hello)
err := grpc.Invoke(ctx, "/api.Client/Discover", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Client service
type ClientServer interface {
TreatPromise(context.Context, *Promise) (*api1.ErrorCode, error)
TreatSignature(context.Context, *Signature) (*api1.ErrorCode, error)
Discover(context.Context, *Hello) (*Hello, error)
}
func RegisterClientServer(s *grpc.Server, srv ClientServer) {
s.RegisterService(&_Client_serviceDesc, srv)
}
func _Client_TreatPromise_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(Promise)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(ClientServer).TreatPromise(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _Client_TreatSignature_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(Signature)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(ClientServer).TreatSignature(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _Client_Discover_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(Hello)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(ClientServer).Discover(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
var _Client_serviceDesc = grpc.ServiceDesc{
ServiceName: "api.Client",
HandlerType: (*ClientServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "TreatPromise",
Handler: _Client_TreatPromise_Handler,
},
{
MethodName: "TreatSignature",
Handler: _Client_TreatSignature_Handler,
},
{
MethodName: "Discover",
Handler: _Client_Discover_Handler,
},
},
Streams: []grpc.StreamDesc{},
}
var fileDescriptor0 = []byte{
// 302 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x92, 0x4f, 0x4e, 0xf3, 0x30,
0x10, 0xc5, 0x9b, 0xaf, 0xff, 0xbe, 0x8c, 0x42, 0x85, 0xbc, 0xaa, 0x02, 0x48, 0x10, 0x75, 0x81,
0x58, 0x24, 0x52, 0x39, 0x42, 0x41, 0xaa, 0xc4, 0x06, 0x05, 0x38, 0x80, 0x71, 0xa6, 0x60, 0x29,
0x8d, 0xa3, 0xb1, 0x8b, 0xc4, 0x35, 0xb8, 0x03, 0x47, 0xe1, 0x5e, 0xa4, 0xd3, 0x26, 0x25, 0x6a,
0x17, 0x6c, 0xd8, 0x44, 0x79, 0xcf, 0xbf, 0xb1, 0xdf, 0x78, 0x0c, 0x27, 0xd9, 0xc2, 0xda, 0x64,
0xfd, 0x51, 0x89, 0x2c, 0x75, 0xa2, 0x72, 0x8d, 0x85, 0x8b, 0x4b, 0x32, 0xce, 0x88, 0x6e, 0xe5,
0x84, 0x67, 0x0d, 0x51, 0x32, 0x51, 0xe6, 0xd2, 0x2d, 0x0c, 0x2d, 0x37, 0x4c, 0xf4, 0xe9, 0xc1,
0xf0, 0x9e, 0xcc, 0x52, 0x5b, 0x14, 0x57, 0x70, 0x4c, 0xa8, 0x74, 0xb9, 0xde, 0xe2, 0x0e, 0xdf,
0xe7, 0xd2, 0xbe, 0x8e, 0xbd, 0x73, 0xef, 0x32, 0x48, 0xf7, 0x7c, 0x31, 0x81, 0x23, 0x8b, 0x45,
0x86, 0x54, 0x83, 0xff, 0x18, 0x6c, 0x9b, 0x4c, 0xe9, 0x97, 0x42, 0xba, 0x15, 0xe1, 0xd3, 0x4a,
0x67, 0xe3, 0x6e, 0x45, 0xf9, 0x69, 0xdb, 0x14, 0x11, 0x04, 0xca, 0x14, 0x8e, 0xa4, 0x72, 0x0c,
0xf5, 0x18, 0x6a, 0x79, 0xd1, 0x97, 0x07, 0xfe, 0x43, 0x5d, 0xf5, 0x07, 0x49, 0x4f, 0xc1, 0x6f,
0x42, 0x6d, 0x53, 0xee, 0x8c, 0xfd, 0x3e, 0x7a, 0xbf, 0xe9, 0xa3, 0x7f, 0xa0, 0x8f, 0x0b, 0xe8,
0xcf, 0x31, 0xcf, 0x8d, 0x18, 0xc3, 0xf0, 0x0d, 0xc9, 0x6a, 0x53, 0x70, 0x72, 0x3f, 0xad, 0xe5,
0xf4, 0xc3, 0x83, 0xc1, 0x8c, 0xe7, 0x28, 0x62, 0x08, 0x1e, 0x09, 0xa5, 0xab, 0x27, 0x14, 0xc4,
0xd5, 0x08, 0xe3, 0xad, 0x0a, 0x47, 0xac, 0x6e, 0x89, 0x0c, 0xcd, 0x4c, 0x86, 0x51, 0x47, 0x4c,
0x61, 0xc4, 0xfc, 0xee, 0xa6, 0x36, 0x4c, 0xa3, 0x0f, 0xd4, 0x4c, 0xe0, 0xff, 0x8d, 0xb6, 0xca,
0x54, 0xc7, 0x0b, 0xe0, 0x55, 0x0e, 0x18, 0xfe, 0xf8, 0x8f, 0x3a, 0xcf, 0x03, 0x7e, 0x2e, 0xd7,
0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x61, 0x73, 0x57, 0xa2, 0x71, 0x02, 0x00, 0x00,
}
syntax = "proto3";
package api;
import "dfss/dfssp/api/platform.proto";
service Client {
rpc TreatPromise(Promise) returns (ErrorCode) {}
rpc TreatSignature(Signature) returns (ErrorCode) {}
rpc Discover(Hello) returns (Hello) {}
}
// Promise message contains all the required information to verify
// the identity of the sender and reciever, and the actual promise
message Promise {
bytes recipientKeyHash = 1; // SHA-512
bytes senderKeyHash = 2; // SHA-512
string signatureUuid = 3;
string contractUuid = 4;
}
// Signature message contains all the required information to verify
// the identity of the sender and reciever, and the actual signature
message Signature {
bytes recipientKeyHash = 1; // SHA-512
bytes senderKeyHash = 2; // SHA-512
string signature = 3; // Cryptographic signature
string signatureUuid = 4;
string contractUuid = 5;
}
// Hello message is used when discovering peers. It contains the current version of the software.
message Hello {
string version = 1;
}
......@@ -8,11 +8,12 @@ import (
)
var (
verbose bool
fca string // Path to the CA
fcert string // Path to the certificate
fkey string // Path to the private key
addrPort string // Address and port of the platform
verbose bool
fca string // Path to the CA
fcert string // Path to the certificate
fkey string // Path to the private key
addrPort string // Address and port of the platform
localPort int // Port to open for P2P communication
)
func init() {
......@@ -22,6 +23,7 @@ func init() {
flag.StringVar(&fcert, "cert", "cert.pem", "Path to the user certificate")
flag.StringVar(&fkey, "key", "key.pem", "Path to the private key")
flag.StringVar(&addrPort, "host", "localhost:9000", "Host of the DFSS platform")
flag.IntVar(&localPort, "port", 9005, "Port to use for P2P communication between clients")
flag.Usage = func() {
fmt.Println("DFSS client command line v" + dfss.Version)
......@@ -39,6 +41,7 @@ func init() {
fmt.Println(" show <c> print contract information from file c")
fmt.Println(" export <c> export certificate and private key of the user to file c")
fmt.Println(" import <c> import private key and certificate from file c")
fmt.Println(" sign <c> sign contract from file c")
fmt.Println("\nFlags:")
flag.PrintDefaults()
......@@ -67,6 +70,8 @@ func main() {
exportConf(flag.Arg(1))
case "import":
importConf(flag.Arg(1))
case "sign":
signContract(flag.Arg(1))
default:
flag.Usage()
}
......
......@@ -11,7 +11,7 @@ func newContract() {
fmt.Println("Creating a new contract")
passphrase, filepath, comment, signers := getContractInfo()
err := sign.NewCreateManager(fca, fcert, fkey, addrPort, passphrase, filepath, comment, signers)
err := sign.SendNewContract(fca, fcert, fkey, addrPort, passphrase, filepath, comment, signers)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
......
package security
import (
"crypto/rsa"
"crypto/x509"
)
// AuthContainer contains common information for TLS authentication.
// Files are not loaded from the beginning, call LoadFiles to load them.
type AuthContainer struct {
FileCA string
FileCert string
FileKey string
AddrPort string
Passphrase string
CA *x509.Certificate
Cert *x509.Certificate
Key *rsa.PrivateKey
}
// NewAuthContainer is a shortcut to build an AuthContainer
func NewAuthContainer(fileCA, fileCert, fileKey, addrPort, passphrase string) *AuthContainer {
return &AuthContainer{
FileCA: fileCA,
FileCert: fileCert,
FileKey: fileKey,
AddrPort: addrPort,
Passphrase: passphrase,
}
}
// LoadFiles tries to load the required certificates and key for TLS authentication
func (a *AuthContainer) LoadFiles() (ca *x509.Certificate, cert *x509.Certificate, key *rsa.PrivateKey, err error) {
ca, err = GetCertificate(a.FileCA)
if err != nil {
return
}
cert, err = GetCertificate(a.FileCert)
if err != nil {
return
}
key, err = GetPrivateKey(a.FileKey, a.Passphrase)
a.CA = ca
a.Cert = cert
a.Key = key
return
}
......@@ -7,6 +7,7 @@ import (
"text/template"
"dfss/dfssc/common"
"dfss/dfssp/contract"
)
const contractShowTemplate = `UUID : {{.UUID}}
......@@ -21,24 +22,24 @@ Signers :
{{range .Signers}} - {{.Email}}
{{end}}`
func showContract(filename string) {
func showContract(filename string) *contract.JSON {
data, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println("Cannot open file:", err)
return
return nil
}
c, err := common.UnmarshalDFSSFile(data)
if err != nil {
fmt.Println("Corrupted file:", err)
return
return nil
}
tmpl, err := template.New("contract").Parse(contractShowTemplate)
if err != nil {
fmt.Println("Internal error:", err)
return
return nil
}
b := new(bytes.Buffer)
......@@ -48,5 +49,5 @@ func showContract(filename string) {
}
fmt.Print(b.String())
return c
}
package main
import (
"fmt"
"os"
"dfss/dfssc/sign"
)
func signContract(filename string) {
fmt.Println("You are going to sign the following contract:")
contract := showContract(filename)
if contract == nil {
os.Exit(1)
}
var passphrase string
_ = readPassword(&passphrase, false)
// Preparation
manager, err := sign.NewSignatureManager(fca, fcert, fkey, addrPort, passphrase, localPort, contract)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}
err = manager.ConnectToPeers()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(3)
}
// Confirmation
var ready string
readStringParam("Do you REALLY want to sign "+contract.File.Name+"? Type 'yes' to confirm", "", &ready)
if ready != "yes" {
os.Exit(4)
}
// Ignition
fmt.Println("Waiting for other signers to be ready...")
signatureUUID, err := manager.SendReadySign()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(5)
}
// TODO Warning, integration tests are checking Stdout
fmt.Println("Everybody is ready, starting the signature", signatureUUID)
// Signature
}
......@@ -14,32 +14,22 @@ import (
)
// CreateManager handles the creation of a new contract.
//
// TODO create a specific structure containing crypto information
type CreateManager struct {
fileCA string
fileCert string
fileKey string
addrPort string
passphrase string
filepath string
comment string
signers []string
hash []byte
filename string
auth *security.AuthContainer
filepath string
comment string
signers []string
hash []byte
filename string
}
// NewCreateManager tries to create a contract on the platform and returns an error or nil
func NewCreateManager(fileCA, fileCert, fileKey, addrPort, passphrase, filepath, comment string, signers []string) error {
// SendNewContract tries to create a contract on the platform and returns an error or nil
func SendNewContract(fileCA, fileCert, fileKey, addrPort, passphrase, filepath, comment string, signers []string) error {
m := &CreateManager{
fileCA: fileCA,
fileCert: fileCert,
fileKey: fileKey,
addrPort: addrPort,
passphrase: passphrase,
filepath: filepath,
comment: comment,
signers: signers,
auth: security.NewAuthContainer(fileCA, fileCert, fileKey, addrPort, passphrase),
filepath: filepath,
comment: comment,
signers: signers,
}
err := m.computeFile()
......@@ -71,22 +61,13 @@ func (m *CreateManager) computeFile() error {
// sendRequest sends a new contract request for the platform and send it
func (m *CreateManager) sendRequest() (*api.ErrorCode, error) {
ca, err := security.GetCertificate(m.fileCA)
if err != nil {
return nil, err
}
cert, err := security.GetCertificate(m.fileCert)
if err != nil {
return nil, err
}
key, err := security.GetPrivateKey(m.fileKey, m.passphrase)
ca, cert, key, err := m.auth.LoadFiles()
if err != nil {
return nil, err
}
conn, err := net.Connect(m.addrPort, cert, key, ca)
conn, err := net.Connect(m.auth.AddrPort, cert, key, ca)
if err != nil {
return nil, err
}
......
......@@ -44,10 +44,10 @@ func TestMain(m *testing.M) {
}
func TestNewCreateManager(t *testing.T) {
err := NewCreateManager(fca, fcert, fkey, addrPort, "password", fcontract, "success", []string{"a@example.com", "b@example.com"})
err := SendNewContract(fca, fcert, fkey, addrPort, "password", fcontract, "success", []string{"a@example.com", "b@example.com"})
assert.Equal(t, nil, err)
err = NewCreateManager(fca, fcert, fkey, addrPort, "password", fcontract, "warning", []string{"a@example.com", "b@example.com"})
err = SendNewContract(fca, fcert, fkey, addrPort, "password", fcontract, "warning", []string{"a@example.com", "b@example.com"})
assert.Equal(t, "Operation succeeded with a warning message: Some users are not ready yet", err.Error())
}
......
package sign
import (
"dfss"
cAPI "dfss/dfssc/api"
pAPI "dfss/dfssp/api"
"dfss/net"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
type clientServer struct{}
// TreatPromise handler
//
// Handle incoming TreatPromise messages
func (s *clientServer) TreatPromise(ctx context.Context, in *cAPI.Promise) (*pAPI.ErrorCode, error) {
// TODO
return nil, nil
}
// TreatSignature handler
//
// Handle incoming TreatSignature messages
func (s *clientServer) TreatSignature(ctx context.Context, in *cAPI.Signature) (*pAPI.ErrorCode, error) {
// TODO
return nil, nil
}
// Discover handler
//
// Handle incoming Discover messages
func (s *clientServer) Discover(ctx context.Context, in *cAPI.Hello) (*cAPI.Hello, error) {
return &cAPI.Hello{Version: dfss.Version}, nil
}
// GetServer create and registers a ClientServer, returning the associated GRPC server
func (m *SignatureManager) GetServer() *grpc.Server {
server := net.NewServer(m.auth.Cert, m.auth.Key, m.auth.CA)
cAPI.RegisterClientServer(server, &clientServer{})
return server
}
package sign
import (
"errors"
"fmt"
"log"
"strconv"
"time"
"dfss"
cAPI "dfss/dfssc/api"
"dfss/dfssc/security"
pAPI "dfss/dfssp/api"
"dfss/dfssp/contract"
"dfss/net"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
// SignatureManager handles the signature of a contract.
type SignatureManager struct {