diff --git a/.github/workflows/server_test.yml b/.github/workflows/server_test._yml similarity index 100% rename from .github/workflows/server_test.yml rename to .github/workflows/server_test._yml diff --git a/.gitignore b/.gitignore index 0fe4ba0e..6745c3fe 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,8 @@ gradle.properties -**/chaincli -**/smccli +chaincli +smccli profile.cov report.json diff --git a/server/go.mod b/server/go.mod index 652a991c..ed961a1c 100644 --- a/server/go.mod +++ b/server/go.mod @@ -6,6 +6,7 @@ go 1.21 require ( github.com/gorilla/mux v1.8.1 + github.com/rs/zerolog v1.31.0 github.com/spf13/viper v1.18.1 github.com/steinfletcher/apitest v1.5.15 github.com/stretchr/testify v1.8.4 @@ -42,7 +43,6 @@ require ( github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/rs/xid v1.5.0 // indirect - github.com/rs/zerolog v1.31.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect diff --git a/server/smc/proxy/proxy.go b/server/smc/proxy/proxy.go index 992776e9..9831b47f 100644 --- a/server/smc/proxy/proxy.go +++ b/server/smc/proxy/proxy.go @@ -12,11 +12,8 @@ import ( "net/http" "go.dedis.ch/hbt/server/smc/proxy/types" - "go.dedis.ch/kyber/v3/suites" ) -var suite = suites.MustFind("ed25519") - // NotFoundHandler defines a generic handler for 404 func NotFoundHandler(w http.ResponseWriter, r *http.Request) { err := types.HTTPError{ @@ -59,7 +56,7 @@ func NotAllowedHandler(w http.ResponseWriter, r *http.Request) { // InternalError sets an internal server error func InternalError(w http.ResponseWriter, r *http.Request, err error, args map[string]interface{}) { - setHttpError(w, r, err, http.StatusInternalServerError, "Internal server error", args) + setHTTPError(w, r, err, http.StatusInternalServerError, "Internal server error", args) } // BadRequestError sets an bad request error @@ -69,7 +66,7 @@ func BadRequestError( err error, args map[string]interface{}, ) { - setHttpError(w, r, err, http.StatusBadRequest, "bad request", args) + setHTTPError(w, r, err, http.StatusBadRequest, "bad request", args) } // ForbiddenError sets a forbidden error error @@ -79,15 +76,15 @@ func ForbiddenError( err error, args map[string]interface{}, ) { - setHttpError(w, r, err, http.StatusForbidden, "not authorized / forbidden", args) + setHTTPError(w, r, err, http.StatusForbidden, "not authorized / forbidden", args) } // NotFoundErr sets a not found error func NotFoundErr(w http.ResponseWriter, r *http.Request, err error, args map[string]interface{}) { - setHttpError(w, r, err, http.StatusNotFound, "not found", args) + setHTTPError(w, r, err, http.StatusNotFound, "not found", args) } -func setHttpError( +func setHTTPError( w http.ResponseWriter, r *http.Request, err error, @@ -120,7 +117,7 @@ func setHttpError( // AllowCORS defines a basic handler that adds wide Access Control Allow origin // headers. -func AllowCORS(w http.ResponseWriter, r *http.Request) { +func AllowCORS(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "*") } diff --git a/server/smc/smccli/controller/action.go b/server/smc/smccli/controller/action.go index bf63520a..eb5f6557 100644 --- a/server/smc/smccli/controller/action.go +++ b/server/smc/smccli/controller/action.go @@ -7,7 +7,8 @@ import ( "strings" "go.dedis.ch/dela" - "go.dedis.ch/dela/cli" + "go.dedis.ch/dela/cli/node" + "go.dedis.ch/dela/dkg" "go.dedis.ch/kyber/v3/util/key" "go.dedis.ch/kyber/v3" @@ -22,7 +23,12 @@ const separator = ":" const malformedEncoded = "malformed encoded: %s" const keyFileName = "key.pair" -func createKeyPairAction(_ cli.Flags) error { +// createKeyPairAction is an action to create a key pair +// +// - implements node.ActionTemplate +type createKeyPairAction struct{} + +func (c createKeyPairAction) Execute(_ node.Context) error { kp := key.NewKeyPair(suites.MustFind("Ed25519")) privk, err := kp.Private.MarshalBinary() @@ -53,26 +59,45 @@ func createKeyPairAction(_ cli.Flags) error { return nil } -func revealAction(flags cli.Flags) error { - xhatString := flags.String("xhatenc") +// revealAction is an action to reveal a message +// +// - implements node.ActionTemplate +type revealAction struct{} + +func (r revealAction) Execute(ctx node.Context) error { + xhatString := ctx.Flags.String("xhatenc") xhatenc, err := decodePublicKey(xhatString) if err != nil { - return xerrors.Errorf("failed to reencrypt: %v", err) + return xerrors.Errorf("failed to reveal: %v", err) } - dkgpubString := flags.String("dkgpub") - dkgpubk, err := decodePublicKey(dkgpubString) - if err != nil { - return xerrors.Errorf("failed to decode public key str: %v", err) + dkgpubString := ctx.Flags.String("dkgpub") + var dkgpubk kyber.Point + if dkgpubString != "" { + dkgpubk, err = decodePublicKey(dkgpubString) + if err != nil { + return xerrors.Errorf("failed to decode public key str: %v", err) + } + } else { + var actor dkg.Actor + err := ctx.Injector.Resolve(&actor) + if err != nil { + return xerrors.Errorf("failed to resolve DKG actor: %v", err) + } + + dkgpubk, err = actor.GetPublicKey() + if err != nil { + return xerrors.Errorf("failed retrieving DKG public key: %v", err) + } } - privkString := flags.String("privk") + privkString := ctx.Flags.String("privk") privateKey, err := decodePrivateKey(privkString) if err != nil { return xerrors.Errorf("failed to decode private key str: %v", err) } - encrypted := flags.String("encrypted") + encrypted := ctx.Flags.String("encrypted") _, cs, err := decodeEncrypted(encrypted) if err != nil { return xerrors.Errorf("failed to decode encrypted str: %v", err) diff --git a/server/smc/smccli/controller/controller.go b/server/smc/smccli/controller/controller.go index 16803a1c..980abb12 100644 --- a/server/smc/smccli/controller/controller.go +++ b/server/smc/smccli/controller/controller.go @@ -20,7 +20,7 @@ func (s smcctl) SetCommands(builder node.Builder) { sub := cmd.SetSubCommand("createkeys") sub.SetDescription("create key pair for reencryption") - sub.SetAction(createKeyPairAction) + sub.SetAction(builder.MakeAction(createKeyPairAction{})) sub = cmd.SetSubCommand("reveal") sub.SetDescription("reveal a reencrypted message") @@ -30,8 +30,9 @@ func (s smcctl) SetCommands(builder node.Builder) { Usage: "the reencrypted key as ", }, cli.StringFlag{ - Name: "dkgpub", - Usage: "the DKG public key as ", + Name: "dkgpub", + Usage: "the DKG public key as ", + Required: false, }, cli.StringFlag{ Name: "encrypted", @@ -42,7 +43,7 @@ func (s smcctl) SetCommands(builder node.Builder) { Usage: "drop me if you can", }, ) - sub.SetAction(revealAction) + sub.SetAction(builder.MakeAction(revealAction{})) } // OnStart implements node.Initializer. It creates and registers a pedersen DKG. diff --git a/server/smc/smccli/sproxy/action.go b/server/smc/smccli/sproxy/action.go new file mode 100644 index 00000000..e6335b2d --- /dev/null +++ b/server/smc/smccli/sproxy/action.go @@ -0,0 +1,228 @@ +package sproxy + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/gorilla/mux" + "github.com/rs/zerolog/log" + "go.dedis.ch/dela" + "go.dedis.ch/dela/cli/node" + "go.dedis.ch/dela/dkg" + "go.dedis.ch/dela/mino/proxy" + eproxy "go.dedis.ch/hbt/server/smc/proxy" + "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/suites" + + "golang.org/x/xerrors" +) + +// suite is the Kyber suite for Pedersen. +var suite = suites.MustFind("Ed25519") + +const separator = ":" +const malformedEncoded = "malformed encoded: %s" + +/* +const ( + smcPath = "/smc" + smcPubKeyPath = "/smc/pubkey" + smcReencryptPath = "/smc/reencrypt" + resolveActorFailed = "failed to resolve actor, did you call listen?: %v" +) +*/ + +// RegisterAction is an action to register the HTTP handlers +// +// - implements node.ActionTemplate +type RegisterAction struct{} + +// Execute implements node.ActionTemplate. It registers the handlers using the +// default proxy from the the injector. +func (a *RegisterAction) Execute(ctx node.Context) error { + var p proxy.Proxy + err := ctx.Injector.Resolve(&p) + if err != nil { + return xerrors.Errorf("failed to resolve proxy: %v", err) + } + + router := mux.NewRouter() + + pk := &pubKeyHandler{&ctx} + router.HandleFunc("/smc/pubkey", pk.ServeHTTP).Methods("GET") + + re := &reencryptHandler{&ctx} + router.HandleFunc("/smc/reencrypt", re.ServeHTTP).Methods("POST") + + router.NotFoundHandler = http.HandlerFunc(eproxy.NotFoundHandler) + router.MethodNotAllowedHandler = http.HandlerFunc(eproxy.NotAllowedHandler) + + p.RegisterHandler("/smc/", router.ServeHTTP) + + dela.Logger.Info().Msg("proxy handlers registered") + + return nil +} + +type pubKeyHandler struct { + ctx *node.Context +} + +func (h *pubKeyHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { + + c := *(h.ctx) + + var actor dkg.Actor + err := c.Injector.Resolve(&actor) + if err != nil { + http.Error(w, fmt.Sprintf("failed to resolve DKG actor: %v", err), + http.StatusInternalServerError) + return + } + + pk, err := actor.GetPublicKey() + if err != nil { + http.Error(w, fmt.Sprintf("failed retrieving public key: %v", err), + http.StatusInternalServerError) + return + } + + b, err := pk.MarshalBinary() + if err != nil { + http.Error(w, fmt.Sprintf("failed to marshal public key: %v", err), + http.StatusInternalServerError) + return + } + + encoder := json.NewEncoder(w) + err = encoder.Encode(b) + if err != nil { + http.Error(w, fmt.Sprintf("failed to respond: %v", err), + http.StatusInternalServerError) + return + } +} + +type reencryptHandler struct { + ctx *node.Context +} + +func (h *reencryptHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + c := *(h.ctx) + + var actor dkg.Actor + err := c.Injector.Resolve(&actor) + if err != nil { + http.Error(w, fmt.Sprintf("failed to resolve DKG actor: %v", err), + http.StatusInternalServerError) + return + } + + err = r.ParseForm() + if err != nil { + log.Err(err).Msg("failed to parse form") + http.Error(w, "failed to parse form", http.StatusInternalServerError) + return + } + + // XHATENC=$(smccli --config /tmp/smc1 dkg reencrypt --encrypted ${CIPHER} --pubk ${PUBK}) + + // retrieve the public key + pubkString := r.FormValue("pubk") + pubk, err := decodePublicKey(pubkString) + if err != nil { + http.Error(w, fmt.Sprintf("failed to decode public key str: %v", err), + http.StatusInternalServerError) + return + } + + // retriev the encrypted cypher + encrypted := r.FormValue("encrypted") + k, _, err := decodeEncrypted(encrypted) + if err != nil { + http.Error(w, fmt.Sprintf("failed to decode encrypted str: %v", err), + http.StatusInternalServerError) + return + } + + // re-encrypt the message + hatenc, err := actor.Reencrypt(k, pubk) + if err != nil { + http.Error(w, fmt.Sprintf("failed to re-encrypt: %v", err), + http.StatusInternalServerError) + return + } + + // write back the re-encrypted message + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + encoder := json.NewEncoder(w) + err = encoder.Encode(hatenc) + if err != nil { + http.Error(w, fmt.Sprintf("failed to encode response: %v", err), + http.StatusInternalServerError) + return + } + + dela.Logger.Debug().Msgf("Re-encrypted message: %v", hatenc) +} + +// ----------------------------------------------------------------------------- +// Helper functions +func decodePublicKey(str string) (kyber.Point, error) { + pkbuff, err := hex.DecodeString(str) + if err != nil { + return nil, xerrors.Errorf(malformedEncoded, str) + } + + pk := suite.Point() + err = pk.UnmarshalBinary(pkbuff) + if err != nil { + return nil, xerrors.Errorf("failed to unmarshal pk: %v", err) + } + + return pk, nil +} + +func decodeEncrypted(str string) (kyber.Point, []kyber.Point, error) { + parts := strings.Split(str, separator) + if len(parts) < 2 { + return nil, nil, xerrors.Errorf(malformedEncoded, str) + } + + // Decode K + kbuff, err := hex.DecodeString(parts[0]) + if err != nil { + return nil, nil, xerrors.Errorf("failed to decode k point: %v", err) + } + + k := suite.Point() + err = k.UnmarshalBinary(kbuff) + if err != nil { + return nil, nil, xerrors.Errorf("failed to unmarshal k point: %v", err) + } + + // Decode Cs + cs := make([]kyber.Point, 0, len(parts)-1) + for _, p := range parts[1:] { + cbuff, err := hex.DecodeString(p) + if err != nil { + return nil, nil, xerrors.Errorf("failed to decode c point: %v", err) + } + + c := suite.Point() + err = c.UnmarshalBinary(cbuff) + if err != nil { + return nil, nil, xerrors.Errorf("failed to unmarshal c point: %v", err) + } + + cs = append(cs, c) + } + + dela.Logger.Debug().Msgf("Decoded K: %v and Cs: %v", k, cs) + + return k, cs, nil +} diff --git a/server/smc/smccli/sproxy/sproxy.go b/server/smc/smccli/sproxy/sproxy.go new file mode 100644 index 00000000..94a8b5ab --- /dev/null +++ b/server/smc/smccli/sproxy/sproxy.go @@ -0,0 +1,87 @@ +package sproxy + +import ( + "os" + "time" + + "go.dedis.ch/dela" + "go.dedis.ch/dela/cli" + "go.dedis.ch/dela/cli/node" + "go.dedis.ch/dela/mino/proxy" + "go.dedis.ch/dela/mino/proxy/http" + "golang.org/x/xerrors" +) + +var defaultRetry = 10 +var proxyFac func(string) proxy.Proxy = http.NewHTTP + +const defaultProxyAddr = "127.0.0.1:0" + +// NewController returns a new controller initializer +func NewController() node.Initializer { + return controller{} +} + +// controller is an initializer with a set of commands. +// +// - implements node.Initializer +type controller struct{} + +// Build implements node.Initializer. +func (m controller) SetCommands(builder node.Builder) { + builder.SetStartFlags( + cli.StringFlag{ + Name: "proxyaddr", + Usage: "the proxy address", + Required: false, + Value: defaultProxyAddr, + }, + ) +} + +// OnStart implements node.Initializer. It creates and registers a pedersen DKG. +func (m controller) OnStart(ctx cli.Flags, inj node.Injector) error { + dela.Logger.Info().Msg("Installing SMC proxy") + + proxyAddr := ctx.String("proxyaddr") + + proxyhttp := proxyFac(proxyAddr) + + inj.Inject(proxyhttp) + + go proxyhttp.Listen() + + for i := 0; i < defaultRetry && proxyhttp.GetAddr() == nil; i++ { + time.Sleep(time.Second) + } + + if proxyhttp.GetAddr() == nil { + return xerrors.Errorf("failed to start proxy server") + } + + // We assume the listen worked proprely, however it might not be the case. + // The log should inform the user about that. + dela.Logger.Info().Msgf("started proxy server on %s", proxyhttp.GetAddr().String()) + + // + // Register the smc proxy handlers + // + + register := RegisterAction{} + err := register.Execute(node.Context{ + Injector: inj, + Flags: node.FlagSet{}, + Out: os.Stdout, + }) + + if err != nil { + return xerrors.Errorf("failed to register evoting handlers: %v", err) + } + + return nil +} + +// OnStop implements node.Initializer. +func (controller) OnStop(_ node.Injector) error { + return nil +}