cryptoUtils.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. Most of this code was borrowed with permission from
  3. Cliford Symack - https://github.com/clifordsymack/
  4. */
  5. /* Import core modules. */
  6. const crypto = require('crypto')
  7. const PrivateKey = require('bitcore-lib-cash').PrivateKey
  8. const PublicKey = require('bitcore-lib-cash').PublicKey
  9. /**
  10. * AES Encrypt with IV
  11. */
  12. const _aesEncryptWithIV = function (key, iv, message) {
  13. let cipher, crypted
  14. cipher = crypto.createCipheriv('aes-128-cbc', key, iv)
  15. cipher.setAutoPadding(true)
  16. crypted = cipher.update(message, 'hex', 'hex')
  17. crypted += cipher.final('hex')
  18. return Buffer.from(crypted, 'hex')
  19. }
  20. /**
  21. * AES Decrypt with IV
  22. */
  23. const _aesDecryptWithIV = function (key, iv, message) {
  24. let cipher, crypted
  25. cipher = crypto.createDecipheriv('aes-128-cbc', key, iv)
  26. cipher.setAutoPadding(true)
  27. crypted = cipher.update(message, 'hex', 'hex')
  28. crypted += cipher.final('hex')
  29. return Buffer.from(crypted, 'hex')
  30. }
  31. /**
  32. * Rebuild Keypair
  33. *
  34. * TODO: Update `generateKeypair` for param and REMOVE THIS FUNCTION,
  35. * as it's never used in the codebase.
  36. */
  37. const rebuildKeypair = function (somePrivateKey) {
  38. /* Initialize keypair. */
  39. const keypair = {
  40. privateKey: new PrivateKey(somePrivateKey)
  41. }
  42. /* Set public key. */
  43. keypair.publicKey = keypair.privateKey.toPublicKey()
  44. /* Set public key (hex). */
  45. keypair.publicKeyHex = keypair.publicKey.toString('hex')
  46. /* Set private key (hex). */
  47. keypair.privateKeyHex = keypair.privateKey.toString('hex')
  48. /* Return keypair. */
  49. return keypair
  50. }
  51. /**
  52. * Generate Keypair
  53. *
  54. * FIXME: DRY this up, using `rebuildKeypair`.
  55. */
  56. const generateKeypair = function () {
  57. /* Initialize keypair. */
  58. const keypair = {
  59. privateKey: new PrivateKey()
  60. }
  61. /* Set public key. */
  62. keypair.publicKey = keypair.privateKey.toPublicKey()
  63. /* Set public key (hex). */
  64. keypair.publicKeyHex = keypair.publicKey.toString('hex')
  65. /* Set private key (hex). */
  66. keypair.privateKeyHex = keypair.privateKey.toString('hex')
  67. /* Return keypair. */
  68. return keypair
  69. }
  70. /**
  71. * Encrypt
  72. */
  73. const encrypt = function (plaintextMessage, pubkey) {
  74. /* Initialize public key. */
  75. const publicKey = PublicKey(pubkey)
  76. /* Initialize ephemeral (private key). */
  77. const ephemeral = new PrivateKey()
  78. /* Set ECDH key. */
  79. // NOTE: This is our shared secret with the pubkey holder.
  80. const ecdhKey = PublicKey(
  81. publicKey.point.mul(ephemeral.toBigNumber())).toBuffer()
  82. /* Set key. */
  83. // NOTE: We hash the shared secret as:
  84. // 1. First 16 bytes is the IV for AES.
  85. // 2. Second 16 bytes is the key for AES.
  86. // 3. Final 32 bytes are used for the HMAC.
  87. const key = crypto.createHash('sha512').update(ecdhKey).digest()
  88. /* Set ciphertext. */
  89. const ciphertext = _aesEncryptWithIV(
  90. key.slice(16, 32),
  91. key.slice(0, 16),
  92. Buffer.from(plaintextMessage, 'utf8')
  93. )
  94. /* Set encrypted. */
  95. // NOTE: We send the public key, for our ephemeral private key,
  96. // as an unencrypted buffer, as part of the prefix to our
  97. // encrypted package. This allows ONLY the `pubkey` holder to
  98. // decrypt our (ciphertext) message.
  99. const encrypted = Buffer.concat([
  100. Buffer.from('BIE1'),
  101. ephemeral.publicKey.toBuffer(),
  102. ciphertext
  103. ])
  104. /* Set MAC. */
  105. const mac = crypto
  106. .createHmac('sha256', key.slice(32))
  107. .update(encrypted)
  108. .digest()
  109. /* Return encrypted. */
  110. return Buffer.concat([encrypted, mac]).toString('base64')
  111. }
  112. /**
  113. * Decrypt
  114. */
  115. const decrypt = function (encryptedMessage, somePrivateKeyHexString) {
  116. /* Initialize private key. */
  117. const privateKey = new PrivateKey(somePrivateKeyHexString)
  118. /* Initialize encrypted (message). */
  119. const encrypted = Buffer.from(encryptedMessage, 'base64')
  120. /* Valiate encrypted (message). */
  121. if (encrypted.length < 85) {
  122. throw new Error('invalid ciphertext: length')
  123. }
  124. /* Set magic (bytes). */
  125. const magic = encrypted.slice(0, 4)
  126. /* Initialize ephemeral public key. */
  127. let ephemeralPubkey = encrypted.slice(4, 37)
  128. /* Set ciphertext. */
  129. const ciphertext = encrypted.slice(37, -32)
  130. /* Set MAC. */
  131. const mac = encrypted.slice(-32)
  132. /* Validate magic (bytes). */
  133. if (magic.toString() !== 'BIE1') {
  134. throw new Error('invalid ciphertext: invalid magic bytes')
  135. }
  136. try {
  137. /* Set ephemeral public key. */
  138. ephemeralPubkey = PublicKey(ephemeralPubkey)
  139. } catch (error) {
  140. throw new Error('invalid ciphertext: invalid ephemeral pubkey')
  141. }
  142. // ???
  143. ephemeralPubkey.point.validate()
  144. /* Set secret multiplier. */
  145. const secretMultiplier = privateKey.toBigNumber()
  146. /* Set ECDH key. */
  147. const ecdhKey = PublicKey(
  148. ephemeralPubkey.point.mul(secretMultiplier)).toBuffer()
  149. /* Set key. */
  150. const key = crypto.createHash('sha512').update(ecdhKey).digest()
  151. /* Set initialization vector. */
  152. const iv = key.slice(0, 16)
  153. /* Set key (E). */
  154. const keyE = key.slice(16, 32)
  155. /* Set key (M). */
  156. const keyM = key.slice(32)
  157. /* Calculate the "valid" MAC. */
  158. const validMac = crypto
  159. .createHmac('sha256', keyM)
  160. .update(encrypted.slice(0, -32))
  161. // .update(encrypted.slice(-32)) // THIS SHOULD WORK AS WELL
  162. .digest('hex')
  163. /* Validate MAC. */
  164. if (mac.toString('hex') !== validMac) {
  165. throw new Error('invalid password')
  166. }
  167. /* Return decrypted string. */
  168. return _aesDecryptWithIV(keyE, iv, ciphertext)
  169. }
  170. /**
  171. * Hash
  172. *
  173. * FIXME: DO WE STILL NEED THIS FUNCTION??
  174. * IT'S NOT BEING USED ANYWHERE IN THE CODEBASE
  175. */
  176. const hash = function (text, algorithm) {
  177. /* Initialize algorithm. */
  178. // algorithm = algorithm || 'sha224'
  179. algorithm = algorithm || 'sha256'
  180. try {
  181. return crypto
  182. .createHash(algorithm)
  183. .update(Buffer.from(text), 'utf8')
  184. .digest()
  185. } catch (error) {
  186. return null
  187. }
  188. }
  189. /* Export module. */
  190. module.exports = {
  191. rebuildKeypair,
  192. generateKeypair,
  193. encrypt,
  194. decrypt,
  195. hash
  196. }