Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

german torres signing service challenge solution #17

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
99 changes: 99 additions & 0 deletions signing-service-challenge-go/api/device.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,102 @@
package api

import (
"encoding/json"
"net/http"
"signing-service-challenge/domain"
"signing-service-challenge/helper"
"signing-service-challenge/service"

"github.com/gorilla/mux"
)

type DeviceHandler struct {
deviceService service.DeviceService
}

func NewDeviceHandler(deviceService service.DeviceService) *DeviceHandler {
return &DeviceHandler{deviceService: deviceService}
}

type CreateSignatureDeviceRequest struct {
Algorithm string `json:"algorithm"`
Label string `json:"label"`
}

type CreateSignatureDeviceResponse struct {
Algorithm string `json:"algorithm"`
Label string `json:"label"`
}

// TODO: REST endpoints ...
func (s *DeviceHandler) CreateSignatureDevice(w http.ResponseWriter, r *http.Request) {
var req CreateSignatureDeviceRequest
var device *domain.Device
w.Header().Set("Content-type", "application/json")
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
WriteErrorResponse(w, http.StatusBadRequest, []string{"Invalid Payload."})
}

if req.Algorithm == "" || req.Label == "" {
WriteErrorResponse(w, http.StatusBadRequest, []string{"Missing required field in payload: algorithm, label."})
return
}

device, err := s.deviceService.CreateDevice(req.Label, domain.AlgorithmType(req.Algorithm))
if err != nil {
code, msg := helper.HandleDeviceServiceError(err)
WriteErrorResponse(w, code, []string{msg})
return
}

response := CreateSignatureDeviceResponse{
Label: device.Label,
Algorithm: string(device.Algorithm),
}

WriteAPIResponse(w, http.StatusCreated, response)

}

func (s *DeviceHandler) ListDevices(w http.ResponseWriter, r *http.Request) {
devices, err := s.deviceService.ListDevices()
if err != nil {
WriteErrorResponse(w, http.StatusInternalServerError, []string{"Failed to retrieve list of devices"})
return
}

response := make([]DeviceListResponse, len(devices))
for i, device := range devices {
response[i] = DeviceListResponse{
Id: device.Id,
Label: device.Label,
Algorithm: string(device.Algorithm),
}
}
WriteAPIResponse(w, http.StatusOK, response)
}

func (s *DeviceHandler) GetDeviceById(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
deviceId := vars["deviceId"]

if deviceId == "" {
WriteErrorResponse(w, http.StatusBadRequest, []string{"Missing required field in parameters: deviceId."})
return
}

device, err := s.deviceService.GetDeviceById(deviceId)
if err != nil {
code, msg := helper.HandleDeviceServiceError(err)
WriteErrorResponse(w, code, []string{msg})
return
}

response := DeviceByIdResponse{
Id: device.Id,
Label: device.Label,
Algorithm: string(device.Algorithm),
SignatureCounter: device.SignatureCounter,
}
WriteAPIResponse(w, http.StatusOK, response)
}
14 changes: 14 additions & 0 deletions signing-service-challenge-go/api/dto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package api

type DeviceListResponse struct {
Id string `json:"id"`
Label string `json:"label"`
Algorithm string `json:"algorithm"`
}

type DeviceByIdResponse struct {
Id string `json:"id"`
Label string `json:"label"`
Algorithm string `json:"algorithm"`
SignatureCounter int `json:"signature_counter"`
}
26 changes: 20 additions & 6 deletions signing-service-challenge-go/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package api
import (
"encoding/json"
"net/http"

"github.com/gorilla/mux"
)

