GpgLinkedDataKeyClass2020.js

  1. const openpgp = require('openpgp');
  2. class GpgLinkedDataKeyClass2020 {
  3. /**
  4. * @param {KeyPairOptions} options - The options to use.
  5. * @param {string} options.id - The key ID.
  6. * @param {string} options.controller - The key controller.
  7. * @param {string} options.publicKeyGpg - The ascii armored Public Key.
  8. * @param {string} options.privateKeyGpg - The ascii armored Private Key.
  9. * @param {string} options.passphrase - The private key passphrase if needed.
  10. *
  11. */
  12. constructor(options = {}) {
  13. this.id = options.id;
  14. this.type = options.type;
  15. this.controller = options.controller;
  16. this.privateKeyGpg = options.privateKeyGpg;
  17. this.publicKeyGpg = options.publicKeyGpg;
  18. this.passphrase = options.passphrase;
  19. this.init();
  20. }
  21. async init() {
  22. if (this.id === undefined) {
  23. this.id = this.controller + "#" + await this.fingerprint();
  24. }
  25. }
  26. /**
  27. * Returns the JWK encoded public key.
  28. *
  29. * @returns {string} The JWK encoded public key.
  30. */
  31. get publicKey() {
  32. return this.publicKeyGpg;
  33. }
  34. /**
  35. * Returns the JWK encoded private key.
  36. *
  37. * @returns {string} The JWK encoded private key.
  38. */
  39. get privateKey() {
  40. return this.privateKeyGpg;
  41. }
  42. /**
  43. * Generates a KeyPair with an optional deterministic seed.
  44. * @param {KeyPairOptions} [options={}] - The options to use.
  45. *
  46. * @returns {Promise<GpgLinkedDataKeyClass2020>} Generates a key pair.
  47. */
  48. static async generate({ userIds, curve, rsaBits, passphrase }, options = {}) {
  49. // let key = jose.JWK.generateSync(kty, crv);
  50. if (curve) {
  51. const { privateKeyArmored, publicKeyArmored, revocationCertificate } = await openpgp.generateKey({
  52. userIds, // you can pass multiple user IDs
  53. curve, // ECC curve name
  54. passphrase // protects the private key
  55. });
  56. return new GpgLinkedDataKeyClass2020({
  57. privateKeyGpg: privateKeyArmored,
  58. publicKeyGpg: publicKeyArmored,
  59. revocationCertificate,
  60. ...options
  61. });
  62. }
  63. if (rsaBits) {
  64. const { privateKeyArmored, publicKeyArmored, revocationCertificate } = await openpgp.generateKey({
  65. userIds, // you can pass multiple user IDs
  66. rsaBits, // RSA key size
  67. passphrase // protects the private key
  68. });
  69. return new GpgLinkedDataKeyClass2020({
  70. privateKeyGpg: privateKeyArmored,
  71. publicKeyGpg: publicKeyArmored,
  72. revocationCertificate,
  73. ...options
  74. });
  75. }
  76. }
  77. /**
  78. * Returns a signer object for use with jsonld-signatures.
  79. *
  80. * @returns {{sign: Function}} A signer for the json-ld block.
  81. */
  82. signer() {
  83. return signerFactory(this);
  84. }
  85. /**
  86. * Returns a verifier object for use with jsonld-signatures.
  87. *
  88. * @returns {{verify: Function}} Used to verify jsonld-signatures.
  89. */
  90. verifier(key) {
  91. return verifierFactory(this);
  92. }
  93. /**
  94. * Adds a public key base to a public key node.
  95. *
  96. * @param {Object} publicKeyNode - The public key node in a jsonld-signature.
  97. * @param {string} publicKeyNode.publicKeyGpg - JWK Public Key for
  98. * jsonld-signatures.
  99. *
  100. * @returns {Object} A PublicKeyNode in a block.
  101. */
  102. addEncodedPublicKey(publicKeyNode) {
  103. publicKeyNode.publicKeyGpg = this.publicKeyGpg;
  104. return publicKeyNode;
  105. }
  106. /**
  107. * Generates and returns a public key fingerprint using https://tools.ietf.org/html/rfc7638
  108. *
  109. * @param {string} publicKeyGpg - The ascii armor encoded public key material.
  110. *
  111. * @returns {string} The fingerprint.
  112. */
  113. static async fingerprintFromPublicKey({ publicKeyGpg }) {
  114. const { keys: [publicKey] } = await openpgp.key.readArmored(publicKeyGpg);
  115. return publicKey.getFingerprint()
  116. }
  117. /**
  118. * Generates and returns a public key fingerprint using https://tools.ietf.org/html/rfc7638
  119. *
  120. * @returns {string} The fingerprint.
  121. */
  122. async fingerprint() {
  123. const { keys: [publicKey] } = await openpgp.key.readArmored(this.publicKeyGpg);
  124. return publicKey.getFingerprint()
  125. }
  126. /**
  127. * Tests whether the fingerprint was generated from a given key pair.
  128. *
  129. * @param {string} fingerprint - A JWK public key.
  130. *
  131. * @returns {Object} An object indicating valid is true or false.
  132. */
  133. async verifyFingerprint(fingerprint) {
  134. const fingerprintFromKey = await this.fingerprint()
  135. return fingerprintFromKey === fingerprint;
  136. }
  137. static async from(options) {
  138. return new GpgLinkedDataKeyClass2020(options);
  139. }
  140. /**
  141. * Contains the public key for the KeyPair
  142. * and other information that json-ld Signatures can use to form a proof.
  143. * @param {Object} [options={}] - Needs either a controller or owner.
  144. * @param {string} [options.controller=this.controller] - DID of the
  145. * person/entity controlling this key pair.
  146. *
  147. * @returns {Object} A public node with
  148. * information used in verification methods by signatures.
  149. */
  150. publicNode({ controller = this.controller } = {}) {
  151. const publicNode = {
  152. id: this.id,
  153. type: this.type
  154. };
  155. if (controller) {
  156. publicNode.controller = controller;
  157. }
  158. this.addEncodedPublicKey(publicNode); // Subclass-specific
  159. return publicNode;
  160. }
  161. }
  162. /**
  163. * @ignore
  164. * Returns an object with an async sign function.
  165. * The sign function is bound to the KeyPair
  166. * and then returned by the KeyPair's signer method.
  167. * @param {GpgLinkedDataKeyClass2020} key - An GpgLinkedDataKeyClass2020.
  168. *
  169. * @returns {{sign: Function}} An object with an async function sign
  170. * using the private key passed in.
  171. */
  172. function signerFactory(key) {
  173. if (!key.privateKeyGpg) {
  174. return {
  175. async sign() {
  176. throw new Error("No private key to sign with.");
  177. }
  178. };
  179. }
  180. return {
  181. async sign({ data }) {
  182. const { keys: [privateKey] } = await openpgp.key.readArmored(key.privateKeyGpg);
  183. if (key.passphrase) {
  184. await privateKey.decrypt(this.passphrase);
  185. }
  186. const { signature: detachedSignature } = await openpgp.sign({
  187. message: openpgp.message.fromBinary(
  188. Buffer.from(data)
  189. ),
  190. privateKeys: [privateKey],
  191. detached: true
  192. });
  193. return detachedSignature;
  194. }
  195. };
  196. }
  197. /**
  198. * @ignore
  199. * Returns an object with an async verify function.
  200. * The verify function is bound to the KeyPair
  201. * and then returned by the KeyPair's verifier method.
  202. * @param {GpgLinkedDataKeyClass2020} key - An GpgLinkedDataKeyClass2020.
  203. *
  204. * @returns {{verify: Function}} An async verifier specific
  205. * to the key passed in.
  206. */
  207. verifierFactory = key => {
  208. if (!key.publicKeyGpg) {
  209. return {
  210. async sign() {
  211. throw new Error("No public key to verify with.");
  212. }
  213. };
  214. }
  215. return {
  216. async verify({ data, signature }) {
  217. const { signatures } = await openpgp.verify({
  218. message: openpgp.message.fromBinary(
  219. Buffer.from(data)
  220. ),
  221. signature: await openpgp.signature.readArmored(signature), // parse detached signature
  222. publicKeys: (await openpgp.key.readArmored(key.publicKeyGpg)).keys // for verification
  223. });
  224. const { valid } = signatures[0];
  225. return valid;
  226. }
  227. };
  228. };
  229. module.exports = GpgLinkedDataKeyClass2020;