diff --git a/go.mod b/go.mod index 85b4922..96c034a 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/andybalholm/brotli v1.0.5 // indirect github.com/caarlos0/env/v8 v8.0.0 // indirect github.com/gofiber/fiber/v2 v2.51.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.0 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/google/uuid v1.4.0 // indirect github.com/klauspost/compress v1.16.7 // indirect diff --git a/go.sum b/go.sum index 3981795..d1d2a42 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4T github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ= github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U= +github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= +github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= diff --git a/internal/app/app.go b/internal/app/app.go index c0e0a18..77fafcf 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -7,6 +7,7 @@ import ( "codeword/internal/repository" httpserver "codeword/internal/server/http" "codeword/internal/services" + "codeword/internal/utils/encrypt" "context" "go.uber.org/zap" "time" @@ -21,9 +22,15 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error { return err } + encryptService := encrypt.New(&encrypt.EncryptDeps{ + PublicKey: cfg.PublicCurveKey, + PrivateKey: cfg.PrivateCurveKey, + SignSecret: cfg.SignSecret, + }) + userRepo := repository.NewUserRepository(mdb) recoveryEmailSender := &client.RecoveryEmailSender{} - recoveryService := services.NewRecoveryService(logger, userRepo, recoveryEmailSender) + recoveryService := services.NewRecoveryService(logger, userRepo, recoveryEmailSender, encryptService) recoveryController := controller.NewRecoveryController(logger, recoveryService) server := httpserver.NewServer(httpserver.ServerConfig{ diff --git a/internal/initialize/config.go b/internal/initialize/config.go index dec81f5..79d5870 100644 --- a/internal/initialize/config.go +++ b/internal/initialize/config.go @@ -5,15 +5,18 @@ import ( ) type Config struct { - AppName string `env:"APP_NAME" envDefault:"codeword"` - HTTPHost string `env:"HTTP_HOST" envDefault:"localhost"` - HTTPPort string `env:"HTTP_PORT" envDefault:"3000"` - MongoHost string `env:"MONGO_HOST" envDefault:"localhost"` - MongoPort string `env:"MONGO_PORT" envDefault:"27017"` - MongoUser string `env:"MONGO_USER" envDefault:"admin"` - MongoPassword string `env:"MONGO_PASSWORD" envDefault:"admin"` - MongoDatabase string `env:"MONGO_DB" envDefault:"codeword_db"` - MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"` + AppName string `env:"APP_NAME" envDefault:"codeword"` + HTTPHost string `env:"HTTP_HOST" envDefault:"localhost"` + HTTPPort string `env:"HTTP_PORT" envDefault:"3000"` + MongoHost string `env:"MONGO_HOST" envDefault:"localhost"` + MongoPort string `env:"MONGO_PORT" envDefault:"27017"` + MongoUser string `env:"MONGO_USER" envDefault:"admin"` + MongoPassword string `env:"MONGO_PASSWORD" envDefault:"admin"` + MongoDatabase string `env:"MONGO_DB" envDefault:"codeword_db"` + MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"` + PublicCurveKey string `env:"PUBLIC_CURVE_KEY,required"` + PrivateCurveKey string `env:"PRIVATE_CURVE_KEY,required"` + SignSecret string `env:"SIGN_SECRET,required"` } func LoadConfig() (*Config, error) { diff --git a/internal/services/recovery_service.go b/internal/services/recovery_service.go index 679ffa3..19c1163 100644 --- a/internal/services/recovery_service.go +++ b/internal/services/recovery_service.go @@ -2,6 +2,7 @@ package services import ( "codeword/internal/models" + "codeword/internal/utils/encrypt" "go.uber.org/zap" "time" ) @@ -16,17 +17,21 @@ type EmailSender interface { SendRecoveryEmail(email, signature string) error } +// todo deps + type RecoveryService struct { - Logger *zap.Logger - Repository UserRepository - Email EmailSender + Logger *zap.Logger + Repository UserRepository + Email EmailSender + EncryptService *encrypt.Encrypt } -func NewRecoveryService(logger *zap.Logger, repository UserRepository, email EmailSender) *RecoveryService { +func NewRecoveryService(logger *zap.Logger, repository UserRepository, email EmailSender, encryptService *encrypt.Encrypt) *RecoveryService { return &RecoveryService{ - Logger: logger, - Repository: repository, - Email: email, + Logger: logger, + Repository: repository, + Email: email, + EncryptService: encryptService, } } diff --git a/internal/utils/encrypt/encrypt_util.go b/internal/utils/encrypt/encrypt_util.go new file mode 100644 index 0000000..36695e3 --- /dev/null +++ b/internal/utils/encrypt/encrypt_util.go @@ -0,0 +1,79 @@ +package encrypt + +import ( + "crypto/ed25519" + "crypto/x509" + "encoding/pem" + "fmt" +) + +type EncryptDeps struct { + PublicKey string + PrivateKey string + SignSecret string +} + +type Encrypt struct { + publicKey string + privateKey string + signSecret string +} + +func New(deps *EncryptDeps) *Encrypt { + return &Encrypt{ + publicKey: deps.PublicKey, + privateKey: deps.PrivateKey, + signSecret: deps.SignSecret, + } +} + +func (receiver *Encrypt) VerifySignature(signature []byte) (isValid bool, err error) { + defer func() { + if recovered := recover(); recovered != nil { + err = fmt.Errorf("recovered verify error on of : %v", recovered) + } + }() + + block, _ := pem.Decode([]byte(receiver.publicKey)) + if block == nil { + return false, fmt.Errorf("public key block is nil") + } + + rawPublicKey, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return false, fmt.Errorf("failed parse public key on of : %w", err) + } + + publicKey, ok := rawPublicKey.(ed25519.PublicKey) + if !ok { + return false, fmt.Errorf("failed convert to ed25519.PrivateKey on of : %w", err) + } + + return ed25519.Verify(publicKey, []byte(receiver.signSecret), signature), nil +} + +func (receiver *Encrypt) SignCommonSecret() (signature []byte, err error) { + defer func() { + if recovered := recover(); recovered != nil { + fmt.Printf("recovered sign error: \n%+v\n", receiver) + err = fmt.Errorf("recovered sign error on of : %v", recovered) + } + }() + + block, _ := pem.Decode([]byte(receiver.privateKey)) + if block == nil { + return []byte{}, fmt.Errorf("failed decode private key %s on of : %w", receiver.privateKey, err) + } + + rawPrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return []byte{}, fmt.Errorf("failed parse private key on of : %w", err) + } + + privateKey, ok := rawPrivateKey.(ed25519.PrivateKey) + if !ok { + return []byte{}, fmt.Errorf("failed convert to ed25519.PrivateKey on of : %w", err) + } + + return ed25519.Sign(privateKey, []byte(receiver.signSecret)), nil +}