Commit 3a45ee5c authored by Loïck Bonniot's avatar Loïck Bonniot

[c] Add `new` command

- Update platform mockup
- Fix hash bug in platform
- Fix CLI interactions
parent 86f13123
Pipeline #280 passed with stage
......@@ -32,6 +32,7 @@ Unit tests:
- "go test -coverprofile dfssc_common.part -v dfss/dfssc/common"
- "go test -coverprofile dfssc_security.part -v dfss/dfssc/security"
- "go test -coverprofile dfssc_user.part -v dfss/dfssc/user"
- "go test -coverprofile dfssc_user.part -v dfss/dfssc/sign"
- "echo 'mode: set' *part > c.out"
- "grep -h -v 'mode: set' *part >> c.out"
- "go tool cover -html=c.out -o coverage.html"
......@@ -55,6 +56,7 @@ ARM tests:
- "go test -cover -short -v dfss/dfssc/common"
- "go test -cover -short -v dfss/dfssc/security"
- "go test -cover -short -v dfss/dfssc/user"
- "go test -cover -short -v dfss/dfssc/sign"
Code lint:
stage: test
......
package main
import (
"fmt"
"dfss/dfssc/user"
)
func authUser() {
fmt.Println("Authenticating user")
var mail, token string
readStringParam("Mail", "", &mail)
readStringParam("Token", "", &token)
err := user.Authenticate(fca, fcert, addrPort, mail, token)
if err != nil {
fmt.Println("An error occurred : ", err.Error())
}
}
package common
import (
"errors"
"dfss/dfssp/api"
)
// EvaluateErrorCodeResponse converts an ErrorCode to human-friendly error
func EvaluateErrorCodeResponse(code *api.ErrorCode) error {
switch code.Code {
case api.ErrorCode_SUCCESS:
return nil
case api.ErrorCode_WARNING:
return errors.New("Operation succeeded with a warning message: " + code.Message)
case api.ErrorCode_BADAUTH:
return errors.New("Authentication error")
}
if len(code.Message) == 0 {
return errors.New("Received error code " + (code.Code).String())
}
return errors.New("Received error code " + (code.Code).String() + ": " + code.Message)
}
package common
import (
"testing"
"dfss/dfssp/api"
"github.com/bmizerany/assert"
)
func TestEvaluateErrorCodeResponse(t *testing.T) {
success := &api.ErrorCode{
Code: api.ErrorCode_SUCCESS,
Message: "Useless message",
}
err := EvaluateErrorCodeResponse(success)
assert.Equal(t, nil, err)
warning := &api.ErrorCode{
Code: api.ErrorCode_WARNING,
Message: "Useful message",
}
err = EvaluateErrorCodeResponse(warning)
assert.Equal(t, "Operation succeeded with a warning message: Useful message", err.Error())
badauth := &api.ErrorCode{
Code: api.ErrorCode_BADAUTH,
Message: "Useless message",
}
err = EvaluateErrorCodeResponse(badauth)
assert.Equal(t, "Authentication error", err.Error())
other := &api.ErrorCode{
Code: api.ErrorCode_INTERR,
}
err = EvaluateErrorCodeResponse(other)
assert.Equal(t, "Received error code INTERR", err.Error())
otherWithMessage := &api.ErrorCode{
Code: api.ErrorCode_INVARG,
Message: "Invalid mail",
}
err = EvaluateErrorCodeResponse(otherWithMessage)
assert.Equal(t, "Received error code INVARG: Invalid mail", err.Error())
}
......@@ -4,11 +4,7 @@ import (
"dfss"
"flag"
"fmt"
osuser "os/user"
"runtime"
"time"
"dfss/dfssc/user"
)
var (
......@@ -24,8 +20,8 @@ func init() {
flag.BoolVar(&verbose, "v", false, "Print verbose messages")
flag.StringVar(&fca, "ca", "ca.pem", "Path to the root certificate")
flag.StringVar(&fcert, "cert", "cert.pem", "Path to the user certificate")
flag.StringVar(&fkey, "key", "priv_key.pem", "Path to the private key")
flag.StringVar(&addrPort, "host", "127.0.0.1:9000", "Host of the DFSS platform (e.g. 127.0.0.1:9000)")
flag.StringVar(&fkey, "key", "key.pem", "Path to the private key")
flag.StringVar(&addrPort, "host", "localhost:9000", "Host of the DFSS platform")
flag.Usage = func() {
fmt.Println("DFSS client command line v" + dfss.Version)
......@@ -39,6 +35,7 @@ func init() {
fmt.Println(" version print dfss client version")
fmt.Println(" register register a new client")
fmt.Println(" auth authenticate a new client")
fmt.Println(" new create a new contract")
fmt.Println(" show <c> print contract information from file c")
fmt.Println("\nFlags:")
......@@ -57,48 +54,11 @@ func main() {
case "version":
fmt.Println("v"+dfss.Version, runtime.GOOS, runtime.GOARCH)
case "register":
fmt.Println("Registering a new user")
// Initialize variables
var country, mail, organization, unit, passphrase string
var bits int
u, err := osuser.Current()
if err != nil {
fmt.Println("An error occurred : ", err.Error())
break
}
// Get all the necessary parameters
readStringParam("Mail", "", &mail)
readStringParam("Country", time.Now().Location().String(), &country)
readStringParam("Organization", u.Name, &organization)
readStringParam("Organizational unit", u.Name, &unit)
readIntParam("Length of the key (2048 or 4096)", 2048, &bits)
err = readPassword(&passphrase)
if err != nil {
fmt.Println("An error occurred : ", err.Error())
break
}
recapUser(fca, fcert, fkey, addrPort, country, organization, unit, mail, bits)
err = user.Register(fca, fcert, fkey, addrPort, passphrase, country, organization, unit, mail, bits)
if err != nil {
fmt.Println("An error occurred : ", err.Error())
}
case "authenticate":
fmt.Println("Authenticating user")
var mail, token string
readStringParam("Mail", "", &mail)
readStringParam("Token", "", &token)
err := user.Authenticate(fca, fcert, addrPort, mail, token)
if err != nil {
fmt.Println("An error occurred : ", err.Error())
}
registerUser()
case "auth":
authUser()
case "new":
newContract()
case "show":
showContract(flag.Arg(1))
default:
......
package main
import (
"fmt"
"dfss/dfssc/sign"
)
func newContract() {
fmt.Println("Creating a new contract")
passphrase, filepath, comment, signers := getContractInfo()
err := sign.NewCreateManager(fca, fcert, fkey, addrPort, passphrase, filepath, comment, signers)
if err != nil {
fmt.Println(err)
}
}
// getContractInfo asks user for contract informations
func getContractInfo() (passphrase string, path string, comment string, signers []string) {
var signersBuf string
_ = readPassword(&passphrase, false)
readStringParam("Contract path", "", &path)
readStringParam("Comment", "(no comment)", &comment)
readStringParam("Signer 1", "mail@example.com", &signers[0])
i := 2
for {
readStringParam(fmt.Sprintf("Signer %d (return to end)", i), "", &signersBuf)
if len(signersBuf) == 0 {
break
}
signers = append(signers, signersBuf)
i++
}
return
}
package main
import (
"bufio"
"errors"
"fmt"
"os"
osuser "os/user"
"strconv"
"strings"
"time"
"dfss/dfssc/user"
"golang.org/x/crypto/ssh/terminal"
)
func registerUser() {
fmt.Println("Registering a new user")
// Initialize variables
var country, mail, organization, unit, passphrase string
var bits int
u, err := osuser.Current()
if err != nil {
fmt.Println("An error occurred : ", err.Error())
return
}
// Get all the necessary parameters
readStringParam("Mail", "", &mail)
readStringParam("Country", time.Now().Location().String(), &country)
readStringParam("Organization", u.Name, &organization)
readStringParam("Organizational unit", u.Name, &unit)
readIntParam("Length of the key (2048 or 4096)", "2048", &bits)
err = readPassword(&passphrase, true)
if err != nil {
fmt.Println("An error occurred:", err.Error())
return
}
recapUser(mail, country, organization, unit)
err = user.Register(fca, fcert, fkey, addrPort, passphrase, country, organization, unit, mail, bits)
if err != nil {
fmt.Println("An error occurred:", err.Error())
}
}
// Get a string parameter from standard input
func readStringParam(message, def string, ptr *string) {
fmt.Printf("%s [%s]: ", message, def)
fmt.Scanf("%s", ptr)
if *ptr == "" {
fmt.Print(message)
if len(def) > 0 {
fmt.Printf(" [%s]", def)
}
fmt.Print(": ")
reader := bufio.NewReader(os.Stdin)
value, _ := reader.ReadString('\n')
// Trim newline symbols
value = strings.TrimRight(value, "\n")
value = strings.TrimRight(value, "\r")
*ptr = value
if value == "" {
*ptr = def
}
}
// Get an integer parameter from standard input
func readIntParam(message string, def int, ptr *int) {
fmt.Printf("%s [%d]: ", message, def)
fmt.Scanf("%d", ptr)
if *ptr == 0 {
*ptr = def
func readIntParam(message, def string, ptr *int) {
var str string
readStringParam(message, def, &str)
value, err := strconv.Atoi(str)
if err != nil {
*ptr = 0
} else {
*ptr = value
}
}
// Get the password from standard input
func readPassword(ptr *string) error {
func readPassword(ptr *string, needConfirm bool) error {
oldState, err := terminal.MakeRaw(0)
if err != nil {
return err
}
fmt.Println("Enter your passphrase :")
fmt.Print("Enter your passphrase: ")
passphrase, err := terminal.ReadPassword(0)
fmt.Println()
if err != nil {
return err
}
fmt.Println("Confirm your passphrase :")
confirm, err := terminal.ReadPassword(0)
if err != nil {
return err
}
if needConfirm {
fmt.Print("Confirm your passphrase: ")
confirm, err := terminal.ReadPassword(0)
fmt.Println()
if err != nil {
return err
}
if fmt.Sprintf("%s", passphrase) != fmt.Sprintf("%s", confirm) {
return errors.New("Password do not match")
if fmt.Sprintf("%s", passphrase) != fmt.Sprintf("%s", confirm) {
return errors.New("Password do not match")
}
}
*ptr = fmt.Sprintf("%s", passphrase)
_ = terminal.Restore(0, oldState)
return nil
}
func recapUser(fca, fcert, fkey, addrPort, mail, country, organization, unit string, bits int) {
// Recap informations
fmt.Println(fmt.Sprintf("Summary of the new user : Mail : %s; Country : %s; Organization : %s; Organizational unit : %s; bits : %d", mail, country, organization, unit, bits))
fmt.Println(fmt.Sprintf("Address of the platform is %s", addrPort))
fmt.Println(fmt.Sprintf("File storing the CA : %s", fca))
fmt.Println(fmt.Sprintf("File to store the certificate : %s", fcert))
fmt.Println(fmt.Sprintf("File to store the private key : %s", fkey))
func recapUser(mail, country, organization, unit string) {
fmt.Println("Summary of the new user:")
fmt.Println(" Common Name:", mail)
fmt.Println(" Country:", country)
fmt.Println(" Organization:", organization)
fmt.Println(" Organizational unit:", unit)
}
package sign
import (
"crypto/sha512"
"fmt"
"io/ioutil"
"path/filepath"
"time"
"dfss/dfssc/common"
"dfss/dfssc/security"
"dfss/dfssp/api"
"dfss/net"
"golang.org/x/net/context"
)
// 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 string
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 {
m := &CreateManager{
fileCA: fileCA,
fileCert: fileCert,
fileKey: fileKey,
addrPort: addrPort,
passphrase: passphrase,
filepath: filepath,
comment: comment,
signers: signers,
}
err := m.computeFile()
if err != nil {
return err
}
result, err := m.sendRequest()
if err != nil {
return err
}
return common.EvaluateErrorCodeResponse(result)
}
// computeFile computes hash and filename providing the contract filepath
func (m *CreateManager) computeFile() error {
data, err := ioutil.ReadFile(m.filepath)
if err != nil {
return err
}
hash := sha512.Sum512(data)
m.hash = fmt.Sprintf("%x", hash)
m.filename = filepath.Base(m.filepath)
return nil
}
// 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)
if err != nil {
return nil, err
}
conn, err := net.Connect(m.addrPort, cert, key, ca)
if err != nil {
return nil, err
}
request := &api.PostContractRequest{
Hash: m.hash,
Filename: m.filename,
Signer: m.signers,
Comment: m.comment,
}
client := api.NewPlatformClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
response, err := client.PostContract(ctx, request)
if err != nil {
return nil, err
}
return response, nil
}
package sign
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
"dfss/auth"
"dfss/mockp/server"
"github.com/bmizerany/assert"
)
var path = filepath.Join("..", "testdata")
var fca = filepath.Join(path, "ca.pem")
var fcert = filepath.Join(path, "cert.pem")
var fkey = filepath.Join(path, "key.pem")
var fcontract = filepath.Join(path, "contract.txt")
var addrPort = "localhost:9090"
func TestMain(m *testing.M) {
// Load ca and key for platform
caData, err := ioutil.ReadFile(fca)
if err != nil {
os.Exit(1)
}
keyData, err := ioutil.ReadFile(filepath.Join(path, "cakey.pem"))
if err != nil {
os.Exit(1)
}
ca, _ := auth.PEMToCertificate(caData)
key, _ := auth.PEMToPrivateKey(keyData)
// Start the platform mock
go server.Run(ca, key, addrPort)
time.Sleep(2 * time.Second)
os.Exit(m.Run())
}
func TestNewCreateManager(t *testing.T) {
err := NewCreateManager(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"})
assert.Equal(t, "Operation succeeded with a warning message: Some users are not ready yet", err.Error())
}
func TestComputeFile(t *testing.T) {
m := &CreateManager{filepath: fcontract}
err := m.computeFile()
assert.Equal(t, nil, err)
assert.Equal(t, "37fd29decfb2d689439478b1f64b60441534c1e373a7023676c94ac6772639edab46f80139d167a2741f159e62b3064eca58bb331d32cd10770f29064af2a9de", m.hash)
assert.Equal(t, "contract.txt", m.filename)
}
-----BEGIN CERTIFICATE-----
MIIB5TCCAY+gAwIBAgIJAKId2y6Lo9T8MA0GCSqGSIb3DQEBCwUAME0xCzAJBgNV
BAYTAkZSMQ0wCwYDVQQKDARERlNTMRswGQYDVQQLDBJERlNTIFBsYXRmb3JtIHYw
LjExEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0xNjAxMjYxNTM2NTNaGA80NDgwMDMw
ODE1MzY1M1owTTELMAkGA1UEBhMCRlIxDTALBgNVBAoMBERGU1MxGzAZBgNVBAsM
EkRGU1MgUGxhdGZvcm0gdjAuMTESMBAGA1UEAwwJbG9jYWxob3N0MFwwDQYJKoZI
hvcNAQEBBQADSwAwSAJBAMGAgCtkRLePYFRTUN0V/0v/6phm0guHGS6f0TkSEas4
CGZTKFJVTBksMGIBtfyYw3XQx2bO8myeypDN5nV05DcCAwEAAaNQME4wHQYDVR0O
BBYEFO09nxx5/qeLK5Wig1+3kg66gn/mMB8GA1UdIwQYMBaAFO09nxx5/qeLK5Wi
g1+3kg66gn/mMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADQQCqNSH+rt/Z
ru2rkabLiHOGjI+AenSOvqWZ2dWAlLksYcyuQHKwjGWgpmqkiQCnkIDwIxZvu69Y
OBz0ASFn7eym
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAMGAgCtkRLePYFRTUN0V/0v/6phm0guHGS6f0TkSEas4CGZTKFJV
TBksMGIBtfyYw3XQx2bO8myeypDN5nV05DcCAwEAAQJAHSdRKDh5KfbOGqZa3pR7
3GV4YPHM37PBFYc6rJCOXO9W8L4Q1kvEhjKXp7ke18Cge7bVmlKspvxvC62gxSQm
QQIhAPMYwpp29ZREdk8yU65Sp6w+EbZS9TjZkC+pk3syYjaxAiEAy8XWnnDMsUxb
6vp1SaaIfxI441AYzh3+8c56CAvt02cCIQDQ2jfvHz7zyDHg7rsILMkTaSwseW9n
DTwcRtOHZ40LsQIgDWEVAVwopG9+DYSaVNahWa6Jm6szpbzkc136NzMJT3sCIQDv
T2KSQQIYEvPYZmE+1b9f3rs/w7setrGtqVFkm/fTWQ==
-----END RSA PRIVATE KEY-----
package user
import (
"dfss/dfssc/common"
"dfss/dfssc/security"
pb "dfss/dfssp/api"
"errors"
"regexp"
"time"
"dfss/dfssc/common"
"dfss/dfssc/security"
pb "dfss/dfssp/api"
"golang.org/x/net/context"
)
......@@ -111,7 +111,7 @@ func (m *RegisterManager) GetCertificate() error {
return err
}
err = m.evaluateResponse(code)
err = common.EvaluateErrorCodeResponse(code)
if err != nil {
common.DeleteQuietly(m.fileKey)
return err
......@@ -158,12 +158,3 @@ func (m *RegisterManager) sendRequest(certRequest string) (*pb.ErrorCode, error)
return response, nil
}
// Evaluate the errorCode received
func (m *RegisterManager) evaluateResponse(code *pb.ErrorCode) error {
if code.Code != pb.ErrorCode_SUCCESS {
return errors.New("Received error code " + (*code).String() + ", message is " + code.Message)
}
return nil
}
......@@ -70,7 +70,7 @@ func (c *Builder) checkInput() *api.ErrorCode {
return &api.ErrorCode{Code: api.ErrorCode_INVARG, Message: "Expecting a valid filename"}
}
if len(c.in.Hash) != sha512.Size {
if len(c.in.Hash) != sha512.Size*2 { // *2 because string format doubles the length
return &api.ErrorCode{Code: api.ErrorCode_INVARG, Message: "Expecting a valid sha512 hash"}
}
......
......@@ -2,6 +2,7 @@ package contract_test
import (
"crypto/sha512"
"fmt"
"io/ioutil"
"path/filepath"
"testing"
......@@ -17,7 +18,7 @@ import (
var user1, user2, user3 *entities.User
var defaultHash = sha512.Sum512([]byte{0})
var defaultHashStr = string(defaultHash[:sha512.Size])
var defaultHashStr = fmt.Sprintf("%x", defaultHash)
func createDataset() {
......
package fixtures
import (
"dfss/dfssp/api"
)
var CreateFixture map[string]*api.ErrorCode = map[string]*api.ErrorCode{
"success": &api.ErrorCode{
Code: api.ErrorCode_SUCCESS,
},
"warning": &api.ErrorCode{
Code: api.ErrorCode_WARNING,
Message: "Some users are not ready yet",
},
}
......@@ -25,7 +25,6 @@ func (s *mockServer) Register(ctx context.Context, in *api.RegisterRequest) (*ap
return response, nil
}
return fixtures.RegisterFixture["default"], nil
}
// Auth handler
......@@ -47,7 +46,7 @@ func (s *mockServer) Unregister(ctx context.Context, in *api.Empty) (*api.ErrorC
//
// Handle incoming PostContractRequest messages
func (s *mockServer) PostContract(ctx context.Context, in *api.PostContractRequest) (*api.ErrorCode, error) {
return nil, nil
return fixtures.CreateFixture[in.Comment], nil
}
// JoinSignature handler
......@@ -77,7 +76,6 @@ func GetServer(ca *x509.Certificate, pkey *rsa.PrivateKey) *grpc.Server {
func Run(ca *x509.Certificate, pkey *rsa.PrivateKey, addrPort string) {
srv := GetServer(ca, pkey)
err := net.Listen(addrPort, srv)
fmt.Println("Started mock server")
if err != nil {
fmt.Println(err)
}
......
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