Skip to content

Commit

Permalink
Add support for version 2 auth
Browse files Browse the repository at this point in the history
  • Loading branch information
maidul98 committed Feb 16, 2023
1 parent c57394b commit f2d7401
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 55 deletions.
8 changes: 4 additions & 4 deletions cli/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ require (
github.com/muesli/mango-cobra v1.2.0
github.com/muesli/roff v0.1.0
github.com/spf13/cobra v1.6.1
golang.org/x/crypto v0.3.0
golang.org/x/term v0.3.0
golang.org/x/crypto v0.6.0
golang.org/x/term v0.5.0
)

require (
Expand All @@ -31,8 +31,8 @@ require (
github.com/oklog/ulid v1.3.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
go.mongodb.org/mongo-driver v1.10.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/net v0.6.0 // indirect
golang.org/x/sys v0.5.0 // indirect
)

require (
Expand Down
8 changes: 8 additions & 0 deletions cli/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,14 @@ go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAV
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -123,9 +127,13 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down
40 changes: 40 additions & 0 deletions cli/packages/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,46 @@ func CallGetSecretsV2(httpClient *resty.Client, request GetEncryptedSecretsV2Req
return secretsResponse, nil
}

func CallLogin1V2(httpClient *resty.Client, request GetLoginOneV2Request) (GetLoginOneV2Response, error) {
var loginOneV2Response GetLoginOneV2Response
response, err := httpClient.
R().
SetResult(&loginOneV2Response).
SetHeader("User-Agent", USER_AGENT).
SetBody(request).
Post(fmt.Sprintf("%v/v2/auth/login1", config.INFISICAL_URL))

if err != nil {
return GetLoginOneV2Response{}, fmt.Errorf("CallLogin1V2: Unable to complete api request [err=%s]", err)
}

if response.IsError() {
return GetLoginOneV2Response{}, fmt.Errorf("CallLogin1V2: Unsuccessful response: [response=%s]", response)
}

return loginOneV2Response, nil
}

func CallLogin2V2(httpClient *resty.Client, request GetLoginTwoV2Request) (GetLoginTwoV2Response, error) {
var loginTwoV2Response GetLoginTwoV2Response
response, err := httpClient.
R().
SetResult(&loginTwoV2Response).
SetHeader("User-Agent", USER_AGENT).
SetBody(request).
Post(fmt.Sprintf("%v/v2/auth/login2", config.INFISICAL_URL))

if err != nil {
return GetLoginTwoV2Response{}, fmt.Errorf("CallLogin2V2: Unable to complete api request [err=%s]", err)
}

if response.IsError() {
return GetLoginTwoV2Response{}, fmt.Errorf("CallLogin2V2: Unsuccessful response: [response=%s]", response)
}

return loginTwoV2Response, nil
}

func CallGetAllWorkSpacesUserBelongsTo(httpClient *resty.Client) (GetWorkSpacesResponse, error) {
var workSpacesResponse GetWorkSpacesResponse
response, err := httpClient.
Expand Down
28 changes: 28 additions & 0 deletions cli/packages/api/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,31 @@ type GetAccessibleEnvironmentsResponse struct {
IsWriteDenied bool `json:"isWriteDenied"`
} `json:"accessibleEnvironments"`
}

type GetLoginOneV2Request struct {
Email string `json:"email"`
ClientPublicKey string `json:"clientPublicKey"`
}

type GetLoginOneV2Response struct {
ServerPublicKey string `json:"serverPublicKey"`
Salt string `json:"salt"`
}

type GetLoginTwoV2Request struct {
Email string `json:"email"`
ClientProof string `json:"clientProof"`
}

type GetLoginTwoV2Response struct {
MfaEnabled bool `json:"mfaEnabled"`
EncryptionVersion int `json:"encryptionVersion"`
Token string `json:"token"`
PublicKey string `json:"publicKey"`
EncryptedPrivateKey string `json:"encryptedPrivateKey"`
Iv string `json:"iv"`
Tag string `json:"tag"`
ProtectedKey string `json:"protectedKey"`
ProtectedKeyIV string `json:"protectedKeyIV"`
ProtectedKeyTag string `json:"protectedKeyTag"`
}
157 changes: 106 additions & 51 deletions cli/packages/cmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"regexp"

"github.com/Infisical/infisical-merge/packages/api"
"github.com/Infisical/infisical-merge/packages/config"
"github.com/Infisical/infisical-merge/packages/crypto"
"github.com/Infisical/infisical-merge/packages/models"
"github.com/Infisical/infisical-merge/packages/srp"
Expand All @@ -23,8 +22,23 @@ import (
"github.com/manifoldco/promptui"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/crypto/argon2"
)

type params struct {
memory uint32
iterations uint32
parallelism uint8
saltLength uint32
keyLength uint32
}

func generateFromPassword(password string, salt []byte, p *params) (hash []byte, err error) {
hash = argon2.IDKey([]byte(password), salt, p.iterations, p.memory, p.parallelism, p.keyLength)

return hash, nil
}

// loginCmd represents the login command
var loginCmd = &cobra.Command{
Use: "login",
Expand Down Expand Up @@ -55,36 +69,100 @@ var loginCmd = &cobra.Command{
util.HandleError(err, "Unable to parse email and password for authentication")
}

userCredentials, err := getFreshUserCredentials(email, password)
loginOneResponse, loginTwoResponse, err := getFreshUserCredentials(email, password)
if err != nil {
log.Infoln("Unable to authenticate with the provided credentials, please try again")
log.Debugln(err)
return
}

encryptedPrivateKey, _ := base64.StdEncoding.DecodeString(userCredentials.EncryptedPrivateKey)
tag, err := base64.StdEncoding.DecodeString(userCredentials.Tag)
if err != nil {
util.HandleError(err)
}
var decryptedPrivateKey []byte

IV, err := base64.StdEncoding.DecodeString(userCredentials.IV)
if err != nil {
util.HandleError(err)
}
if loginTwoResponse.EncryptionVersion == 1 {
encryptedPrivateKey, _ := base64.StdEncoding.DecodeString(loginTwoResponse.EncryptedPrivateKey)
tag, err := base64.StdEncoding.DecodeString(loginTwoResponse.Tag)
if err != nil {
util.HandleError(err)
}

paddedPassword := fmt.Sprintf("%032s", password)
key := []byte(paddedPassword)
IV, err := base64.StdEncoding.DecodeString(loginTwoResponse.Iv)
if err != nil {
util.HandleError(err)
}

decryptedPrivateKey, err := crypto.DecryptSymmetric(key, encryptedPrivateKey, tag, IV)
if err != nil || len(decryptedPrivateKey) == 0 {
util.HandleError(err)
paddedPassword := fmt.Sprintf("%032s", password)
key := []byte(paddedPassword)

decryptedPrivateKey, err := crypto.DecryptSymmetric(key, encryptedPrivateKey, tag, IV)
if err != nil || len(decryptedPrivateKey) == 0 {
util.HandleError(err)
}
} else if loginTwoResponse.EncryptionVersion == 2 {
protectedKey, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKey)
if err != nil {
util.HandleError(err)
}

protectedKeyTag, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKeyTag)
if err != nil {
util.HandleError(err)
}

protectedKeyIV, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKeyIV)
if err != nil {
util.HandleError(err)
}

nonProtectedTag, err := base64.StdEncoding.DecodeString(loginTwoResponse.Tag)
if err != nil {
util.HandleError(err)
}

nonProtectedIv, err := base64.StdEncoding.DecodeString(loginTwoResponse.Iv)
if err != nil {
util.HandleError(err)
}

parameters := &params{
memory: 64 * 1024,
iterations: 3,
parallelism: 1,
keyLength: 32,
}

derivedKey, err := generateFromPassword(password, []byte(loginOneResponse.Salt), parameters)
if err != nil {
util.HandleError(fmt.Errorf("unable to generate argon hash from password [err=%s]", err))
}

decryptedProtectedKey, err := crypto.DecryptSymmetric(derivedKey, protectedKey, protectedKeyTag, protectedKeyIV)
if err != nil {
util.HandleError(fmt.Errorf("unable to get decrypted protected key [err=%s]", err))
}

encryptedPrivateKey, err := base64.StdEncoding.DecodeString(loginTwoResponse.EncryptedPrivateKey)
if err != nil {
util.HandleError(err)
}

decryptedProtectedKeyInHex, err := hex.DecodeString(string(decryptedProtectedKey))
if err != nil {
util.HandleError(err)
}

decryptedPrivateKey, err = crypto.DecryptSymmetric(decryptedProtectedKeyInHex, encryptedPrivateKey, nonProtectedTag, nonProtectedIv)

if err != nil {
util.HandleError(err)
}
} else {
util.PrintErrorMessageAndExit("Insufficient details to decrypt private key")
}

userCredentialsToBeStored := &models.UserCredentials{
Email: email,
PrivateKey: string(decryptedPrivateKey),
JTWToken: userCredentials.JTWToken,
JTWToken: loginTwoResponse.Token,
}

err = util.StoreUserCredsInKeyRing(userCredentialsToBeStored)
Expand Down Expand Up @@ -155,7 +233,7 @@ func askForLoginCredentials() (email string, password string, err error) {
return userEmail, userPassword, nil
}

func getFreshUserCredentials(email string, password string) (*api.LoginTwoResponse, error) {
func getFreshUserCredentials(email string, password string) (*api.GetLoginOneV2Response, *api.GetLoginTwoV2Response, error) {
log.Debugln("getFreshUserCredentials:", "email", email, "password", password)
httpClient := resty.New()
httpClient.SetRetryCount(5)
Expand All @@ -166,64 +244,41 @@ func getFreshUserCredentials(email string, password string) (*api.LoginTwoRespon
srpA := hex.EncodeToString(srpClient.ComputeA())

// ** Login one
loginOneRequest := api.LoginOneRequest{
loginOneResponseResult, err := api.CallLogin1V2(httpClient, api.GetLoginOneV2Request{
Email: email,
ClientPublicKey: srpA,
}

var loginOneResponseResult api.LoginOneResponse

loginOneResponse, err := httpClient.
R().
SetBody(loginOneRequest).
SetResult(&loginOneResponseResult).
Post(fmt.Sprintf("%v/v1/auth/login1", config.INFISICAL_URL))
})

if err != nil {
return nil, err
}

if loginOneResponse.StatusCode() > 299 {
return nil, fmt.Errorf("ops, unsuccessful response code. [response=%v]", loginOneResponse)
util.HandleError(err)
}

// **** Login 2
serverPublicKey_bytearray, err := hex.DecodeString(loginOneResponseResult.ServerPublicKey)
if err != nil {
return nil, err
return nil, nil, err
}

userSalt, err := hex.DecodeString(loginOneResponseResult.ServerSalt)
userSalt, err := hex.DecodeString(loginOneResponseResult.Salt)
if err != nil {
return nil, err
return nil, nil, err
}

srpClient.SetSalt(userSalt, []byte(email), []byte(password))
srpClient.SetB(serverPublicKey_bytearray)

srpM1 := srpClient.ComputeM1()

LoginTwoRequest := api.LoginTwoRequest{
loginTwoResponseResult, err := api.CallLogin2V2(httpClient, api.GetLoginTwoV2Request{
Email: email,
ClientProof: hex.EncodeToString(srpM1),
}

var loginTwoResponseResult api.LoginTwoResponse
loginTwoResponse, err := httpClient.
R().
SetBody(LoginTwoRequest).
SetResult(&loginTwoResponseResult).
Post(fmt.Sprintf("%v/v1/auth/login2", config.INFISICAL_URL))
})

if err != nil {
return nil, err
}

if loginTwoResponse.StatusCode() > 299 {
return nil, fmt.Errorf("ops, unsuccessful response code. [response=%v]", loginTwoResponse)
util.HandleError(err)
}

return &loginTwoResponseResult, nil
return &loginOneResponseResult, &loginTwoResponseResult, nil
}

func shouldOverrideLoginPrompt(currentLoggedInUserEmail string) (bool, error) {
Expand Down

0 comments on commit f2d7401

Please sign in to comment.