Skip to content

Commit

Permalink
implement service AES key management [#677]
Browse files Browse the repository at this point in the history
  • Loading branch information
roberlander2 committed Oct 4, 2023
1 parent a441ec4 commit d35f706
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 896 deletions.
40 changes: 23 additions & 17 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@
{
"path": "detect_secrets.filters.allowlist.is_line_allowlisted"
},
{
"path": "detect_secrets.filters.common.is_baseline_file",
"filename": ".secrets.baseline"
},
{
"path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
"min_level": 2
Expand Down Expand Up @@ -109,12 +105,6 @@
},
{
"path": "detect_secrets.filters.heuristic.is_templated_secret"
},
{
"path": "detect_secrets.filters.regex.should_exclude_file",
"pattern": [
"go.sum"
]
}
],
"results": {
Expand Down Expand Up @@ -149,30 +139,37 @@
{
"type": "Secret Keyword",
"filename": "core/auth/auth.go",
"hashed_secret": "417355fe2b66baa6826739a6d8006ab2ddcf5186",
"hashed_secret": "0f0d45ff859e02a769ee29a57b3307ab355c3c98",
"is_verified": false,
"line_number": 154
"line_number": 159
},
{
"type": "Secret Keyword",
"filename": "core/auth/auth.go",
"hashed_secret": "bbdb97274c94b9605a766e317fca26186c34c510",
"is_verified": false,
"line_number": 156
"line_number": 161
},
{
"type": "Secret Keyword",
"filename": "core/auth/auth.go",
"hashed_secret": "58f3388441fbce0e48aef2bf74413a6f43f6dc70",
"is_verified": false,
"line_number": 936
"line_number": 946
},
{
"type": "Secret Keyword",
"filename": "core/auth/auth.go",
"hashed_secret": "44e17306b837162269a410204daaa5ecee4ec22c",
"is_verified": false,
"line_number": 2387
},
{
"type": "Secret Keyword",
"filename": "core/auth/auth.go",
"hashed_secret": "94a7f0195bbbd2260c4e4d02b6348fbcd90b2b30",
"is_verified": false,
"line_number": 2442
"line_number": 2515
}
],
"core/auth/auth_type_email.go": [
Expand Down Expand Up @@ -312,7 +309,7 @@
"filename": "driven/storage/database.go",
"hashed_secret": "6547f385c6d867e20f8217018a4d468a7d67d638",
"is_verified": false,
"line_number": 216
"line_number": 224
}
],
"driver/web/apis_system.go": [
Expand Down Expand Up @@ -368,7 +365,16 @@
"is_verified": false,
"line_number": 24
}
],
"main.go": [
{
"type": "Secret Keyword",
"filename": "main.go",
"hashed_secret": "44e17306b837162269a410204daaa5ecee4ec22c",
"is_verified": false,
"line_number": 123
}
]
},
"generated_at": "2023-10-03T22:07:15Z"
"generated_at": "2023-10-04T21:47:50Z"
}
95 changes: 84 additions & 11 deletions core/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"core-building-block/core/model"
"core-building-block/driven/storage"
"core-building-block/utils"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
Expand Down Expand Up @@ -58,6 +59,8 @@ const (
// UpdateScopesPermission is the permission that allows an admin to update account/role scopes
UpdateScopesPermission string = "update_auth_scopes"

serviceAESKey string = "service_aes_key"

typeMail logutils.MessageDataType = "mail"
typeExternalAuthType logutils.MessageDataType = "external auth type"
typeAnonymousAuthType logutils.MessageDataType = "anonymous auth type"
Expand Down Expand Up @@ -94,7 +97,8 @@ type Auth struct {
serviceAuthTypes map[string]serviceAuthType
mfaTypes map[string]mfaType

authPrivKey *keys.PrivKey
currentAuthPrivKey *keys.PrivKey
oldAuthPrivKey *keys.PrivKey

ServiceRegManager *authservice.ServiceRegManager
SignatureAuth *sigauth.SignatureAuth
Expand Down Expand Up @@ -122,8 +126,9 @@ type Auth struct {
}

// NewAuth creates a new auth instance
func NewAuth(serviceID string, host string, authPrivKey *keys.PrivKey, authService *authservice.AuthService, storage Storage, emailer Emailer, minTokenExp *int64, maxTokenExp *int64, supportLegacySigs bool, twilioAccountSID string,
twilioToken string, twilioServiceSID string, profileBB ProfileBuildingBlock, smtpHost string, smtpPortNum int, smtpUser string, smtpPassword string, smtpFrom string, logger *logs.Logger) (*Auth, error) {
func NewAuth(serviceID string, host string, currentAuthPrivKey *keys.PrivKey, oldAuthPrivKey *keys.PrivKey, authService *authservice.AuthService, storage Storage,
emailer Emailer, minTokenExp *int64, maxTokenExp *int64, supportLegacySigs bool, twilioAccountSID string, twilioToken string, twilioServiceSID string,
profileBB ProfileBuildingBlock, smtpHost string, smtpPortNum int, smtpUser string, smtpPassword string, smtpFrom string, logger *logs.Logger) (*Auth, error) {
if minTokenExp == nil {
var minTokenExpVal int64 = 5
minTokenExp = &minTokenExpVal
Expand Down Expand Up @@ -151,11 +156,16 @@ func NewAuth(serviceID string, host string, authPrivKey *keys.PrivKey, authServi
deleteSessionsTimerDone := make(chan bool)

auth := &Auth{storage: storage, emailer: emailer, logger: logger, authTypes: authTypes, externalAuthTypes: externalAuthTypes, anonymousAuthTypes: anonymousAuthTypes,
serviceAuthTypes: serviceAuthTypes, mfaTypes: mfaTypes, authPrivKey: authPrivKey, ServiceRegManager: nil, serviceID: serviceID, host: host, minTokenExp: *minTokenExp,
maxTokenExp: *maxTokenExp, profileBB: profileBB, cachedIdentityProviders: cachedIdentityProviders, identityProvidersLock: identityProvidersLock,
serviceAuthTypes: serviceAuthTypes, mfaTypes: mfaTypes, currentAuthPrivKey: currentAuthPrivKey, oldAuthPrivKey: oldAuthPrivKey, ServiceRegManager: nil, serviceID: serviceID,
host: host, minTokenExp: *minTokenExp, maxTokenExp: *maxTokenExp, profileBB: profileBB, cachedIdentityProviders: cachedIdentityProviders, identityProvidersLock: identityProvidersLock,
apiKeys: apiKeys, apiKeysLock: apiKeysLock, deleteSessionsTimerDone: deleteSessionsTimerDone, emailDialer: emailDialer, emailFrom: smtpFrom}

err := auth.storeCoreRegs()
err := auth.storeServiceAESKey()
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionSave, "service AES key", nil, err)
}

err = auth.storeCoreRegs()
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionSave, model.TypeServiceReg, nil, err)
}
Expand All @@ -171,7 +181,7 @@ func NewAuth(serviceID string, host string, authPrivKey *keys.PrivKey, authServi

auth.ServiceRegManager = serviceRegManager

signatureAuth, err := sigauth.NewSignatureAuth(authPrivKey, serviceRegManager, true, supportLegacySigs)
signatureAuth, err := sigauth.NewSignatureAuth(currentAuthPrivKey, serviceRegManager, true, supportLegacySigs)
if err != nil {
return nil, errors.WrapErrorAction(logutils.ActionInitialize, "signature auth", nil, err)
}
Expand Down Expand Up @@ -2161,12 +2171,12 @@ func (a *Auth) buildAccessToken(claims tokenauth.Claims, permissions string, sco
claims.Permissions = permissions
}
claims.Scope = scope
return tokenauth.GenerateSignedToken(&claims, a.authPrivKey)
return tokenauth.GenerateSignedToken(&claims, a.currentAuthPrivKey)
}

func (a *Auth) buildCsrfToken(claims tokenauth.Claims) (string, error) {
claims.Purpose = "csrf"
return tokenauth.GenerateSignedToken(&claims, a.authPrivKey)
return tokenauth.GenerateSignedToken(&claims, a.currentAuthPrivKey)
}

func (a *Auth) buildRefreshToken() (string, error) {
Expand Down Expand Up @@ -2338,6 +2348,69 @@ func (a *Auth) setLogContext(account *model.Account, l *logs.Log) {
l.SetContext("account_id", accountID)
}

func (a *Auth) storeServiceAESKey() error {
// if can decrypt stored key (keys collection) with "current" key or old key not specified, no action necessary
// if cannot decrypt with "current" key and old key specified, perform rotation (decrypt with old key, encrypt and update with "current" key)

key, err := a.storage.FindKey(serviceAESKey)
if err != nil {
return errors.WrapErrorAction(logutils.ActionFind, model.TypeKey, nil, err)
}
if key == nil {
keyBytes, err := utils.GenerateAESKey()
if err != nil {
return errors.WrapErrorAction(logutils.ActionGenerate, "service AES key", nil, err)
}

// encrypt the new service AES key with the current service public key
encryptedKeyBytes, err := a.currentAuthPrivKey.PubKey.Encrypt(keyBytes, nil)
if err != nil {
return errors.WrapErrorAction(logutils.ActionEncrypt, "service AES key", nil, err)
}
newKey := model.Key{Name: serviceAESKey, Key: base64.StdEncoding.EncodeToString(encryptedKeyBytes)}

err = a.storage.InsertKey(newKey)
if err != nil {
return errors.WrapErrorAction(logutils.ActionInsert, model.TypeKey, nil, err)
}
} else {
decodedKey, err := base64.StdEncoding.DecodeString(key.Key)
if err != nil {
return errors.WrapErrorAction(logutils.ActionDecode, "service AES key", nil, err)
}

// attempt to decode and decrypt the stored service AES key with the current private key
_, err = a.currentAuthPrivKey.Decrypt(decodedKey, nil)
if err != nil {
a.logger.Infof("failed to decrypt service AES key using current private key: %v", err)

if a.oldAuthPrivKey == nil {
return errors.ErrorData(logutils.StatusMissing, "previous service private key", nil)
}

// attempt to decode and decrypt the stored service AES key with the old private key
decryptedKey, err := a.oldAuthPrivKey.Decrypt(decodedKey, nil)
if err != nil {
return errors.WrapErrorAction(logutils.ActionDecrypt, "service AES key", nil, err)
}

// re-encrypt the service AES key with the current private key and store it
encryptedKeyBytes, err := a.currentAuthPrivKey.PubKey.Encrypt([]byte(decryptedKey), nil)
if err != nil {
return errors.WrapErrorAction(logutils.ActionEncrypt, "service AES key", nil, err)
}
updatedKey := model.Key{Name: serviceAESKey, Key: base64.StdEncoding.EncodeToString(encryptedKeyBytes)}

err = a.storage.UpdateKey(updatedKey)
if err != nil {
return errors.WrapErrorAction(logutils.ActionInsert, model.TypeKey, nil, err)
}
}
}

return nil
}

// storeCoreRegs stores the service registration records for the Core BB
func (a *Auth) storeCoreRegs() error {
err := a.storage.MigrateServiceRegs()
Expand All @@ -2346,15 +2419,15 @@ func (a *Auth) storeCoreRegs() error {
}

// Setup "auth" registration for token validation
authReg := model.ServiceRegistration{Registration: authservice.ServiceReg{ServiceID: authServiceID, Host: a.host, PubKey: a.authPrivKey.PubKey}, CoreHost: a.host,
authReg := model.ServiceRegistration{Registration: authservice.ServiceReg{ServiceID: authServiceID, Host: a.host, PubKey: a.currentAuthPrivKey.PubKey}, CoreHost: a.host,
Name: "ROKWIRE Auth Service", Description: "The Auth Service is a subsystem of the Core Building Block that manages authentication and authorization.", FirstParty: true}
err = a.storage.SaveServiceReg(&authReg, true)
if err != nil {
return errors.WrapErrorAction(logutils.ActionSave, model.TypeServiceReg, logutils.StringArgs(authServiceID), err)
}

// Setup core registration for signature validation
coreReg := model.ServiceRegistration{Registration: authservice.ServiceReg{ServiceID: a.serviceID, ServiceAccountID: a.serviceID, Host: a.host, PubKey: a.authPrivKey.PubKey}, CoreHost: a.host,
coreReg := model.ServiceRegistration{Registration: authservice.ServiceReg{ServiceID: a.serviceID, ServiceAccountID: a.serviceID, Host: a.host, PubKey: a.currentAuthPrivKey.PubKey}, CoreHost: a.host,
Name: "ROKWIRE Core Building Block", Description: "The Core Building Block manages user, auth, and organization data for the ROKWIRE platform.", FirstParty: true}
err = a.storage.SaveServiceReg(&coreReg, true)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions core/auth/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,11 @@ type Storage interface {
SaveServiceAuthorization(authorization *model.ServiceAuthorization) error
DeleteServiceAuthorization(userID string, orgID string) error

//Keys
FindKey(name string) (*model.Key, error)
InsertKey(key model.Key) error
UpdateKey(key model.Key) error

//APIKeys
LoadAPIKeys() ([]model.APIKey, error)
InsertAPIKey(context storage.TransactionContext, apiKey model.APIKey) (*model.APIKey, error)
Expand Down
8 changes: 8 additions & 0 deletions core/model/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ const (
TypeJSONWebKeySet logutils.MessageDataType = "jwks"
//TypePubKey pub key type
TypePubKey logutils.MessageDataType = "pub key"
//TypeKey key type
TypeKey logutils.MessageDataType = "key"
//TypeAPIKey api key type
TypeAPIKey logutils.MessageDataType = "api key"
//TypeCreds cred type
Expand Down Expand Up @@ -220,6 +222,12 @@ func (ls LoginSession) LogInfo() string {
ls.StateExpires, ls.MfaAttempts, ls.DateRefreshed, ls.DateUpdated, ls.DateCreated)
}

// Key represents a service key and any associataed data
type Key struct {
Name string `bson:"name"`
Key string `bson:"key"`
}

// APIKey represents an API key entity
type APIKey struct {
ID string `json:"id" bson:"_id"`
Expand Down
Loading

0 comments on commit d35f706

Please sign in to comment.