rclone/vendor/github.com/t3rm1n4l/go-mega/utils.go

377 lines
7.9 KiB
Go
Raw Normal View History

package mega
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"encoding/binary"
"encoding/json"
"errors"
"math/big"
"net"
"net/http"
"regexp"
"strings"
"time"
)
func newHttpClient(timeout time.Duration) *http.Client {
// TODO: Need to test this out
// Doesn't seem to work as expected
c := &http.Client{
Transport: &http.Transport{
Dial: func(netw, addr string) (net.Conn, error) {
c, err := net.DialTimeout(netw, addr, timeout)
if err != nil {
return nil, err
}
return c, nil
},
Proxy: http.ProxyFromEnvironment,
},
}
return c
}
// bytes_to_a32 converts the byte slice b to uint32 slice considering
// the bytes to be in big endian order.
func bytes_to_a32(b []byte) ([]uint32, error) {
length := len(b) + 3
a := make([]uint32, length/4)
buf := bytes.NewBuffer(b)
for i, _ := range a {
err := binary.Read(buf, binary.BigEndian, &a[i])
if err != nil {
return nil, err
}
}
return a, nil
}
// a32_to_bytes converts the uint32 slice a to byte slice where each
// uint32 is decoded in big endian order.
func a32_to_bytes(a []uint32) ([]byte, error) {
buf := new(bytes.Buffer)
buf.Grow(len(a) * 4) // To prevent reallocations in Write
for _, v := range a {
err := binary.Write(buf, binary.BigEndian, v)
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
2019-11-11 15:04:53 +00:00
// base64urlencode encodes byte slice b using base64 url encoding
// without `=` padding.
func base64urlencode(b []byte) string {
return base64.RawURLEncoding.EncodeToString(b)
}
2019-11-11 15:04:53 +00:00
// base64urldecode decodes the byte slice b using unpadded base64 url
// decoding. It also allows the characters from standard base64 to be
// compatible with the mega decoder.
func base64urldecode(s string) ([]byte, error) {
enc := base64.RawURLEncoding
// mega base64 decoder accepts the characters from both URLEncoding and StdEncoding
// though nearly all strings are URL encoded
s = strings.Replace(s, "+", "-", -1)
s = strings.Replace(s, "/", "_", -1)
return enc.DecodeString(s)
}
// base64_to_a32 converts base64 encoded byte slice b to uint32 slice.
2019-11-11 15:04:53 +00:00
func base64_to_a32(s string) ([]uint32, error) {
d, err := base64urldecode(s)
if err != nil {
return nil, err
}
return bytes_to_a32(d)
}
// a32_to_base64 converts uint32 slice to base64 encoded byte slice.
2019-11-11 15:04:53 +00:00
func a32_to_base64(a []uint32) (string, error) {
d, err := a32_to_bytes(a)
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
return base64urlencode(d), nil
}
// paddnull pads byte slice b such that the size of resulting byte
// slice is a multiple of q.
func paddnull(b []byte, q int) []byte {
if rem := len(b) % q; rem != 0 {
l := q - rem
for i := 0; i < l; i++ {
b = append(b, 0)
}
}
return b
}
// password_key calculates password hash from the user password.
func password_key(p string) ([]byte, error) {
a, err := bytes_to_a32(paddnull([]byte(p), 4))
if err != nil {
return nil, err
}
pkey, err := a32_to_bytes([]uint32{0x93C467E3, 0x7DB0C7A4, 0xD1BE3F81, 0x0152CB56})
if err != nil {
return nil, err
}
n := (len(a) + 3) / 4
ciphers := make([]cipher.Block, n)
for j := 0; j < len(a); j += 4 {
key := []uint32{0, 0, 0, 0}
for k := 0; k < 4; k++ {
if j+k < len(a) {
key[k] = a[k+j]
}
}
bkey, err := a32_to_bytes(key)
if err != nil {
return nil, err
}
ciphers[j/4], err = aes.NewCipher(bkey) // Uses AES in ECB mode
if err != nil {
return nil, err
}
}
for i := 65536; i > 0; i-- {
for j := 0; j < n; j++ {
ciphers[j].Encrypt(pkey, pkey)
}
}
return pkey, nil
}
// stringhash computes generic string hash. Uses k as the key for AES
// cipher.
2019-11-11 15:04:53 +00:00
func stringhash(s string, k []byte) (string, error) {
a, err := bytes_to_a32(paddnull([]byte(s), 4))
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
h := []uint32{0, 0, 0, 0}
for i, v := range a {
h[i&3] ^= v
}
hb, err := a32_to_bytes(h)
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
cipher, err := aes.NewCipher(k)
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
for i := 16384; i > 0; i-- {
cipher.Encrypt(hb, hb)
}
ha, err := bytes_to_a32(paddnull(hb, 4))
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
return a32_to_base64([]uint32{ha[0], ha[2]})
}
// getMPI returns the length encoded Int and the next slice.
func getMPI(b []byte) (*big.Int, []byte) {
p := new(big.Int)
plen := (uint64(b[0])*256 + uint64(b[1]) + 7) >> 3
p.SetBytes(b[2 : plen+2])
b = b[plen+2:]
return p, b
}
// getRSAKey decodes the RSA Key from the byte slice b.
func getRSAKey(b []byte) (*big.Int, *big.Int, *big.Int) {
p, b := getMPI(b)
q, b := getMPI(b)
d, _ := getMPI(b)
return p, q, d
}
// decryptRSA decrypts message m using RSA private key (p,q,d)
func decryptRSA(m, p, q, d *big.Int) []byte {
n := new(big.Int)
r := new(big.Int)
n.Mul(p, q)
r.Exp(m, d, n)
return r.Bytes()
}
// blockDecrypt decrypts using the block cipher blk in ECB mode.
func blockDecrypt(blk cipher.Block, dst, src []byte) error {
if len(src) > len(dst) || len(src)%blk.BlockSize() != 0 {
return errors.New("Block decryption failed")
}
l := len(src) - blk.BlockSize()
for i := 0; i <= l; i += blk.BlockSize() {
blk.Decrypt(dst[i:], src[i:])
}
return nil
}
// blockEncrypt encrypts using the block cipher blk in ECB mode.
func blockEncrypt(blk cipher.Block, dst, src []byte) error {
if len(src) > len(dst) || len(src)%blk.BlockSize() != 0 {
return errors.New("Block encryption failed")
}
l := len(src) - blk.BlockSize()
for i := 0; i <= l; i += blk.BlockSize() {
blk.Encrypt(dst[i:], src[i:])
}
return nil
}
// decryptSeessionId decrypts the session id using the given private
// key.
2019-11-11 15:04:53 +00:00
func decryptSessionId(privk string, csid string, mk []byte) (string, error) {
block, err := aes.NewCipher(mk)
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
pk, err := base64urldecode(privk)
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
err = blockDecrypt(block, pk, pk)
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
c, err := base64urldecode(csid)
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
m, _ := getMPI(c)
p, q, d := getRSAKey(pk)
r := decryptRSA(m, p, q, d)
return base64urlencode(r[:43]), nil
}
// chunkSize describes a size and position of chunk
type chunkSize struct {
position int64
size int
}
func getChunkSizes(size int64) (chunks []chunkSize) {
p := int64(0)
for i := 1; size > 0; i++ {
var chunk int
if i <= 8 {
chunk = i * 131072
} else {
chunk = 1048576
}
if size < int64(chunk) {
chunk = int(size)
}
chunks = append(chunks, chunkSize{position: p, size: chunk})
p += int64(chunk)
size -= int64(chunk)
}
return chunks
}
var attrMatch = regexp.MustCompile(`{".*"}`)
2019-11-11 15:04:53 +00:00
func decryptAttr(key []byte, data string) (attr FileAttr, err error) {
err = EBADATTR
block, err := aes.NewCipher(key)
if err != nil {
return attr, err
}
iv, err := a32_to_bytes([]uint32{0, 0, 0, 0})
if err != nil {
return attr, err
}
mode := cipher.NewCBCDecrypter(block, iv)
buf := make([]byte, len(data))
2019-11-11 15:04:53 +00:00
ddata, err := base64urldecode(data)
if err != nil {
return attr, err
}
mode.CryptBlocks(buf, ddata)
if string(buf[:4]) == "MEGA" {
str := strings.TrimRight(string(buf[4:]), "\x00")
trimmed := attrMatch.FindString(str)
if trimmed != "" {
str = trimmed
}
err = json.Unmarshal([]byte(str), &attr)
}
return attr, err
}
2019-11-11 15:04:53 +00:00
func encryptAttr(key []byte, attr FileAttr) (b string, err error) {
err = EBADATTR
block, err := aes.NewCipher(key)
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
data, err := json.Marshal(attr)
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
attrib := []byte("MEGA")
attrib = append(attrib, data...)
attrib = paddnull(attrib, 16)
iv, err := a32_to_bytes([]uint32{0, 0, 0, 0})
if err != nil {
2019-11-11 15:04:53 +00:00
return "", err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(attrib, attrib)
b = base64urlencode(attrib)
return b, nil
}
func randString(l int) (string, error) {
encoding := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"
b := make([]byte, l)
_, err := rand.Read(b)
if err != nil {
return "", err
}
enc := base64.NewEncoding(encoding)
d := make([]byte, enc.EncodedLen(len(b)))
enc.Encode(d, b)
d = d[:l]
return string(d), nil
}