robertabcd / lol-ob

a league of legends spectator mode game downloader
MIT License
83 stars 22 forks source link

Question #1

Closed ryancole closed 9 years ago

ryancole commented 11 years ago

Hello,

I'm trying to understand your blowfish decryption of the chunk and keyframe data. I had a few questions. The payload header contains an encryption key, but in decrypt.rb LN31-33 it looks like you're pulling the blowfish decryption key from the JSON metadata portion of the file.

To decrypt the keyframe and chunk data, we do not use the encryption key from the payload header, but instead we use a key derived from the JSON metadata - is this correct?

This is confusing me, because in the JSON metadata that I'm extracting from my own replay files, I do not see the gameKey.gameId, or key keys that you are using to extract the decryption key. I do see a gameId, which I assume is the same as the first, but I do not see a JSON metadata property named key. Have these changed, or am I not understanding your code? I'm not a Ruby programmer and your code is pretty straightforward, but I may be misinterpreting something.

Thanks!

snowl commented 10 years ago

Hey guys - I'm super interested in this... including replay analysis in my client would be super awesome :)

I'd be willing to help at all. While my reverse decompiling isn't the best, I can still help out if needed :P I'll be in the IRC channel (and also in #riotcontrol, which is where other loldevs hang out). Looks like stuff is getting super interesting from when I checked last time :+1:

Zero3 commented 10 years ago

@Divi Yup, that's what I'm talking about :).

@tyscorp Awesome. Somebody should make a good test set of replays. One could do some simple things like picking specific champions, buying specific items, placing wards at specific positions, ... Each replay could then be accompanied by a description stating exactly what was done, which could aid the decoding process significantly.

@themasch @jaagupkymmel Since we are doing similar things, I figured I'd release my code for everyone to play around with. See https://github.com/Zero3/LolSpecAnalyzer. I've implemented heuristics for finding strings and timestamps so far.

@trebonius2 Very nice. I will try implementing an analyzer for that structure in my framework when I get the time.

Random knowledge dump: Besides the two different kinds of strings (null-terminated and length-prefixed) and timestamps I've found (see my framework), I noticed that some of the strings were clustered together in a map-like structure prefixed with the number of elements. We are obviously dealing with a lot of data serialization here, so it would be interesting to figure out if they are using a known format (see for example https://en.wikipedia.org/wiki/Comparison_of_data_serialization_formats#Comparison_of_binary_formats). If they are, we could save an enormous amount of time on the reverse engineering!

jaagupkymmel commented 10 years ago

I worked on @tyscorp gist that contained some data about keyframe format, you can find it at https://gist.github.com/jaagupkymmel/a36e07f4abb4b012c66e

themasch commented 10 years ago

Thats nice work. I've found some additions, too. Maybe we could put this doc into loldevs/leaguespec? @tyscorp ?

tyscorp commented 10 years ago

https://github.com/loldevs/leaguespec/wiki/Keyframe-Specification

themasch commented 10 years ago

wow you are fast. Great work!

Valandur commented 10 years ago

Hey guys, thought I'd join in on the discussion because this sounds rather interesting. @themasch You asked in an earlier comment if the REST API was using the same kind of encryption method as the .rofl files. Then you edited saying that you confused the key with the password for the blowfish decipherer. I have tried doing the same, by taking a game from the /featured list, which already contains the encryption keys, but I then keep getting an incorret header check after decyphering when unzipping. Can you elaborate on how you went about doing this?

tyscorp commented 10 years ago

Are you decrypting the "encryption key" with the gameId to get the real key?

On Tuesday, 4 February 2014, Valandur notifications@github.com wrote:

Hey guys, thought I'd join in on the discussion because this sounds rather interesting. @themasch https://github.com/themasch You asked in an earlier comment if the REST API was using the same kind of encryption method as the .rofl files. Then you edited saying that you confused the key with the password for the blowfish decipherer. I have tried doing the same, by taking a game from the /featured list, which already contains the encryption keys, but I then keep getting an incorret header check after decyphering when unzipping. Can you elaborate on how you went about doing this?

Reply to this email directly or view it on GitHubhttps://github.com/robertabcd/lol-ob/issues/1#issuecomment-33978457 .

Valandur commented 10 years ago

@tyscorp Yes. If I understood the documentation correctly you use the gameId to decrpyt the base64-decoded "encryption key", then use this key to decrypt the chunks and keyframes.

tyscorp commented 10 years ago

@Valandur are you using unzip or gunzip? The data is gzipped before being encrypted.

Valandur commented 10 years ago

@tyscorp I'm using gunzip. First I decrypt the data as decribed above (Blowfish in ECB mode, with Pkcs5/Pkcs7 padding), then I try to unzip it. According to the gzip file specifications the magic number (first two bytes) should be 0x1f followed by 0x8b, which I never actually get.

