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:
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:~$Google+
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?
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:
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]).
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 ??
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
HI Mans,
Do you found a solution for this problem ? because i have the same.
Thanks in advance.
Do you know how anonymous uploads work?
From the web UI you don’t necessarily have to log in to upload a file.
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.
I was also looking for this. A brief example would be great, thanks a lot!
Okay, this does the trick:
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:
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:
Interesting, thanks a lot for your time
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 ?
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 ?
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
Oui mais celà compliquera déjà les choses !
As tu une idée que comment faire là chose ?
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
).
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
Pingback: Using the Mega API: how to upload a file anonymously (without logging in). | Julien Marchand • Dev blog
Now i have this error :
How i can repair ?
Kind regards
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!!
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?
Does anybody know a PHP code for that?
Can you do this in php? how?
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?
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' % (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.