AdbKeyPair.kt 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package eu.mjdev.dadb.helpers
  2. import java.io.File
  3. import java.math.BigInteger
  4. import java.nio.ByteBuffer
  5. import java.nio.ByteOrder
  6. import java.security.KeyPairGenerator
  7. import java.security.PrivateKey
  8. import java.security.interfaces.RSAPublicKey
  9. import java.util.*
  10. import javax.crypto.Cipher
  11. class AdbKeyPair(
  12. private val privateKey: PrivateKey,
  13. internal val publicKeyBytes: ByteArray
  14. ) {
  15. internal fun signPayload(message: AdbMessage): ByteArray {
  16. val cipher = Cipher.getInstance("RSA/ECB/NoPadding")
  17. cipher.init(Cipher.ENCRYPT_MODE, privateKey)
  18. cipher.update(SIGNATURE_PADDING)
  19. return cipher.doFinal(message.payload, 0, message.payloadLength)
  20. }
  21. companion object {
  22. private const val KEY_LENGTH_BITS = 2048
  23. private const val KEY_LENGTH_BYTES = KEY_LENGTH_BITS / 8
  24. private const val KEY_LENGTH_WORDS = KEY_LENGTH_BYTES / 4
  25. @OptIn(ExperimentalUnsignedTypes::class)
  26. private val SIGNATURE_PADDING = ubyteArrayOf(
  27. 0x00u, 0x01u, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  28. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  29. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  30. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  31. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  32. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  33. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  34. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  35. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  36. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  37. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  38. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  39. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  40. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  41. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  42. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
  43. 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0x00u,
  44. 0x30u, 0x21u, 0x30u, 0x09u, 0x06u, 0x05u, 0x2bu, 0x0eu, 0x03u, 0x02u, 0x1au, 0x05u, 0x00u,
  45. 0x04u, 0x14u
  46. ).toByteArray()
  47. @JvmStatic
  48. fun readDefault(): AdbKeyPair {
  49. val privateKeyFile = File(System.getenv("HOME"), ".android/adbkey")
  50. val publicKeyFile = File(System.getenv("HOME"), ".android/adbkey.pub")
  51. if (!privateKeyFile.exists()) {
  52. generate(privateKeyFile, publicKeyFile)
  53. }
  54. return read(privateKeyFile, publicKeyFile)
  55. }
  56. @JvmStatic
  57. @JvmOverloads
  58. fun read(privateKeyFile: File, publicKeyFile: File? = null): AdbKeyPair {
  59. val privateKey = PKCS8.parse(privateKeyFile.readBytes())
  60. val publicKeyBytes = if (publicKeyFile?.exists() == true) {
  61. readAdbPublicKey(publicKeyFile)
  62. } else {
  63. ByteArray(0)
  64. }
  65. return AdbKeyPair(privateKey, publicKeyBytes)
  66. }
  67. @JvmStatic
  68. fun generate(privateKeyFile: File, publicKeyFile: File) {
  69. val keyPair = KeyPairGenerator.getInstance("RSA").let {
  70. it.initialize(KEY_LENGTH_BITS)
  71. it.genKeyPair()
  72. }
  73. privateKeyFile.absoluteFile.parentFile?.mkdirs()
  74. publicKeyFile.absoluteFile.parentFile?.mkdirs()
  75. privateKeyFile.writer().use { out ->
  76. val base64 = Base64.getMimeEncoder(64, "\n".toByteArray())
  77. out.write("-----BEGIN PRIVATE KEY-----\n")
  78. out.write(base64.encodeToString(keyPair.private.encoded))
  79. out.write("\n-----END PRIVATE KEY-----")
  80. }
  81. publicKeyFile.writer().use { out ->
  82. val base64 = Base64.getEncoder()
  83. val bytes = convertRsaPublicKeyToAdbFormat(keyPair.public as RSAPublicKey)
  84. out.write(base64.encodeToString(bytes))
  85. out.write(" unknown@unknown")
  86. }
  87. }
  88. private fun readAdbPublicKey(file: File): ByteArray {
  89. val bytes = file.readBytes()
  90. val publicKeyBytes = bytes.copyOf(bytes.size + 1)
  91. publicKeyBytes[bytes.size] = 0
  92. return publicKeyBytes
  93. }
  94. // https://github.com/cgutman/AdbLib/blob/d6937951eb98557c76ee2081e383d50886ce109a/src/com/cgutman/adblib/AdbCrypto.java#L83-L137
  95. @Suppress("JoinDeclarationAndAssignment")
  96. private fun convertRsaPublicKeyToAdbFormat(pubkey: RSAPublicKey): ByteArray {
  97. /*
  98. * ADB literally just saves the RSAPublicKey struct to a file.
  99. *
  100. * typedef struct RSAPublicKey {
  101. * int len; // Length of n[] in number of uint32_t
  102. * uint32_t n0inv; // -1 / n[0] mod 2^32
  103. * uint32_t n[RSANUMWORDS]; // modulus as little endian array
  104. * uint32_t rr[RSANUMWORDS]; // R^2 as little endian array
  105. * int exponent; // 3 or 65537
  106. * } RSAPublicKey;
  107. */
  108. /* ------ This part is a Java-ified version of RSA_to_RSAPublicKey from adb_host_auth.c ------ */
  109. val r32: BigInteger
  110. val r: BigInteger
  111. var rr: BigInteger
  112. var rem: BigInteger
  113. var n: BigInteger
  114. val n0inv: BigInteger
  115. r32 = BigInteger.ZERO.setBit(32)
  116. n = pubkey.modulus
  117. r = BigInteger.ZERO.setBit(KEY_LENGTH_WORDS * 32)
  118. rr = r.modPow(BigInteger.valueOf(2), n)
  119. rem = n.remainder(r32)
  120. n0inv = rem.modInverse(r32)
  121. val myN = IntArray(KEY_LENGTH_WORDS)
  122. val myRr = IntArray(KEY_LENGTH_WORDS)
  123. var res: Array<BigInteger>
  124. for (i in 0 until KEY_LENGTH_WORDS) {
  125. res = rr.divideAndRemainder(r32)
  126. rr = res[0]
  127. rem = res[1]
  128. myRr[i] = rem.toInt()
  129. res = n.divideAndRemainder(r32)
  130. n = res[0]
  131. rem = res[1]
  132. myN[i] = rem.toInt()
  133. }
  134. /* ------------------------------------------------------------------------------------------- */
  135. val bbuf: ByteBuffer = ByteBuffer.allocate(524).order(ByteOrder.LITTLE_ENDIAN)
  136. bbuf.putInt(KEY_LENGTH_WORDS)
  137. bbuf.putInt(n0inv.negate().toInt())
  138. for (i in myN) bbuf.putInt(i)
  139. for (i in myRr) bbuf.putInt(i)
  140. bbuf.putInt(pubkey.publicExponent.toInt())
  141. return bbuf.array()
  142. }
  143. }
  144. }