Skip to content

Commit

Permalink
Merge pull request #9 from openfort-xyz/fix/migrate-sss-library
Browse files Browse the repository at this point in the history
feat: new sss library
  • Loading branch information
gllm-dev authored Jul 15, 2024
2 parents a8ec245 + f28e0d6 commit 22a7f20
Show file tree
Hide file tree
Showing 28 changed files with 465 additions and 43 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v0.1.11]
## [v0.1.12] - 2024-07-15
### Added
- Using Openfort SSS library to split/reconstruct encryption keys.
- Add `shld_shamir_migrations` to manage the migrations of the Shamir secret sharing library.
- Add Migration Jobs to manage the migrations of the Shamir secret sharing library.


## [v0.1.11] - 2024-07-11
### Added
- Encryption Sessions, allow projects to register a on time use session with an encryption part to encrypt/decrypt a secret.
12 changes: 12 additions & 0 deletions di/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"go.openfort.xyz/shield/internal/adapters/repositories/sql/sharerepo"
"go.openfort.xyz/shield/internal/adapters/repositories/sql/userrepo"
"go.openfort.xyz/shield/internal/applications/projectapp"
"go.openfort.xyz/shield/internal/applications/shamirjob"
"go.openfort.xyz/shield/internal/applications/shareapp"
"go.openfort.xyz/shield/internal/core/ports/factories"
"go.openfort.xyz/shield/internal/core/ports/repositories"
Expand Down Expand Up @@ -137,13 +138,24 @@ func ProvideShareService() (s services.ShareService, err error) {
return
}

func ProvideShamirJob() (j *shamirjob.Job, err error) {
wire.Build(
shamirjob.New,
ProvideSQLProjectRepository,
ProvideSQLShareRepository,
)

return
}

