Last active
September 30, 2015 04:59
-
-
Save sweis/eb9d9a4b43946b685208 to your computer and use it in GitHub Desktop.
Cryptfs from Secret's Android App
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//package ly.secret.android.net; | |
/** | |
* This is source code derived from the Cryptfs class found in Secret.ly's android app. | |
* | |
* Comments and variable names are my own. | |
* | |
* Here's how I generated this file: | |
* 1. Downloaded the Secret.ly APK via this link: | |
* http://storage.evozi.com/apk/dl/14/05/21/ly.secret.android.apk?vc=1600040 | |
* 2. Unzipped the APK | |
* 3. Used dex2jar (https://code.google.com/p/dex2jar/) to convert the | |
* classes.dex to a jar file | |
* 4. Unpacked the jar | |
* 5. Looked for instances of "aes", "sha-1", "md5", etc. in the ly.secret package: | |
* $ find . -name "*.class" | grep "ly" | xargs javap -c | less | |
* 6. Noticed the ly/secret/android/net/Crypts.class | |
* 7. Used JD-GUI (http://jd.benow.ca/) to generate some source code | |
* 8. Manually renamed variables and cleaned up code to make it readable. | |
*/ | |
import java.security.MessageDigest; | |
import javax.crypto.Cipher; | |
import javax.crypto.Mac; | |
import javax.crypto.spec.IvParameterSpec; | |
import javax.crypto.spec.SecretKeySpec; | |
final class Crypts { | |
private static final byte[] ZERO_PADDING = new byte[64]; | |
static byte[] encrypt(byte[] inputArray, byte[] keyValue) throws Exception { | |
byte[] sha256HashOfKeyValue = | |
MessageDigest.getInstance("SHA-256").digest(keyValue); | |
// Why are they padding with zeros? | |
byte[] inputPaddedWithZeros = mergeArrays(inputArray, ZERO_PADDING); | |
// Using a fixed zero-value IV for CBC | |
byte[] IV = new byte[16]; | |
// This throws a NoSuchAlgorithmException for the default Java 6 and 7 | |
// providers due to the PKCS7 padding | |
Cipher aesCbcCipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); | |
// Maybe they're using a different provider or this class is never used. | |
// I had to replace PKCS7 with PKCS5 to get it to work: | |
// Cipher aesCbcCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); | |
aesCbcCipher.init(Cipher.ENCRYPT_MODE, | |
new SecretKeySpec(sha256HashOfKeyValue, "AES"), | |
new IvParameterSpec(IV)); | |
byte[] ciphertext = aesCbcCipher.doFinal(inputPaddedWithZeros); | |
// Using the same key for MAC as encryption | |
SecretKeySpec hmacMD5Key = new SecretKeySpec(sha256HashOfKeyValue, "HmacMD5"); | |
Mac localMac = Mac.getInstance(hmacMD5Key.getAlgorithm()); | |
localMac.init(hmacMD5Key); | |
// Forgot to HMAC the IV. | |
// Also they are MACing the plaintext, not the ciphertext. | |
localMac.update(inputPaddedWithZeros); | |
byte[] hmac = localMac.doFinal(); | |
return mergeArrays(ciphertext, hmac); | |
} | |
// This method was generated from com.google.common.primitives.Bytes | |
// Merged and made readable manually. | |
static byte[] mergeArrays(byte[]... arraysToMerge) { | |
int totalLen = 0; | |
for (int i = 0; i < arraysToMerge.length; i++) { | |
totalLen += arraysToMerge[i].length; | |
} | |
byte[] outputArray = new byte[totalLen]; | |
int outputIndexPosition = 0; | |
for (int i = 0; i < arraysToMerge.length; i++) { | |
System.arraycopy(arraysToMerge[i], 0, | |
outputArray, outputIndexPosition, | |
arraysToMerge[i].length); | |
outputIndexPosition += arraysToMerge[i].length; | |
} | |
return outputArray; | |
} | |
public static void main(String[] args) throws Exception { | |
byte[] ciphertext = encrypt("hello".getBytes(), "world".getBytes()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment