Using the Mega API: how to download a public file (or a file you know the key), without logging in.

In my first article, I showed how to log into the Mega API, list all of your own files, download them, and upload new files. But I didn’t talk about how to download public files (files that you know the link/the key), without logging in, just as a visitor on http://mega.co.nz can do.

Let’s take this file as an example:

Capture du 2013-01-29 22:46:25

If we look at the URL, we can notice two components, separated by a ‘!‘:

  • RtQFAZZQ: the file ID ;
  • OH8OnHm0VFw-9IzkYQa7VUdsjMp1G7hucXEk7QIZWvE: the file key, already decrypted (the key is stored encrypted with the owner’s master key on Mega’s servers, but when he decides to share the file, he shares the decrypted key so that other people can decrypt the attributes of the file and its contents).

To download the file, we can follow almost the same steps as in the getfiles() and downloadfile() functions (see my previous article for more details):

  • Decompose the key into its three components: k, iv and meta_mac ;
  • Get informations about the file (its attributes, size and download URL): this is done with the API g method, that we used to get the download URL of our files in the previous article. But instead of giving the ID of the file as a n parameter, we will pass it as a p parameter.
  • Download the file using the download URL, decrypt it and check its meta-MAC.

So… here we go!

def getfile(file_id, file_key):
  key = base64_to_a32(file_key)
  k = (key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6], key[3] ^ key[7])
  iv = key[4:6] + (0, 0)
  meta_mac = key[6:8]
 
  file = api_req({'a': 'g', 'g': 1, 'p': file_id})
  dl_url = file['g']
  size = file['s']
  attributes = base64urldecode(file['at']) 
  attributes = dec_attr(attributes, k)
 
  print "Downloading %s (size: %d), url = %s" % (attributes['n'], size, dl_url)
 
  infile = urllib.urlopen(dl_url)
  outfile = open(attributes['n'], 'wb')
  decryptor = AES.new(a32_to_str(k), AES.MODE_CTR, counter = Counter.new(128, initial_value = ((iv[0] << 32) + iv[1]) << 64))
 
  file_mac = [0, 0, 0, 0]
  for chunk_start, chunk_size in sorted(get_chunks(file['s']).items()):
    chunk = infile.read(chunk_size)
    chunk = decryptor.decrypt(chunk)
    outfile.write(chunk)
 
    chunk_mac = [iv[0], iv[1], iv[0], iv[1]]
    for i in xrange(0, len(chunk), 16):
      block = chunk[i:i+16]
      if len(block) % 16:
        block += '\0' * (16 - (len(block) % 16))
      block = str_to_a32(block)
      chunk_mac = [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 = [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)
 
  outfile.close()
  infile.close()
 
  if (file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3]) != meta_mac:
    print "MAC mismatch"
  else:
    print "MAC OK"
getfile('RtQFAZZQ', 'OH8OnHm0VFw-9IzkYQa7VUdsjMp1G7hucXEk7QIZWvE')

All the utility functions are the same as in the previous article.

We can now test our program and see the result :-)

julienm@rchand:~$ python mega/megalol_propre.py 
Downloading donjon-de-naheulbeuk10.mp3 (size: 4676674), url = http://gfs262n152.userstorage.mega.co.nz/dl/yKpztNG6YnZ1bQLVBMVnNxMWOljOEEFZmWXVHJHNiF9EBDxvBn3kk06JwbCCNQudAJtvjruEwtMeypRrjG2zLPgf8r6PTR4XBvq-ziJorNryrUt4sA
MAC OK
julienm@rchand:~$ ls -l donjon-de-naheulbeuk10.mp3 
-rw-rw-r-- 1 julienm julienm 4676674 janv. 29 23:04 donjon-de-naheulbeuk10.mp3
julienm@rchand:~$ file donjon-de-naheulbeuk10.mp3 
donjon-de-naheulbeuk10.mp3: MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
julienm@rchand:~$

