Esempio n. 1
0
        public static void Main(String[] args)
        {
            //Security.addProvider(new Bouncy());
            if ((args.Length < 3))
            {
                usage();
                return;
            }

            String mode = args[0];

            if ((!"pack".Equals(mode) &&
                 (!"unpack".Equals(mode) &&
                  !"pack-kk".Equals(mode))))
            {
                usage();
                return;
            }

            bool   unpack         = "unpack".Equals(mode);
            String backupFilename = unpack ? args[1] : args[2];
            // TODO: Warning!!!, inline IF is not supported ?
            String tarFilename = unpack ? args[2] : args[1];
            // TODO: Warning!!!, inline IF is not supported ?
            String password = null;

            if ((args.Length > 3))
            {
                password = args[3];
            }

            if ((password == null))
            {
                password = Environment.GetEnvironmentVariable("ABE_PASSWD");
            }

            if (unpack)
            {
                try
                {
                    AndroidBackup.extractAsTar(backupFilename, tarFilename, password);
                }
                catch (InvalidCipherTextException exception)
                {
                    Console.WriteLine($"Error: {exception.Message} , Check supplied password");
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Error: {e.Message}");
                }
            }
            else
            {
                bool isKitKat = "pack-kk".Equals(mode);
                AndroidBackup.packTar(tarFilename, backupFilename, password, isKitKat);
            }
        }
Esempio n. 2
0
 public static byte[] buildCharArrayKey(char[] pwArray, byte[] salt, int rounds, bool useUtf8)
 {
     //  Original code from BackupManagerService
     //  this produces different results when run with Sun/Oracale Java SE
     //  which apparently treats password bytes as UTF-8 (16?)
     //  (the encoding is left unspecified in PKCS#5)
     //  try {
     //  byte[]Factory keyFactory = byte[]Factory
     //  .getInstance("PBKDF2WithHmacSHA1");
     //  KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
     //  return keyFactory.generateSecret(ks);
     //  } catch (InvalidKeySpecException e) {
     //  throw new RuntimeException(e);
     //  } catch (NoSuchAlgorithmException e) {
     //  throw new RuntimeException(e);
     //  } catch (NoSuchProviderException e) {
     //  throw new RuntimeException(e);
     //  }
     //  return null;
     return(AndroidBackup.androidPBKDF2(pwArray, salt, rounds, useUtf8));
 }
Esempio n. 3
0
 private static byte[] buildPasswordKey(String pw, byte[] salt, int rounds, bool useUtf8)
 {
     return(AndroidBackup.buildCharArrayKey(pw.ToCharArray(), salt, rounds, useUtf8));
 }
