Add Auth, add computeKeyPassword

This commit is contained in:
emersion 2017-08-22 09:41:47 +02:00
parent 24aec2c52a
commit 653a8a0dc0
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
5 changed files with 99 additions and 51 deletions

View File

@ -31,8 +31,10 @@ func main() {
scanner.Scan() scanner.Scan()
code := scanner.Text() code := scanner.Text()
err := c.Auth(username, password, code, nil) auth, err := c.Auth(username, password, code, nil)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
log.Println(auth)
} }

View File

@ -79,8 +79,7 @@ const (
PasswordTwo = 2 PasswordTwo = 2
) )
type authResp struct { type Auth struct {
resp
AccessToken string AccessToken string
ExpiresIn int ExpiresIn int
TokenType string TokenType string
@ -88,17 +87,32 @@ type authResp struct {
UID string `json:"Uid"` UID string `json:"Uid"`
RefreshToken string RefreshToken string
EventID string EventID string
ServerProof string
PasswordMode PasswordMode PasswordMode PasswordMode
privateKey string
keySalt string
}
type authResp struct {
resp
Auth
ServerProof string
PrivateKey string PrivateKey string
KeySalt string KeySalt string
} }
func (c *Client) Auth(username, password, twoFactorCode string, info *AuthInfo) error { func (resp *authResp) auth() *Auth {
auth := &resp.Auth
auth.privateKey = resp.PrivateKey
auth.keySalt = resp.KeySalt
return auth
}
func (c *Client) Auth(username, password, twoFactorCode string, info *AuthInfo) (*Auth, error) {
if info == nil { if info == nil {
var err error var err error
if info, err = c.AuthInfo(username); err != nil { if info, err = c.AuthInfo(username); err != nil {
return err return nil, err
} }
} }
@ -106,7 +120,7 @@ func (c *Client) Auth(username, password, twoFactorCode string, info *AuthInfo)
proofs, err := srp([]byte(password), info) proofs, err := srp([]byte(password), info)
if err != nil { if err != nil {
return err return nil, err
} }
reqData := &authReq{ reqData := &authReq{
@ -118,23 +132,22 @@ func (c *Client) Auth(username, password, twoFactorCode string, info *AuthInfo)
ClientProof: base64.StdEncoding.EncodeToString(proofs.clientProof), ClientProof: base64.StdEncoding.EncodeToString(proofs.clientProof),
TwoFactorCode: twoFactorCode, TwoFactorCode: twoFactorCode,
} }
log.Printf("%#v\n", reqData)
req, err := c.newJSONRequest(http.MethodPost, "/auth", reqData) req, err := c.newJSONRequest(http.MethodPost, "/auth", reqData)
if err != nil { if err != nil {
return err return nil, err
} }
var respData authResp var respData authResp
if err := c.doJSON(req, &respData); err != nil { if err := c.doJSON(req, &respData); err != nil {
return err return nil, err
} }
log.Printf("%#v\n", respData) log.Printf("%+v\n", respData)
if err := proofs.VerifyServerProof(respData.ServerProof); err != nil { if err := proofs.VerifyServerProof(respData.ServerProof); err != nil {
return err return nil, err
} }
return nil return respData.auth(), nil
} }

63
protonmail/password.go Normal file
View File

@ -0,0 +1,63 @@
package protonmail
import (
"bytes"
"crypto/sha512"
"errors"
"github.com/emersion/go-bcrypt"
)
const bcryptCost = 10
func hashBcrypt(password, salt []byte) ([]byte, error) {
hashed, err := bcrypt.GenerateFromPasswordAndSalt(password, bcryptCost, salt)
if err != nil {
return nil, err
}
hashed = bytes.Replace(hashed, []byte("$2a$"), []byte("$2y$"), 1)
return hashed, nil
}
func expandHash(b []byte) []byte {
var expanded []byte
var part [64]byte
part = sha512.Sum512(append(b, 0))
expanded = append(expanded, part[:]...)
part = sha512.Sum512(append(b, 1))
expanded = append(expanded, part[:]...)
part = sha512.Sum512(append(b, 2))
expanded = append(expanded, part[:]...)
part = sha512.Sum512(append(b, 3))
expanded = append(expanded, part[:]...)
return expanded
}
func hashPassword(version int, password, salt, modulus []byte) ([]byte, error) {
switch version {
case 3, 4:
salt = append(salt, []byte("proton")...)
hashed, err := hashBcrypt(password, salt)
if err != nil {
return nil, err
}
return expandHash(append([]byte(hashed), modulus...)), nil
default:
return nil, errors.New("unsupported auth version")
}
}
func computeKeyPassword(password, salt []byte) ([]byte, error) {
hashed, err := hashBcrypt(password, salt)
if err != nil {
return nil, err
}
// Remove bcrypt prefix and salt (first 29 characters)
return hashed[29:], nil
}

View File

@ -63,7 +63,12 @@ func (c *Client) newJSONRequest(method, path string, body interface{}) (*http.Re
if err := json.NewEncoder(&b).Encode(body); err != nil { if err := json.NewEncoder(&b).Encode(body); err != nil {
return nil, err return nil, err
} }
return c.newRequest(method, path, &b) req, err := c.newRequest(method, path, &b)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
return req, nil
} }
func (c *Client) do(req *http.Request) (*http.Response, error) { func (c *Client) do(req *http.Request) (*http.Response, error) {
@ -75,6 +80,8 @@ func (c *Client) do(req *http.Request) (*http.Response, error) {
} }
func (c *Client) doJSON(req *http.Request, respData interface{}) error { func (c *Client) doJSON(req *http.Request, respData interface{}) error {
req.Header.Set("Accept", "application/json")
resp, err := c.do(req) resp, err := c.do(req)
if err != nil { if err != nil {
return err return err

View File

@ -3,14 +3,12 @@ package protonmail
import ( import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"crypto/sha512"
"crypto/subtle" "crypto/subtle"
"encoding/base64" "encoding/base64"
"errors" "errors"
"io" "io"
"math/big" "math/big"
"github.com/emersion/go-bcrypt"
"golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/clearsign" "golang.org/x/crypto/openpgp/clearsign"
openpgperrors "golang.org/x/crypto/openpgp/errors" openpgperrors "golang.org/x/crypto/openpgp/errors"
@ -33,41 +31,6 @@ func decodeModulus(msg string) ([]byte, error) {
return base64.StdEncoding.DecodeString(string(block.Plaintext)) return base64.StdEncoding.DecodeString(string(block.Plaintext))
} }
func expandHash(b []byte) []byte {
var expanded []byte
var part [64]byte
part = sha512.Sum512(append(b, 0))
expanded = append(expanded, part[:]...)
part = sha512.Sum512(append(b, 1))
expanded = append(expanded, part[:]...)
part = sha512.Sum512(append(b, 2))
expanded = append(expanded, part[:]...)
part = sha512.Sum512(append(b, 3))
expanded = append(expanded, part[:]...)
return expanded
}
func hashPassword(version int, password, salt, modulus []byte) ([]byte, error) {
switch version {
case 3, 4:
salt = append(salt, []byte("proton")...)
hashed, err := bcrypt.GenerateFromPasswordAndSalt(password, 10, salt)
if err != nil {
return nil, err
}
hashed = bytes.Replace(hashed, []byte("$2a$"), []byte("$2y$"), 1)
return expandHash(append([]byte(hashed), modulus...)), nil
default:
return nil, errors.New("unsupported auth version")
}
}
func reverse(b []byte) { func reverse(b []byte) {
for i := 0; i < len(b)/2; i++ { for i := 0; i < len(b)/2; i++ {
j := len(b) - 1 - i j := len(b) - 1 - i