|
@@ -1,236 +0,0 @@
|
|
|
-package org.las2mile.scrcpy.adblib;
|
|
|
|
|
-
|
|
|
|
|
-import java.io.FileInputStream;
|
|
|
|
|
-import java.io.FileOutputStream;
|
|
|
|
|
-import java.io.IOException;
|
|
|
|
|
-import java.math.BigInteger;
|
|
|
|
|
-import java.nio.ByteBuffer;
|
|
|
|
|
-import java.nio.ByteOrder;
|
|
|
|
|
-import java.security.GeneralSecurityException;
|
|
|
|
|
-import java.security.KeyFactory;
|
|
|
|
|
-import java.security.KeyPair;
|
|
|
|
|
-import java.security.KeyPairGenerator;
|
|
|
|
|
-import java.security.NoSuchAlgorithmException;
|
|
|
|
|
-import java.security.interfaces.RSAPublicKey;
|
|
|
|
|
-import java.security.spec.EncodedKeySpec;
|
|
|
|
|
-import java.security.spec.InvalidKeySpecException;
|
|
|
|
|
-import java.security.spec.PKCS8EncodedKeySpec;
|
|
|
|
|
-import java.security.spec.X509EncodedKeySpec;
|
|
|
|
|
-
|
|
|
|
|
-import javax.crypto.Cipher;
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * This class encapsulates the ADB cryptography functions and provides
|
|
|
|
|
- * an interface for the storage and retrieval of keys.
|
|
|
|
|
- *
|
|
|
|
|
- * @author Cameron Gutman
|
|
|
|
|
- */
|
|
|
|
|
-public class AdbCrypto {
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * The ADB RSA key length in bits
|
|
|
|
|
- */
|
|
|
|
|
- public static final int KEY_LENGTH_BITS = 2048;
|
|
|
|
|
- /**
|
|
|
|
|
- * The ADB RSA key length in bytes
|
|
|
|
|
- */
|
|
|
|
|
- public static final int KEY_LENGTH_BYTES = KEY_LENGTH_BITS / 8;
|
|
|
|
|
- /**
|
|
|
|
|
- * The ADB RSA key length in words
|
|
|
|
|
- */
|
|
|
|
|
- public static final int KEY_LENGTH_WORDS = KEY_LENGTH_BYTES / 4;
|
|
|
|
|
- /**
|
|
|
|
|
- * The RSA signature padding as an int array
|
|
|
|
|
- */
|
|
|
|
|
- public static final int[] SIGNATURE_PADDING_AS_INT = new int[]
|
|
|
|
|
- {
|
|
|
|
|
- 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
|
|
|
|
- 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00,
|
|
|
|
|
- 0x04, 0x14
|
|
|
|
|
- };
|
|
|
|
|
- /**
|
|
|
|
|
- * The RSA signature padding as a byte array
|
|
|
|
|
- */
|
|
|
|
|
- public static byte[] SIGNATURE_PADDING;
|
|
|
|
|
-
|
|
|
|
|
- static {
|
|
|
|
|
- SIGNATURE_PADDING = new byte[SIGNATURE_PADDING_AS_INT.length];
|
|
|
|
|
-
|
|
|
|
|
- for (int i = 0; i < SIGNATURE_PADDING.length; i++)
|
|
|
|
|
- SIGNATURE_PADDING[i] = (byte) SIGNATURE_PADDING_AS_INT[i];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * An RSA keypair encapsulated by the AdbCrypto object
|
|
|
|
|
- */
|
|
|
|
|
- private KeyPair keyPair;
|
|
|
|
|
- /**
|
|
|
|
|
- * The base 64 conversion interface to use
|
|
|
|
|
- */
|
|
|
|
|
- private AdbBase64 base64;
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Converts a standard RSAPublicKey object to the special ADB format
|
|
|
|
|
- *
|
|
|
|
|
- * @param pubkey RSAPublicKey object to convert
|
|
|
|
|
- * @return Byte array containing the converted RSAPublicKey object
|
|
|
|
|
- */
|
|
|
|
|
- private static byte[] convertRsaPublicKeyToAdbFormat(RSAPublicKey pubkey) {
|
|
|
|
|
- /*
|
|
|
|
|
- * ADB literally just saves the RSAPublicKey struct to a file.
|
|
|
|
|
- *
|
|
|
|
|
- * typedef struct RSAPublicKey {
|
|
|
|
|
- * int len; // Length of n[] in number of uint32_t
|
|
|
|
|
- * uint32_t n0inv; // -1 / n[0] mod 2^32
|
|
|
|
|
- * uint32_t n[RSANUMWORDS]; // modulus as little endian array
|
|
|
|
|
- * uint32_t rr[RSANUMWORDS]; // R^2 as little endian array
|
|
|
|
|
- * int exponent; // 3 or 65537
|
|
|
|
|
- * } RSAPublicKey;
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- /* ------ This part is a Java-ified version of RSA_to_RSAPublicKey from adb_host_auth.c ------ */
|
|
|
|
|
- BigInteger r32, r, rr, rem, n, n0inv;
|
|
|
|
|
-
|
|
|
|
|
- r32 = BigInteger.ZERO.setBit(32);
|
|
|
|
|
- n = pubkey.getModulus();
|
|
|
|
|
- r = BigInteger.ZERO.setBit(KEY_LENGTH_WORDS * 32);
|
|
|
|
|
- rr = r.modPow(BigInteger.valueOf(2), n);
|
|
|
|
|
- rem = n.remainder(r32);
|
|
|
|
|
- n0inv = rem.modInverse(r32);
|
|
|
|
|
-
|
|
|
|
|
- int myN[] = new int[KEY_LENGTH_WORDS];
|
|
|
|
|
- int myRr[] = new int[KEY_LENGTH_WORDS];
|
|
|
|
|
- BigInteger res[];
|
|
|
|
|
- for (int i = 0; i < KEY_LENGTH_WORDS; i++) {
|
|
|
|
|
- res = rr.divideAndRemainder(r32);
|
|
|
|
|
- rr = res[0];
|
|
|
|
|
- rem = res[1];
|
|
|
|
|
- myRr[i] = rem.intValue();
|
|
|
|
|
-
|
|
|
|
|
- res = n.divideAndRemainder(r32);
|
|
|
|
|
- n = res[0];
|
|
|
|
|
- rem = res[1];
|
|
|
|
|
- myN[i] = rem.intValue();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* ------------------------------------------------------------------------------------------- */
|
|
|
|
|
-
|
|
|
|
|
- ByteBuffer bbuf = ByteBuffer.allocate(524).order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- bbuf.putInt(KEY_LENGTH_WORDS);
|
|
|
|
|
- bbuf.putInt(n0inv.negate().intValue());
|
|
|
|
|
- for (int i : myN)
|
|
|
|
|
- bbuf.putInt(i);
|
|
|
|
|
- for (int i : myRr)
|
|
|
|
|
- bbuf.putInt(i);
|
|
|
|
|
-
|
|
|
|
|
- bbuf.putInt(pubkey.getPublicExponent().intValue());
|
|
|
|
|
- return bbuf.array();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- public static AdbCrypto loadAdbKeyPair(AdbBase64 base64, FileInputStream privIn, FileInputStream pubIn) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
|
|
|
|
- AdbCrypto crypto = new AdbCrypto();
|
|
|
|
|
-
|
|
|
|
|
- byte[] privKeyBytes = new byte[privIn.available()];
|
|
|
|
|
- byte[] pubKeyBytes = new byte[pubIn.available()];
|
|
|
|
|
-
|
|
|
|
|
- privIn.read(privKeyBytes);
|
|
|
|
|
- pubIn.read(pubKeyBytes);
|
|
|
|
|
-
|
|
|
|
|
- privIn.close();
|
|
|
|
|
- pubIn.close();
|
|
|
|
|
-
|
|
|
|
|
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
|
|
|
|
- EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privKeyBytes);
|
|
|
|
|
- EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pubKeyBytes);
|
|
|
|
|
-
|
|
|
|
|
- crypto.keyPair = new KeyPair(keyFactory.generatePublic(publicKeySpec),
|
|
|
|
|
- keyFactory.generatePrivate(privateKeySpec));
|
|
|
|
|
- crypto.base64 = base64;
|
|
|
|
|
-
|
|
|
|
|
- return crypto;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Creates a new AdbCrypto object by generating a new key pair.
|
|
|
|
|
- *
|
|
|
|
|
- * @param base64 Implementation of base 64 conversion interface required by ADB
|
|
|
|
|
- * @return A new AdbCrypto object
|
|
|
|
|
- * @throws NoSuchAlgorithmException If an RSA key factory cannot be found
|
|
|
|
|
- */
|
|
|
|
|
- public static AdbCrypto generateAdbKeyPair(AdbBase64 base64) throws NoSuchAlgorithmException {
|
|
|
|
|
- AdbCrypto crypto = new AdbCrypto();
|
|
|
|
|
-
|
|
|
|
|
- KeyPairGenerator rsaKeyPg = KeyPairGenerator.getInstance("RSA");
|
|
|
|
|
- rsaKeyPg.initialize(KEY_LENGTH_BITS);
|
|
|
|
|
-
|
|
|
|
|
- crypto.keyPair = rsaKeyPg.genKeyPair();
|
|
|
|
|
- crypto.base64 = base64;
|
|
|
|
|
-
|
|
|
|
|
- return crypto;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Signs the ADB SHA1 payload with the private key of this object.
|
|
|
|
|
- *
|
|
|
|
|
- * @param payload SHA1 payload to sign
|
|
|
|
|
- * @return Signed SHA1 payload
|
|
|
|
|
- * @throws GeneralSecurityException If signing fails
|
|
|
|
|
- */
|
|
|
|
|
- public byte[] signAdbTokenPayload(byte[] payload) throws GeneralSecurityException {
|
|
|
|
|
- Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
|
|
|
|
|
-
|
|
|
|
|
- c.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate());
|
|
|
|
|
-
|
|
|
|
|
- c.update(SIGNATURE_PADDING);
|
|
|
|
|
-
|
|
|
|
|
- return c.doFinal(payload);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Gets the RSA public key in ADB format.
|
|
|
|
|
- *
|
|
|
|
|
- * @return Byte array containing the RSA public key in ADB format.
|
|
|
|
|
- * @throws IOException If the key cannot be retrived
|
|
|
|
|
- */
|
|
|
|
|
- public byte[] getAdbPublicKeyPayload() throws IOException {
|
|
|
|
|
- byte[] convertedKey = convertRsaPublicKeyToAdbFormat((RSAPublicKey) keyPair.getPublic());
|
|
|
|
|
- StringBuilder keyString = new StringBuilder(720);
|
|
|
|
|
-
|
|
|
|
|
- /* The key is base64 encoded with a user@host suffix and terminated with a NUL */
|
|
|
|
|
- keyString.append(base64.encodeToString(convertedKey));
|
|
|
|
|
- keyString.append(" unknown@unknown");
|
|
|
|
|
- keyString.append('\0');
|
|
|
|
|
-
|
|
|
|
|
- return keyString.toString().getBytes("UTF-8");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- public void saveAdbKeyPair(FileOutputStream privOut, FileOutputStream pubOut) throws IOException {
|
|
|
|
|
-
|
|
|
|
|
- privOut.write(keyPair.getPrivate().getEncoded());
|
|
|
|
|
- pubOut.write(keyPair.getPublic().getEncoded());
|
|
|
|
|
-
|
|
|
|
|
- privOut.close();
|
|
|
|
|
- pubOut.close();
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|