Esempio n. 4
0
        public static void extractAsTar(String backupFilename, String filename, String password)
        {
            try
            {
                Stream inStream = AndroidBackup.getInputStream(backupFilename);

                CipherStream cipherStream = null;
                String       magic        = AndroidBackup.readHeaderLine(inStream); //  1
                if (DEBUG)
                {
                    Console.WriteLine(("Magic: " + magic));
                }

                String versionStr = AndroidBackup.readHeaderLine(inStream);  //  2
                if (DEBUG)
                {
                    Console.WriteLine(("Version: " + versionStr));
                }

                int version = int.Parse(versionStr);
                if (((version < BACKUP_FILE_V1) ||
                     (version > BACKUP_FILE_V4)))
                {
                    throw new ArgumentException("Don\'t know how to process version " + versionStr);
                }

                String compressed = AndroidBackup.readHeaderLine(inStream);  //  3

                bool isCompressed = (int.Parse(compressed) == 1);
                if (DEBUG)
                {
                    Console.WriteLine(("Compressed: " + compressed));
                }

                String encryptionAlg = AndroidBackup.readHeaderLine(inStream); //  4
                if (DEBUG)
                {
                    Console.WriteLine(("Algorithm: " + encryptionAlg));
                }

                bool isEncrypted = false;
                if (encryptionAlg.Equals(ENCRYPTION_ALGORITHM_NAME))
                {
                    isEncrypted = true;


                    //if ((Cipher.getMaxAllowedKeyLength("AES") < MASTER_KEY_SIZE))
                    //{
                    //    Console.WriteLine("WARNING: Maximum allowed key-Length seems smaller than needed. " +
                    //             "Please check that unlimited strength cryptography is available, see README.md for details");
                    //}

                    if (((password == null) ||
                         "".Equals(password)))
                    {
                        Console.WriteLine("This backup is encrypted, please provide the password: "******"Invalid salt Length: " + userSalt.Length));
                    }

                    String ckSaltHex = AndroidBackup.readHeaderLine(inStream); //  6
                    byte[] ckSalt    = AndroidBackup.hexToByteArray(ckSaltHex);

                    int    rounds    = int.Parse(AndroidBackup.readHeaderLine(inStream)); //  7
                    String userIvHex = AndroidBackup.readHeaderLine(inStream);            //  8

                    String masterKeyBlobHex = AndroidBackup.readHeaderLine(inStream);     //  9

                    //  decrypt the master key blob
                    IBufferedCipher c = CipherUtilities.GetCipher(ENCRYPTION_MECHANISM);

                    //  XXX we don't support non-ASCII passwords
                    byte[] userKey = AndroidBackup.buildPasswordKey(password, userSalt, rounds, false);
                    byte[] IV      = AndroidBackup.hexToByteArray(userIvHex);
                    byte[] ivSpec  = IV;

                    c.Init(false, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES256", userKey), ivSpec));
                    //c.Init(false, new ParametersWithIV(new KeyParameter(userKey), ivSpec));

                    byte[] mkCipher = AndroidBackup.hexToByteArray(masterKeyBlobHex);
                    byte[] mkBlob   = c.DoFinal(mkCipher);
                    //  first, the master key IV
                    int offset = 0;
                    int len    = mkBlob[offset++];
                    IV = Arrays.CopyOfRange(mkBlob, offset, (offset + len));

                    if (DEBUG)
                    {
                        Console.WriteLine(("IV: " + AndroidBackup.toHex(IV)));
                    }

                    offset = (offset + len);
                    //  then the master key itself
                    len = mkBlob[offset++];
                    byte[] mk = Arrays.CopyOfRange(mkBlob, offset, (offset + len));
                    if (DEBUG)
                    {
                        Console.WriteLine(("MK: " + AndroidBackup.toHex(mk)));
                    }

                    offset = (offset + len);
                    //  and finally the master key checksum hash
                    len = mkBlob[offset++];
                    byte[] mkChecksum = Arrays.CopyOfRange(mkBlob, offset, (offset + len));
                    if (DEBUG)
                    {
                        Console.WriteLine(("MK checksum: " + AndroidBackup.toHex(mkChecksum)));
                    }

                    //  now validate the decrypted master key against the checksum
                    //  first try the algorithm matching the archive version
                    bool   useUtf       = (version >= BACKUP_FILE_V2);
                    byte[] calculatedCk = AndroidBackup.makeKeyChecksum(mk, ckSalt, rounds, useUtf);
                    Console.Error.WriteLine("Calculated MK checksum (use UTF-8: {0}): {1}\n", useUtf,
                                            AndroidBackup.toHex(calculatedCk));
                    if (!Arrays.Equals(calculatedCk, mkChecksum))
                    {
                        Console.WriteLine("Checksum does not match.");
                        //  try the reverse
                        calculatedCk = AndroidBackup.makeKeyChecksum(mk, ckSalt, rounds, !useUtf);
                        Console.Error.WriteLine("Calculated MK checksum (use UTF-8: {0}): {1}\n", useUtf,
                                                AndroidBackup.toHex(calculatedCk));
                    }


                    // Even if checksum doesn't match, it works .. No idea why.//
                    //  if (Arrays.Equals(calculatedCk, mkChecksum))
                    {
                        ivSpec = IV;

                        c.Init(false, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES256", mk), ivSpec));
                        //c.init(Cipher.DECRYPT_MODE, new byte[]Spec(mk, "AES"), ivSpec);
                        //  Only if all of the above worked properly will 'result' be
                        //  assigned
                        cipherStream = new CipherStream(inStream, c, null);
                    }
                }

                if ((isEncrypted &&
                     (cipherStream == null)))
                {
                    throw new Exception("Invalid password or master key checksum.");
                }

                Stream baseStream = isEncrypted ? cipherStream : inStream;
                Stream input      = isCompressed ? new ZInputStream(baseStream) : baseStream;
                Stream output     = null;

                try
                {
                    output = getOutputStream(filename);
                    byte[] buff      = new byte[(10 * 1024)];
                    int    read      = -1;
                    int    totalRead = 0;
                    while ((read = input.Read(buff, 0, buff.Length)) > 0)
                    {
                        output.Write(buff, 0, read);
                        totalRead = (totalRead + read);
                        if ((DEBUG &&
                             ((totalRead % (100 * 1024))
                              == 0)))
                        {
                            Console.Error.WriteLine("{0} bytes written\n", totalRead);
                        }
                    }
                    Console.Error.WriteLine("{0} bytes written to {1}.\n", totalRead, backupFilename);
                }
                finally
                {
                    if (input != null)
                    {
                        input.Close();
                    }

                    if (output != null)
                    {
                        output.Flush();
                        output.Close();
                    }
                }
            }
            catch (Exception e)
            {
                throw;
            }
        }
Esempio n. 5
0
        public static byte[] makeKeyChecksum(byte[] pwBytes, byte[] salt, int rounds, bool useUtf8)
        {
            if (DEBUG)
            {
                Console.WriteLine(("key bytes: " + AndroidBackup.toHex(pwBytes)));
                Console.WriteLine(("salt bytes: " + AndroidBackup.toHex(salt)));
            }

            // From https://github.com/lclevy/ab_decrypt

            /***
             *     because of byte to Java char before using password data as PBKDF2 key, special handling is required
             *     from: https://android.googlesource.com/platform/frameworks/base/+/master/services/backup/java/com/android/server/backup/BackupManagerService.java
             *     private byte[] makeKeyChecksum(byte[] pwBytes, byte[] salt, int rounds)
             *         {
             *             char[] mkAsChar = new char[pwBytes.length];
             *             for (int i = 0; i < pwBytes.length; i++)
             *             {
             *                 mkAsChar[i] = (char)pwBytes[i];               <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< HERE
             *             }
             *             Key checksum = buildCharArrayKey(mkAsChar, salt, rounds);
             *             return checksum.getEncoded();
             *         }
             *
             *         Java byte to char conversion(as "Widening and Narrowing Primitive Conversion") is defined here:
             *         https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.4
             *         First, the byte is converted to an int via widening primitive conversion(chapter 5.1.2),
             *         and then the resulting int is converted to a char by narrowing primitive conversion(chapter 5.1.3)
             ***/

            // Widening Primitive Conversion : https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.2
            sbyte[] mkAsSigned = new sbyte[pwBytes.Length];  //sign extension
            for (int i = 0; (i < pwBytes.Length); i++)
            {
                mkAsSigned[i] = ((sbyte)(pwBytes[i]));
            }

            // Narrowing Primitive Conversion : https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.3
            ushort[] unSigned16Bits = new ushort[mkAsSigned.Length];
            for (int i = 0; (i < mkAsSigned.Length); i++)
            {
                unSigned16Bits[i] = (ushort)((mkAsSigned[i]) & 0xffff);
            }

            /***
             * The Java programming language represents text in sequences of 16 - bit code UNITS, using the UTF-16 encoding.
             * https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.1
             ***/
            var byteArray = unSigned16Bits.SelectMany(x => BitConverter.GetBytes(x)).ToArray();


            /***
             * https://developer.android.com/reference/javax/crypto/spec/PBEKeySpec.html
             \"Different PBE mechanisms may consume different bits of each password character.
             * For example, the PBE mechanism defined in PKCS #5 looks at only the low order 8 bits of each character,
             * whereas PKCS #12 looks at all 16 bits of each character. \"
             ***/
            var curr = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, byteArray);

            // COnverting to char array
            char[] mkAsChar = new char[curr.Length];
            for (int i = 0; (i < curr.Length); i++)
            {
                mkAsChar[i] = ((char)(curr[i]));
            }


            if (DEBUG)
            {
                Console.WriteLine(String.Format("MK as string: {0}\n", new String(mkAsChar)));
            }

            byte[] checksum = AndroidBackup.buildCharArrayKey(mkAsChar, salt, rounds, useUtf8);

            if (DEBUG)
            {
                Console.WriteLine(("Key format: " + toHex(checksum)));
            }

            return(checksum);
        }