// Response is the generic API response container.
Expand All @@ -17,26 +19,38 @@ type ErrorResponse struct {

// Server manages HTTP requests and dispatches them to the appropriate services.
type Server struct {
listenAddress string
listenAddress string
deviceHandler *DeviceHandler
transactionHandler *TransactionHandler
}

// NewServer is a factory to instantiate a new Server.
func NewServer(listenAddress string) *Server {
func NewServer(listenAddress string, deviceServiceHanlder *DeviceHandler, transactionHandler *TransactionHandler) *Server {

return &Server{
listenAddress: listenAddress,
listenAddress: listenAddress,
transactionHandler: transactionHandler,
deviceHandler: deviceServiceHanlder,
// TODO: add services / further dependencies here ...
}
}

// Run registers all HandlerFuncs for the existing HTTP routes and starts the Server.
func (s *Server) Run() error {
mux := http.NewServeMux()
router := mux.NewRouter()

mux.Handle("/api/v0/health", http.HandlerFunc(s.Health))
router.Handle("/api/v0/health", http.HandlerFunc(s.Health)).Methods("GET")

// TODO: register further HandlerFuncs here ...
//Device Handler
router.Handle("/api/v0/devices/create-device", http.HandlerFunc(s.deviceHandler.CreateSignatureDevice)).Methods("POST")
router.Handle("/api/v0/devices/list-devices", http.HandlerFunc(s.deviceHandler.ListDevices)).Methods("GET")
router.Handle("/api/v0/devices/{deviceId}", http.HandlerFunc(s.deviceHandler.GetDeviceById)).Methods("GET")

//Transaction Handler
router.Handle("/api/v0/transactions/{deviceId}/sign", http.HandlerFunc(s.transactionHandler.SignTransactionHandler)).Methods("POST")

return http.ListenAndServe(s.listenAddress, mux)
return http.ListenAndServe(s.listenAddress, router)
}

// WriteInternalError writes a default internal error message as an HTTP response.
Expand Down
55 changes: 55 additions & 0 deletions signing-service-challenge-go/api/transaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package api

import (
"encoding/base64"
"encoding/json"
"net/http"
"signing-service-challenge/helper"
"signing-service-challenge/service"

"github.com/gorilla/mux"
)

type TransactionHandler struct {
transactionService service.TransactionService
}

func NewTransactionHandler(transactionService service.TransactionService) *TransactionHandler {
return &TransactionHandler{transactionService: transactionService}
}

type SignTransactionRequest struct {
Data string
}

type SignTransacitonResponse struct {
Signature string
Signed_data string
}

func (s *TransactionHandler) SignTransactionHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var req SignTransactionRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
WriteErrorResponse(w, http.StatusBadRequest, []string{"Invalid Payload."})
return
}

if vars["deviceId"] == "" || req.Data == "" {
WriteErrorResponse(w, http.StatusBadRequest, []string{"Missing required field in payload: deviceId, data."})
return
}

signature, dataToBeSigned, err := s.transactionService.SignTransaction(vars["deviceId"], req.Data)
if err != nil {
code, msg := helper.HandleDeviceServiceError(err)
WriteErrorResponse(w, code, []string{msg})
return
}

response := SignTransacitonResponse{
Signature: base64.RawStdEncoding.EncodeToString(signature),
Signed_data: string(dataToBeSigned),
}
WriteAPIResponse(w, http.StatusOK, response)
}
15 changes: 7 additions & 8 deletions signing-service-challenge-go/crypto/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,22 @@ import (
"encoding/pem"
)

type ECCMarshaler interface {
Encode(keyPair ECCKeyPair) ([]byte, []byte, error)
}

// ECCKeyPair is a DTO that holds ECC private and public keys.
type ECCKeyPair struct {
Public *ecdsa.PublicKey
Private *ecdsa.PrivateKey
}

// ECCMarshaler can encode and decode an ECC key pair.
type ECCMarshaler struct{}

// NewECCMarshaler creates a new ECCMarshaler.
func NewECCMarshaler() ECCMarshaler {
return ECCMarshaler{}
}
type DefaultECCMarshaler struct{}

// Encode takes an ECCKeyPair and encodes it to be written on disk.
// It returns the public and the private key as a byte slice.
func (m ECCMarshaler) Encode(keyPair ECCKeyPair) ([]byte, []byte, error) {
func (m DefaultECCMarshaler) Encode(keyPair ECCKeyPair) ([]byte, []byte, error) {
privateKeyBytes, err := x509.MarshalECPrivateKey(keyPair.Private)
if err != nil {
return nil, nil, err
Expand All @@ -47,7 +46,7 @@ func (m ECCMarshaler) Encode(keyPair ECCKeyPair) ([]byte, []byte, error) {
}

// Decode assembles an ECCKeyPair from an encoded private key.
func (m ECCMarshaler) Decode(privateKeyBytes []byte) (*ECCKeyPair, error) {
func (m DefaultECCMarshaler) Decode(privateKeyBytes []byte) (*ECCKeyPair, error) {
block, _ := pem.Decode(privateKeyBytes)
privateKey, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
Expand Down
28 changes: 28 additions & 0 deletions signing-service-challenge-go/crypto/ecdsa_generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package crypto

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
)

type ECCGenerator interface {
Generate() (*ECCKeyPair, error)
}

// ECCGenerator generates an ECC key pair.
type DefaultECCGenerator struct{}

// Generate generates a new ECCKeyPair.
func (g *DefaultECCGenerator) Generate() (*ECCKeyPair, error) {
// Security has been ignored for the sake of simplicity.
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
return nil, err
}

return &ECCKeyPair{
Public: &key.PublicKey,
Private: key,
}, nil
}
39 changes: 39 additions & 0 deletions signing-service-challenge-go/crypto/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package crypto

import "fmt"

// SignOperationError definition
type SignOperationError struct {
Algorithm string
Err error
}

func (e *SignOperationError) Error() string {
return fmt.Sprintf("error while signing the data with algorithm %s: %s", e.Algorithm, e.Err)
}

func (e *SignOperationError) Unwrap() error { //Unwrap included for logging purposes
return e.Err
}

func NewSignOperationError(algorithm string, err error) *SignOperationError {
return &SignOperationError{Algorithm: algorithm, Err: err}
}

// MarshalError definition
type MarshalError struct {
Algorithm string
Err error
}

func (e *MarshalError) Error() string {
return fmt.Sprintf("error while marshaling the data with the algorithm %s: %s", e.Algorithm, e.Err)
}

func (e *MarshalError) Unwrap() error { //Unwrap included for logging purposes
return e.Err
}

func NewMarshalError(algorithm string, err error) *MarshalError {
return &MarshalError{Algorithm: algorithm, Err: err}
}
42 changes: 0 additions & 42 deletions signing-service-challenge-go/crypto/generation.go

This file was deleted.

Loading