stores.sks.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. /*
  2. * Copyright 2018 Scytl Secure Electronic Voting SA
  3. *
  4. * All rights reserved
  5. *
  6. * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
  7. */
  8. /**
  9. * Scytl Key Store. It is a custom keystore.
  10. */
  11. cryptolib.modules.stores.sks = function(box) {
  12. 'use strict';
  13. box.stores = box.stores || {};
  14. box.stores.sks = {};
  15. var converters;
  16. var secretKeyGeneratorFactory;
  17. var cipher;
  18. var pkcs12Module;
  19. var exceptions;
  20. var homomorphicKeyFactory;
  21. var f = function(box) {
  22. converters = new box.commons.utils.Converters();
  23. cipher = (new box.symmetric.cipher.factory.SymmetricCipherFactory())
  24. .getCryptoSymmetricCipher();
  25. secretKeyGeneratorFactory =
  26. new box.primitives.derivation.factory.SecretKeyGeneratorFactory();
  27. pkcs12Module = box.stores.pkcs12;
  28. exceptions = box.commons.exceptions;
  29. homomorphicKeyFactory = new box.homomorphic.keypair.factory.KeyFactory();
  30. };
  31. f.policies = {
  32. symmetric: {
  33. secretkey: {
  34. encryption:
  35. {length: box.policies.symmetric.secretkey.encryption.length},
  36. mac: {length: box.policies.symmetric.secretkey.mac.length},
  37. secureRandom:
  38. {provider: box.policies.symmetric.secretkey.secureRandom.provider}
  39. },
  40. cipher: {
  41. provider: box.policies.symmetric.cipher.provider,
  42. algorithm: box.policies.symmetric.cipher.algorithm,
  43. initializationVectorLengthBytes:
  44. box.policies.symmetric.cipher.initializationVectorLengthBytes,
  45. secureRandom:
  46. {provider: box.policies.symmetric.cipher.secureRandom.provider}
  47. }
  48. },
  49. primitives: {
  50. derivation: {
  51. pbkdf: {
  52. provider: box.policies.primitives.derivation.pbkdf.provider,
  53. hash: box.policies.primitives.derivation.pbkdf.hash,
  54. saltLengthBytes:
  55. box.policies.primitives.derivation.pbkdf.saltLengthBytes,
  56. keyLengthBytes:
  57. box.policies.primitives.derivation.pbkdf.keyLengthBytes,
  58. iterations: box.policies.primitives.derivation.pbkdf.iterations
  59. }
  60. }
  61. }
  62. };
  63. cryptolib(
  64. 'commons', 'primitives.derivation', 'symmetric.cipher', 'stores.pkcs12',
  65. 'homomorphic.keypair', f);
  66. /**
  67. * Defines a SksReader.
  68. *
  69. * @param data.
  70. */
  71. box.stores.sks.SksReader = function(data) {
  72. // A cache of keys derived from store passwords, to prevent consecutive
  73. // operations on a keystore to derive keys each time.
  74. var bigintKeys = {};
  75. var derivedKeys = {};
  76. // Create the PBKDF generator.
  77. var cryptoPbkdfSecretKeyGenerator = secretKeyGeneratorFactory.createPBKDF();
  78. try {
  79. var loadKeyStore = function(data) {
  80. var keyStore;
  81. if (data) {
  82. if (typeof data === 'string') {
  83. keyStore = JSON.parse(data);
  84. } else if (typeof data === 'object') {
  85. keyStore = data;
  86. }
  87. }
  88. return keyStore;
  89. };
  90. var validateKeyStoreStructure = function(keyStore) {
  91. if (typeof keyStore.salt === 'undefined') {
  92. throw new Error('Missing salt');
  93. }
  94. if (typeof keyStore.store === 'undefined') {
  95. throw new Error('Missing store');
  96. }
  97. };
  98. this.keyStore = loadKeyStore(data);
  99. validateKeyStoreStructure(this.keyStore);
  100. this.store = new pkcs12Module.Pkcs12(this.keyStore.store);
  101. } catch (error) {
  102. throw new Error('Invalid keyStore: ' + error.message);
  103. }
  104. function validateEncryptedKey(encryptedKey, keyType) {
  105. if (!encryptedKey) {
  106. throw new exceptions.CryptoLibException(
  107. 'The keyStore does not contain a key of type ' + keyType);
  108. }
  109. }
  110. /**
  111. * Derive a key from the keystore password.
  112. *
  113. * @param {string} passwordB64 the Base64-encoded keystore password
  114. * @returns the Base64-encoded key derived from the password
  115. */
  116. this._deriveKey = function(passwordB64) {
  117. return cryptoPbkdfSecretKeyGenerator.generateSecret(
  118. passwordB64, this.keyStore.salt);
  119. };
  120. /**
  121. * Get the Base64-encoded key derived from the store password.
  122. *
  123. * @param {string} passwordB64 the Base64-encoded store password
  124. * @return the Base64-encoded key derived from the store password
  125. */
  126. this._getDerivedKey = function(passwordB64) {
  127. if (!derivedKeys.hasOwnProperty(passwordB64)) {
  128. derivedKeys[passwordB64] = this._deriveKey(passwordB64);
  129. }
  130. return derivedKeys[passwordB64];
  131. };
  132. /**
  133. * Get the Base64-encoded BigInteger representation of the key derived from
  134. * the store password.
  135. *
  136. * @param {string} passwordB64 the Base64-encoded store password
  137. * @returns the Base64-encoded BigInteger representation of the key derived
  138. * from the store password
  139. */
  140. this._getBigIntKey = function(passwordB64) {
  141. if (!bigintKeys.hasOwnProperty(passwordB64)) {
  142. var derivedPasswordBase64 = this._getDerivedKey(passwordB64);
  143. var derivedPassword = converters.bytesFromString(
  144. converters.base64Decode(derivedPasswordBase64));
  145. var derivedPasswordString =
  146. new box.forge.jsbn.BigInteger(derivedPassword);
  147. var longPasswordB64 =
  148. converters.base64Encode(derivedPasswordString.toString(36));
  149. bigintKeys[passwordB64] = longPasswordB64;
  150. }
  151. return bigintKeys[passwordB64];
  152. };
  153. this._getKey = function(alias, encryptedKey, passwordB64, keyType) {
  154. validateEncryptedKey(encryptedKey, keyType);
  155. var aliasAndKeyBase64 = this.decryptKey(encryptedKey, passwordB64);
  156. var aliasAndKey = converters.base64Decode(aliasAndKeyBase64);
  157. var decryptedAlias = aliasAndKey.slice(0, alias.length);
  158. if (decryptedAlias !== alias) {
  159. throw new exceptions.CryptoLibException(
  160. 'The decrypted ' + keyType +
  161. ' key does not correspond to the given alias.');
  162. }
  163. var key = aliasAndKey.slice(alias.length, aliasAndKey.length);
  164. var keyBase64 = converters.base64Encode(key);
  165. return keyBase64;
  166. };
  167. };
  168. box.stores.sks.SksReader.prototype = {
  169. getPrivateKeys: function(passwordB64) {
  170. var updateArguments = function(bigIntKey, args) {
  171. var argumentsCopy = Array.prototype.slice.call(args, 1);
  172. argumentsCopy.unshift(bigIntKey);
  173. return argumentsCopy;
  174. };
  175. return this.store.getPrivateKey.apply(
  176. this.store,
  177. updateArguments(this._getBigIntKey(passwordB64), arguments));
  178. },
  179. getSecretKey: function(alias, passwordB64) {
  180. var encryptedKey = this.keyStore.secrets[alias];
  181. return this._getKey(alias, encryptedKey, passwordB64, 'secret');
  182. },
  183. getElGamalPrivateKey: function(alias, passwordB64) {
  184. var encryptedKey = this.keyStore.egPrivKeys[alias];
  185. var keyBase64 =
  186. this._getKey(alias, encryptedKey, passwordB64, 'ElGamal private');
  187. return homomorphicKeyFactory.createPrivateKey(keyBase64);
  188. },
  189. decryptKey: function(encryptedKey, passwordB64) {
  190. return cipher.decrypt(this._getDerivedKey(passwordB64), encryptedKey);
  191. },
  192. /**
  193. * Retrieves the private key chain
  194. *
  195. * @param passwordB64
  196. * Password as string in Base64 encoded format.
  197. * @return Private key chain of as PrivateKeyChain object, which
  198. * contains aliases and their related private keys in PEM
  199. * format.
  200. */
  201. getPrivateKeyChain: function(passwordB64) {
  202. return this.store.getPrivateKeyChain(this._getBigIntKey(passwordB64));
  203. },
  204. /**
  205. * Retrieves the full certificate chain.
  206. *
  207. * @param passwordB64
  208. * Password as string in Base64 encoded format.
  209. * @return Certificate chain as array of strings in PEM format.
  210. */
  211. getCertificateChain: function(passwordB64) {
  212. return this.store.getCertificateChain(this._getBigIntKey(passwordB64));
  213. },
  214. /**
  215. * Retrieves a certificate chain by private key alias
  216. *
  217. * @param passwordB64
  218. * Password as string in Base64 encoded format.
  219. * @param alias
  220. * Alias to filter
  221. * @return Certificate chain as array of strings in PEM format.
  222. */
  223. getCertificateChainByAlias: function(passwordB64, alias) {
  224. return this.store.getCertificateChainByAlias(
  225. this._getBigIntKey(passwordB64), alias);
  226. },
  227. /**
  228. * Retrieves a certificate by subject common name.
  229. *
  230. * @param passwordB64
  231. * Password as string in Base64 encoded format.
  232. * @param subjectCn
  233. * Subject common name of certificate to retrieve, as string.
  234. * @return Certificate with given subject common name, as string in PEM
  235. * format.
  236. */
  237. getCertificateBySubject: function(passwordB64, subjectCn) {
  238. return this.store.getCertificateBySubject(
  239. this._getBigIntKey(passwordB64), subjectCn);
  240. }
  241. };
  242. };