Source: jose/index.js

  1. /**
  2. * @module jose
  3. */
  4. const jose = require('jose');
  5. const config = require("../../config").jose;
  6. const fs = require("fs");
  7. const {
  8. JWE, // JSON Web Encryption (JWE)
  9. JWK, // JSON Web Key (JWK)
  10. JWKS, // JSON Web Key Set (JWKS)
  11. JWT, // JSON Web Token (JWT)
  12. } = jose;
  13. /**
  14. * Import encryption key if user sessions are configured to be
  15. * preserved on reboot or synchronously generate an encryption
  16. * key with config-defined type and curve/size.
  17. */
  18. const encKey = (
  19. process.env.PRESERVE_SESSIONS_ON_REBOOT === '1' ?
  20. /* istanbul ignore next */
  21. JWK.asKey({
  22. key: fs.readFileSync("./keys/enc.key"),
  23. format: "pem",
  24. passphrase: process.env.PRIVATE_KEY_PASSPHRASE,
  25. },
  26. {
  27. alg: config.alg,
  28. use: "enc",
  29. }) :
  30. JWK.generateSync(config.kty, config.crvOrSize, {
  31. alg: config.alg,
  32. use: "enc",
  33. key_ops: ["deriveKey"],
  34. })
  35. );
  36. /**
  37. * Import signing key if user sessions are configured to be
  38. * preserved on reboot or synchronously generate a signing
  39. * key with config-defined type and curve/size.
  40. */
  41. const sigKey = (
  42. process.env.PRESERVE_SESSIONS_ON_REBOOT === '1' ?
  43. /* istanbul ignore next */
  44. JWK.asKey({
  45. key: fs.readFileSync("./keys/sig.key"),
  46. format: "pem",
  47. passphrase: process.env.PRIVATE_KEY_PASSPHRASE,
  48. },
  49. {
  50. use: "sig",
  51. alg: config.sigAlg,
  52. }) :
  53. JWK.generateSync(config.kty, config.crvOrSize, {
  54. alg: config.sigAlg,
  55. use: "sig",
  56. key_ops: ["sign", "verify"],
  57. })
  58. );
  59. /**
  60. * Initialise JSON Web Key Store
  61. */
  62. const keystore = new JWKS.KeyStore(encKey, sigKey);
  63. /**
  64. * Get the public key used for encryption-decryption
  65. * in Privacy-Enhanced Mail format
  66. * @return {object} enc public key in PEM format
  67. */
  68. const getEncPubAsPEM = () => {
  69. return keystore.get({
  70. kty: config.kty,
  71. crv: config.crvOrSize,
  72. use: "enc",
  73. key_ops: ["deriveKey"],
  74. }).toPEM();
  75. };
  76. /**
  77. * Get the public key used for signing-verifying
  78. * in Privacy-Enhanced Mail format
  79. * @return {object} sig public key in PEM format
  80. */
  81. const getSigPubAsPEM = () => {
  82. return keystore.get({
  83. kty: config.kty,
  84. crv: config.crvOrSize,
  85. use: "sig",
  86. key_ops: ["sign", "verify"],
  87. }).toPEM();
  88. };
  89. /**
  90. * Encrypt given cleartext with specified public
  91. * key and return the resulting JWE object as a string.
  92. * If no public key is provided by the recipient and
  93. * symmetric encryption is enabled, the JWT is symmetrically
  94. * encrypted by the server's key instead.
  95. * In case asymmetric encryption is enforced and the client
  96. * does not provide a public key, an error is thrown.
  97. * @param {string} cleartext
  98. * @param {object} pub JWK compatible public key of recipient
  99. * @return {string} JWE object as string
  100. */
  101. const encrypt = (cleartext, pub) => {
  102. if (pub === undefined && process.env.SYMMETRIC_ENC_ENABLED === '1') {
  103. pub = encKey;
  104. };
  105. return JWE.encrypt(cleartext, JWK.asKey(pub),
  106. {
  107. enc: config.enc,
  108. alg: config.alg,
  109. });
  110. };
  111. /**
  112. * Decrypt given JWK object with owned
  113. * private key and return cleartext as a
  114. * utf8 string.
  115. * @param {string} jwe JWE object as string
  116. * @return {string} cleartext (utf8)
  117. */
  118. const decrypt = (jwe) => {
  119. return JWE.decrypt(jwe, encKey, {
  120. complete: false,
  121. }).toString("utf8");
  122. };
  123. /**
  124. * Sign provided payload and return the
  125. * signed JWT.
  126. * @param {JSON} payload
  127. * @param {string} [exp=default] default expiry will be used if omitted
  128. * @return {string} signed JWT
  129. */
  130. const sign = (payload, exp) => {
  131. return JWT.sign(payload, sigKey, {
  132. header: {
  133. typ: "JWT",
  134. },
  135. issuer: config.iss,
  136. audience: config.aud,
  137. kid: true,
  138. expiresIn: exp !== undefined ? exp : config.exp,
  139. });
  140. };
  141. /**
  142. * Verify provided JWT with given params and
  143. * the server's signing key.
  144. * The audience may also be optionally provided,
  145. * for instance when validating access to routes
  146. * with custom permissions. An example is when
  147. * an unauthenticated user wishes to reset their
  148. * password and have been granted access via a
  149. * reset token. In this case the aud="/reset".
  150. * If the audience param is left out, the default
  151. * configuration will be used.
  152. * @param {object} jwt JWT token
  153. * @param {string} [aud=default] default config will be used if omitted
  154. * @return {string} payload
  155. * @throws {JWSVerificationFailed} if JWT blacklisted
  156. * @throws {jose.errors} for failed verification
  157. */
  158. const verify = (jwt, aud) => {
  159. return JWT.verify(jwt, sigKey, {
  160. audience: aud !== undefined ? aud : config.aud,
  161. complete: false,
  162. issuer: config.iss,
  163. });
  164. };
  165. /**
  166. * Sign provided payload with the server's private
  167. * key and asymmetrically encrypt the signed JWT by
  168. * the client provided public key.
  169. * This returns the encrypted JWE object as a string.
  170. * @param {JSON} payload
  171. * @param {object} pub client's pub used for encryption
  172. * @param {string} [exp=default] default expiry will be used if omitted
  173. * @return {string} JWE object as string
  174. */
  175. const signAndEncrypt = (payload, pub, exp) => {
  176. const jwt = sign(payload, exp);
  177. return encrypt(jwt, pub);
  178. };
  179. /**
  180. * Decrypt given JWE with the server's encryption
  181. * key and verify resulting JWT with the server's
  182. * signing key. Returns the payload object if
  183. * successful.
  184. * @param {string} jwe JWE object as string
  185. * @param {string} [aud=default] default config will be used if omitted
  186. * @return {string} decrypted and verified payload
  187. */
  188. const decryptAndVerify = (jwe, aud) => {
  189. const jwt = decrypt(jwe);
  190. return verify(jwt, aud);
  191. };
  192. module.exports = {
  193. getEncPubAsPEM,
  194. getSigPubAsPEM,
  195. encrypt,
  196. decrypt,
  197. sign,
  198. verify,
  199. signAndEncrypt,
  200. decryptAndVerify,
  201. };