24 thoughts on “Using the Mega API: how to download a public file (or a file you know the key), without logging in.

  1. foobar

    The MAC verification is missing from this function. I tried to reintroduce it but it failed to compute the correct MAC, I haven’t had time to debug it. Was it failing for you as well?

    Reply
    1. Julien Marchand Post author

      Whoops, I missed my copy/paste, I forgot the lines checking the MAC at the end. I just added them back, and it’s working (they were actually included in the script I tested, which prints “MAC OK”).

      You have to check that you’re not comparing a list and a tuple (I had this problem at first). meta_mac is a tuple because decrypt_key() is defined as:

      def decrypt_key(a, key):
        return sum((aes_cbc_decrypt_a32(a[i:i+4], key) for i in xrange(0, len(a), 4)), ())

      And meta_mac = key[6:8], so you have to compare it with a tuple: (file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3]).

      Reply
    2. Mans

      I get an error in the download pyhton !!!

      At line 17:

      decryptor = AES.new(a32_to_str(k), AES.MODE_CTR, counter = Counter.new(128, initial_value = ((iv[0] << 32) + iv[1]) << 64))

      Can you helP ??

      Reply
      1. Mans

        I have repaired the < to << !!

        But now this errr /var/www/vhosts/aa/httpdocs/mega$ /usr/bin/python /var/www/vhosts/aa/httpdocs/mega/mega.py
        Traceback (most recent call last):
        File "/var/www/vhosts/aa/httpdocs/mega/mega.py", line 45, in
        getfile('RtQeFAZaZQ', 'OHa8OnHm0VFw-9aIzkYQa7VeUdsjMp1G7hucXEk7QIZWvE')
        File "/var/www/vhosts/aa/httpdocs/mega/mega.py", line 2, in getfile
        key = base64_to_a32(file_key)
        NameError: global name 'base64_to_a32' is not defined

        Please help

        Reply
    1. Julien Marchand Post author

      Yeap, they use “ephemeral accounts” (accounts without a RSA key pair and with a randomly generated master key). Once this account is created, the upload works exactly as with any other account. I’ll try to wrap up a few lines of codes to demo that.

      Reply
        1. Julien Marchand Post author

          Okay, this does the trick:

          def login_anon():
            global sid, master_key
            master_key = [random.randint(0, 0xFFFFFFFF)] * 4
            password_key = [random.randint(0, 0xFFFFFFFF)] * 4
            session_self_challenge = [random.randint(0, 0xFFFFFFFF)] * 4
           
            user_handle = api_req({
                'a': 'up',
                'k': a32_to_base64(encrypt_key(master_key, password_key)),
                'ts': base64urlencode(a32_to_str(session_self_challenge) + a32_to_str(encrypt_key(session_self_challenge, master_key)))
            })
           
            print "ephemeral user handle: %s" % user_handle
            res = api_req({'a': 'us', 'user': user_handle})
           
            enc_master_key = base64_to_a32(res['k'])
            master_key = decrypt_key(enc_master_key, password_key)
            if 'tsid' in res:
              tsid = base64urldecode(res['tsid'])
              if a32_to_str(encrypt_key(str_to_a32(tsid[:16]), master_key)) == tsid[-16:]:
                sid = res['tsid']

          We randomly generate a master key, a “password key” (equivalent to the hash of a regular user’s password) to encrypt to master key, and a session self challenge (that will be used to check the generated password and get the session ID, since our ephemeral account does not have a RSA key pair).

          Then, the getfiles() and uploadfile() functions are the same as in my first article. So, let’s upload a file anonymously and get its public URL to share it on the web:

          login_anon()
          getfiles()
          uploaded_file = uploadfile('/home/julienm/mega/test_file.png')
          print getpublicurl(uploaded_file['f'][0])

          We have to call the getfiles() function to get the ID of the root node, to which we are uploading our file. The uploadfile() method is simply modified to change the final “print” into a “return” and return informations about the uploaded file.

          The getpublicurl method gets the public handle of the file, decrypts its key, and concatenates the two informations to obtain the public URL:

          def getpublicurl(file):
            public_handle = api_req({'a': 'l', 'n': file['h']})
            key = file['k'][file['k'].index(':') + 1:]
            decrypted_key = a32_to_base64(decrypt_key(base64_to_a32(key), master_key))
            return "http://mega.co.nz/#!%s!%s" % (public_handle, decrypted_key)
          julienm@rchand:~/mega$ python anon_upload.py
          ephemeral user handle: 5hq-EIBu_yc
          http://mega.co.nz/#!V51SVYzY!pMS4P8hyBqFBC3QdhOYNG4xEbJ8Kj8dYQFuxdDt6dMU
          julienm@rchand:~/mega$
          Reply
          1. Rupert

            Whenever I try to download a file now it says :”Error writing file, is your harddrive almost full? (getFile)” , when I try to do it through Google Chrome .
            However if I do not use Google Chrome and go though Windows Explorer onto the Internet , there’s no problem , and I can dl the file . Can you resolve ?

  2. NeoMega

    Salut Julien,

    Merci pour ton blog très instructif !
    Cependant j’ai besoin de réaliser un script assez spécifique pour MEGA et je ne sais pas comment m’y prendre.

    Pour résumer, j’anime une communauté qui se partage des liens de téléchargement sur MEGA.
    Cependant l’affichage des liens de façon public fait qu’on a vite un soucis de copyright et un fichier effacer.

    Mon idée est donc au lieu de proposer un lien directe vers mega de créer une url crypter voir jetable qui permet de télécharger un fichier sans en dévoiler la clé publiquement. La clé serait transmise en paramètre lier à une base de donnée mysql.
    Je dois réalisé ce script en PHP;

    Pense tu que c’est faisable et si oui a tu une idée de comment faire ?

    Reply
    1. Julien Marchand Post author

      Hello :)

      Je ne pense pas que s’étendre sur ce sujet soit une très bonne idée (si ces fichiers ont été supprimés, il y doit y avoir une bonne raison :) ), mais si un ayant-droit a trouvé sur ton site un lien vers un fichier copyrighté, il peut sans problème demander à Mega de le supprimer. Garde donc en tête que quel que soit le mécanisme que tu mettras en place, s’il permet au final à l’utilisateur d’accéder au fichier sur mega.co.nz, le problème restera le même (si l’utilisateur peut voir le lien, l’ayant-droit aussi). Ne reste que le degré de “difficulté” pour automatiser ce processus de découverte de liens sur ton site ; donc oui, ne pas publier de liens en clair et utiliser des URL de redirection qui vont chercher la véritable URL du fichier dans ta base de données peut aider… mais ce n’est pas infaillible, loin de la :)

      Reply
        1. Julien Marchand Post author

          Une manière simple et efficace d’implémenter la chose est de créer une table à deux colonnes (disons “id” et “url”), qui associe… un “ID” à une URL Mega :)

          L’ID doit être choisi par tes soins, mais évite un simple identifiant numérique auto-incrémenté : plus il est long et complexe, mieux c’est. Pour ne pas t’embêter dans le choix de l’ID, tu peux simplement prendre le hash SHA1 de l’URL.

          Ensuite, ta page de redirection redir.php prendra tout simplement en paramètre un ID, et redirigera vers l’URL associée (header(“Location: $url”));). C’est pour cela qu’il vaut mieux que les ID soient un minimum complexes : si tu prends un simple auto-increment, toutes les URL pourront être découvertes en interrogeant successivement redir.php?id=1, redir.php?id=2, redir.php?id=3, redir.php?id=4

          Si ta page de redirection est servie en HTTPS, c’est encore mieux car l’en-tête Referer ne sera pas renseignée par le navigateur lors de la redirection.

          On sort un peu du cadre de l’API Mega, donc je t’invite à me contacter en privé si tu veux en discuter ou si tu veux plus de précisions (je viens de voir que c’était jusqu’à présent impossible, désolé, je viens d’ajouter un formulaire de contact :) ).

          Reply
  3. DinoEO

    Salut,

    Merci pour cette fonction en Python mais vous pourriez nous la donnez aussi en PHP

    avec le même principe de ” Fonction getfile(): ” SVP

    Reply
  4. Pingback: Using the Mega API: how to upload a file anonymously (without logging in). | Julien Marchand • Dev blog

  5. Mans

    Now i have this error :

    How i can repair ?

    Traceback (most recent call last):
    File “/var/www/vhosts/gamekeys.at/httpdocs2/mega/mega.py”, line 3, in
    from Crypto.Util import Counter
    ImportError: cannot import name Counter

    Kind regards

    Reply
  6. Fernando

    Hi guys,

    First of all, thanks for the article, it was very helpful.
    Now I’m trying to do the same in Java Code. I just want to get the file name and the file size.

    The steps I’m following are:

    - Making an HTTP JSON request to https://eu.api.mega.co.nz/cs?id=1 passing the JSON mentioned before {‘a’: ‘g’, ‘g’: 1, ‘p’: file_id} (this bit works fine).
    - Once I get the response from mega.co.nz, I’m trying to decrypt the attributes (at:) using the file key provided with the link.
    - To do this I:
    - Replace the characters mentioned in mega API “-” for “+”, “/” for “” and “_” for “/” in both strings (fileKey and encrypted attributes).
    - Decode Base64 of both strings.
    - Once I have the fileKey array of bytes I build the SecretKey using BouncyCastle and try to decrypt the encrypted attributes.

    Here I paste the source code:


    String fileKey = "dTnm8hpFg_nUOeCmSj2ocENur8cXsLzK-ChN7yVZ6sQ";
    String encAttributes = "xLzRcGj3WndnR071VOCMHrLRQB_ZHlayDEsfrG9-bPTYVcJp4aH0FmcB7ekVR-BP";

    fileKey += "==".substring(2 - fileKey.length() * 3 & 3);
    fileKey = fileKey.replaceAll("-", "+").replaceAll("/", "").replaceAll("_", "/");
    //fileKey now is "xLzRcGj3WndnR071VOCMHrLRQB/ZHlayDEsfrG9+bPTYVcJp4aH0FmcB7ekVR+BP"

    encAttributes += "==".substring(2 - encAttributes.length() * 3 & 3);
    encAttributes = encAttributes.replaceAll("-", "+").replaceAll("/", "").replaceAll("_", "/");
    //encAttributes now is "dTnm8hpFg/nUOeCmSj2ocENur8cXsLzK+ChN7yVZ6sQ="

    byte[] fileKeyBase64Decoded = Base64.decode(fileKey);
    byte[] encAttributesDecoded = Base64.decode(encAttributes);

    SecretKey sk = new SecretKeySpec(fileKeyBase64Decoded, "AES");
    KeyParameter key = new KeyParameter(sk.getEncoded());

    PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESLightEngine()), new PKCS7Padding());
    pbbc.init(false, key); //decrypt

    byte[] output = new byte[pbbc.getOutputSize(encAttributesDecoded.length)];
    int bytesWrittenOut = pbbc.processBytes(encAttributesDecoded, 0, encAttributesDecoded.length, output, 0);
    pbbc.doFinal(output, bytesWrittenOut); //FAILS HERE

    For some reason I’m getting a: org.bouncycastle.crypto.InvalidCipherTextException: pad block corrupted

    Anyone can tell me if I’m doing the proper steps or I am missing something?

    Thanks!! :)

    Reply
  7. shinchiro

    just wondering,let say I want to distribute my upload links but without the file key. In exchange I give out the file’s owner’s public RSA key. Can we make use of the public RSA key to get the file key for the file?Is imposible to do or not? :)

    Reply
  8. Esteban

    I’m an error in dec_attr. Apparently aes_cbc_decrypt returns some weird characters (other than null) appended to the string that json can not decode.

    Any fix for this?

    Reply
  9. Ahmed

    OK, take a look here, this is a sample of Julien’s work :


    from Crypto.Cipher import AES
    from Crypto.PublicKey import RSA
    from Crypto.Util import Counter

    import base64
    import binascii
    import json
    import os
    import random
    import struct
    import sys
    import urllib

    sid = ''
    seqno = random.randint(0, 0xFFFFFFFF)

    master_key = ''
    rsa_priv_key = ''

    def base64urldecode(data):
    data += '=='[(2 - len(data) * 3) % 4:]
    for search, replace in (('-', '+'), ('_', '/'), (',', '')):
    data = data.replace(search, replace)
    return base64.b64decode(data)

    def base64urlencode(data):
    data = base64.b64encode(data)
    for search, replace in (('+', '-'), ('/', '_'), ('=', '')):
    data = data.replace(search, replace)
    return data

    def a32_to_str(a):
    return struct.pack('>%dI' % len(a), *a)

    def a32_to_base64(a):
    return base64urlencode(a32_to_str(a))

    def str_to_a32(b):
    if len(b) % 4: # Add padding, we need a string with a length multiple of 4
    b += '' * (4 - len(b) % 4)
    return struct.unpack('>%dI' % (len(b) / 4), b)

    def base64_to_a32(s):
    return str_to_a32(base64urldecode(s))

    def aes_cbc_encrypt(data, key):
    encryptor = AES.new(key, AES.MODE_CBC, '' * 16)
    return encryptor.encrypt(data)

    def aes_cbc_decrypt(data, key):
    decryptor = AES.new(key, AES.MODE_CBC, '' * 16)
    return decryptor.decrypt(data)

    def aes_cbc_encrypt_a32(data, key):
    return str_to_a32(aes_cbc_encrypt(a32_to_str(data), a32_to_str(key)))

    def aes_cbc_decrypt_a32(data, key):
    return str_to_a32(aes_cbc_decrypt(a32_to_str(data), a32_to_str(key)))

    def stringhash(s, aeskey):
    s32 = str_to_a32(s)
    h32 = [0, 0, 0, 0]
    for i in xrange(len(s32)):
    h32[i % 4] ^= s32[i]
    for _ in xrange(0x4000):
    h32 = aes_cbc_encrypt_a32(h32, aeskey)
    return a32_to_base64((h32[0], h32[2]))

    def prepare_key(a):
    pkey = [0x93C467E3, 0x7DB0C7A4, 0xD1BE3F81, 0x0152CB56]
    for _ in xrange(0x10000):
    for j in xrange(0, len(a), 4):
    key = [0, 0, 0, 0]
    for i in xrange(4):
    if i + j < len(a):
    key[i] = a[i + j]
    pkey = aes_cbc_encrypt_a32(pkey, key)
    return pkey

    def encrypt_key(a, key):
    return sum((aes_cbc_encrypt_a32(a[i:i+4], key) for i in xrange(0, len(a), 4)), ())

    def decrypt_key(a, key):
    return sum((aes_cbc_decrypt_a32(a[i:i+4], key) for i in xrange(0, len(a), 4)), ())

    def mpi2int(s):
    return int(binascii.hexlify(s[2:]), 16)

    def api_req(req):
    global seqno
    url = 'https://g.api.mega.co.nz/cs?id=%d%s&#039; % (seqno, '&sid=%s' % sid if sid else '')
    seqno += 1
    return json.loads(post(url, json.dumps([req])))[0]

    def post(url, data):
    return urllib.urlopen(url, data).read()

    def login(email, password):
    global sid, master_key, rsa_priv_key
    password_aes = prepare_key(str_to_a32(password))
    uh = stringhash(email.lower(), password_aes)
    res = api_req({'a': 'us', 'user': email, 'uh': uh})

    enc_master_key = base64_to_a32(res['k'])
    master_key = decrypt_key(enc_master_key, password_aes)
    if 'tsid' in res:
    tsid = base64urldecode(res['tsid'])
    if a32_to_str(encrypt_key(str_to_a32(tsid[:16]), master_key)) == tsid[-16:]:
    sid = res['tsid']
    elif 'csid' in res:
    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 = [0, 0, 0, 0]

    for i in xrange(4):
    l = ((ord(privk[0]) * 256 + ord(privk[1]) + 7) / 8) + 2;
    rsa_priv_key[i] = mpi2int(privk[:l])
    privk = privk[l:]

    enc_sid = mpi2int(base64urldecode(res['csid']))
    decrypter = RSA.construct((rsa_priv_key[0] * rsa_priv_key[1], 0L, rsa_priv_key[2], rsa_priv_key[0], rsa_priv_key[1]))
    sid = '%x' % decrypter.key._decrypt(enc_sid)
    sid = binascii.unhexlify('0' + sid if len(sid) % 2 else sid)
    sid = base64urlencode(sid[:43])

    def enc_attr(attr, key):
    attr = 'MEGA' + json.dumps(attr)
    if len(attr) % 16:
    attr += '' * (16 - len(attr) % 16)
    return aes_cbc_encrypt(attr, a32_to_str(key))

    def dec_attr(attr, key):
    attr = aes_cbc_decrypt(attr, a32_to_str(key)).rstrip('')
    return json.loads(attr[4:]) if attr[:6] == 'MEGA{"' else False

    def get_chunks(size):
    chunks = {}
    p = pp = 0
    i = 1

    while i <= 8 and p < size - i * 0x20000:
    chunks[p] = i * 0x20000;
    pp = p
    p += chunks[p]
    i += 1

    while p < size:
    chunks[p] = 0x100000;
    pp = p
    p += chunks[p]

    chunks[pp] = size - pp
    if not chunks[pp]:
    del chunks[pp]

    return chunks

    def uploadfile(filename):
    infile = open(filename, 'rb')
    size = os.path.getsize(filename)
    ul_url = api_req({'a': 'u', 's': size})['p']

    ul_key = [random.randint(0, 0xFFFFFFFF) for _ in xrange(6)]
    encryptor = AES.new(a32_to_str(ul_key[:4]), AES.MODE_CTR, counter = Counter.new(128, initial_value = ((ul_key[4] << 32) + ul_key[5]) << 64))

    file_mac = [0, 0, 0, 0]
    for chunk_start, chunk_size in sorted(get_chunks(size).items()):
    chunk = infile.read(chunk_size)

    chunk_mac = [ul_key[4], ul_key[5], ul_key[4], ul_key[5]]
    for i in xrange(0, len(chunk), 16):
    block = chunk[i:i+16]
    if len(block) % 16:
    block += '' * (16 - len(block) % 16)
    block = str_to_a32(block)
    chunk_mac = [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, ul_key[:4])

    file_mac = [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, ul_key[:4])

    chunk = encryptor.encrypt(chunk)
    outfile = urllib.urlopen(ul_url + "/" + str(chunk_start), chunk)
    completion_handle = outfile.read()
    outfile.close()

    infile.close()

    meta_mac = (file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3])

    attributes = {'n': os.path.basename(filename)}
    enc_attributes = enc_attr(attributes, ul_key[:4])
    key = [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]]
    print api_req({'a': 'p', 't': root_id, 'n': [{'h': completion_handle, 't': 0, 'a': base64urlencode(enc_attributes), 'k': a32_to_base64(encrypt_key(key, master_key))}]})

    def downloadfile(file, attributes, k, iv, meta_mac):
    dl_url = api_req({'a': 'g', 'g': 1, 'n': file['h']})['g']

    infile = urllib.urlopen(dl_url)
    outfile = open(attributes['n'], 'wb')
    decryptor = AES.new(a32_to_str(k), AES.MODE_CTR, counter = Counter.new(128, initial_value = ((iv[0] << 32) + iv[1]) << 64))

    file_mac = [0, 0, 0, 0]
    for chunk_start, chunk_size in sorted(get_chunks(file['s']).items()):
    chunk = infile.read(chunk_size)
    chunk = decryptor.decrypt(chunk)
    outfile.write(chunk)

    chunk_mac = [iv[0], iv[1], iv[0], iv[1]]
    for i in xrange(0, len(chunk), 16):
    block = chunk[i:i+16]
    if len(block) % 16:
    block += '' * (16 - (len(block) % 16))
    block = str_to_a32(block)
    chunk_mac = [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 = [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)

    outfile.close()
    infile.close()

    if (file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3]) != meta_mac:
    print "MAC mismatch"

    def getfiles():
    global root_id, inbox_id, trashbin_id

    files = api_req({'a': 'f', 'c': 1})
    for file in files['f']:
    if file['t'] == 0 or file['t'] == 1:
    key = file['k'][file['k'].index(':') + 1:]
    key = decrypt_key(base64_to_a32(key), master_key)
    if file['t'] == 0:
    k = (key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6], key[3] ^ key[7])
    iv = key[4:6] + (0, 0)
    meta_mac = key[6:8]
    else:
    k = key
    attributes = base64urldecode(file['a'])
    attributes = dec_attr(attributes, k)
    print attributes['n']

    if file['h'] == '0wFEFCTa':
    downloadfile(file, attributes, k, iv, meta_mac)
    elif file['t'] == 2:
    root_id = file['h']
    elif file['t'] == 3:
    inbox_id = file['h']
    elif file['t'] == 4:
    trashbin_id = file['h']
    def base64_to_a32(s):
    return str_to_a32(base64urldecode(s))
    def getfile(file_id, file_key):
    key = base64_to_a32(file_key)
    k = (key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6], key[3] ^ key[7])
    iv = key[4:6] + (0, 0)
    meta_mac = key[6:8]

    file = api_req({'a': 'g', 'g': 1, 'p': file_id})
    dl_url = file['g']
    size = file['s']
    attributes = base64urldecode(file['at'])
    attributes = dec_attr(attributes, k)

    print "Downloading %s (size: %d), url = %s" % (attributes['n'], size, dl_url)

    infile = urllib.urlopen(dl_url)
    outfile = open(attributes['n'], 'wb')
    decryptor = AES.new(a32_to_str(k), AES.MODE_CTR, counter = Counter.new(128, initial_value = ((iv[0] < 32) + iv[1]) < 64))

    file_mac = [0, 0, 0, 0]
    for chunk_start, chunk_size in sorted(get_chunks(file['s']).items()):
    chunk = infile.read(chunk_size)
    chunk = decryptor.decrypt(chunk)
    outfile.write(chunk)

    chunk_mac = [iv[0], iv[1], iv[0], iv[1]]
    for i in xrange(0, len(chunk), 16):
    block = chunk[i:i+16]
    if len(block) % 16:
    block += '' * (16 - (len(block) % 16))
    block = str_to_a32(block)
    chunk_mac = [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 = [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)

    outfile.close()
    infile.close()

    if (file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3]) != meta_mac:
    print "MAC mismatch"
    else:
    print "MAC OK"
    getfile('cYMhSSCD', 'eBlJqgK4xh7RZoobDnXOCmGgZIMm1hYA4wtuVxCOZgA')

    This file download a mega file, but i always have an error in the file : “MAC mismatch”.

    In this example, this is an mp3, if you test it, it doesn’t work.

    Have you got an idea Julien ?

    Ahmed.

    Reply

Leave a Reply to foobar Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>