Esempio n. 6
0
        private static Stream emitAesBackupHeader(StringBuilder headerbuf, Stream ofstream,
                                                  String encryptionPassword, bool useUtf8)
        {
            //  User key will be used to encrypt the master key.
            byte[] newUserSalt = AndroidBackup.randomBytes(PBKDF2_SALT_SIZE);
            byte[] userKey     = AndroidBackup.buildPasswordKey(encryptionPassword, newUserSalt, PBKDF2_HASH_ROUNDS, useUtf8);
            //  the master key is random for each backup
            byte[] masterPw = new byte[(MASTER_KEY_SIZE / 8)];
            random.NextBytes(masterPw);
            byte[] checksumSalt = AndroidBackup.randomBytes(PBKDF2_SALT_SIZE);


            ////  primary encryption of the datastream with the random key
            //IBufferedCipher c = CipherUtilities.GetCipher("AES");
            //AesEngine aes = new AesEngine();

            //var spec1 = ParameterUtilities.CreateKeyParameter("AES256", masterPw);
            ////var parameters1 = new ParametersWithIV(spec1, new byte[128]); // Block size  == IV size == 16 in BouncyCastle for AES
            //c.Init(true, spec1);

            AesEngine      engine      = new AesEngine();
            CbcBlockCipher blockCipher = new CbcBlockCipher(engine);                       //CBC

            PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(blockCipher); //Default scheme is PKCS5/PKCS7

            KeyParameter     keyParam       = new KeyParameter(masterPw);
            ParametersWithIV keyParamWithIV = new ParametersWithIV(keyParam, AndroidBackup.randomBytes(blockCipher.GetBlockSize() * 8), 0, blockCipher.GetBlockSize());

            // Encrypt
            cipher.Init(true, keyParamWithIV);

            Stream finalOutput = new CipherStream(ofstream, null, cipher);

            //  line 4: name of encryption algorithm
            headerbuf.Append(ENCRYPTION_ALGORITHM_NAME);
            headerbuf.Append('\n');
            //  line 5: user password salt [hex]
            headerbuf.Append(AndroidBackup.toHex(newUserSalt));
            headerbuf.Append('\n');
            //  line 6: master key checksum salt [hex]
            headerbuf.Append(AndroidBackup.toHex(checksumSalt));
            headerbuf.Append('\n');
            //  line 7: number of PBKDF2 rounds used [decimal]
            headerbuf.Append(PBKDF2_HASH_ROUNDS);
            headerbuf.Append('\n');
            //  line 8: IV of the user key [hex]
            //IBufferedCipher mkC = CipherUtilities.GetCipher(ENCRYPTION_MECHANISM);
            //Cipher mkC = Cipher.getInstance(ENCRYPTION_MECHANISM);
            //mkC.init(Cipher.ENCRYPT_MODE, userKey);


            AesEngine                 engine2         = new AesEngine();
            CbcBlockCipher            blockCipher2    = new CbcBlockCipher(engine2);                 //CBC
            PaddedBufferedBlockCipher mkC             = new PaddedBufferedBlockCipher(blockCipher2); //Default scheme is PKCS5/PKCS7
            KeyParameter              keyParam2       = new KeyParameter(userKey);
            ParametersWithIV          keyParamWithIV2 = new ParametersWithIV(keyParam2, AndroidBackup.randomBytes(blockCipher.GetBlockSize() * 8), 0, blockCipher2.GetBlockSize());

            // Encrypt
            mkC.Init(true, keyParamWithIV2);


            byte[] IV = keyParamWithIV2.GetIV();



            headerbuf.Append(AndroidBackup.toHex(IV));
            headerbuf.Append('\n');
            //  line 9: master IV + key blob, encrypted by the user key [hex]. Blob
            //  format:
            //  [byte] IV Length = Niv
            //  [array of Niv bytes] IV itself
            //  [byte] master key Length = Nmk
            //  [array of Nmk bytes] master key itself
            //  [byte] MK checksum hash Length = Nck
            //  [array of Nck bytes] master key checksum hash
            //
            //  The checksum is the (master key + checksum salt), run through the
            //  stated number of PBKDF2 rounds
            IV = keyParamWithIV.GetIV();
            byte[] mk       = keyParam.GetKey();
            byte[] checksum = AndroidBackup.makeKeyChecksum(mk, checksumSalt, PBKDF2_HASH_ROUNDS,
                                                            useUtf8);



            MemoryStream mkOut = new MemoryStream(IV.Length + (mk.Length + (checksum.Length + 3)));

            //DataOutputStream mkOut = new DataOutputStream(blob);

            mkOut.WriteByte((byte)IV.Length);
            mkOut.Write(IV, 0, IV.Length);
            mkOut.WriteByte((byte)mk.Length);
            mkOut.Write(mk, 0, mk.Length);
            mkOut.WriteByte((byte)checksum.Length);
            mkOut.Write(checksum, 0, checksum.Length);
            mkOut.Flush();
            byte[] encryptedMk = mkC.DoFinal(mkOut.GetBuffer());
            headerbuf.Append(AndroidBackup.toHex(encryptedMk));
            headerbuf.Append('\n');
            return(finalOutput);
        }