Maybe an example would help - taken from one of the featured games. The game has the id

751095183

The encryption key is

vzRVWdZRzBKby4BiBvVOaaMHVNe6TITq

or converted from base64 to a byte array in hex notation

BF-34-55-59-D6-51-CC-12-9B-CB-80-62-06-F5-4E-69-A3-07-54-D7-BA-4C-84-EA

For me this yields a real encryption key of

CA-7A-8F-3E-20-DB-BC-CD-6F-0C-0E-0C-E3-E5-73-B1

what would your decrypted real key be?

tyscorp commented 10 years ago

I get

C2-8A-5E-48-C3-AC-C3-9F-C3-80-C3-A5-1D-2E-49-19-C3-BB-C3-B8-09-47-C3-A6
Valandur commented 10 years ago

Ok so it looks like I'm doing something wrong when decrypting the key. Maybe I'm using some weird Blowfish algorithm, or I'm not using the class how it's meant to be used. I'll look into it, thanks for your help so far :)

tyscorp commented 10 years ago

I had problems where I was using the wrong input format. Converting the observerEncryptionKey from base64 to binary didn't seem to work. I ended up using the "base64" flag in my encryption lib instead of converting it myself.

Valandur commented 10 years ago

Hmm, I think I'm just like, being stupid right now :/ Are you interpreting the game id as a 64bit integer? so the game id mentioned above would become

8F-CD-C4-2C-00-00-00-00

tyscorp commented 10 years ago

It has to be a string.

Divi commented 10 years ago

@Valandur You have your encryption key (as string, like @tyscorp said), base64 decode on it, and Blowfish ECB with padding#5 with game id as the key. Do not unzip the result.

After, do a blowfish ECB padding#5 on chunk/keyframe with the decrypted result string above, then GZ decode it.

Valandur commented 10 years ago

@tyscorp @Divi What I meant with my previous comment is that the blowfish decryption library I'm using requires your key to be a byte array. So, I take the game id as a 64bit integer, and convert it to a byte array. Then I pass that array as the key of the blowfish algorithm to my decrypter, which decrypts the base64-decoded observerEncryptionKey. And this is gives me the "real" decryption key, which seems to differ from what it should actually be.

tyscorp commented 10 years ago

gameId has to be a string.

robertabcd commented 10 years ago

As @tyscorp said, you have to convert gameId into string (eg. "751095183"). Blowfish supports variable key length, most implementation just repeats the bytes until it fits in.

themasch commented 10 years ago

Thats the code I'm using in node.js:

function decrypt(pass, data) {
    if(!Buffer.isBuffer(pass)) {
        pass  = new Buffer(pass.toString());
    }
    var decipher  = crypto.createDecipheriv('bf-ecb', pass, "")
    var inBuf = new Buffer(data, 'base64');
    decipher.write(inBuf);
    return decipher.read();
}

Related doc entry: http://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password

Valandur commented 10 years ago

Thanks a lot for the help guys, was able to fix the problem. It was a combination of not removing the padding from the encryptionKey after decrypting it, and not using the gameId as a string.

snowl commented 10 years ago

How did your remove the padding from the encryptionKey?

Divi commented 10 years ago

You must not remove the encryption key padding.

jaagupkymmel commented 10 years ago

May I suggest making the wiki private, atleast for the time being. I'm fairly certain none of us want this information to get in hands of people who might use it to create malicious content and I don't think Riot would be too happy to see that someone can read all their spectator data, especially if they share it publicly.

We should still give everyone who wishes to contribute access to it.

Zero3 commented 10 years ago

@jaagupkymmel I think this is an important ethical decision we ought to make ASAP. I would personally support either way.

themasch commented 10 years ago

I'm really against close-sourceing this. This started as a public project, done by and done for the community. There may be ways to misuse this data but currently I don't see any evil things to do with what we got yet.

We should still give everyone who wishes to contribute access to it. How should this protect the information from getting misused?

But this isn't my project and I might be the least relevant contributor so I'd really like to hear some more opinions ( @robertabcd, @tyscorp, @trebonius2, @Divi ... )

If the project members decide to hide this, I won't stop them ;)

lukegb commented 10 years ago

I disagree with close-sourcing this. It's useful information - it's nothing especially private; after all, League Replays does this sort of thing (but much more coarsely, I admit) and they haven't been explicitly killed.

Doesn't mean I won't advise that we keep individual copies of the wiki git repository though, and mirror that offsite...

themasch commented 10 years ago

Why do we discuss this on another projects issues anyways? https://github.com/loldevs/leaguespec/issues/3