You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
5.1 KiB

4 years ago
  1. /**
  2. * Javascript implementation of RSA-KEM.
  3. *
  4. * @author Lautaro Cozzani Rodriguez
  5. * @author Dave Longley
  6. *
  7. * Copyright (c) 2014 Lautaro Cozzani <lautaro.cozzani@scytl.com>
  8. * Copyright (c) 2014 Digital Bazaar, Inc.
  9. */
  10. var forge = require('./forge');
  11. require('./util');
  12. require('./random');
  13. require('./jsbn');
  14. module.exports = forge.kem = forge.kem || {};
  15. var BigInteger = forge.jsbn.BigInteger;
  16. /**
  17. * The API for the RSA Key Encapsulation Mechanism (RSA-KEM) from ISO 18033-2.
  18. */
  19. forge.kem.rsa = {};
  20. /**
  21. * Creates an RSA KEM API object for generating a secret asymmetric key.
  22. *
  23. * The symmetric key may be generated via a call to 'encrypt', which will
  24. * produce a ciphertext to be transmitted to the recipient and a key to be
  25. * kept secret. The ciphertext is a parameter to be passed to 'decrypt' which
  26. * will produce the same secret key for the recipient to use to decrypt a
  27. * message that was encrypted with the secret key.
  28. *
  29. * @param kdf the KDF API to use (eg: new forge.kem.kdf1()).
  30. * @param options the options to use.
  31. * [prng] a custom crypto-secure pseudo-random number generator to use,
  32. * that must define "getBytesSync".
  33. */
  34. forge.kem.rsa.create = function(kdf, options) {
  35. options = options || {};
  36. var prng = options.prng || forge.random;
  37. var kem = {};
  38. /**
  39. * Generates a secret key and its encapsulation.
  40. *
  41. * @param publicKey the RSA public key to encrypt with.
  42. * @param keyLength the length, in bytes, of the secret key to generate.
  43. *
  44. * @return an object with:
  45. * encapsulation: the ciphertext for generating the secret key, as a
  46. * binary-encoded string of bytes.
  47. * key: the secret key to use for encrypting a message.
  48. */
  49. kem.encrypt = function(publicKey, keyLength) {
  50. // generate a random r where 1 < r < n
  51. var byteLength = Math.ceil(publicKey.n.bitLength() / 8);
  52. var r;
  53. do {
  54. r = new BigInteger(
  55. forge.util.bytesToHex(prng.getBytesSync(byteLength)),
  56. 16).mod(publicKey.n);
  57. } while(r.compareTo(BigInteger.ONE) <= 0);
  58. // prepend r with zeros
  59. r = forge.util.hexToBytes(r.toString(16));
  60. var zeros = byteLength - r.length;
  61. if(zeros > 0) {
  62. r = forge.util.fillString(String.fromCharCode(0), zeros) + r;
  63. }
  64. // encrypt the random
  65. var encapsulation = publicKey.encrypt(r, 'NONE');
  66. // generate the secret key
  67. var key = kdf.generate(r, keyLength);
  68. return {encapsulation: encapsulation, key: key};
  69. };
  70. /**
  71. * Decrypts an encapsulated secret key.
  72. *
  73. * @param privateKey the RSA private key to decrypt with.
  74. * @param encapsulation the ciphertext for generating the secret key, as
  75. * a binary-encoded string of bytes.
  76. * @param keyLength the length, in bytes, of the secret key to generate.
  77. *
  78. * @return the secret key as a binary-encoded string of bytes.
  79. */
  80. kem.decrypt = function(privateKey, encapsulation, keyLength) {
  81. // decrypt the encapsulation and generate the secret key
  82. var r = privateKey.decrypt(encapsulation, 'NONE');
  83. return kdf.generate(r, keyLength);
  84. };
  85. return kem;
  86. };
  87. // TODO: add forge.kem.kdf.create('KDF1', {md: ..., ...}) API?
  88. /**
  89. * Creates a key derivation API object that implements KDF1 per ISO 18033-2.
  90. *
  91. * @param md the hash API to use.
  92. * @param [digestLength] an optional digest length that must be positive and
  93. * less than or equal to md.digestLength.
  94. *
  95. * @return a KDF1 API object.
  96. */
  97. forge.kem.kdf1 = function(md, digestLength) {
  98. _createKDF(this, md, 0, digestLength || md.digestLength);
  99. };
  100. /**
  101. * Creates a key derivation API object that implements KDF2 per ISO 18033-2.
  102. *
  103. * @param md the hash API to use.
  104. * @param [digestLength] an optional digest length that must be positive and
  105. * less than or equal to md.digestLength.
  106. *
  107. * @return a KDF2 API object.
  108. */
  109. forge.kem.kdf2 = function(md, digestLength) {
  110. _createKDF(this, md, 1, digestLength || md.digestLength);
  111. };
  112. /**
  113. * Creates a KDF1 or KDF2 API object.
  114. *
  115. * @param md the hash API to use.
  116. * @param counterStart the starting index for the counter.
  117. * @param digestLength the digest length to use.
  118. *
  119. * @return the KDF API object.
  120. */
  121. function _createKDF(kdf, md, counterStart, digestLength) {
  122. /**
  123. * Generate a key of the specified length.
  124. *
  125. * @param x the binary-encoded byte string to generate a key from.
  126. * @param length the number of bytes to generate (the size of the key).
  127. *
  128. * @return the key as a binary-encoded string.
  129. */
  130. kdf.generate = function(x, length) {
  131. var key = new forge.util.ByteBuffer();
  132. // run counter from counterStart to ceil(length / Hash.len)
  133. var k = Math.ceil(length / digestLength) + counterStart;
  134. var c = new forge.util.ByteBuffer();
  135. for(var i = counterStart; i < k; ++i) {
  136. // I2OSP(i, 4): convert counter to an octet string of 4 octets
  137. c.putInt32(i);
  138. // digest 'x' and the counter and add the result to the key
  139. md.start();
  140. md.update(x + c.getBytes());
  141. var hash = md.digest();
  142. key.putBytes(hash.getBytes(digestLength));
  143. }
  144. // truncate to the correct key length
  145. key.truncate(key.length() - length);
  146. return key.getBytes();
  147. };
  148. }