Closed ryancole closed 9 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:
@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!
I worked on @tyscorp gist that contained some data about keyframe format, you can find it at https://gist.github.com/jaagupkymmel/a36e07f4abb4b012c66e
Thats nice work. I've found some additions, too. Maybe we could put this doc into loldevs/leaguespec? @tyscorp ?
wow you are fast. Great work!
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?
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 .
@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.
@Valandur are you using unzip or gunzip? The data is gzipped before being encrypted.
@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?
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
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 :)
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.
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
It has to be a string.
@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.
@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.
gameId has to be a string.
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.
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
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.
How did your remove the padding from the encryptionKey?
You must not remove the encryption key padding.
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.
@jaagupkymmel I think this is an important ethical decision we ought to make ASAP. I would personally support either way.
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 ;)
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...
Why do we discuss this on another projects issues anyways? https://github.com/loldevs/leaguespec/issues/3
Hello,
I'm trying to understand your blowfish decryption of the
chunk
andkeyframe
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
, orkey
keys that you are using to extract the decryption key. I do see agameId
, which I assume is the same as the first, but I do not see a JSON metadata property namedkey
. 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!