/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package utils.megasdk; import java.util.Random; import neembuu.uploader.httpclient.NUHttpClient; import neembuu.uploader.httpclient.httprequest.NUHttpPost; import org.apache.http.HttpResponse; import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import java.lang.Math; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.util.List; import java.util.ArrayList; import java.util.Collections; /** * * @author RD */ public class MegaSdk { private final HttpClient httpclient = NUHttpClient.getHttpClient(); private HttpContext httpContext = new BasicHttpContext(); private HttpResponse httpResponse; private NUHttpPost httpPost; private CookieStore cookieStore; private String responseString; private String sid = ""; Random rand = new Random(); private int seq_no = rand.nextInt((0xFFFFFFFF - 0) + 1) + 0; public String api_req(String request) { } public String[] array_merge (String[] first, String[] second) { List<String> both = new ArrayList<String>(first.length + second.length); Collections.addAll(both, first); Collections.addAll(both, second); return both.toArray(new String[both.size()]); } public String a32_to_str (int[] hex) { // i think hex and other arrays could also be byte[] //return call_user_func_array('pack', array_merge(array('N*'), $hex)); int[]r = new int[hex.length + 2]; r[0] = 'N'; r[1]='*'; System.arraycopy(hex, 0, r, 2, hex.length); String ret = ""; for (int i = 0; i < r.length; i++) { ret+=packN(r[i]); } return ret; } // from https://gist.github.com/enrobsop/8403667 static String packN(int value) { byte[]bytes = ByteBuffer.allocate(4).putInt(value).array(); char[]cbytes = toPositiveByteArray(bytes); return new String(cbytes); } static char[] toPositiveByteArray(byte[]bytes) { char[]c = new char[bytes.length]; for (int i = 0; i < bytes.length; i++) { byte it = bytes[i]; c[i] = it<0?((char)(256+it)):(char)it; } return c; } public int[] str_to_a32(String b) { double str_len = (double) b.length(); double mult = 4.0; double rounded_val = Math.ceil(str_len / mult); int pad_length = ((int) rounded_val) * 4; String pad_with = "\0"; // Add padding, we need a string with a length multiple of 4 b = str_pad(b, pad_length, pad_with); return unpack(b.getBytes()); } public String str_pad(String str, Integer length, String pad) { // str_pad("Hi", 10, 'R') //gives "HiRRRRRRRR" return String.format("%" + (length - str.length()) + "s", "") .replace(" ", String.valueOf(pad)) + str; } public static int[] unpack ( byte[] bytes ) { // first, wrap the input array in a ByteBuffer: ByteBuffer byteBuf = ByteBuffer.wrap( bytes ); // then turn it into an IntBuffer, using big-endian ("Network") byte order: byteBuf.order( ByteOrder.BIG_ENDIAN ); IntBuffer intBuf = byteBuf.asIntBuffer(); // finally, dump the contents of the IntBuffer into an array int[] integers = new int[ intBuf.remaining() ]; intBuf.get( integers ); return integers; } public String stringhash (String s, String aeskey) { int[] s32 = str_to_a32(s); int[] h32 = {0, 0, 0, 0}; int i = 0; for(i=0 ; i<s32.length; i++){ h32[i%4] ^= s32[i]; } for (i=0; i<0x4000; i++) { h32 = aes_cbc_encrypt_a32(h32, aeskey); } } /* ------------------------------------------------------------ --------This is the PHP code that needs to be ported-------- ------------------------------------------------------------ <?php $sid = ''; $seqno = rand(0, 0xFFFFFFFF); $master_key = ''; $rsa_priv_key = ''; function base64urldecode($data) { $data .= substr('==', (2 - strlen($data) * 3) % 4); $data = str_replace(array('-', '_', ','), array('+', '/', ''), $data); return base64_decode($data); } function base64urlencode($data) { return str_replace(array('+', '/', '='), array('-', '_', ''), base64_encode($data)); } function a32_to_str($hex) { return call_user_func_array('pack', array_merge(array('N*'), $hex)); } function a32_to_base64($a) { return base64urlencode(a32_to_str($a)); } function str_to_a32($b) { // Add padding, we need a string with a length multiple of 4 $b = str_pad($b, 4 * ceil(strlen($b) / 4), "\0"); return array_values(unpack('N*', $b)); } function base64_to_a32($s) { return str_to_a32(base64urldecode($s)); } function aes_cbc_encrypt($data, $key) { return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); } function aes_cbc_decrypt($data, $key) { return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); } function aes_cbc_encrypt_a32($data, $key) { return str_to_a32(aes_cbc_encrypt(a32_to_str($data), a32_to_str($key))); } function aes_cbc_decrypt_a32($data, $key) { return str_to_a32(aes_cbc_decrypt(a32_to_str($data), a32_to_str($key))); } function aes_ctr_encrypt($data, $key, $iv) { return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, 'ctr', $iv); } function aes_ctr_decrypt($data, $key, $iv) { return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, 'ctr', $iv); } // BEGIN RSA-related stuff -- taken from PEAR Crypt_RSA package // http://pear.php.net/package/Crypt_RSA function bin2int($str) { $result = 0; $n = strlen($str); do { $result = bcadd(bcmul($result, 256), ord($str[--$n])); } while ($n > 0); return $result; } function int2bin($num) { $result = ''; do { $result .= chr(bcmod($num, 256)); $num = bcdiv($num, 256); } while (bccomp($num, 0)); return $result; } function bitOr($num1, $num2, $start_pos) { $start_byte = intval($start_pos / 8); $start_bit = $start_pos % 8; $tmp1 = int2bin($num1); $num2 = bcmul($num2, 1 << $start_bit); $tmp2 = int2bin($num2); if ($start_byte < strlen($tmp1)) { $tmp2 |= substr($tmp1, $start_byte); $tmp1 = substr($tmp1, 0, $start_byte) . $tmp2; } else { $tmp1 = str_pad($tmp1, $start_byte, '\0') . $tmp2; } return bin2int($tmp1); } function bitLen($num) { $tmp = int2bin($num); $bit_len = strlen($tmp) * 8; $tmp = ord($tmp[strlen($tmp) - 1]); if (!$tmp) { $bit_len -= 8; } else { while (!($tmp & 0x80)) { $bit_len--; $tmp <<= 1; } } return $bit_len; } function rsa_decrypt($enc_data, $p, $q, $d) { $enc_data = int2bin($enc_data); $exp = $d; $modulus = bcmul($p, $q); $data_len = strlen($enc_data); $chunk_len = bitLen($modulus) - 1; $block_len = (int) ceil($chunk_len / 8); $curr_pos = 0; $bit_pos = 0; $plain_data = 0; while ($curr_pos < $data_len) { $tmp = bin2int(substr($enc_data, $curr_pos, $block_len)); $tmp = bcpowmod($tmp, $exp, $modulus); $plain_data = bitOr($plain_data, $tmp, $bit_pos); $bit_pos += $chunk_len; $curr_pos += $block_len; } return int2bin($plain_data); } // END RSA-related stuff function stringhash($s, $aeskey) { $s32 = str_to_a32($s); $h32 = array(0, 0, 0, 0); for ($i = 0; $i < count($s32); $i++) { $h32[$i % 4] ^= $s32[$i]; } for ($i = 0; $i < 0x4000; $i++) { $h32 = aes_cbc_encrypt_a32($h32, $aeskey); } return a32_to_base64(array($h32[0], $h32[2])); } function prepare_key($a) { $pkey = array(0x93C467E3, 0x7DB0C7A4, 0xD1BE3F81, 0x0152CB56); for ($r = 0; $r < 0x10000; $r++) { for ($j = 0; $j < count($a); $j += 4) { $key = array(0, 0, 0, 0); for ($i = 0; $i < 4; $i++) { if ($i + $j < count($a)) { $key[$i] = $a[$i + $j]; } } $pkey = aes_cbc_encrypt_a32($pkey, $key); } } return $pkey; } function encrypt_key($a, $key) { $x = array(); for ($i = 0; $i < count($a); $i += 4) { $x = array_merge($x, aes_cbc_encrypt_a32(array_slice($a, $i, 4), $key)); } return $x; } function decrypt_key($a, $key) { $x = array(); for ($i = 0; $i < count($a); $i += 4) { $x = array_merge($x, aes_cbc_decrypt_a32(array_slice($a, $i, 4), $key)); } return $x; } function mpi2bc($s) { $s = bin2hex(substr($s, 2)); $len = strlen($s); $n = 0; for ($i = 0; $i < $len; $i++) { $n = bcadd($n, bcmul(hexdec($s[$i]), bcpow(16, $len - $i - 1))); } return $n; } function api_req($req) { global $seqno, $sid; $resp = post('https://g.api.mega.co.nz/cs?id=' . ($seqno++) . ($sid ? '&sid=' . $sid : ''), json_encode(array($req))); $resp = json_decode($resp); return $resp[0]; } function post($url, $data) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $resp = curl_exec($ch); curl_close($ch); return $resp; } function login($email, $password) { global $sid, $master_key, $rsa_priv_key; $password_aes = prepare_key(str_to_a32($password)); $uh = stringhash(strtolower($email), $password_aes); $res = api_req(array('a' => 'us', 'user' => $email, 'uh' => $uh)); $enc_master_key = base64_to_a32($res->k); $master_key = decrypt_key($enc_master_key, $password_aes); if (!empty($res->csid)) { $enc_rsa_priv_key = base64_to_a32($res->privk); $rsa_priv_key = decrypt_key($enc_rsa_priv_key, $master_key); $privk = a32_to_str($rsa_priv_key); $rsa_priv_key = array(0, 0, 0, 0); for ($i = 0; $i < 4; $i++) { $l = ((ord($privk[0]) * 256 + ord($privk[1]) + 7) / 8) + 2; $rsa_priv_key[$i] = mpi2bc(substr($privk, 0, $l)); $privk = substr($privk, $l); } $enc_sid = mpi2bc(base64urldecode($res->csid)); $sid = rsa_decrypt($enc_sid, $rsa_priv_key[0], $rsa_priv_key[1], $rsa_priv_key[2]); $sid = base64urlencode(substr(strrev($sid), 0, 43)); } } function enc_attr($attr, $key) { $attr = 'MEGA' . json_encode($attr); return aes_cbc_encrypt($attr, a32_to_str($key)); } function dec_attr($attr, $key) { $attr = trim(aes_cbc_decrypt($attr, a32_to_str($key))); if (substr($attr, 0, 6) != 'MEGA{"') { return false; } return json_decode(substr($attr, 4)); } function get_chunks($size) { $chunks = array(); $p = $pp = 0; for ($i = 1; $i <= 8 && $p < $size - $i * 0x20000; $i++) { $chunks[$p] = $i * 0x20000; $pp = $p; $p += $chunks[$p]; } while ($p < $size) { $chunks[$p] = 0x100000; $pp = $p; $p += $chunks[$p]; } $chunks[$pp] = ($size - $pp); if (!$chunks[$pp]) { unset($chunks[$pp]); } return $chunks; } function cbc_mac($data, $k, $n) { $padding_size = (strlen($data) % 16) == 0 ? 0 : 16 - strlen($data) % 16; $data .= str_repeat("\0", $padding_size); $chunks = get_chunks(strlen($data)); $file_mac = array(0, 0, 0, 0); foreach ($chunks as $pos => $size) { $chunk_mac = array($n[0], $n[1], $n[0], $n[1]); for ($i = $pos; $i < $pos + $size; $i += 16) { $block = str_to_a32(substr($data, $i, 16)); $chunk_mac = array($chunk_mac[0] ^ $block[0], $chunk_mac[1] ^ $block[1], $chunk_mac[2] ^ $block[2], $chunk_mac[3] ^ $block[3]); $chunk_mac = aes_cbc_encrypt_a32($chunk_mac, $k); } $file_mac = array($file_mac[0] ^ $chunk_mac[0], $file_mac[1] ^ $chunk_mac[1], $file_mac[2] ^ $chunk_mac[2], $file_mac[3] ^ $chunk_mac[3]); $file_mac = aes_cbc_encrypt_a32($file_mac, $k); } return $file_mac; } function uploadfile($filename) { global $master_key, $root_id; $data = file_get_contents($filename); $size = strlen($data); $ul_url = api_req(array('a' => 'u', 's' => $size)); $ul_url = $ul_url->p; $ul_key = array(0, 1, 2, 3, 4, 5); for ($i = 0; $i < 6; $i++) { $ul_key[$i] = rand(0, 0xFFFFFFFF); } $data_crypted = aes_ctr_encrypt($data, a32_to_str(array_slice($ul_key, 0, 4)), a32_to_str(array($ul_key[4], $ul_key[5], 0, 0))); $completion_handle = post($ul_url, $data_crypted); $data_mac = cbc_mac($data, array_slice($ul_key, 0, 4), array_slice($ul_key, 4, 2)); $meta_mac = array($data_mac[0] ^ $data_mac[1], $data_mac[2] ^ $data_mac[3]); $attributes = array('n' => basename($filename)); $enc_attributes = enc_attr($attributes, array_slice($ul_key, 0, 4)); $key = array($ul_key[0] ^ $ul_key[4], $ul_key[1] ^ $ul_key[5], $ul_key[2] ^ $meta_mac[0], $ul_key[3] ^ $meta_mac[1], $ul_key[4], $ul_key[5], $meta_mac[0], $meta_mac[1]); return api_req(array('a' => 'p', 't' => $root_id, 'n' => array(array('h' => $completion_handle, 't' => 0, 'a' => base64urlencode($enc_attributes), 'k' => a32_to_base64(encrypt_key($key, $master_key)))))); } function downloadfile($file, $attributes, $k, $iv, $meta_mac) { $dl_url = api_req(array('a' => 'g', 'g' => 1, 'n' => $file->h)); $data_enc = file_get_contents($dl_url->g); $data = aes_ctr_decrypt($data_enc, a32_to_str($k), a32_to_str($iv)); file_put_contents($attributes->n, $data); $file_mac = cbc_mac($data, $k, $iv); if (array($file_mac[0] ^ $file_mac[1], $file_mac[2] ^ $file_mac[3]) != $meta_mac) { echo "MAC mismatch"; } } function getfiles() { global $master_key, $root_id, $inbox_id, $trashbin_id; $files = api_req(array('a' => 'f', 'c' => 1)); foreach ($files->f as $file) { if ($file->t == 0 || $file->t == 1) { $key = substr($file->k, strpos($file->k, ':') + 1); $key = decrypt_key(base64_to_a32($key), $master_key); if ($file->t == 0) { $k = array($key[0] ^ $key[4], $key[1] ^ $key[5], $key[2] ^ $key[6], $key[3] ^ $key[7]); $iv = array_merge(array_slice($key, 4, 2), array(0, 0)); $meta_mac = array_slice($key, 6, 2); } else { $k = $key; } $attributes = base64urldecode($file->a); $attributes = dec_attr($attributes, $k); if ($file->h == 'gldU3Tab') { downloadfile($file, $attributes, $k, $iv, $meta_mac); } } else if ($file->t == 2) { $root_id = $file->k; } else if ($file->t == 3) { $inbox_id = $file->k; } else if ($file->t == 4) { $trashbin_id = $file->k; } } } ?> */ }