Guest User

Untitled

a guest
Jul 17th, 2018
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.38 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. import hid
  4. import sys
  5. import os
  6. import zlib
  7. import io
  8. import time
  9. from ctypes import *
  10.  
  11. from Crypto.Hash import SHA256
  12. from Crypto.PublicKey import RSA
  13. from Crypto.Signature import PKCS1_PSS
  14. from Crypto.Util.number import bytes_to_long
  15.  
  16. JEDI_CA_FINGERPRINT = b'\xe5\xe0\x95\xe6C\xb5h\x8b@\x0cu{LD\xef\xac\xc2\x93aH\xe5\xce\xbdlmA\x0fT\xf1H\x7fI'
  17.  
  18. class DS4Auth(LittleEndianStructure):
  19. _fields_ = [
  20. ('type', c_uint8),
  21. ('seq', c_uint8),
  22. ('page', c_uint8),
  23. ('sbz', c_uint8),
  24. ('data', c_uint8 * 56),
  25. ('crc32', c_uint32)
  26. ]
  27.  
  28. class DS4AuthResult(LittleEndianStructure):
  29. _fields_ = [
  30. ('type', c_uint8),
  31. ('seq', c_uint8),
  32. ('status', c_uint8),
  33. ('padding', c_uint8 * 9),
  34. ('crc32', c_uint32)
  35. ]
  36.  
  37. class DS4IdentityBlock(BigEndianStructure):
  38. _fields_ = [
  39. ('serial', c_uint8 * 0x10),
  40. ('modulus', c_uint8 * 0x100),
  41. ('padding', c_uint8 * 0xfc),
  42. ('exponent', c_uint32)
  43. ]
  44.  
  45. class DS4Response(LittleEndianStructure):
  46. _fields_ = [
  47. ('sig_nonce', c_uint8 * 0x100),
  48. ('identity', DS4IdentityBlock),
  49. ('sig_identity', c_uint8 * 0x100)
  50. ]
  51.  
  52. #DS4_VID = 0x16c0
  53. #DS4_PID = 0x04d5
  54.  
  55. DS4_VID = 0x054c
  56. DS4_PID = 0x05c4
  57.  
  58. if __name__ == '__main__':
  59. dev = hid.device()
  60. try:
  61. dev.open(DS4_VID, DS4_PID)
  62. nonce = io.BytesIO(os.urandom(256))
  63.  
  64. print('nonce =', nonce.getvalue().hex())
  65. challengebuf = DS4Auth()
  66. challengebuf.seq = 1
  67. challengebuf.type = 0xf0
  68. while nonce.readinto(challengebuf.data) != 0:
  69. print('page =', challengebuf.page, 'data =', memoryview(challengebuf.data).hex())
  70. challengebuf.crc32 = zlib.crc32(bytes(challengebuf)[:sizeof(DS4Auth)-sizeof(c_uint32)])
  71. print('crc =', challengebuf.crc32)
  72. dev.send_feature_report(challengebuf)
  73. challengebuf.page += 1
  74. print('sleeping')
  75. time.sleep(1)
  76. memset(challengebuf.data, 0, sizeof(challengebuf.data))
  77.  
  78. while True:
  79. begin = time.perf_counter()
  80. recv = dev.get_feature_report(0xf2, sizeof(DS4AuthResult))
  81. result = DS4AuthResult()
  82. size = min(len(recv), sizeof(result))
  83. memmove(addressof(result), bytes(recv), size)
  84. print('seq =', result.seq, 'status =', result.status)
  85. if result.seq != challengebuf.seq:
  86. print('oops')
  87. break
  88. if result.status == 0:
  89. break
  90. if time.perf_counter() - begin > 10.0:
  91. print('timeout waiting for ok')
  92. break
  93. time.sleep(1)
  94. print('auth ok')
  95.  
  96. resp = io.BytesIO()
  97.  
  98. for i in range(0x13):
  99. recv = dev.get_feature_report(0xf1, sizeof(DS4Auth))
  100. respbuf = DS4Auth()
  101. size = min(len(recv), sizeof(respbuf))
  102. memmove(addressof(respbuf), bytes(recv), size)
  103. print('seq =', respbuf.seq, 'page =', respbuf.page)
  104. print('crc =', respbuf.crc32)
  105. if zlib.crc32(bytes(respbuf)[:sizeof(DS4Auth)-sizeof(c_uint32)]) != respbuf.crc32:
  106. print('crc mismatch')
  107. print('data =', memoryview(respbuf.data).hex())
  108. resp.write(respbuf.data)
  109.  
  110. time.sleep(1)
  111.  
  112. print('resp =', resp.getbuffer().hex())
  113.  
  114. # verify
  115. resp.seek(0)
  116. resp_check = DS4Response()
  117. resp.readinto(resp_check)
  118. print('serial =', memoryview(resp_check.identity.serial).hex())
  119.  
  120. pss_ca = None
  121. if os.path.isfile('jedi.pem'):
  122. with open('jedi.pem', 'rb') as f:
  123. ca = RSA.importKey(f.read())
  124.  
  125. if SHA256.new(ca.exportKey('DER')).digest() != JEDI_CA_FINGERPRINT:
  126. print('WARNING: Wrong fingerprint for Jedi CA, disabling authenticity check')
  127. else:
  128. pss_ca = PKCS1_PSS.new(ca)
  129. else:
  130. print('WARNING: Cannot open jedi.pem, disabling authenticity check')
  131.  
  132. cuk = RSA.construct((bytes_to_long(bytes(resp_check.identity.modulus)), resp_check.identity.exponent))
  133.  
  134. sha_nonce = SHA256.new(nonce.getvalue())
  135. sha_identity = SHA256.new(resp_check.identity)
  136.  
  137. pss_cuk = PKCS1_PSS.new(cuk)
  138.  
  139.  
  140. print('sig_nonce =', memoryview(resp_check.sig_nonce).hex())
  141. print('identity =', memoryview(resp_check.identity).hex())
  142. print('sig_identity =', memoryview(resp_check.sig_identity).hex())
  143.  
  144. result_sig_nonce = pss_cuk.verify(sha_nonce, bytes(resp_check.sig_nonce))
  145. if pss_ca is not None:
  146. result_identity = pss_ca.verify(sha_identity, bytes(resp_check.sig_identity))
  147. else:
  148. result_identity = None
  149.  
  150. if result_sig_nonce:
  151. print('good sig for nonce')
  152. else:
  153. print('bad sig for nonce')
  154.  
  155. if result_identity:
  156. print('good sig for controller unique key')
  157. elif result_identity is None:
  158. print('cannot decide the authenticity of controller unique key')
  159. else:
  160. print('bad sig for controller unique key')
  161.  
  162. if result_sig_nonce and result_identity:
  163. print('the controller seems to be genuine')
  164. elif result_identity is None:
  165. print('cannot decide the authenticity of the controller')
  166. else:
  167. print('the controller may be fake')
  168. finally:
  169. dev.close()
Add Comment
Please, Sign In to add comment