Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Session login, not fully working yet #36

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 100 additions & 2 deletions mega.go
Original file line number Diff line number Diff line change
@@ -417,6 +417,7 @@ func (m *Mega) api_request(r []byte) (buf []byte, err error) {
m.debugf("Retry API request %d/%d: %v", i, m.retries, err)
backOffSleep(&sleepTime)
}

resp, err = m.client.Post(url, "application/json", bytes.NewBuffer(r))
if err != nil {
continue
@@ -510,7 +511,7 @@ func (m *Mega) prelogin(email string) error {
}

// Authenticate and start a session
func (m *Mega) login(email string, passwd string) error {
func (m *Mega) login(email string, passwd string, multiFactor string) error {
var msg [1]LoginMsg
var res [1]LoginResp
var err error
@@ -531,6 +532,10 @@ func (m *Mega) login(email string, passwd string) error {

msg[0].Cmd = "us"
msg[0].User = email
if multiFactor != "" {
msg[0].Mfa = multiFactor
}

if m.accountVersion == 1 {
msg[0].Handle = uhandle
} else {
@@ -585,7 +590,32 @@ func (m *Mega) Login(email string, passwd string) error {
return err
}

err = m.login(email, passwd)
err = m.login(email, passwd, "")
if err != nil {
return err
}

waitEvent := m.WaitEventsStart()

err = m.getFileSystem()
if err != nil {
return err
}

// Wait until the all the pending events have been received
m.WaitEvents(waitEvent, 5*time.Second)

return nil
}

// MultiFactorLogin - Authenticate and start a session with 2FA
func (m *Mega) MultiFactorLogin(email, passwd, multiFactor string) error {
err := m.prelogin(email)
if err != nil {
return err
}

err = m.login(email, passwd, multiFactor)
if err != nil {
return err
}
@@ -603,6 +633,74 @@ func (m *Mega) Login(email string, passwd string) error {
return nil
}

// SessionLogin - Authenticate and resume a session with session key
func (m *Mega) SessionLogin(sessionKey string) error {
var msg [1]SessionLoginMsg
var res [1]LoginResp
var err error
var result []byte

var sessionVersion uint8 = 0
USERHANDLE := 8
SIDLEN := 2*aes.BlockSize + USERHANDLE*4/3 + 1
if len(sessionKey) == aes.BlockSize+SIDLEN+1 {
sessionVersion = sessionKey[0]
if sessionVersion != 1 {
return EARGS
}

sessionKey = sessionKey[1:]
}

var sek []byte
var buf []byte
if len(sessionKey) == aes.BlockSize+SIDLEN {
m.k = []byte(sessionKey[:aes.BlockSize])

sid := sessionKey[aes.BlockSize:]
sida := string(megaBtoa(sid[:SIDLEN]))
m.sid = sida[:len(sida)-3]
sek = make([]byte, aes.BlockSize)
rand.Read(sek)

buf = megaBtoa(string(sek))
// TODO: fetch timezone
} else if len(sessionKey) > 0 && sessionKey[0] == 2 {
return fmt.Errorf("the session key passed is a folder link")
} else {
return EARGS
}

msg[0].Cmd = "us"
msg[0].SessionKey = string(buf[:len(buf)-3])

req, err := json.Marshal(msg)
if err != nil {
return err
}
result, err = m.api_request(req)
if err != nil {
return err
}

err = json.Unmarshal(result, &res)
if err != nil {
return err
}

waitEvent := m.WaitEventsStart()

err = m.getFileSystem() // FIXME: We need to fetch masterkey in some way in order to decrypt node attributes
if err != nil {
return err
}

// Wait until the all the pending events have been received
m.WaitEvents(waitEvent, 5*time.Second)

return nil
}

// WaitEventsStart - call this before you do the action which might
// generate events then use the returned channel as a parameter to
// WaitEvents to wait for the event(s) to be received.
9 changes: 9 additions & 0 deletions messages.go
Original file line number Diff line number Diff line change
@@ -30,10 +30,19 @@ type LoginResp struct {
U string `json:"u"`
}

type SessionLoginMsg struct {
Cmd string `json:"a"`
SessionKey string `json:"sek,omitempty"`
Si string `json:"si,omitempty"`
}

type UserMsg struct {
Cmd string `json:"a"`
}

type KeyResp struct {
}

type UserResp struct {
U string `json:"u"`
S int `json:"s"`
154 changes: 154 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
@@ -374,3 +374,157 @@ func randString(l int) (string, error) {
d = d[:l]
return string(d), nil
}

// megaAtob - Mega custom base64 encoding
func megaAtob(in string) []byte {
out := make([]byte, len(in)*3/4)
atob([]byte(in), &out, len(out))
return out
}

// Do not call this function directly, use megaAtob instead
func atob(a []byte, buf *[]byte, blen int) int {
c := make([]byte, 4)
var i int
p := 0

c[3] = 0

for j := 0; j < len(a); j += 4 {
b := 4
if len(a)-j < 4 {
b = len(a) - j
}
for i = 0; i < b; i++ {
c[i] = from64(a[i+j])
if (c[i]) == 255 {
break
}
}

if p >= blen || i == 0 {
return p
}

(*buf)[p] = (c[0] << 2) | ((c[1] & 0x30) >> 4)
p++

if (p >= blen) || (i < 3) {
return p
}

(*buf)[p] = (c[1] << 4) | ((c[2] & 0x3c) >> 2)
p++

if (p >= blen) || (i < 4) {
return p
}

(*buf)[p] = (c[2] << 6) | c[3]
p++
}

return p
}

func from64(c byte) uint8 {
if (c >= 'A') && (c <= 'Z') {
return c - 'A'
}

if (c >= 'a') && (c <= 'z') {
return c - 'a' + 26
}

if (c >= '0') && (c <= '9') {
return c - '0' + 52
}

if c == '-' || c == '+' {
return 62
}

if c == '_' || c == '/' {
return 63
}

return 255
}

// megaBtoa - Mega custom base64 decoding
func megaBtoa(in string) []byte {
out := make([]byte, len(in)*4/3+4)
btoa([]byte(in), len(in), &out)
return out
}

// Do not call this function directly, use megaBtoa instead
func btoa(buf []byte, blen int, a *[]byte) int {
p := 0
index := 0

for {
if blen <= 0 {
break
}

(*a)[p] = to64(buf[index] >> 2)
p++

var tmp uint8
if blen > 1 {
tmp = buf[index+1]
} else {
tmp = 0
}
(*a)[p] = to64((buf[index] << 4) | (tmp >> 4))
p++

if blen < 2 {
break
}

if blen > 2 {
tmp = buf[index+2]
} else {
tmp = 0
}
(*a)[p] = to64(buf[index+1]<<2 | (tmp >> 6))
p++

if blen < 3 {
break
}

(*a)[p] = to64(buf[index+2])
p++

blen -= 3
index += 3
}

(*a)[p] = 0
return p
}

func to64(c byte) uint8 {
c &= 63

if c < 26 {
return c + 'A'
}

if c < 52 {
return c - 26 + 'a'
}

if c < 62 {
return c - 52 + '0'
}

if c == 62 {
return '-'
}

return '_'
}