func ProvideShareApplication() (a *shareapp.ShareApplication, err error) {
wire.Build(
shareapp.New,
ProvideShareService,
ProvideSQLShareRepository,
ProvideSQLProjectRepository,
ProvideEncryptionFactory,
ProvideShamirJob,
)

return
Expand Down
20 changes: 19 additions & 1 deletion di/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ require (
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/rtred v0.1.2 // indirect
github.com/tidwall/tinyqueue v0.1.1 // indirect
go.openfort.xyz/shamir-secret-sharing-go v0.0.1 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSR
github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE=
github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.openfort.xyz/shamir-secret-sharing-go v0.0.0-20240711164902-c1a2df301b09 h1:fPnhsnHpX8xztLlMqXU1xM7ohNkUxW5bCkzk0n/e2c0=
go.openfort.xyz/shamir-secret-sharing-go v0.0.0-20240711164902-c1a2df301b09/go.mod h1:EdgAbmmHezcyCO3ucE+JFMCGwcvV9CSoEsQuHkRM9js=
go.openfort.xyz/shamir-secret-sharing-go v0.0.1 h1:/WeytO+6yXSH9op3zU8Eirxyj8UK/0MbhbS32uy+yYQ=
go.openfort.xyz/shamir-secret-sharing-go v0.0.1/go.mod h1:EdgAbmmHezcyCO3ucE+JFMCGwcvV9CSoEsQuHkRM9js=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0=
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package depsssrec

import (
"go.openfort.xyz/shield/internal/core/domain/errors"
"go.openfort.xyz/shield/internal/core/ports/strategies"
"go.openfort.xyz/shield/pkg/cypher"
)

const (
MaxReties = 5
)

type SSSReconstructionStrategy struct{}

func NewSSSReconstructionStrategy() strategies.ReconstructionStrategy {
return &SSSReconstructionStrategy{}
}

func (s *SSSReconstructionStrategy) Split(data string) (storedPart string, projectPart string, err error) {
for i := 0; i < MaxReties; i++ {
storedPart, projectPart, err = cypher.SplitEncryptionKey(data)
if err != nil {
continue
}

err = s.validateSplit(data, storedPart, projectPart)
if err == nil {
return
}
}

return
}

func (s *SSSReconstructionStrategy) Reconstruct(storedPart string, projectPart string) (string, error) {
return cypher.ReconstructEncryptionKey(storedPart, projectPart)
}

func (s *SSSReconstructionStrategy) validateSplit(data string, storedPart string, projectPart string) error {
reconstructed, err := s.Reconstruct(storedPart, projectPart)
if err != nil {
return err
}

if data != reconstructed {
return errors.ErrReconstructedKeyMismatch
}

return nil
}
20 changes: 15 additions & 5 deletions internal/adapters/encryption/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package encryption

import (
aesencryptionstrategy "go.openfort.xyz/shield/internal/adapters/encryption/aes_encryption_strategy"
depsssrec "go.openfort.xyz/shield/internal/adapters/encryption/deprecated_sss_reconstruction_strategy"
plnbldr "go.openfort.xyz/shield/internal/adapters/encryption/plain_builder"
sessbldr "go.openfort.xyz/shield/internal/adapters/encryption/session_builder"
sssrec "go.openfort.xyz/shield/internal/adapters/encryption/sss_reconstruction_strategy"
Expand All @@ -24,19 +25,28 @@ func NewEncryptionFactory(encryptionPartsRepo repositories.EncryptionPartsReposi
}
}

func (e *encryptionFactory) CreateEncryptionKeyBuilder(builderType factories.EncryptionKeyBuilderType) (builders.EncryptionKeyBuilder, error) {
func (e *encryptionFactory) CreateEncryptionKeyBuilder(builderType factories.EncryptionKeyBuilderType, projectMigrated bool) (builders.EncryptionKeyBuilder, error) {
var reconstructionStrategy strategies.ReconstructionStrategy
if projectMigrated {
reconstructionStrategy = sssrec.NewSSSReconstructionStrategy()
} else {
reconstructionStrategy = depsssrec.NewSSSReconstructionStrategy()
}
switch builderType {
case factories.Plain:
return plnbldr.NewEncryptionKeyBuilder(e.projectRepo, sssrec.NewSSSReconstructionStrategy()), nil
return plnbldr.NewEncryptionKeyBuilder(e.projectRepo, reconstructionStrategy), nil
case factories.Session:
return sessbldr.NewEncryptionKeyBuilder(e.encryptionPartsRepo, e.projectRepo, sssrec.NewSSSReconstructionStrategy()), nil
return sessbldr.NewEncryptionKeyBuilder(e.encryptionPartsRepo, e.projectRepo, reconstructionStrategy), nil
}

return nil, errors.ErrInvalidEncryptionKeyBuilderType
}

func (e *encryptionFactory) CreateReconstructionStrategy() strategies.ReconstructionStrategy {
return sssrec.NewSSSReconstructionStrategy()
func (e *encryptionFactory) CreateReconstructionStrategy(projectMigrated bool) strategies.ReconstructionStrategy {
if projectMigrated {
return sssrec.NewSSSReconstructionStrategy()
}
return depsssrec.NewSSSReconstructionStrategy()
}

func (e *encryptionFactory) CreateEncryptionStrategy(key string) strategies.EncryptionStrategy {
Expand Down
8 changes: 8 additions & 0 deletions internal/adapters/encryption/plain_builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ func (b *plainBuilder) SetDatabasePart(ctx context.Context, identifier string) e
return nil
}

func (b *plainBuilder) GetProjectPart(_ context.Context) string {
return b.projectPart
}

func (b *plainBuilder) GetDatabasePart(_ context.Context) string {
return b.databasePart
}

func (b *plainBuilder) Build(_ context.Context) (string, error) {
if b.projectPart == "" {
return "", domainErrors.ErrProjectPartRequired
Expand Down
8 changes: 8 additions & 0 deletions internal/adapters/encryption/session_builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ func (b *sessionBuilder) SetDatabasePart(ctx context.Context, identifier string)
return nil
}

func (b *sessionBuilder) GetProjectPart(_ context.Context) string {
return b.projectPart
}

func (b *sessionBuilder) GetDatabasePart(_ context.Context) string {
return b.databasePart
}

func (b *sessionBuilder) Build(_ context.Context) (string, error) {
if b.projectPart == "" {
return "", domainErrors.ErrProjectPartRequired
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package sssrec

import (
"encoding/base64"

sss "go.openfort.xyz/shamir-secret-sharing-go"
"go.openfort.xyz/shield/internal/core/domain/errors"
"go.openfort.xyz/shield/internal/core/ports/strategies"
"go.openfort.xyz/shield/pkg/cypher"
)

const (
Expand All @@ -16,24 +18,60 @@ func NewSSSReconstructionStrategy() strategies.ReconstructionStrategy {
return &SSSReconstructionStrategy{}
}

func (s *SSSReconstructionStrategy) Split(data string) (storedPart string, projectPart string, err error) {
func (s *SSSReconstructionStrategy) Split(data string) (string, string, error) {
for i := 0; i < MaxReties; i++ {
storedPart, projectPart, err = cypher.SplitEncryptionKey(data)
rawKey, err := base64.StdEncoding.DecodeString(data)
if err != nil {
continue
return "", "", err
}

parts, err := sss.Split(2, 2, rawKey)
if err != nil {
return "", "", err
}

if len(parts) != 2 {
return "", "", errors.ErrFailedToSplitKey
}

storedPart := base64.StdEncoding.EncodeToString(parts[0])
projectPart := base64.StdEncoding.EncodeToString(parts[1])

err = s.validateSplit(data, storedPart, projectPart)
if err == nil {
return
return storedPart, projectPart, nil
}
}

return
return "", "", errors.ErrFailedToSplitKey
}

func (s *SSSReconstructionStrategy) Reconstruct(storedPart string, projectPart string) (string, error) {
return cypher.ReconstructEncryptionKey(storedPart, projectPart)
rawStoredPart, err := base64.StdEncoding.DecodeString(storedPart)
if err != nil {
return "", err
}

// Backward compatibility with old keys
if len(rawStoredPart) == 32 {
rawStoredPart = append([]byte{1}, rawStoredPart...)
}

rawProjectPart, err := base64.StdEncoding.DecodeString(projectPart)
if err != nil {
return "", err
}
// Backward compatibility with old keys
if len(rawProjectPart) == 32 {
rawProjectPart = append([]byte{2}, rawProjectPart...)
}

combined, err := sss.Combine([][]byte{rawStoredPart, rawProjectPart})
if err != nil {
return "", err
}

return base64.StdEncoding.EncodeToString(combined), nil
}

func (s *SSSReconstructionStrategy) validateSplit(data string, storedPart string, projectPart string) error {
Expand Down
10 changes: 10 additions & 0 deletions internal/adapters/repositories/mocks/projectmockrepo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,13 @@ func (m *MockProjectRepository) SetEncryptionPart(ctx context.Context, projectID
args := m.Mock.Called(ctx, projectID, part)
return args.Error(0)
}

func (m *MockProjectRepository) CreateMigration(ctx context.Context, projectID string, success bool) error {
args := m.Mock.Called(ctx, projectID, success)
return args.Error(0)
}

func (m *MockProjectRepository) HasSuccessfulMigration(ctx context.Context, projectID string) (bool, error) {
args := m.Mock.Called(ctx, projectID)
return args.Bool(0), args.Error(1)
}
9 changes: 7 additions & 2 deletions internal/adapters/repositories/mocks/sharemockrepo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ func (m *MockShareRepository) Delete(ctx context.Context, shareID string) error
return args.Error(0)
}

func (m *MockShareRepository) ListDecryptedByProjectID(ctx context.Context, projectID string) ([]*share.Share, error) {
args := m.Mock.Called(ctx, projectID)
func (m *MockShareRepository) ListProjectIDAndEntropy(ctx context.Context, projectID string, entropy share.Entropy) ([]*share.Share, error) {
args := m.Mock.Called(ctx, projectID, entropy)
if args.Get(0) == nil {
return nil, args.Error(1)
}
Expand All @@ -49,3 +49,8 @@ func (m *MockShareRepository) Update(ctx context.Context, shr *share.Share) erro
args := m.Mock.Called(ctx, shr)
return args.Error(0)
}

func (m *MockShareRepository) BulkUpdate(ctx context.Context, shrs []*share.Share) error {
args := m.Mock.Called(ctx, shrs)
return args.Error(0)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS shld_shamir_migrations (
id VARCHAR(36) PRIMARY KEY,
project_id VARCHAR(36) NOT NULL,
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
success BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX idx_project_id_success ON shld_shamir_migrations(project_id, success);
DROP TABLE IF EXISTS shld_allowed_origins;
-- +goose StatementBegin
-- +goose StatementEnd

-- +goose Down
DROP INDEX idx_project_id_success ON shld_shamir_migrations;
DROP TABLE IF EXISTS shld_shamir_migrations;
CREATE TABLE IF NOT EXISTS shld_allowed_origins (
id VARCHAR(36) PRIMARY KEY,
project_id VARCHAR(36) NOT NULL,
origin VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP DEFAULT NULL
);
ALTER TABLE shld_allowed_origins ADD CONSTRAINT fk_origin_project FOREIGN KEY (project_id) REFERENCES shld_projects(id) ON DELETE CASCADE;
-- +goose StatementBegin
-- +goose StatementEnd
Loading

0 comments on commit 22a7f20

Please sign in to comment.