Browse Source

v1.0.0: just wrap Rasha and Eckles for now

tags/v1.0.0
AJ ONeal 9 months ago
parent
commit
265977e2c0
50 changed files with 126 additions and 3134 deletions
  1. 1
    0
      .gitignore
  2. 56
    83
      README.md
  3. 0
    20
      bin/keypairs.js
  4. 0
    134
      browser.js
  5. 0
    13
      convert-from-hex.js
  6. 0
    16
      convert-to-der.js
  7. 0
    7
      fixtures/privkey-ec-p256.jwk.json
  8. 0
    5
      fixtures/privkey-ec-p256.pkcs8.pem
  9. 0
    5
      fixtures/privkey-ec-p256.sec1.pem
  10. 0
    7
      fixtures/privkey-ec-p384.jwk.json
  11. 0
    6
      fixtures/privkey-ec-p384.pkcs8.pem
  12. 0
    6
      fixtures/privkey-ec-p384.sec1.pem
  13. 0
    11
      fixtures/privkey-rsa-2048.jwk.json
  14. 0
    27
      fixtures/privkey-rsa-2048.pkcs1.pem
  15. 0
    28
      fixtures/privkey-rsa-2048.pkcs8.pem
  16. 0
    6
      fixtures/pub-ec-p256.jwk.json
  17. 0
    4
      fixtures/pub-ec-p256.spki.pem
  18. 0
    1
      fixtures/pub-ec-p256.ssh.pub
  19. 0
    6
      fixtures/pub-ec-p384.jwk.json
  20. 0
    5
      fixtures/pub-ec-p384.spki.pem
  21. 0
    1
      fixtures/pub-ec-p384.ssh.pub
  22. 0
    5
      fixtures/pub-rsa-2048.jwk.json
  23. 0
    8
      fixtures/pub-rsa-2048.pkcs1.pem
  24. 0
    9
      fixtures/pub-rsa-2048.spki.pem
  25. 0
    1
      fixtures/pub-rsa-2048.ssh.pub
  26. 0
    3
      index.js
  27. 42
    0
      keypairs.js
  28. 0
    111
      lib/asn1-packer.js
  29. 0
    147
      lib/asn1-parser.js
  30. 0
    3
      lib/crypto.js
  31. 0
    63
      lib/ec.js
  32. 0
    493
      lib/eckles.js
  33. 0
    42
      lib/encoding.js
  34. 0
    143
      lib/keypairs.js
  35. 0
    14
      lib/pem-packer.js
  36. 0
    21
      lib/pem-parser.js
  37. 0
    28
      lib/pem.js
  38. 0
    204
      lib/rasha.js
  39. 0
    80
      lib/ssh.js
  40. 0
    111
      lib/telemetry.js
  41. 0
    123
      lib/x509-ec-parser.js
  42. 0
    14
      lib/x509-packer.js
  43. 0
    46
      lib/x509-parser.js
  44. 0
    3
      lib/x509-rsa-parser.js
  45. 0
    153
      lib/x509.js
  46. 18
    0
      package-lock.json
  47. 9
    6
      package.json
  48. 0
    23
      pubkey-cli.js
  49. 0
    292
      pubkey.js
  50. 0
    597
      re-sign.js

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
1
+node_modules

+ 56
- 83
README.md View File

@@ -1,106 +1,79 @@
1
-This is being ported from code from rsa-compat.js, greenlock.html (bacme.js), and others.
1
+# Keypairs for node.js
2 2
 
3
-This was intended to be just a weekend project, but it's grown a bit.
3
+Lightweight JavaScript RSA and ECDSA utils that work on Windows, Mac, and Linux
4
+using modern node.js APIs (no need for C compiler).
4 5
 
5
-* 2018-10-10 (Saturday) work has begun
6
-* 2018-10-11 (Sunday) W00T! got a CSR generated for RSA with VanillaJS ArrayBuffer
7
-* 2018-10-12 (Monday) Figuring out ECDSA CSRs right now
8
-* 2018-10-15 (Thursday) ECDSA is a trixy hobbit... but I think I've got it...
9
-* 2018-12-02 (Sunday) Been mostly done for a while, individually, slowly merging everything together
10
-  * [Rasha.js](https://git.coolaj86.com/coolaj86/rasha.js) (RSA utils)
11
-  * [Eckles.js](https://git.coolaj86.com/coolaj86/eckles.js) (EC utils)
6
+A thin wrapper around [Eckles.js (ECDSA)](https://git.coolaj86.com/coolaj86/eckles.js/)
7
+and [Rasha.js (RSA)](https://git.coolaj86.com/coolaj86/rasha.js/).
8
+
9
+# Features
10
+
11
+  * [x] Generate keypairs
12
+    * [x] RSA
13
+    * [x] ECDSA (P-256, P-384)
14
+  * [x] PEM-to-JWK
15
+  * [x] JWK-to-PEM
16
+  * [x] SHA256 JWK Thumbprints
17
+  * [ ] JWK fetching. See [Keyfetch.js](https://npmjs.com/packages/keyfetch/)
18
+    * [ ] OIDC
19
+    * [ ] Auth0
12 20
 
13 21
 <!--
14
-Keypairs&trade; for node.js
15
-===========================
16 22
 
17
-JavaScript RSA and ECDSA utils that work on Windows, Mac, and Linux with or without C compiler.
23
+  * [ ] sign JWS
24
+  * [ ] generate CSR (DER as PEM or base64url)
25
+
26
+-->
27
+
28
+# Usage
18 29
 
19
-There are many different RSA and ECDSA libraries for node and it seems like they're
20
-all incompatible in different ways. This isn't [yet another library](https://xkcd.com/927/),
21
-but rather [one to rule them all and bind them](https://en.wikipedia.org/wiki/One_Ring).
30
+A brief (albeit somewhat nonsensical) introduction to the APIs:
22 31
 
23
-Features
24
-========
32
+```
33
+Keypairs.generate().then(function (jwk) {
34
+  return Keypairs.export({ jwk: jwk }).then(function (pem) {
35
+    return Keypairs.import({ pem: pem }).then(function (jwk) {
36
+      return Keypairs.thumbprint({ jwk: jwk }).then(function (thumb) {
37
+        console.log(thumb);
38
+      });
39
+    });
40
+  });
41
+});
42
+```
25 43
 
26
-  * [x] RSA
27
-  * [ ] ECDSA (in-progress)
28
-  * [x] generate keypair
29
-  * [x] export to JWK
30
-  * [x] import from JWK
31
-  * [x] export to PEM
32
-  * [x] import from PEM
33
-  * [x] sign JWS
34
-  * [x] generate CSR (DER as PEM or base64url)
44
+By default ECDSA keys will be used since they've had native support in node
45
+_much_ longer than RSA has, and they're smaller, and faster to generate.
35 46
 
36
-API
37
-===
47
+## API
48
+
49
+Each of these return a Promise.
38 50
 
39 51
 * `Keypairs.generate(options)`
40
-  * options example `{ type: 'RSA' || 'ECDSA', bitlength: 2048 || 256 }`
52
+  * options example `{ kty: 'RSA', modulusLength: 2048 }`
53
+  * options example `{ kty: 'ECDSA', namedCurve: 'P-256' }`
41 54
 * `Keypairs.import(options)`
42
-  * options example `{ pem: '...', crv: 'P-256' || 'ECC', bitlength: 2048 || 256 }`
55
+  * options example `{ pem: '...' }`
43 56
 * `Keypairs.export(options)`
44
-  * options example `{ private: true || false, pem: true || false }`
57
+  * options example `{ jwk: jwk }`
58
+  * options example `{ jwk: jwk, public: true }`
59
+* `Keypairs.thumbprint({ jwk: jwk })`
60
+
61
+<!--
62
+
45 63
 * `Keypairs.jws.sign(options)`
46 64
   * options example `{ keypair, header, protected, payload }`
47 65
 * `Keypairs.csr.generate(options)`
48 66
   * options example `{ keypair, [ 'example.com' ] }`
49 67
 
50
-`keypair` can be any object with
51
-any of these keys `publicKeyPem, privateKeyPem, publicKeyJwk, privateKeyJwk`.
52
-
53
-Examples
54
-========
55
-
56
-These are quick examples of how to use the library.
57
-If you have a specific question, please open an issue.
58
-
59
-Keypairs.generate(options)
60
--------------------
61
-
62
-Simple RSA
63
-
64
-```js
65
-return Keypairs.generate({
66
-  type: 'RSA'
67
-, bitlength: 2048
68
-}).then(function (keypair) {
69
-
70
-  // we won't bother describing this object
71
-  // because it's only useful once exported
72
-
73
-});
74
-```
75
-
76
-Advanced RSA
77
-
78
-```js
79
-return Keypairs.generate({
80
-  type: 'RSA'
81
-, bitlength: 2048 // or 4096
82
-, exponent: 65537 // don't change this
83
-, public: true    // pre-cache public key
84
-, pem: true       // pre-export the PEM
85
-, internal: true  // pre-cache internal representations
86
-}).then(function (keypair) {
87
-
88
-  // we won't bother describing this object
89
-  // because it's only useful once exported
90
-
91
-});
92
-```
68
+-->
93 69
 
94
-Keypairs.export(options)
95
--------------------
70
+# Full Documentation
96 71
 
97
-Keypairs.import(options)
98
--------------------
72
+Keypairs.js provides a 1-to-1 mapping to the Rasha.js and Eckles.js APIs.
99 73
 
100
-Keypairs.jws.sign(options)
101
--------------------
74
+The full RSA documentation is at [Rasha.js](https://git.coolaj86.com/coolaj86/rasha.js/)
102 75
 
103
-Keypairs.csr.generate(options)
104
--------------------
76
+The full ECDSA documentation is at [Eckles.js](https://git.coolaj86.com/coolaj86/eckles.js/)
105 77
 
106
--->
78
+Any option you pass to Keypairs will be passed directly to the corresponding API
79
+of either Rasha or Eckles.

+ 0
- 20
bin/keypairs.js View File

@@ -1,20 +0,0 @@
1
-'use strict';
2
-
3
-var keypairs = require('../');
4
-
5
-var infile = process.argv[2];
6
-var key = require('fs').readFileSync(infile).toString('ascii');
7
-var pem;
8
-var jwk;
9
-
10
-try {
11
-  jwk = JSON.parse(key);
12
-} catch(e) {
13
-  pem = key;
14
-}
15
-
16
-keypairs.import({ jwk: jwk, pem: pem }).then(function (keypair) {
17
-  console.log(keypair);
18
-}).catch(function (e) {
19
-  console.error(e);
20
-});

+ 0
- 134
browser.js View File

@@ -1,134 +0,0 @@
1
-;(function (exports) {
2
-'use strict';
3
-
4
-var PromiseA;
5
-try {
6
-  /*global Promise*/
7
-  PromiseA = Promise;
8
-} catch(e) {
9
-  PromiseA = require('bluebird');
10
-}
11
-
12
-
13
-// https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
14
-function derToPem(keydata, pemName, privacy){
15
-  var keydataS = arrayBufferToString(keydata);
16
-  var keydataB64 = window.btoa(keydataS);
17
-  var keydataB64Pem = formatAsPem(keydataB64, pemName, privacy);
18
-  return keydataB64Pem;
19
-}
20
-
21
-function arrayBufferToString( buffer ) {
22
-  var binary = [];
23
-  var bytes = new Uint8Array( buffer );
24
-  var len = bytes.byteLength;
25
-  for (var i = 0; i < len; i++) {
26
-      binary.push(String.fromCharCode( bytes[ i ] ));
27
-  }
28
-  return binary.join('');
29
-}
30
-
31
-
32
-function formatAsPem(str, pemName, privacy) {
33
-  var privstr = (privacy ? privacy + ' ' : '');
34
-  var finalString = '-----BEGIN ' + pemName + ' ' + privstr + 'KEY-----\n';
35
-
36
-  while (str.length > 0) {
37
-      finalString += str.substring(0, 64) + '\n';
38
-      str = str.substring(64);
39
-  }
40
-
41
-  finalString = finalString + '-----END ' + pemName + ' ' + privstr + 'KEY-----';
42
-
43
-  return finalString;
44
-}
45
-
46
-var Keypairs = exports.Keypairs = {
47
-  generate: function(opts) {
48
-    if (!opts) { opts = {}; }
49
-    if (!opts.type) { opts.type = 'EC'; }
50
-
51
-    var supported = [ 'EC', 'RSA' ];
52
-    if (-1 === supported.indexOf(opts.type)) {
53
-      return PromiseA.reject(new Error("'" + opts.type + "' not implemented. Try one of " + supported.join(', ')));
54
-    }
55
-
56
-    if ('EC' === opts.type) {
57
-      return Keypairs._generateEc(opts);
58
-    }
59
-    if ('RSA' === opts.type) {
60
-      return Keypairs._generateRsa(opts);
61
-    }
62
-  }
63
-, _generateEc: function (opts) {
64
-    if (!opts.namedCurve) { opts.namedCurve = 'P-256'; }
65
-    if ('P-256' !== opts.namedCurve) {
66
-      console.warn("'" + opts.namedCurve + "' is not supported, but it _might_ happen to work anyway.");
67
-    }
68
-
69
-    // https://github.com/diafygi/webcrypto-examples#ecdsa---generatekey
70
-    var extractable = true;
71
-
72
-    return window.crypto.subtle.generateKey(
73
-      { name: "ECDSA", namedCurve: opts.namedCurve }
74
-    , extractable
75
-    , [ 'sign', 'verify' ]
76
-    ).then(function (result) {
77
-      return window.crypto.subtle.exportKey(
78
-        "jwk"
79
-      , result.privateKey
80
-      ).then(function (jwk) {
81
-        return window.crypto.subtle.exportKey(
82
-          "pkcs8"
83
-        , result.privateKey
84
-        ).then(function (keydata) {
85
-          return {
86
-            type: 'EC'
87
-          , privateJwk: jwk
88
-          , privatePem: derToPem(keydata, 'EC', 'PRIVATE')
89
-          };
90
-        });
91
-      });
92
-    });
93
-  }
94
-, _generateRsa: function (opts) {
95
-    if (!opts.bitlength) { opts.bitlength = 2048; }
96
-    if (-1 === [ 2048, 4096 ].indexOf(opts.bitlength)) {
97
-      return PromiseA.reject("opts.bitlength = (" + typeof opts.bitlength + ") " + opts.bitlength + ": Are you serious?");
98
-    }
99
-
100
-    // https://github.com/diafygi/webcrypto-examples#rsa---generatekey
101
-    var extractable = true;
102
-
103
-    return window.crypto.subtle.generateKey(
104
-      { name: "RSASSA-PKCS1-v1_5"
105
-      , modulusLength: opts.bitlength
106
-      , publicExponent: new Uint8Array([0x01, 0x00, 0x01])
107
-      , hash: { name: "SHA-256" }
108
-      }
109
-    , extractable
110
-    , [ 'sign', 'verify' ]
111
-    ).then(function (result) {
112
-      return window.crypto.subtle.exportKey(
113
-        "jwk"
114
-      , result.privateKey
115
-      ).then(function (jwk) {
116
-        return window.crypto.subtle.exportKey(
117
-          "pkcs8"
118
-        , result.privateKey
119
-        ).then(function (keydata) {
120
-          return {
121
-            type: 'RSA'
122
-          , privateJwk: jwk
123
-          , privatePem: derToPem(keydata, 'RSA', 'PRIVATE')
124
-          };
125
-        });
126
-      });
127
-    });
128
-  }
129
-};
130
-
131
-}('undefined' === typeof module ? window : module.exports));
132
-
133
-// How we might use this
134
-// var Keypairs = require('keypairs').Keypairs

+ 0
- 13
convert-from-hex.js View File

@@ -1,13 +0,0 @@
1
-'use strict';
2
-
3
-var fs = require('fs');
4
-var path = require('path');
5
-
6
-function convert(name) {
7
-  var ext = path.extname(name);
8
-  var csr = fs.readFileSync(name, 'ascii').replace(/\s\+/g, '');
9
-  var bin = Buffer.from(csr, 'hex');
10
-  fs.writeFileSync(name.replace(new RegExp('\\' + ext + '$'), '') + '.bin', bin);
11
-}
12
-
13
-convert(process.argv[2]);

+ 0
- 16
convert-to-der.js View File

@@ -1,16 +0,0 @@
1
-'use strict';
2
-
3
-var fs = require('fs');
4
-var path = require('path');
5
-
6
-function convert(name) {
7
-  var ext = path.extname(name);
8
-  var csr = fs.readFileSync(name, 'ascii').split(/\n/).filter(function (line) {
9
-    return !/---/.test(line);
10
-  }).join('');
11
-  console.log(csr);
12
-  var der = Buffer.from(csr, 'base64');
13
-  fs.writeFileSync(name.replace(new RegExp('\\' + ext + '$'), '') + '.der', der);
14
-}
15
-
16
-convert(process.argv[2]);

+ 0
- 7
fixtures/privkey-ec-p256.jwk.json View File

@@ -1,7 +0,0 @@
1
-{
2
-  "kty": "EC",
3
-  "crv": "P-256",
4
-  "d": "iYydo27aNGO9DBUWeGEPD8oNi1LZDqfxPmQlieLBjVQ",
5
-  "x": "IT1SWLxsacPiE5Z16jkopAn8_-85rMjgyCokrnjDft4",
6
-  "y": "mP2JwOAOdMmXuwpxbKng3KZz27mz-nKWIlXJ3rzSGMo"
7
-}

+ 0
- 5
fixtures/privkey-ec-p256.pkcs8.pem View File

@@ -1,5 +0,0 @@
1
------BEGIN PRIVATE KEY-----
2
-MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiYydo27aNGO9DBUW
3
-eGEPD8oNi1LZDqfxPmQlieLBjVShRANCAAQhPVJYvGxpw+ITlnXqOSikCfz/7zms
4
-yODIKiSueMN+3pj9icDgDnTJl7sKcWyp4Nymc9u5s/pyliJVyd680hjK
5
------END PRIVATE KEY-----

+ 0
- 5
fixtures/privkey-ec-p256.sec1.pem View File

@@ -1,5 +0,0 @@
1
------BEGIN EC PRIVATE KEY-----
2
-MHcCAQEEIImMnaNu2jRjvQwVFnhhDw/KDYtS2Q6n8T5kJYniwY1UoAoGCCqGSM49
3
-AwEHoUQDQgAEIT1SWLxsacPiE5Z16jkopAn8/+85rMjgyCokrnjDft6Y/YnA4A50
4
-yZe7CnFsqeDcpnPbubP6cpYiVcnevNIYyg==
5
------END EC PRIVATE KEY-----

+ 0
- 7
fixtures/privkey-ec-p384.jwk.json View File

@@ -1,7 +0,0 @@
1
-{
2
-  "kty": "EC",
3
-  "crv": "P-384",
4
-  "d": "XlyuCEWSTTS8U79O_Mz05z18vh4kb10szvu_7pdXuGWV6lfEyPExyUYWsA6A2kdV",
5
-  "x": "2zEU0bKCa7ejKLIJ8oPGnLhqhxyiv4_w38K2a0SPC6dsSd9_glNJ8lcqv0sff5Gb",
6
-  "y": "VD4jnu83S6scn6_TeAj3EZOREGbOs6dzoVpaugn-XQMMyC9O4VLbDDFGBZTJlMsb"
7
-}

+ 0
- 6
fixtures/privkey-ec-p384.pkcs8.pem View File

@@ -1,6 +0,0 @@
1
------BEGIN PRIVATE KEY-----
2
-MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBeXK4IRZJNNLxTv078
3
-zPTnPXy+HiRvXSzO+7/ul1e4ZZXqV8TI8THJRhawDoDaR1WhZANiAATbMRTRsoJr
4
-t6Mosgnyg8acuGqHHKK/j/DfwrZrRI8Lp2xJ33+CU0nyVyq/Sx9/kZtUPiOe7zdL
5
-qxyfr9N4CPcRk5EQZs6zp3OhWlq6Cf5dAwzIL07hUtsMMUYFlMmUyxs=
6
------END PRIVATE KEY-----

+ 0
- 6
fixtures/privkey-ec-p384.sec1.pem View File

@@ -1,6 +0,0 @@
1
------BEGIN EC PRIVATE KEY-----
2
-MIGkAgEBBDBeXK4IRZJNNLxTv078zPTnPXy+HiRvXSzO+7/ul1e4ZZXqV8TI8THJ
3
-RhawDoDaR1WgBwYFK4EEACKhZANiAATbMRTRsoJrt6Mosgnyg8acuGqHHKK/j/Df
4
-wrZrRI8Lp2xJ33+CU0nyVyq/Sx9/kZtUPiOe7zdLqxyfr9N4CPcRk5EQZs6zp3Oh
5
-Wlq6Cf5dAwzIL07hUtsMMUYFlMmUyxs=
6
------END EC PRIVATE KEY-----

+ 0
- 11
fixtures/privkey-rsa-2048.jwk.json View File

@@ -1,11 +0,0 @@
1
-{
2
-  "kty": "RSA",
3
-  "n": "m2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC-xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6-I70on0_iDZm7-jcqOPgADAmbWHhy67BXkk4yy_YzD4yOGZFXZcNp915_TW5bRd__AKPHUHxJasPiyEFqlNKBR2DSD-LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps_50-CNw",
4
-  "e": "AQAB",
5
-  "d": "Cpfo7Mm9Nu8YMC_xrZ54W9mKHPkCG9rZ93Ds9PNp-RXUgb-ljTbFPZWsYxGNKLllFz8LNosr1pT2ZDMrwNk0Af1iWNvD6gkyXaiQdCyiDPSBsJyNv2LJZon-e85X74nv53UlIkmo9SYxdLz2JaJ-iIWEe8Qh-7llLktrTJV_xr98_tbhgSppz_IeOymq3SEZaQHM8pTU7w7XvCj2pb9r8fN0M0XcgWZIaf3LGEfkhF_WtX67XJ0C6-LbkT51jtlLRNGX6haGdscXS0OWWjKOJzKGuV-NbthEn5rmRtVnjRZ3yaxQ0ud8vC-NONn7yvGUlOur1IdDzJ_YfHPt9sHMQQ",
6
-  "p": "ynG-t9HwKCN3MWRYFdnFzi9-02Qcy3p8B5pu3ary2E70hYn2pHlUG2a9BNE8c5xHQ3Hx43WoWf6s0zOunPV1G28LkU_UYEbAtPv_PxSmzpQp9n9XnYvBLBF8Y3z7gxgLn1vVFNARrQdRtj87qY3aw7E9S4DsGcAarIuOT2TsTCE",
7
-  "q": "xIkAjgUzB1zaUzJtW2Zgvp9cYYr1DmpH30ePZl3c_8397_DZDDo46fnFYjs6uPa03HpmKUnbjwr14QHlfXlntJBEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnFD2E5ll0sVeeDeMJHQw38ahSrBFEVnxjpnPh1Q1c",
8
-  "dp": "tzDGjECFOU0ehqtuqhcuT63a7h8hj19-7MJqoFwY9HQ-ALkfXyYLXeBSGxHbyiIYuodZg6LsfMNgUJ3r3Eyhc_nAVfYPEC_2IdAG4WYmq7iXYF9LQV09qEsKbFykm7QekE3hO7wswo5k-q2tp3ieBYdVGAXJoGOdv5VpaZ7B1QE",
9
-  "dq": "kh5dyDk7YCz7sUFbpsmuAeuPjoH2ghooh2u3xN7iUVmAg-ToKjwbVnG5-7eXiC779rQVwnrD_0yh1AFJ8wjRPqDIR7ObXGHikIxT1VSQWqiJm6AfZzDsL0LUD4YS3iPdhob7-NxLKWzqao_u4lhnDQaX9PKa12HFlny6K1daL48",
10
-  "qi": "AlHWbx1gp6Z9pbw_1hlS7HuXAgWoX7IjbTUelldf4gkriDWLOrj3QCZcO4ZvZvEwJhVlsny9LO8IkbwGJEL6cXraK08ByVS2mwQyflgTgGNnpzixyEUL_mrQLx6y145FHcxfeqNInMhep-0Mxn1D5nlhmIOgRApS0t9VoXtHhFU"
11
-}

+ 0
- 27
fixtures/privkey-rsa-2048.pkcs1.pem View File

@@ -1,27 +0,0 @@
1
------BEGIN RSA PRIVATE KEY-----
2
-MIIEpAIBAAKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhD
3
-NzUJefLukC+xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ
4
-38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57
5
-qAZM6+I70on0/iDZm7+jcqOPgADAmbWHhy67BXkk4yy/YzD4yOGZFXZcNp915/TW
6
-5bRd//AKPHUHxJasPiyEFqlNKBR2DSD+LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By
7
-5AB0Qi49OykUCfNZeQlEz7UNNj9RGps/50+CNwIDAQABAoIBAAqX6OzJvTbvGDAv
8
-8a2eeFvZihz5Ahva2fdw7PTzafkV1IG/pY02xT2VrGMRjSi5ZRc/CzaLK9aU9mQz
9
-K8DZNAH9Yljbw+oJMl2okHQsogz0gbCcjb9iyWaJ/nvOV++J7+d1JSJJqPUmMXS8
10
-9iWifoiFhHvEIfu5ZS5La0yVf8a/fP7W4YEqac/yHjspqt0hGWkBzPKU1O8O17wo
11
-9qW/a/HzdDNF3IFmSGn9yxhH5IRf1rV+u1ydAuvi25E+dY7ZS0TRl+oWhnbHF0tD
12
-lloyjicyhrlfjW7YRJ+a5kbVZ40Wd8msUNLnfLwvjTjZ+8rxlJTrq9SHQ8yf2Hxz
13
-7fbBzEECgYEAynG+t9HwKCN3MWRYFdnFzi9+02Qcy3p8B5pu3ary2E70hYn2pHlU
14
-G2a9BNE8c5xHQ3Hx43WoWf6s0zOunPV1G28LkU/UYEbAtPv/PxSmzpQp9n9XnYvB
15
-LBF8Y3z7gxgLn1vVFNARrQdRtj87qY3aw7E9S4DsGcAarIuOT2TsTCECgYEAxIkA
16
-jgUzB1zaUzJtW2Zgvp9cYYr1DmpH30ePZl3c/8397/DZDDo46fnFYjs6uPa03Hpm
17
-KUnbjwr14QHlfXlntJBEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnF
18
-D2E5ll0sVeeDeMJHQw38ahSrBFEVnxjpnPh1Q1cCgYEAtzDGjECFOU0ehqtuqhcu
19
-T63a7h8hj19+7MJqoFwY9HQ+ALkfXyYLXeBSGxHbyiIYuodZg6LsfMNgUJ3r3Eyh
20
-c/nAVfYPEC/2IdAG4WYmq7iXYF9LQV09qEsKbFykm7QekE3hO7wswo5k+q2tp3ie
21
-BYdVGAXJoGOdv5VpaZ7B1QECgYEAkh5dyDk7YCz7sUFbpsmuAeuPjoH2ghooh2u3
22
-xN7iUVmAg+ToKjwbVnG5+7eXiC779rQVwnrD/0yh1AFJ8wjRPqDIR7ObXGHikIxT
23
-1VSQWqiJm6AfZzDsL0LUD4YS3iPdhob7+NxLKWzqao/u4lhnDQaX9PKa12HFlny6
24
-K1daL48CgYACUdZvHWCnpn2lvD/WGVLse5cCBahfsiNtNR6WV1/iCSuINYs6uPdA
25
-Jlw7hm9m8TAmFWWyfL0s7wiRvAYkQvpxetorTwHJVLabBDJ+WBOAY2enOLHIRQv+
26
-atAvHrLXjkUdzF96o0icyF6n7QzGfUPmeWGYg6BEClLS31Whe0eEVQ==
27
------END RSA PRIVATE KEY-----

+ 0
- 28
fixtures/privkey-rsa-2048.pkcs8.pem View File

@@ -1,28 +0,0 @@
1
------BEGIN PRIVATE KEY-----
2
-MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCba21UHE+VbDTp
3
-mYYFZUOV+OQ8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo
4
-6HF5JijfWzK7haHFuRMEsgI4VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/R
5
-JSE3Vw/uz2bxiT22uSkSqOyShyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZ
6
-tYeHLrsFeSTjLL9jMPjI4ZkVdlw2n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYN
7
-IP4ttfl5ObMKHaKSvBMyNruZR0El/ZsrcHLkAHRCLj07KRQJ81l5CUTPtQ02P1Ea
8
-mz/nT4I3AgMBAAECggEACpfo7Mm9Nu8YMC/xrZ54W9mKHPkCG9rZ93Ds9PNp+RXU
9
-gb+ljTbFPZWsYxGNKLllFz8LNosr1pT2ZDMrwNk0Af1iWNvD6gkyXaiQdCyiDPSB
10
-sJyNv2LJZon+e85X74nv53UlIkmo9SYxdLz2JaJ+iIWEe8Qh+7llLktrTJV/xr98
11
-/tbhgSppz/IeOymq3SEZaQHM8pTU7w7XvCj2pb9r8fN0M0XcgWZIaf3LGEfkhF/W
12
-tX67XJ0C6+LbkT51jtlLRNGX6haGdscXS0OWWjKOJzKGuV+NbthEn5rmRtVnjRZ3
13
-yaxQ0ud8vC+NONn7yvGUlOur1IdDzJ/YfHPt9sHMQQKBgQDKcb630fAoI3cxZFgV
14
-2cXOL37TZBzLenwHmm7dqvLYTvSFifakeVQbZr0E0TxznEdDcfHjdahZ/qzTM66c
15
-9XUbbwuRT9RgRsC0+/8/FKbOlCn2f1edi8EsEXxjfPuDGAufW9UU0BGtB1G2Pzup
16
-jdrDsT1LgOwZwBqsi45PZOxMIQKBgQDEiQCOBTMHXNpTMm1bZmC+n1xhivUOakff
17
-R49mXdz/zf3v8NkMOjjp+cViOzq49rTcemYpSduPCvXhAeV9eWe0kES5fFyouOR0
18
-p0nihvvG54tMriy6j1XwtKuQsKFXGVlDCcUPYTmWXSxV54N4wkdDDfxqFKsEURWf
19
-GOmc+HVDVwKBgQC3MMaMQIU5TR6Gq26qFy5PrdruHyGPX37swmqgXBj0dD4AuR9f
20
-Jgtd4FIbEdvKIhi6h1mDoux8w2BQnevcTKFz+cBV9g8QL/Yh0AbhZiaruJdgX0tB
21
-XT2oSwpsXKSbtB6QTeE7vCzCjmT6ra2neJ4Fh1UYBcmgY52/lWlpnsHVAQKBgQCS
22
-Hl3IOTtgLPuxQVumya4B64+OgfaCGiiHa7fE3uJRWYCD5OgqPBtWcbn7t5eILvv2
23
-tBXCesP/TKHUAUnzCNE+oMhHs5tcYeKQjFPVVJBaqImboB9nMOwvQtQPhhLeI92G
24
-hvv43EspbOpqj+7iWGcNBpf08prXYcWWfLorV1ovjwKBgAJR1m8dYKemfaW8P9YZ
25
-Uux7lwIFqF+yI201HpZXX+IJK4g1izq490AmXDuGb2bxMCYVZbJ8vSzvCJG8BiRC
26
-+nF62itPAclUtpsEMn5YE4BjZ6c4schFC/5q0C8esteORR3MX3qjSJzIXqftDMZ9
27
-Q+Z5YZiDoEQKUtLfVaF7R4RV
28
------END PRIVATE KEY-----

+ 0
- 6
fixtures/pub-ec-p256.jwk.json View File

@@ -1,6 +0,0 @@
1
-{
2
-  "kty": "EC",
3
-  "crv": "P-256",
4
-  "x": "IT1SWLxsacPiE5Z16jkopAn8_-85rMjgyCokrnjDft4",
5
-  "y": "mP2JwOAOdMmXuwpxbKng3KZz27mz-nKWIlXJ3rzSGMo"
6
-}

+ 0
- 4
fixtures/pub-ec-p256.spki.pem View File

@@ -1,4 +0,0 @@
1
------BEGIN PUBLIC KEY-----
2
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIT1SWLxsacPiE5Z16jkopAn8/+85
3
-rMjgyCokrnjDft6Y/YnA4A50yZe7CnFsqeDcpnPbubP6cpYiVcnevNIYyg==
4
------END PUBLIC KEY-----

+ 0
- 1
fixtures/pub-ec-p256.ssh.pub View File

@@ -1 +0,0 @@
1
-ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCE9Uli8bGnD4hOWdeo5KKQJ/P/vOazI4MgqJK54w37emP2JwOAOdMmXuwpxbKng3KZz27mz+nKWIlXJ3rzSGMo= P-256@localhost

+ 0
- 6
fixtures/pub-ec-p384.jwk.json View File

@@ -1,6 +0,0 @@
1
-{
2
-  "kty": "EC",
3
-  "crv": "P-384",
4
-  "x": "2zEU0bKCa7ejKLIJ8oPGnLhqhxyiv4_w38K2a0SPC6dsSd9_glNJ8lcqv0sff5Gb",
5
-  "y": "VD4jnu83S6scn6_TeAj3EZOREGbOs6dzoVpaugn-XQMMyC9O4VLbDDFGBZTJlMsb"
6
-}

+ 0
- 5
fixtures/pub-ec-p384.spki.pem View File

@@ -1,5 +0,0 @@
1
------BEGIN PUBLIC KEY-----
2
-MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2zEU0bKCa7ejKLIJ8oPGnLhqhxyiv4/w
3
-38K2a0SPC6dsSd9/glNJ8lcqv0sff5GbVD4jnu83S6scn6/TeAj3EZOREGbOs6dz
4
-oVpaugn+XQMMyC9O4VLbDDFGBZTJlMsb
5
------END PUBLIC KEY-----

+ 0
- 1
fixtures/pub-ec-p384.ssh.pub View File

@@ -1 +0,0 @@
1
-ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNsxFNGygmu3oyiyCfKDxpy4aoccor+P8N/CtmtEjwunbEnff4JTSfJXKr9LH3+Rm1Q+I57vN0urHJ+v03gI9xGTkRBmzrOnc6FaWroJ/l0DDMgvTuFS2wwxRgWUyZTLGw== P-384@localhost

+ 0
- 5
fixtures/pub-rsa-2048.jwk.json View File

@@ -1,5 +0,0 @@
1
-{
2
-  "kty": "RSA",
3
-  "n": "m2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC-xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6-I70on0_iDZm7-jcqOPgADAmbWHhy67BXkk4yy_YzD4yOGZFXZcNp915_TW5bRd__AKPHUHxJasPiyEFqlNKBR2DSD-LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps_50-CNw",
4
-  "e": "AQAB"
5
-}

+ 0
- 8
fixtures/pub-rsa-2048.pkcs1.pem View File

@@ -1,8 +0,0 @@
1
------BEGIN RSA PUBLIC KEY-----
2
-MIIBCgKCAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJ
3
-efLukC+xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8
4
-LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM
5
-6+I70on0/iDZm7+jcqOPgADAmbWHhy67BXkk4yy/YzD4yOGZFXZcNp915/TW5bRd
6
-//AKPHUHxJasPiyEFqlNKBR2DSD+LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0
7
-Qi49OykUCfNZeQlEz7UNNj9RGps/50+CNwIDAQAB
8
------END RSA PUBLIC KEY-----

+ 0
- 9
fixtures/pub-rsa-2048.spki.pem View File

@@ -1,9 +0,0 @@
1
------BEGIN PUBLIC KEY-----
2
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2ttVBxPlWw06ZmGBWVD
3
-lfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC+xu0LBKylYojT5vTkxaOhxeSYo
4
-31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP
5
-7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6+I70on0/iDZm7+jcqOPgADAmbWHhy67
6
-BXkk4yy/YzD4yOGZFXZcNp915/TW5bRd//AKPHUHxJasPiyEFqlNKBR2DSD+LbX5
7
-eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps/50+C
8
-NwIDAQAB
9
------END PUBLIC KEY-----

+ 0
- 1
fixtures/pub-rsa-2048.ssh.pub View File

@@ -1 +0,0 @@
1
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCba21UHE+VbDTpmYYFZUOV+OQ8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo6HF5JijfWzK7haHFuRMEsgI4VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/RJSE3Vw/uz2bxiT22uSkSqOyShyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZtYeHLrsFeSTjLL9jMPjI4ZkVdlw2n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYNIP4ttfl5ObMKHaKSvBMyNruZR0El/ZsrcHLkAHRCLj07KRQJ81l5CUTPtQ02P1Eamz/nT4I3 rsa@localhost

+ 0
- 3
index.js View File

@@ -1,3 +0,0 @@
1
-'use strict';
2
-
3
-module.exports = require('./lib/keypairs.js');

+ 42
- 0
keypairs.js View File

@@ -0,0 +1,42 @@
1
+'use strict';
2
+
3
+var Eckles = require('eckles');
4
+var Rasha = require('rasha');
5
+var Keypairs = {};
6
+
7
+/*global Promise*/
8
+
9
+Keypairs.generate = function (opts) {
10
+  opts = opts || {};
11
+  var kty = opts.kty || opts.type;
12
+  if ('RSA' === kty) {
13
+    return Rasha.generate(opts);
14
+  }
15
+  return Eckles.generate(opts);
16
+};
17
+
18
+Keypairs.import = function (opts) {
19
+  return Eckles.import(opts.pem).catch(function () {
20
+    return Rasha.import(opts.pem);
21
+  });
22
+};
23
+
24
+Keypairs.export = function (opts) {
25
+  return Promise.resolve().then(function () {
26
+    if ('RSA' === opts.jwk.kty) {
27
+      return Rasha.export(opts);
28
+    } else {
29
+      return Eckles.export(opts);
30
+    }
31
+  });
32
+};
33
+
34
+Keypairs.thumbprint = function (opts) {
35
+  return Promise.resolve().then(function () {
36
+    if ('RSA' === opts.jwk.kty) {
37
+      return Rasha.thumbprint(opts);
38
+    } else {
39
+      return Eckles.thumbprint(opts);
40
+    }
41
+  });
42
+};

+ 0
- 111
lib/asn1-packer.js View File

@@ -1,111 +0,0 @@
1
-'use strict';
2
-
3
-var ASN1 = module.exports;
4
-var Enc = require('./encoding.js');
5
-
6
-//
7
-// Packer
8
-//
9
-
10
-// Almost every ASN.1 type that's important for CSR
11
-// can be represented generically with only a few rules.
12
-ASN1.Any = function (/*type, hexstrings...*/) {
13
-  var args = Array.prototype.slice.call(arguments);
14
-  var typ = args.shift();
15
-  var str = args.join('').replace(/\s+/g, '').toLowerCase();
16
-  var len = (str.length/2);
17
-  var lenlen = 0;
18
-  var hex = typ;
19
-
20
-  // We can't have an odd number of hex chars
21
-  if (len !== Math.round(len)) {
22
-    throw new Error("invalid hex");
23
-  }
24
-
25
-  // The first byte of any ASN.1 sequence is the type (Sequence, Integer, etc)
26
-  // The second byte is either the size of the value, or the size of its size
27
-
28
-  // 1. If the second byte is < 0x80 (128) it is considered the size
29
-  // 2. If it is > 0x80 then it describes the number of bytes of the size
30
-  //    ex: 0x82 means the next 2 bytes describe the size of the value
31
-  // 3. The special case of exactly 0x80 is "indefinite" length (to end-of-file)
32
-
33
-  if (len > 127) {
34
-    lenlen += 1;
35
-    while (len > 255) {
36
-      lenlen += 1;
37
-      len = len >> 8;
38
-    }
39
-  }
40
-
41
-  if (lenlen) { hex += Enc.numToHex(0x80 + lenlen); }
42
-  return hex + Enc.numToHex(str.length/2) + str;
43
-};
44
-
45
-// The Integer type has some special rules
46
-ASN1.UInt = function UINT() {
47
-  var str = Array.prototype.slice.call(arguments).join('');
48
-  var first = parseInt(str.slice(0, 2), 16);
49
-
50
-  // If the first byte is 0x80 or greater, the number is considered negative
51
-  // Therefore we add a '00' prefix if the 0x80 bit is set
52
-  if (0x80 & first) { str = '00' + str; }
53
-
54
-  return ASN1.Any('02', str);
55
-};
56
-
57
-// The Bit String type also has a special rule
58
-ASN1.BitStr = function BITSTR() {
59
-  var str = Array.prototype.slice.call(arguments).join('');
60
-  // '00' is a mask of how many bits of the next byte to ignore
61
-  return ASN1.Any('03', '00' + str);
62
-};
63
-
64
-ASN1._pack = function (arr) {
65
-  var typ = Enc.numToHex(arr[0]);
66
-  var str = '';
67
-  if (Array.isArray(arr[1])) {
68
-    arr[1].forEach(function (a) {
69
-      str += ASN1._pack(a);
70
-    });
71
-  } else if ('string' === typeof arr[1]) {
72
-    str = arr[1];
73
-  } else {
74
-    throw new Error("unexpected array");
75
-  }
76
-  if ('03' === typ) {
77
-    return ASN1.BitStr(str);
78
-  } else if ('02' === typ) {
79
-    return ASN1.UInt(str);
80
-  } else {
81
-    return ASN1.Any(typ, str);
82
-  }
83
-};
84
-ASN1.pack = function (arr) {
85
-  return Enc.hexToBuf(ASN1._pack(arr));
86
-};
87
-
88
-Enc.bufToBase64 = function (u8) {
89
-  var bin = '';
90
-  u8.forEach(function (i) {
91
-    bin += String.fromCharCode(i);
92
-  });
93
-  return Buffer.from(bin, 'binary').toString('base64');
94
-  //return btoa(bin);
95
-};
96
-
97
-Enc.hexToBuf = function (hex) {
98
-  var arr = [];
99
-  hex.match(/.{2}/g).forEach(function (h) {
100
-    arr.push(parseInt(h, 16));
101
-  });
102
-  return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
103
-};
104
-
105
-Enc.numToHex = function (d) {
106
-  d = d.toString(16);
107
-  if (d.length % 2) {
108
-    return '0' + d;
109
-  }
110
-  return d;
111
-};

+ 0
- 147
lib/asn1-parser.js View File

@@ -1,147 +0,0 @@
1
-// Copyright 2016-2018 AJ ONeal. All rights reserved
2
-// https://git.coolaj86.com/coolaj86/asn1-parser.js
3
-/* This Source Code Form is subject to the terms of the Mozilla Public
4
- * License, v. 2.0. If a copy of the MPL was not distributed with this
5
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
-;(function (exports) {
7
-'use strict';
8
-
9
-if (!exports.ASN1) { exports.ASN1 = {}; }
10
-if (!exports.Enc) { exports.Enc = {}; }
11
-if (!exports.PEM) { exports.PEM = {}; }
12
-
13
-var ASN1 = exports.ASN1;
14
-var Enc = require('./encoding.js');
15
-var PEM = exports.PEM;
16
-
17
-//
18
-// Parser
19
-//
20
-
21
-ASN1.ELOOPN = 20; // I've seen 9 max in https certificates
22
-ASN1.ELOOP = "uASN1.js Error: iterated over " + ASN1.ELOOPN + "+ elements (probably a malformed file)";
23
-ASN1.EDEEPN = 60; // I've seen 29 deep in https certificates
24
-ASN1.EDEEP = "uASN1.js Error: element nested " + ASN1.EDEEPN + "+ layers deep (probably a malformed file)";
25
-// Container Types are Sequence 0x30, Container Array? (0xA0, 0xA1)
26
-// Value Types are Boolean 0x01, Integer 0x02, Null 0x05, Object ID 0x06, String 0x0C, 0x16, 0x13, 0x1e Value Array? (0x82)
27
-// Bit String (0x03) and Octet String (0x04) may be values or containers
28
-// Sometimes Bit String is used as a container (RSA Pub Spki)
29
-ASN1.CTYPES = [ 0x30, 0x31, 0xa0, 0xa1 ];
30
-ASN1.VTYPES = [ 0x01, 0x02, 0x05, 0x06, 0x0c, 0x82 ];
31
-ASN1.parse = function parseAsn1Helper(buf) {
32
-  //var ws = '  ';
33
-  function parseAsn1(buf, depth, eager) {
34
-    if (depth.length >= ASN1.EDEEPN) { throw new Error(ASN1.EDEEP); }
35
-
36
-    var index = 2; // we know, at minimum, data starts after type (0) and lengthSize (1)
37
-    var asn1 = { type: buf[0], lengthSize: 0, length: buf[1] };
38
-    var child;
39
-    var iters = 0;
40
-    var adjust = 0;
41
-    var adjustedLen;
42
-
43
-    // Determine how many bytes the length uses, and what it is
44
-    if (0x80 & asn1.length) {
45
-      asn1.lengthSize = 0x7f & asn1.length;
46
-      // I think that buf->hex->int solves the problem of Endianness... not sure
47
-      asn1.length = parseInt(Enc.bufToHex(buf.slice(index, index + asn1.lengthSize)), 16);
48
-      index += asn1.lengthSize;
49
-    }
50
-
51
-    // High-order bit Integers have a leading 0x00 to signify that they are positive.
52
-    // Bit Streams use the first byte to signify padding, which x.509 doesn't use.
53
-    if (0x00 === buf[index] && (0x02 === asn1.type || 0x03 === asn1.type)) {
54
-      // However, 0x00 on its own is a valid number
55
-      if (asn1.length > 1) {
56
-        index += 1;
57
-        adjust = -1;
58
-      }
59
-    }
60
-    adjustedLen = asn1.length + adjust;
61
-
62
-    //console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
63
-
64
-    function parseChildren(eager) {
65
-      asn1.children = [];
66
-      //console.warn('1 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', 0);
67
-      while (iters < ASN1.ELOOPN && index < (2 + asn1.length + asn1.lengthSize)) {
68
-        iters += 1;
69
-        depth.length += 1;
70
-        child = parseAsn1(buf.slice(index, index + adjustedLen), depth, eager);
71
-        depth.length -= 1;
72
-        // The numbers don't match up exactly and I don't remember why...
73
-        // probably something with adjustedLen or some such, but the tests pass
74
-        index += (2 + child.lengthSize + child.length);
75
-        //console.warn('2 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', (2 + child.lengthSize + child.length));
76
-        if (index > (2 + asn1.lengthSize + asn1.length)) {
77
-          if (!eager) { console.error(JSON.stringify(asn1, ASN1._replacer, 2)); }
78
-          throw new Error("Parse error: child value length (" + child.length
79
-            + ") is greater than remaining parent length (" + (asn1.length - index)
80
-            + " = " + asn1.length + " - " + index + ")");
81
-        }
82
-        asn1.children.push(child);
83
-        //console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
84
-      }
85
-      if (index !== (2 + asn1.lengthSize + asn1.length)) {
86
-        //console.warn('index:', index, 'length:', (2 + asn1.lengthSize + asn1.length));
87
-        throw new Error("premature end-of-file");
88
-      }
89
-      if (iters >= ASN1.ELOOPN) { throw new Error(ASN1.ELOOP); }
90
-
91
-      delete asn1.value;
92
-      return asn1;
93
-    }
94
-
95
-    // Recurse into types that are _always_ containers
96
-    if (-1 !== ASN1.CTYPES.indexOf(asn1.type)) { return parseChildren(eager); }
97
-
98
-    // Return types that are _always_ values
99
-    asn1.value = buf.slice(index, index + adjustedLen);
100
-    if (-1 !== ASN1.VTYPES.indexOf(asn1.type)) { return asn1; }
101
-
102
-    // For ambigious / unknown types, recurse and return on failure
103
-    // (and return child array size to zero)
104
-    try { return parseChildren(true); }
105
-    catch(e) { asn1.children.length = 0; return asn1; }
106
-  }
107
-
108
-  var asn1 = parseAsn1(buf, []);
109
-  var len = buf.byteLength || buf.length;
110
-  if (len !== 2 + asn1.lengthSize + asn1.length) {
111
-    throw new Error("Length of buffer does not match length of ASN.1 sequence.");
112
-  }
113
-  return asn1;
114
-};
115
-ASN1._replacer = function (k, v) {
116
-  if ('type' === k) { return '0x' + Enc.numToHex(v); }
117
-  if (v && 'value' === k) { return '0x' + Enc.bufToHex(v.data || v); }
118
-  return v;
119
-};
120
-
121
-// don't replace the full parseBlock, if it exists
122
-PEM.parseBlock = PEM.parseBlock || function (str) {
123
-  var b64 = str.split(/\n/).filter(function (line) {
124
-    return !/-----/.test(line);
125
-  }).join('');
126
-  var der = Enc.base64ToBuf(b64);
127
-  return { bytes: der };
128
-};
129
-
130
-Enc.base64ToBuf = function (b64) {
131
-  return Buffer.from(b64, 'base64');
132
-};
133
-Enc.binToBuf = function (bin) {
134
-  return Buffer.from(bin, 'binary');
135
-};
136
-Enc.bufToHex = function (u8) {
137
-  return Buffer.from(u8).toString('hex');
138
-};
139
-Enc.numToHex = function (d) {
140
-  d = d.toString(16);
141
-  if (d.length % 2) {
142
-    return '0' + d;
143
-  }
144
-  return d;
145
-};
146
-
147
-}('undefined' !== typeof window ? window : module.exports));

+ 0
- 3
lib/crypto.js View File

@@ -1,3 +0,0 @@
1
-'use strict';
2
-
3
-var wrap = module.exports;

+ 0
- 63
lib/ec.js View File

@@ -1,63 +0,0 @@
1
-'use strict';
2
-
3
-var x509 = module.exports;
4
-
5
-var ASN1 = require('./asn1-packer.js');
6
-var Enc = require('./encoding.js');
7
-
8
-// 1.2.840.10045.3.1.7
9
-// prime256v1 (ANSI X9.62 named elliptic curve)
10
-var OBJ_ID_EC_256  = '06 08 2A8648CE3D030107'.replace(/\s+/g, '').toLowerCase();
11
-// 1.3.132.0.34
12
-// secp384r1 (SECG (Certicom) named elliptic curve)
13
-var OBJ_ID_EC_384 = '06 05 2B81040022'.replace(/\s+/g, '').toLowerCase();
14
-// 1.2.840.10045.2.1
15
-// ecPublicKey (ANSI X9.62 public key type)
16
-var OBJ_ID_EC_PUB = '06 07 2A8648CE3D0201'.replace(/\s+/g, '').toLowerCase();
17
-
18
-x509.packSec1 = function (jwk) {
19
-  var d = Enc.base64ToHex(jwk.d);
20
-  var x = Enc.base64ToHex(jwk.x);
21
-  var y = Enc.base64ToHex(jwk.y);
22
-  var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC_256 : OBJ_ID_EC_384;
23
-  return Enc.hexToUint8(
24
-    ASN1('30'
25
-    , ASN1.UInt('01')
26
-    , ASN1('04', d)
27
-    , ASN1('A0', objId)
28
-    , ASN1('A1', ASN1.BitStr('04' + x + y)))
29
-  );
30
-};
31
-x509.packPkcs8 = function (jwk) {
32
-  var d = Enc.base64ToHex(jwk.d);
33
-  var x = Enc.base64ToHex(jwk.x);
34
-  var y = Enc.base64ToHex(jwk.y);
35
-  var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC_256 : OBJ_ID_EC_384;
36
-  return Enc.hexToUint8(
37
-    ASN1('30'
38
-    , ASN1.UInt('00')
39
-    , ASN1('30'
40
-      , OBJ_ID_EC_PUB
41
-      , objId
42
-      )
43
-    , ASN1('04'
44
-      , ASN1('30'
45
-        , ASN1.UInt('01')
46
-        , ASN1('04', d)
47
-        , ASN1('A1', ASN1.BitStr('04' + x + y)))))
48
-  );
49
-};
50
-x509.packSpki = function (jwk) {
51
-  var x = Enc.base64ToHex(jwk.x);
52
-  var y = Enc.base64ToHex(jwk.y);
53
-  var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC_256 : OBJ_ID_EC_384;
54
-  return Enc.hexToUint8(
55
-    ASN1('30'
56
-    , ASN1('30'
57
-      , OBJ_ID_EC_PUB
58
-      , objId
59
-      )
60
-    , ASN1.BitStr('04' + x + y))
61
-  );
62
-};
63
-x509.packPkix = x509.packSpki;

+ 0
- 493
lib/eckles.js View File

@@ -1,493 +0,0 @@
1
-'use strict';
2
-
3
-var EC = module.exports;
4
-
5
-var Enc = require('./encoding.js');
6
-var ASN1;
7
-var PEM = require('./pem.js');
8
-
9
-// 1.2.840.10045.3.1.7
10
-// prime256v1 (ANSI X9.62 named elliptic curve)
11
-var OBJ_ID_EC  = '06 08 2A8648CE3D030107'.replace(/\s+/g, '').toLowerCase();
12
-// 1.3.132.0.34
13
-// secp384r1 (SECG (Certicom) named elliptic curve)
14
-var OBJ_ID_EC_384 = '06 05 2B81040022'.replace(/\s+/g, '').toLowerCase();
15
-
16
-// 1.2.840.10045.2.1
17
-// ecPublicKey (ANSI X9.62 public key type)
18
-var OBJ_ID_EC_PUB = '06 07 2A8648CE3D0201'.replace(/\s+/g, '').toLowerCase();
19
-
20
-                      // 19  e  c  d  s  a  -  s  h  a  2  -  n  i  s  t  p  2  5  6
21
-var SSH_EC_P256 = '00000013 65 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70 32 35 36'
22
-  .replace(/\s+/g, '').toLowerCase();
23
-
24
-                      // 19  e  c  d  s  a  -  s  h  a  2  -  n  i  s  t  p  3  8  4
25
-var SSH_EC_P384 = '00000013 65 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70 33 38 34'
26
-  .replace(/\s+/g, '').toLowerCase();
27
-
28
-// The one good thing that came from the b***kchain hysteria: good EC documentation
29
-// https://davidederosa.com/basic-blockchain-programming/elliptic-curve-keys/
30
-
31
-EC.parseSec1 = function parseEcOnlyPrivkey(u8, jwk) {
32
-  var index = 7;
33
-  var len = 32;
34
-  var olen = OBJ_ID_EC.length/2;
35
-
36
-  if ("P-384" === jwk.crv) {
37
-    olen = OBJ_ID_EC_384.length/2;
38
-    index = 8;
39
-    len = 48;
40
-  }
41
-  if (len !== u8[index - 1]) {
42
-    throw new Error("Unexpected bitlength " + len);
43
-  }
44
-
45
-  // private part is d
46
-  var d = u8.slice(index, index + len);
47
-  // compression bit index
48
-  var ci = index + len + 2 + olen + 2 + 3;
49
-  var c = u8[ci];
50
-  var x, y;
51
-
52
-  if (0x04 === c) {
53
-    y = u8.slice(ci + 1 + len, ci + 1 + len + len);
54
-  } else if (0x02 !== c) {
55
-    throw new Error("not a supported EC private key");
56
-  }
57
-  x = u8.slice(ci + 1, ci + 1 + len);
58
-
59
-  return {
60
-    kty: jwk.kty
61
-  , crv: jwk.crv
62
-  , d: Enc.bufToUrlBase64(d)
63
-  //, dh: Enc.bufToHex(d)
64
-  , x: Enc.bufToUrlBase64(x)
65
-  //, xh: Enc.bufToHex(x)
66
-  , y: Enc.bufToUrlBase64(y)
67
-  //, yh: Enc.bufToHex(y)
68
-  };
69
-};
70
-
71
-EC.parsePkcs8 = function parseEcPkcs8(u8, jwk) {
72
-  var index = 24 + (OBJ_ID_EC.length/2);
73
-  var len = 32;
74
-  if ("P-384" === jwk.crv) {
75
-    index = 24 + (OBJ_ID_EC_384.length/2) + 2;
76
-    len = 48;
77
-  }
78
-
79
-  //console.log(index, u8.slice(index));
80
-  if (0x04 !== u8[index]) {
81
-    //console.log(jwk);
82
-    throw new Error("privkey not found");
83
-  }
84
-  var d = u8.slice(index+2, index+2+len);
85
-  var ci = index+2+len+5;
86
-  var xi = ci+1;
87
-  var x = u8.slice(xi, xi + len);
88
-  var yi = xi+len;
89
-  var y;
90
-  if (0x04 === u8[ci]) {
91
-    y = u8.slice(yi, yi + len);
92
-  } else if (0x02 !== u8[ci]) {
93
-    throw new Error("invalid compression bit (expected 0x04 or 0x02)");
94
-  }
95
-
96
-  return {
97
-    kty: jwk.kty
98
-  , crv: jwk.crv
99
-  , d: Enc.bufToUrlBase64(d)
100
-  //, dh: Enc.bufToHex(d)
101
-  , x: Enc.bufToUrlBase64(x)
102
-  //, xh: Enc.bufToHex(x)
103
-  , y: Enc.bufToUrlBase64(y)
104
-  //, yh: Enc.bufToHex(y)
105
-  };
106
-};
107
-
108
-EC.parseSpki = function parsePem(u8, jwk) {
109
-  var ci = 16 + OBJ_ID_EC.length/2;
110
-  var len = 32;
111
-
112
-  if ("P-384" === jwk.crv) {
113
-    ci = 16 + OBJ_ID_EC_384.length/2;
114
-    len = 48;
115
-  }
116
-
117
-  var c = u8[ci];
118
-  var xi = ci + 1;
119
-  var x = u8.slice(xi, xi + len);
120
-  var yi = xi + len;
121
-  var y;
122
-  if (0x04 === c) {
123
-    y = u8.slice(yi, yi + len);
124
-  } else if (0x02 !== c) {
125
-    throw new Error("not a supported EC private key");
126
-  }
127
-
128
-  return {
129
-    kty: jwk.kty
130
-  , crv: jwk.crv
131
-  , x: Enc.bufToUrlBase64(x)
132
-  //, xh: Enc.bufToHex(x)
133
-  , y: Enc.bufToUrlBase64(y)
134
-  //, yh: Enc.bufToHex(y)
135
-  };
136
-};
137
-EC.parsePkix = EC.parseSpki;
138
-
139
-EC.parseSsh = function (pem) {
140
-  var jwk = { kty: 'EC', crv: null, x: null, y: null };
141
-  var b64 = pem.split(/\s+/g)[1];
142
-  var buf = Buffer.from(b64, 'base64');
143
-  var hex = Enc.bufToHex(buf);
144
-  var index = 40;
145
-  var len;
146
-  if (0 === hex.indexOf(SSH_EC_P256)) {
147
-    jwk.crv = 'P-256';
148
-    len = 32;
149
-  } else if (0 === hex.indexOf(SSH_EC_P384)) {
150
-    jwk.crv = 'P-384';
151
-    len = 48;
152
-  }
153
-  var x = buf.slice(index, index + len);
154
-  var y = buf.slice(index + len, index + len + len);
155
-  jwk.x = Enc.bufToUrlBase64(x);
156
-  jwk.y = Enc.bufToUrlBase64(y);
157
-  return jwk;
158
-};
159
-
160
-/*global Promise*/
161
-EC.generate = function (opts) {
162
-  return Promise.resolve().then(function () {
163
-    var typ = 'ec';
164
-    var format = opts.format;
165
-    var encoding = opts.encoding;
166
-    var priv;
167
-    var pub = 'spki';
168
-
169
-    if (!format) {
170
-      format = 'jwk';
171
-    }
172
-    if (-1 !== [ 'spki', 'pkcs8', 'ssh' ].indexOf(format)) {
173
-      format = 'pkcs8';
174
-    }
175
-
176
-    if ('pem' === format) {
177
-      format = 'sec1';
178
-      encoding = 'pem';
179
-    } else if ('der' === format) {
180
-      format = 'sec1';
181
-      encoding = 'der';
182
-    }
183
-
184
-    if ('jwk' === format || 'json' === format) {
185
-      format = 'jwk';
186
-      encoding = 'json';
187
-    } else {
188
-      priv = format;
189
-    }
190
-
191
-    if (!encoding) {
192
-      encoding = 'pem';
193
-    }
194
-
195
-    if (priv) {
196
-      priv = { type: priv, format: encoding };
197
-      pub = { type: pub, format: encoding };
198
-    } else {
199
-      // jwk
200
-      priv = { type: 'sec1', format: 'pem' };
201
-      pub = { type: 'spki', format: 'pem' };
202
-    }
203
-
204
-    return new Promise(function (resolve, reject) {
205
-      return require('crypto').generateKeyPair(typ, {
206
-        namedCurve: opts.crv || opts.namedCurve || 'P-256'
207
-      , privateKeyEncoding: priv
208
-      , publicKeyEncoding: pub
209
-      }, function (err, pubkey, privkey) {
210
-        if (err) { reject(err); }
211
-        resolve({
212
-          private: privkey
213
-        , public: pubkey
214
-        });
215
-      });
216
-    }).then(function (keypair) {
217
-      if ('jwk' === format) {
218
-        return {
219
-          private: EC.importSync({ pem: keypair.private, format: priv.type })
220
-        , public: EC.importSync({ pem: keypair.public, format: pub.type, public: true })
221
-        };
222
-      }
223
-
224
-      if ('ssh' !== opts.format) {
225
-        return keypair;
226
-      }
227
-
228
-      return {
229
-        private: keypair.private
230
-      , public: EC.exportSync({ jwk: EC.importSync({
231
-          pem: keypair.public, format: format, public: true
232
-        }), format: opts.format, public: true })
233
-      };
234
-    });
235
-  });
236
-};
237
-
238
-EC.importSync = function importEcSync(opts) {
239
-  if (!opts || !opts.pem || 'string' !== typeof opts.pem) {
240
-    throw new Error("must pass { pem: pem } as a string");
241
-  }
242
-  if (0 === opts.pem.indexOf('ecdsa-sha2-')) {
243
-    return EC.parseSsh(opts.pem);
244
-  }
245
-  var pem = opts.pem;
246
-  var u8 = PEM.parseBlock(pem).bytes;
247
-  var hex = Enc.bufToHex(u8);
248
-  var jwk = { kty: 'EC', crv: null, x: null, y: null };
249
-
250
-  //console.log();
251
-  if (-1 !== hex.indexOf(OBJ_ID_EC)) {
252
-    jwk.crv = "P-256";
253
-
254
-    // PKCS8
255
-    if (0x02 === u8[3] && 0x30 === u8[6] && 0x06 === u8[8]) {
256
-      //console.log("PKCS8", u8[3].toString(16), u8[6].toString(16), u8[8].toString(16));
257
-      jwk = EC.parsePkcs8(u8, jwk);
258
-    // EC-only
259
-    } else if (0x02 === u8[2] && 0x04 === u8[5] && 0xA0 === u8[39]) {
260
-      //console.log("EC---", u8[2].toString(16), u8[5].toString(16), u8[39].toString(16));
261
-      jwk = EC.parseSec1(u8, jwk);
262
-    // SPKI/PKIK (Public)
263
-    } else if (0x30 === u8[2] && 0x06 === u8[4] && 0x06 === u8[13]) {
264
-      //console.log("SPKI-", u8[2].toString(16), u8[4].toString(16), u8[13].toString(16));
265
-      jwk = EC.parseSpki(u8, jwk);
266
-    // Error
267
-    } else {
268
-      //console.log("PKCS8", u8[3].toString(16), u8[6].toString(16), u8[8].toString(16));
269
-      //console.log("EC---", u8[2].toString(16), u8[5].toString(16), u8[39].toString(16));
270
-      //console.log("SPKI-", u8[2].toString(16), u8[4].toString(16), u8[13].toString(16));
271
-      throw new Error("unrecognized key format");
272
-    }
273
-  } else if (-1 !== hex.indexOf(OBJ_ID_EC_384)) {
274
-    jwk.crv = "P-384";
275
-
276
-    // PKCS8
277
-    if (0x02 === u8[3] && 0x30 === u8[6] && 0x06 === u8[8]) {
278
-      //console.log("PKCS8", u8[3].toString(16), u8[6].toString(16), u8[8].toString(16));
279
-      jwk = EC.parsePkcs8(u8, jwk);
280
-    // EC-only
281
-    } else if (0x02 === u8[3] && 0x04 === u8[6] && 0xA0 === u8[56]) {
282
-      //console.log("EC---", u8[3].toString(16), u8[6].toString(16), u8[56].toString(16));
283
-      jwk = EC.parseSec1(u8, jwk);
284
-    // SPKI/PKIK (Public)
285
-    } else if (0x30 === u8[2] && 0x06 === u8[4] && 0x06 === u8[13]) {
286
-      //console.log("SPKI-", u8[2].toString(16), u8[4].toString(16), u8[13].toString(16));
287
-      jwk = EC.parseSpki(u8, jwk);
288
-    // Error
289
-    } else {
290
-      //console.log("PKCS8", u8[3].toString(16), u8[6].toString(16), u8[8].toString(16));
291
-      //console.log("EC---", u8[3].toString(16), u8[6].toString(16), u8[56].toString(16));
292
-      //console.log("SPKI-", u8[2].toString(16), u8[4].toString(16), u8[13].toString(16));
293
-      throw new Error("unrecognized key format");
294
-    }
295
-  } else {
296
-    throw new Error("Supported key types are P-256 and P-384");
297
-  }
298
-  if (opts.public) {
299
-    if (true !== opts.public) {
300
-      throw new Error("options.public must be either `true` or `false` not ("
301
-        + typeof opts.public + ") '" + opts.public + "'");
302
-    }
303
-    delete jwk.d;
304
-  }
305
-  return jwk;
306
-};
307
-EC.parse = function parseEc(opts) {
308
-  return Promise.resolve().then(function () {
309
-    return EC.importSync(opts);
310
-  });
311
-};
312
-EC.toJwk = EC.import = EC.parse;
313
-
314
-EC.exportSync = function (opts) {
315
-  if (!opts || !opts.jwk || 'object' !== typeof opts.jwk) {
316
-    throw new Error("must pass { jwk: jwk } as a JSON object");
317
-  }
318
-  var jwk = JSON.parse(JSON.stringify(opts.jwk));
319
-  var format = opts.format;
320
-  if (opts.public || -1 !== [ 'spki', 'pkix', 'ssh', 'rfc4716' ].indexOf(format)) {
321
-    jwk.d = null;
322
-  }
323
-  if ('EC' !== jwk.kty) {
324
-    throw new Error("options.jwk.kty must be 'EC' for EC keys");
325
-  }
326
-  if (!jwk.d) {
327
-    if (!format || -1 !== [ 'spki', 'pkix' ].indexOf(format)) {
328
-      format = 'spki';
329
-    } else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
330
-      format = 'ssh';
331
-    } else {
332
-      throw new Error("options.format must be 'spki' or 'ssh' for public EC keys, not ("
333
-        + typeof format + ") " + format);
334
-    }
335
-  } else {
336
-    if (!format || 'sec1' === format) {
337
-      format = 'sec1';
338
-    } else if ('pkcs8' !== format) {
339
-      throw new Error("options.format must be 'sec1' or 'pkcs8' for private EC keys, not '" + format + "'");
340
-    }
341
-  }
342
-  if (-1 === [ 'P-256', 'P-384' ].indexOf(jwk.crv)) {
343
-    throw new Error("options.jwk.crv must be either P-256 or P-384 for EC keys, not '" + jwk.crv + "'");
344
-  }
345
-  if (!jwk.y) {
346
-    throw new Error("options.jwk.y must be a urlsafe base64-encoded either P-256 or P-384");
347
-  }
348
-
349
-  if ('sec1' === format) {
350
-    return PEM.packBlock({ type: "EC PRIVATE KEY", bytes: EC.packSec1(jwk) });
351
-  } else if ('pkcs8' === format) {
352
-    return PEM.packBlock({ type: "PRIVATE KEY", bytes: EC.packPkcs8(jwk) });
353
-  } else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) {
354
-    return PEM.packBlock({ type: "PUBLIC KEY", bytes: EC.packSpki(jwk) });
355
-  } else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
356
-    return EC.packSsh(jwk);
357
-  } else {
358
-    throw new Error("Sanity Error: reached unreachable code block with format: " + format);
359
-  }
360
-};
361
-EC.pack = function (opts) {
362
-  return Promise.resolve().then(function () {
363
-    return EC.exportSync(opts);
364
-  });
365
-};
366
-
367
-EC.packSec1 = function (jwk) {
368
-  var d = Enc.base64ToHex(jwk.d);
369
-  var x = Enc.base64ToHex(jwk.x);
370
-  var y = Enc.base64ToHex(jwk.y);
371
-  var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
372
-  return Enc.hexToUint8(
373
-    ASN1('30'
374
-    , ASN1.UInt('01')
375
-    , ASN1('04', d)
376
-    , ASN1('A0', objId)
377
-    , ASN1('A1', ASN1.BitStr('04' + x + y)))
378
-  );
379
-};
380
-EC.packPkcs8 = function (jwk) {
381
-  var d = Enc.base64ToHex(jwk.d);
382
-  var x = Enc.base64ToHex(jwk.x);
383
-  var y = Enc.base64ToHex(jwk.y);
384
-  var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
385
-  return Enc.hexToUint8(
386
-    ASN1('30'
387
-    , ASN1.UInt('00')
388
-    , ASN1('30'
389
-      , OBJ_ID_EC_PUB
390
-      , objId
391
-      )
392
-    , ASN1('04'
393
-      , ASN1('30'
394
-        , ASN1.UInt('01')
395
-        , ASN1('04', d)
396
-        , ASN1('A1', ASN1.BitStr('04' + x + y)))))
397
-  );
398
-};
399
-EC.packSpki = function (jwk) {
400
-  var x = Enc.base64ToHex(jwk.x);
401
-  var y = Enc.base64ToHex(jwk.y);
402
-  var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
403
-  return Enc.hexToUint8(
404
-    ASN1('30'
405
-    , ASN1('30'
406
-      , OBJ_ID_EC_PUB
407
-      , objId
408
-      )
409
-    , ASN1.BitStr('04' + x + y))
410
-  );
411
-};
412
-EC.packPkix = EC.packSpki;
413
-EC.packSsh = function (jwk) {
414
-  // Custom SSH format
415
-  var typ = 'ecdsa-sha2-nistp256';
416
-	var a = '32 35 36';
417
-  var b = '41';
418
-  var comment = jwk.crv + '@localhost';
419
-  if ('P-256' !== jwk.crv) {
420
-    typ = 'ecdsa-sha2-nistp384';
421
-    a = '33 38 34';
422
-    b = '61';
423
-  }
424
-  var x = Enc.base64ToHex(jwk.x);
425
-  var y = Enc.base64ToHex(jwk.y);
426
-  var ssh = Enc.hexToUint8(
427
-    ('00 00 00 13 65 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70'
428
-    + a + '00 00 00 08 6e 69 73 74 70' + a + '00 00 00' + b
429
-    + '04' + x + y).replace(/\s+/g, '').toLowerCase()
430
-  );
431
-
432
-  return typ + ' ' + Enc.bufToBase64(ssh) + ' ' + comment;
433
-};
434
-
435
-//
436
-// A dumbed-down, minimal ASN.1 packer
437
-//
438
-
439
-// Almost every ASN.1 type that's important for CSR
440
-// can be represented generically with only a few rules.
441
-ASN1 = function ASN1(/*type, hexstrings...*/) {
442
-  var args = Array.prototype.slice.call(arguments);
443
-  var typ = args.shift();
444
-  var str = args.join('').replace(/\s+/g, '').toLowerCase();
445
-  var len = (str.length/2);
446
-  var lenlen = 0;
447
-  var hex = typ;
448
-
449
-  // We can't have an odd number of hex chars
450
-  if (len !== Math.round(len)) {
451
-    throw new Error("invalid hex");
452
-  }
453
-
454
-  // The first byte of any ASN.1 sequence is the type (Sequence, Integer, etc)
455
-  // The second byte is either the size of the value, or the size of its size
456
-
457
-  // 1. If the second byte is < 0x80 (128) it is considered the size
458
-  // 2. If it is > 0x80 then it describes the number of bytes of the size
459
-  //    ex: 0x82 means the next 2 bytes describe the size of the value
460
-  // 3. The special case of exactly 0x80 is "indefinite" length (to end-of-file)
461
-
462
-  if (len > 127) {
463
-    lenlen += 1;
464
-    while (len > 255) {
465
-      lenlen += 1;
466
-      len = len >> 8;
467
-    }
468
-  }
469
-
470
-  if (lenlen) { hex += Enc.numToHex(0x80 + lenlen); }
471
-  return hex + Enc.numToHex(str.length/2) + str;
472
-};
473
-
474
-// The Integer type has some special rules
475
-ASN1.UInt = function UINT() {
476
-  var str = Array.prototype.slice.call(arguments).join('');
477
-  var first = parseInt(str.slice(0, 2), 16);
478
-
479
-  // If the first byte is 0x80 or greater, the number is considered negative
480
-  // Therefore we add a '00' prefix if the 0x80 bit is set
481
-  if (0x80 & first) { str = '00' + str; }
482
-
483
-  return ASN1('02', str);
484
-};
485
-
486
-// The Bit String type also has a special rule
487
-ASN1.BitStr = function BITSTR() {
488
-  var str = Array.prototype.slice.call(arguments).join('');
489
-  // '00' is a mask of how many bits of the next byte to ignore
490
-  return ASN1('03', '00' + str);
491
-};
492
-
493
-EC.toPem = EC.export = EC.pack;

+ 0
- 42
lib/encoding.js View File

@@ -1,42 +0,0 @@
1
-'use strict';
2
-
3
-var Enc = module.exports;
4
-
5
-Enc.base64ToBuf = function (str) {
6
-  // node handles both base64 and urlBase64 equally
7
-  return Buffer.from(str, 'base64');
8
-};
9
-
10
-Enc.base64ToHex = function (b64) {
11
-  return Enc.bufToHex(Enc.base64ToBuf(b64));
12
-};
13
-
14
-Enc.bufToBase64 = function (u8) {
15
-  // Ensure a node buffer, even if TypedArray
16
-  return Buffer.from(u8).toString('base64');
17
-};
18
-
19
-Enc.bufToHex = function (u8) {
20
-  // Ensure a node buffer, even if TypedArray
21
-  return Buffer.from(u8).toString('hex');
22
-};
23
-
24
-Enc.bufToUrlBase64 = function (u8) {
25
-  return Enc.bufToBase64(u8)
26
-    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
27
-};
28
-
29
-Enc.hexToUint8 = function (hex) {
30
-  // TODO: I don't remember why I chose Uint8Array over Buffer...
31
-  var buf = Buffer.from(hex, 'hex');
32
-  var ab = buf.buffer.slice(buf.offset, buf.offset + buf.byteLength);
33
-  return new Uint8Array(ab);
34
-};
35
-
36
-Enc.numToHex = function (d) {
37
-  d = d.toString(16);
38
-  if (d.length % 2) {
39
-    return '0' + d;
40
-  }
41
-  return d;
42
-};

+ 0
- 143
lib/keypairs.js View File

@@ -1,143 +0,0 @@
1
-'use strict';
2
-
3
-/*global Promise*/
4
-var keypairs = module.exports;
5
-
6
-var PEM = require('./pem-parser.js');
7
-PEM.packBlock = require('./pem-packer.js').packBlock;
8
-
9
-var crypto = require('./crypto.js');
10
-var Enc = require('./encoding.js');
11
-
12
-var ASN1 = require('./asn1-parser.js');
13
-ASN1.pack = require('./asn1-packer.js').pack;
14
-
15
-var x509 = require('./x509-parser.js');
16
-//x509 = require('./x509-packer.js').pack;
17
-
18
-var SSH = require('./ssh-parser.js');
19
-SSH.pack = require('./ssh-packer.js').pack;
20
-
21
-// sign, signJws, signJwt
22
-/*
23
-var JWS = require('./jws.js');
24
-var JWT = require('./jwt.js');
25
-*/
26
-
27
-keypairs.signJws = function (opts) {
28
-  opts = JSON.stringify(JSON.parse(opts));
29
-  if (!opts.header) { opts.header = {}; }
30
-  if (!opts.protected) { opts.protected = {}; }
31
-  if (!opts.payload) { opts.payload = {}; }
32
-  var protect = Enc.binToBase64(JSON.stringify(opts.protected));
33
-  var payload = Enc.binToBase64(JSON.stringify(opts.payload));
34
-  if (!opts.jwt) { opts.jwt = keypairs.import(opts).jwt; }
35
-  opts.header.typ = 'JWT';
36
-  opts.header.alg = ('RSA' === opts.jwk) ? 'RS256' : 'ES256';
37
-  // key, jwk, pem, der
38
-  return crypto.sign(opts, Enc.binToBuf(protect + '.' + payload), 'SHA256').then(function (sig) {
39
-    return {
40
-      header: opts.header
41
-    , protected: protect
42
-    , payload: payload
43
-    , signature: sig
44
-    };
45
-  });
46
-};
47
-
48
-keypairs.signJwt = function (opts) {
49
-  opts = JSON.stringify(JSON.parse(opts));
50
-  if (!opts.header) { opts.header = {}; }
51
-  if (!opts.payload) { opts.payload = {}; }
52
-  var protect = Enc.binToBase64(JSON.stringify(opts.header)) + '.'
53
-    + Enc.binToBase64(JSON.stringify(opts.payload));
54
-  if (!opts.jwt) { opts.jwt = keypairs.import(opts).jwt; }
55
-  opts.header.alg = ('RSA' === opts.jwk) ? 'RS256' : 'ES256';
56
-  // key, jwk, pem, der
57
-  return crypto.sign(opts, Enc.binToBuf(protect), 'SHA256').then(function (sig) {
58
-    return protect + '.' + sig;
59
-  });
60
-};
61
-
62
-keypairs.import = function (opts) {
63
-  return Promise.resolve().then(function () {
64
-    var jwk = opts.jwk;
65
-    var pem;
66
-    var der;
67
-    var typ;
68
-
69
-    if (opts.pem) {
70
-      pem = PEM.parseBlock(opts.pem);
71
-      if ('OPENSSH PRIVATE KEY' === pem.type) {
72
-        jwk = SSH.parse(pem);
73
-      } else {
74
-        der = pem.bytes;
75
-        jwk = x509.parse({ der: der });
76
-      }
77
-    }
78
-    if (opts.ssh) {
79
-      jwk = SSH.parse(opts.ssh);
80
-    }
81
-    // TODO re-export to PKCS8 just because
82
-    if (jwk && !pem) {
83
-      // Both RSA and EC use 'd' as part of the private key
84
-      if (jwk.d) {
85
-        typ = 'PRIVATE KEY';
86
-        der = x509.pack({ jwk: jwk, format: 'pkcs8', encoding: 'pem' });
87
-      } else {
88
-        typ = 'PUBLIC KEY';
89
-        der = x509.pack({ jwk: jwk, format: 'spki', encoding: 'pem' });
90
-      }
91
-      pem = PEM.packBlock({ type: typ, bytes: der });
92
-    }
93
-
94
-    return { pem: pem, jwk: jwk };
95
-  });
96
-};
97
-
98
-keypairs.export = function (opts) {
99
-  // { pem, jwk, format, encoding }
100
-  var format = opts.format;
101
-  var encoding = opts.encoding;
102
-  var jwk = opts.jwk;
103
-  var pem = opts.pem;
104
-  var der = opts.der;
105
-  var pub = opts.public;
106
-
107
-  if (opts.key) {
108
-    if ('string' === typeof opts.key) {
109
-      pem = opts.key;
110
-    } else if (opts.key.d) {
111
-      jwk = opts.key;
112
-    } else if (opts.key.length) {
113
-      der = opts.der;
114
-    } else {
115
-      throw new Error("'key' must be of type 'string' (PEM), 'object' (JWK), Buffer, or Array (DER)");
116
-    }
117
-  }
118
-  if (!format) { format = 'jwk'; }
119
-
120
-  if (!jwk) {
121
-    jwk = keypairs.import({ pem: pem }).jwk;
122
-  }
123
-  if (pub) {
124
-    if ('RSA' === jwk.kty) {
125
-      jwk = { kty: jwk.kty, n: jwk.n, e: jwk.e };
126
-    } else {
127
-      jwk = { kty: jwk.kty, x: jwk.x, y: jwk.y };
128
-    }
129
-  }
130
-  if ('jwk' === format) {
131
-    if (encoding && 'json' !== encoding) {
132
-      throw new Error("'encoding' must be 'json' for 'jwk'");
133
-    }
134
-    return jwk;
135
-  }
136
-
137
-  if ('openssh' === format || 'ssh' === format) {
138
-    // TODO if ('ssh' === format) { format = 'pkcs8'; }
139
-    // TODO 'ssh2' public key is a special variant of pkcs8
140
-    return SSH.pack({ jwk: jwk, public: opts.public });
141
-  }
142
-  return x509.pack({ jwk: jwk, format: opts.format, encoding: opts.encoding, public: opts.public });
143
-};

+ 0
- 14
lib/pem-packer.js View File

@@ -1,14 +0,0 @@
1
-'use strict';
2
-
3
-var PEM = module.exports;
4
-var Enc = require('./encoding.js');
5
-
6
-PEM.packBlock = function (opts) {
7
-  // TODO allow for headers?
8
-  var n = opts.lineLength || 64;
9
-  var re = new RegExp('.{1,' + n + '}', 'g');
10
-  return '-----BEGIN ' + opts.type + '-----\n'
11
-    + Enc.bufToBase64(opts.bytes).match(re).join('\n') + '\n'
12
-    + '-----END ' + opts.type + '-----'
13
-  ;
14
-};

+ 0
- 21
lib/pem-parser.js View File

@@ -1,21 +0,0 @@
1
-'use strict';
2
-
3
-var PEM = module.exports;
4
-var Enc = require('./encoding.js');
5
-
6
-PEM.parseBlock = function pemToDer(pem) {
7
-  var lines = pem.trim().split(/\n/);
8
-  var end = lines.length - 1;
9
-  var head = lines[0].match(/-----BEGIN (.*)-----/);
10
-  var foot = lines[end].match(/-----END (.*)-----/);
11
-
12
-  if (head) {
13
-    lines = lines.slice(1, end);
14
-    head = head[1];
15
-    if (head !== foot[1]) {
16
-      throw new Error("headers and footers do not match");
17
-    }
18
-  }
19
-
20
-  return { type: head, bytes: Enc.base64ToBuf(lines.join('')) };
21
-};

+ 0
- 28
lib/pem.js View File

@@ -1,28 +0,0 @@
1
-'use strict';
2
-
3
-var PEM = module.exports;
4
-var Enc = require('./encoding.js');
5
-
6
-PEM.parseBlock = function pemToDer(pem) {
7
-  var lines = pem.trim().split(/\n/);
8
-  var end = lines.length - 1;
9
-  var head = lines[0].match(/-----BEGIN (.*)-----/);
10
-  var foot = lines[end].match(/-----END (.*)-----/);
11
-
12
-  if (head) {
13
-    lines = lines.slice(1, end);
14
-    head = head[1];
15
-    if (head !== foot[1]) {
16
-      throw new Error("headers and footers do not match");
17
-    }
18
-  }
19
-
20
-  return { type: head, bytes: Enc.base64ToBuf(lines.join('')) };
21
-};
22
-
23
-PEM.packBlock = function (opts) {
24
-  return '-----BEGIN ' + opts.type + '-----\n'
25
-    + Enc.bufToBase64(opts.bytes).match(/.{1,64}/g).join('\n') + '\n'
26
-    + '-----END ' + opts.type + '-----'
27
-  ;
28
-};

+ 0
- 204
lib/rasha.js View File

@@ -1,204 +0,0 @@
1
-'use strict';
2
-
3
-var RSA = module.exports;
4
-var SSH = require('./ssh.js');
5
-var PEM = require('./pem.js');
6
-var x509 = require('./x509.js');
7
-var ASN1 = require('./asn1.js');
8
-
9
-/*global Promise*/
10
-RSA.generate = function (opts) {
11
-  return Promise.resolve().then(function () {
12
-    var typ = 'rsa';
13
-    var format = opts.format;
14
-    var encoding = opts.encoding;
15
-    var priv;
16
-    var pub;
17
-
18
-    if (!format) {
19
-      format = 'jwk';
20
-    }
21
-    if ('spki' === format || 'pkcs8' === format) {
22
-      format = 'pkcs8';
23
-      pub = 'spki';
24
-    }
25
-
26
-    if ('pem' === format) {
27
-      format = 'pkcs1';
28
-      encoding = 'pem';
29
-    } else if ('der' === format) {
30
-      format = 'pkcs1';
31
-      encoding = 'der';
32
-    }
33
-
34
-    if ('jwk' === format || 'json' === format) {
35
-      format = 'jwk';
36
-      encoding = 'json';
37
-    } else {
38
-      priv = format;
39
-      pub = pub || format;
40
-    }
41
-
42
-    if (!encoding) {
43
-      encoding = 'pem';
44
-    }
45
-
46
-    if (priv) {
47
-      priv = { type: priv, format: encoding };
48
-      pub = { type: pub, format: encoding };
49
-    } else {
50
-      // jwk
51
-      priv = { type: 'pkcs1', format: 'pem' };
52
-      pub = { type: 'pkcs1', format: 'pem' };
53
-    }
54
-
55
-    return new Promise(function (resolve, reject) {
56
-      return require('crypto').generateKeyPair(typ, {
57
-        modulusLength: opts.modulusLength || 2048
58
-      , publicExponent: opts.publicExponent || 0x10001
59
-      , privateKeyEncoding: priv
60
-      , publicKeyEncoding: pub
61
-      }, function (err, pubkey, privkey) {
62
-        if (err) { reject(err); }
63
-        resolve({
64
-          private: privkey
65
-        , public: pubkey
66
-        });
67
-      });
68
-    }).then(function (keypair) {
69
-      if ('jwk' !== format) {
70
-        return keypair;
71
-      }
72
-
73
-      return {
74
-        private: RSA.importSync({ pem: keypair.private, format: priv.type })
75
-      , public: RSA.importSync({ pem: keypair.public, format: pub.type, public: true })
76
-      };
77
-    });
78
-  });
79
-};
80
-
81
-RSA.importSync = function (opts) {
82
-  if (!opts || !opts.pem || 'string' !== typeof opts.pem) {
83
-    throw new Error("must pass { pem: pem } as a string");
84
-  }
85
-
86
-  var jwk = { kty: 'RSA', n: null, e: null };
87
-  if (0 === opts.pem.indexOf('ssh-rsa ')) {
88
-    return SSH.parse(opts.pem, jwk);
89
-  }
90
-  var pem = opts.pem;
91
-  var block = PEM.parseBlock(pem);
92
-  //var hex = toHex(u8);
93
-  var asn1 = ASN1.parse(block.bytes);
94
-
95
-  var meta = x509.guess(block.bytes, asn1);
96
-
97
-  if ('pkcs1' === meta.format) {
98
-    jwk = x509.parsePkcs1(block.bytes, asn1, jwk);
99
-  } else {
100
-    jwk = x509.parsePkcs8(block.bytes, asn1, jwk);
101
-  }
102
-
103
-  if (opts.public) {
104
-    jwk = RSA.nueter(jwk);
105
-  }
106
-  return jwk;
107
-};
108
-RSA.parse = function parseRsa(opts) {
109
-  // wrapped in a promise for API compatibility
110
-  // with the forthcoming browser version
111
-  // (and potential future native node capability)
112
-  return Promise.resolve().then(function () {
113
-    return RSA.importSync(opts);
114
-  });
115
-};
116
-RSA.toJwk = RSA.import = RSA.parse;
117
-
118
-/*
119
-RSAPrivateKey ::= SEQUENCE {
120
-  version           Version,
121
-  modulus           INTEGER,  -- n
122
-  publicExponent    INTEGER,  -- e
123
-  privateExponent   INTEGER,  -- d
124
-  prime1            INTEGER,  -- p
125
-  prime2            INTEGER,  -- q
126
-  exponent1         INTEGER,  -- d mod (p-1)
127
-  exponent2         INTEGER,  -- d mod (q-1)
128
-  coefficient       INTEGER,  -- (inverse of q) mod p
129
-  otherPrimeInfos   OtherPrimeInfos OPTIONAL
130
-}
131
-*/
132
-
133
-RSA.exportSync = function (opts) {
134
-  if (!opts || !opts.jwk || 'object' !== typeof opts.jwk) {
135
-    throw new Error("must pass { jwk: jwk }");
136
-  }
137
-  var jwk = JSON.parse(JSON.stringify(opts.jwk));
138
-  var format = opts.format;
139
-  var pub = opts.public;
140
-  if (pub || -1 !== [ 'spki', 'pkix', 'ssh', 'rfc4716' ].indexOf(format)) {
141
-    jwk = RSA.nueter(jwk);
142
-  }
143
-  if ('RSA' !== jwk.kty) {
144
-    throw new Error("options.jwk.kty must be 'RSA' for RSA keys");
145
-  }
146
-  if (!jwk.p) {
147
-    // TODO test for n and e
148
-    pub = true;
149
-    if (!format || 'pkcs1' === format) {
150
-      format = 'pkcs1';
151
-    } else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) {
152
-      format = 'spki';
153
-    } else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
154
-      format = 'ssh';
155
-    } else {
156
-      throw new Error("options.format must be 'spki', 'pkcs1', or 'ssh' for public RSA keys, not ("
157
-        + typeof format + ") " + format);
158
-    }
159
-  } else {
160
-    // TODO test for all necessary keys (d, p, q ...)
161
-    if (!format || 'pkcs1' === format) {
162
-      format = 'pkcs1';
163
-    } else if ('pkcs8' !== format) {
164
-      throw new Error("options.format must be 'pkcs1' or 'pkcs8' for private RSA keys");
165
-    }
166
-  }
167
-
168
-  if ('pkcs1' === format) {
169
-    if (jwk.d) {
170
-      return PEM.packBlock({ type: "RSA PRIVATE KEY", bytes: x509.packPkcs1(jwk) });
171
-    } else {
172
-      return PEM.packBlock({ type: "RSA PUBLIC KEY", bytes: x509.packPkcs1(jwk) });
173
-    }
174
-  } else if ('pkcs8' === format) {
175
-    return PEM.packBlock({ type: "PRIVATE KEY", bytes: x509.packPkcs8(jwk) });
176
-  } else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) {
177
-    return PEM.packBlock({ type: "PUBLIC KEY", bytes: x509.packSpki(jwk) });
178
-  } else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
179
-    return SSH.pack({ jwk: jwk, comment: opts.comment });
180
-  } else {
181
-    throw new Error("Sanity Error: reached unreachable code block with format: " + format);
182
-  }
183
-};
184
-RSA.pack = function (opts) {
185
-  // wrapped in a promise for API compatibility
186
-  // with the forthcoming browser version
187
-  // (and potential future native node capability)
188
-  return Promise.resolve().then(function () {
189
-    return RSA.exportSync(opts);
190
-  });
191
-};
192
-RSA.toPem = RSA.export = RSA.pack;
193
-
194
-// snip the _private_ parts... hAHAHAHA!
195
-RSA.nueter = function (jwk) {
196
-  // (snip rather than new object to keep potential extra data)
197
-  // otherwise we could just do this:
198
-  // return { kty: jwk.kty, n: jwk.n, e: jwk.e };
199
-  [ 'p', 'q', 'd', 'dp', 'dq', 'qi' ].forEach(function (key) {
200
-    if (key in jwk) { jwk[key] = undefined; }
201
-    return jwk;
202
-  });
203
-  return jwk;
204
-};

+ 0
- 80
lib/ssh.js View File

@@ -1,80 +0,0 @@
1
-'use strict';
2
-
3
-var SSH = module.exports;
4
-var Enc = require('./encoding.js');
5
-
6
-              //  7  s  s  h  -  r  s  a
7
-SSH.RSA = '00000007 73 73 68 2d 72 73 61'.replace(/\s+/g, '').toLowerCase();
8
-
9
-SSH.parse = function (pem, jwk) {
10
-
11
-  var parts = pem.split(/\s+/);
12
-  var buf = Enc.base64ToBuf(parts[1]);
13
-  var els = [];
14
-  var index = 0;
15
-  var len;
16
-  var i = 0;
17
-  var offset = (buf.byteOffset || 0);
18
-  // using dataview to be browser-compatible (I do want _some_ code reuse)
19
-  var dv = new DataView(buf.buffer.slice(offset, offset + buf.byteLength));
20
-  var el;
21
-
22
-  if (SSH.RSA !== Enc.bufToHex(buf.slice(0, SSH.RSA.length/2))) {
23
-    throw new Error("does not lead with ssh header");
24
-  }
25
-
26
-  while (index < buf.byteLength) {
27
-    i += 1;
28
-    if (i > 3) { throw new Error("15+ elements, probably not a public ssh key"); }
29
-    len = dv.getUint32(index, false);
30
-    index += 4;
31
-    el = buf.slice(index, index + len);
32
-    // remove BigUInt '00' prefix
33
-    if (0x00 === el[0]) {
34
-      el = el.slice(1);
35
-    }
36
-    els.push(el);
37
-    index += len;
38
-  }
39
-
40
-  jwk.n = Enc.bufToUrlBase64(els[2]);
41
-  jwk.e = Enc.bufToUrlBase64(els[1]);
42
-
43
-  return jwk;
44
-};
45
-
46
-SSH.pack = function (opts) {
47
-  var jwk = opts.jwk;
48
-  var header = 'ssh-rsa';
49
-  var comment = opts.comment || 'rsa@localhost';
50
-  var e = SSH._padHexInt(Enc.base64ToHex(jwk.e));
51
-  var n = SSH._padHexInt(Enc.base64ToHex(jwk.n));
52
-  var hex = [
53
-    SSH._numToUint32Hex(header.length)
54
-  , Enc.strToHex(header)
55
-  , SSH._numToUint32Hex(e.length/2)
56
-  , e
57
-  , SSH._numToUint32Hex(n.length/2)
58
-  , n
59
-  ].join('');
60
-  return [ header, Enc.hexToBase64(hex), comment ].join(' ');
61
-};
62
-
63
-SSH._numToUint32Hex = function (num) {
64
-  var hex = num.toString(16);
65
-  while (hex.length < 8) {
66
-    hex = '0' + hex;
67
-  }
68
-  return hex;
69
-};
70
-
71
-SSH._padHexInt = function (hex) {
72
-  // BigInt is negative if the high order bit 0x80 is set,
73
-  // so ASN1, SSH, and many other formats pad with '0x00'
74
-  // to signifiy a positive number.
75
-  var i = parseInt(hex.slice(0, 2), 16);
76
-  if (0x80 & i) {
77
-    return '00' + hex;
78
-  }
79
-  return hex;
80
-};

+ 0
- 111
lib/telemetry.js View File

@@ -1,111 +0,0 @@
1
-'use strict';
2
-
3
-// We believe in a proactive approach to sustainable open source.
4
-// As part of that we make it easy for you to opt-in to following our progress
5
-// and we also stay up-to-date on telemetry such as operating system and node
6
-// version so that we can focus our efforts where they'll have the greatest impact.
7
-//
8
-// Want to learn more about our Terms, Privacy Policy, and Mission?
9
-// Check out https://therootcompany.com/legal/
10
-
11
-var os = require('os');
12
-var crypto = require('crypto');
13
-var https = require('https');
14
-var pkg = require('../package.json');
15
-
16
-// to help focus our efforts in the right places
17
-var data = {
18
-  package: pkg.name
19
-, version: pkg.version
20
-, node: process.version
21
-, arch: process.arch || os.arch()
22
-, platform: process.platform || os.platform()
23
-, release: os.release()
24
-};
25
-
26
-function addCommunityMember(opts) {
27
-  setTimeout(function () {
28
-    var req = https.request({
29
-      hostname: 'api.therootcompany.com'
30
-    , port: 443
31
-    , path: '/api/therootcompany.com/public/community'
32
-    , method: 'POST'
33
-    , headers: { 'Content-Type': 'application/json' }
34
-    }, function (resp) {
35
-      // let the data flow, so we can ignore it
36
-      resp.on('data', function () {});
37
-      //resp.on('data', function (chunk) { console.log(chunk.toString()); });
38
-      resp.on('error', function () { /*ignore*/ });
39
-      //resp.on('error', function (err) { console.error(err); });
40
-    });
41
-    var obj = JSON.parse(JSON.stringify(data));
42
-    obj.action = 'updates';
43
-    try {
44
-      obj.ppid = ppid(obj.action);
45
-    } catch(e) {
46
-      // ignore
47
-      //console.error(e);
48
-    }
49
-    obj.name = opts.name || undefined;
50
-    obj.address = opts.email;
51
-    obj.community = 'node.js@therootcompany.com';
52
-
53
-    req.write(JSON.stringify(obj, 2, null));
54
-    req.end();
55
-    req.on('error', function () { /*ignore*/ });
56
-    //req.on('error', function (err) { console.error(err); });
57
-  }, 50);
58
-}
59
-
60
-function ping(action) {
61
-  setTimeout(function () {
62
-    var req = https.request({
63
-      hostname: 'api.therootcompany.com'
64
-    , port: 443
65
-    , path: '/api/therootcompany.com/public/ping'
66
-    , method: 'POST'
67
-    , headers: { 'Content-Type': 'application/json' }
68
-    }, function (resp) {
69
-      // let the data flow, so we can ignore it
70
-      resp.on('data', function () { });
71
-      //resp.on('data', function (chunk) { console.log(chunk.toString()); });
72
-      resp.on('error', function () { /*ignore*/ });
73
-      //resp.on('error', function (err) { console.error(err); });
74
-    });
75
-    var obj = JSON.parse(JSON.stringify(data));
76
-    obj.action = action;
77
-    try {
78
-      obj.ppid = ppid(obj.action);
79
-    } catch(e) {
80
-      // ignore
81
-      //console.error(e);
82
-    }
83
-
84
-    req.write(JSON.stringify(obj, 2, null));
85
-    req.end();
86
-    req.on('error', function (/*e*/) { /*console.error('req.error', e);*/ });
87
-  }, 50);
88
-}
89
-
90
-// to help identify unique installs without getting
91
-// the personally identifiable info that we don't want
92
-function ppid(action) {
93
-  var parts = [ action, data.package, data.version, data.node, data.arch, data.platform, data.release ];
94
-  var ifaces = os.networkInterfaces();
95
-  Object.keys(ifaces).forEach(function (ifname) {
96
-    if (/^en/.test(ifname) || /^eth/.test(ifname) || /^wl/.test(ifname)) {
97
-      if  (ifaces[ifname] && ifaces[ifname].length) {
98
-        parts.push(ifaces[ifname][0].mac);
99
-      }
100
-    }
101
-  });
102
-  return crypto.createHash('sha1').update(parts.join(',')).digest('base64');
103
-}
104
-
105
-module.exports.ping = ping;
106
-module.exports.joinCommunity = addCommunityMember;
107
-
108
-if (require.main === module) {
109
-  ping('install');
110
-  //addCommunityMember({ name: "AJ ONeal", email: 'coolaj86@gmail.com' });
111
-}

+ 0
- 123
lib/x509-ec-parser.js View File

@@ -1,123 +0,0 @@
1
-'use strict';
2
-
3
-var Enc = require('./encoding.js');
4
-var x509 = module.exports;
5
-
6
-// 1.2.840.10045.3.1.7
7
-// prime256v1 (ANSI X9.62 named elliptic curve)
8
-var OBJ_ID_EC_256  = '06 08 2A8648CE3D030107'.replace(/\s+/g, '').toLowerCase();
9
-// 1.3.132.0.34
10
-// secp384r1 (SECG (Certicom) named elliptic curve)
11
-var OBJ_ID_EC_384 = '06 05 2B81040022'.replace(/\s+/g, '').toLowerCase();
12
-
13
-x509.parseSec1 = function parseEcOnlyPrivkey(u8, jwk) {
14
-  var index = 7;
15
-  var len = 32;
16
-  var olen = OBJ_ID_EC_256.length/2;
17
-
18
-  if ("P-384" === jwk.crv) {
19
-    olen = OBJ_ID_EC_384.length/2;
20
-    index = 8;
21
-    len = 48;
22
-  }
23
-  if (len !== u8[index - 1]) {
24
-    throw new Error("Unexpected bitlength " + len);
25
-  }
26
-
27
-  // private part is d
28
-  var d = u8.slice(index, index + len);
29
-  // compression bit index
30
-  var ci = index + len + 2 + olen + 2 + 3;
31
-  var c = u8[ci];
32
-  var x, y;
33
-
34
-  if (0x04 === c) {
35
-    y = u8.slice(ci + 1 + len, ci + 1 + len + len);
36
-  } else if (0x02 !== c) {
37
-    throw new Error("not a supported EC private key");
38
-  }
39
-  x = u8.slice(ci + 1, ci + 1 + len);
40
-
41
-  return {
42
-    kty: jwk.kty
43
-  , crv: jwk.crv
44
-  , d: Enc.bufToUrlBase64(d)
45
-  //, dh: Enc.bufToHex(d)
46
-  , x: Enc.bufToUrlBase64(x)
47
-  //, xh: Enc.bufToHex(x)
48
-  , y: Enc.bufToUrlBase64(y)
49
-  //, yh: Enc.bufToHex(y)
50
-  };
51
-};
52
-
53
-x509.parsePkcs8 = function parseEcPkcs8(u8, jwk) {
54
-  console.log("here", u8);
55
-  var index = 24 + (OBJ_ID_EC_256.length/2);
56
-  var len = 32;
57
-  /*
58
-  if ("P-384" === jwk.crv) {
59
-    index = 24 + (OBJ_ID_EC_384.length/2) + 2;
60
-    len = 48;
61
-  }
62
-  */
63
-
64
-  //console.log(index, u8.slice(index));
65
-  if (0x04 !== u8[index]) {
66
-    //console.log(jwk);
67
-    throw new Error("privkey not found");
68
-  }
69
-  var d = u8.slice(index+2, index+2+len);
70
-  var ci = index+2+len+5;
71
-  var xi = ci+1;
72
-  var x = u8.slice(xi, xi + len);
73
-  var yi = xi+len;
74
-  var y;
75
-  if (0x04 === u8[ci]) {
76
-    y = u8.slice(yi, yi + len);
77
-  } else if (0x02 !== u8[ci]) {
78
-    throw new Error("invalid compression bit (expected 0x04 or 0x02)");
79
-  }
80
-
81
-  var jwk = {};
82
-  return {
83
-    kty: jwk.kty
84
-  , crv: jwk.crv
85
-  , d: Enc.bufToUrlBase64(d)
86
-  //, dh: Enc.bufToHex(d)
87
-  , x: Enc.bufToUrlBase64(x)
88
-  //, xh: Enc.bufToHex(x)
89
-  , y: Enc.bufToUrlBase64(y)
90
-  //, yh: Enc.bufToHex(y)
91
-  };
92
-};
93
-
94
-x509.parseSpki = function parsePem(u8, jwk) {
95
-  var ci = 16 + OBJ_ID_EC_256.length/2;
96
-  var len = 32;
97
-
98
-  if ("P-384" === jwk.crv) {
99
-    ci = 16 + OBJ_ID_EC_384.length/2;
100
-    len = 48;
101
-  }
102
-
103
-  var c = u8[ci];
104
-  var xi = ci + 1;
105
-  var x = u8.slice(xi, xi + len);
106
-  var yi = xi + len;
107
-  var y;
108
-  if (0x04 === c) {
109
-    y = u8.slice(yi, yi + len);
110
-  } else if (0x02 !== c) {
111
-    throw new Error("not a supported EC private key");
112
-  }
113
-
114
-  return {
115
-    kty: jwk.kty
116
-  , crv: jwk.crv
117
-  , x: Enc.bufToUrlBase64(x)
118
-  //, xh: Enc.bufToHex(x)
119
-  , y: Enc.bufToUrlBase64(y)
120
-  //, yh: Enc.bufToHex(y)
121
-  };
122
-};
123
-x509.parsePkix = x509.parseSpki;

+ 0
- 14
lib/x509-packer.js View File

@@ -1,14 +0,0 @@
1
-'use strict';
2
-
3
-var x509 = module.exports;
4
-
5
-var RSA = require('./rsa.js');
6
-var EC = require('./ec.js');
7
-
8
-x509.pack = function (opts) {
9
-  if ('RSA' === opts.jwk.kty) {
10
-    return RSA.pack(opts);
11
-  } else {
12
-    return EC.pack(opts);
13
-  }
14
-};

+ 0
- 46
lib/x509-parser.js View File

@@ -1,46 +0,0 @@
1
-'use strict';
2
-
3
-var x509 = module.exports;
4
-
5
-var PEM = require('./pem-parser.js');
6
-var EC = require('./x509-ec-parser.js');
7
-var RSA = require('./x509-rsa-parser.js');
8
-
9
-x509.parse = function (opts) {
10
-  console.log(opts);
11
-  var pem = opts.pem;
12
-  var der = opts.der;
13
-  if ('string' === opts.key) {
14
-    pem = opts.key;
15
-  } else if (opts.key && opts.key.length) {
16
-    der = opts.key;
17
-  }
18
-  if (pem) { pem = PEM.parseBlock(pem); }
19
-  else { pem = { bytes: der, type: '' }; }
20
-  der = pem.bytes;
21
-  var typ = pem.type;
22
-  var pub = /PUBLIC KEY/.test(typ);
23
-  var prv = /PRIVATE KEY/.test(typ);
24
-  var ec = /EC P/.test(typ);
25
-  var rsa = /RSA P/.test(typ);
26
-
27
-  // Try EC Private and Public keys
28
-  if (!rsa && !pub) {
29
-    try { return EC.parsePkcs8(der); } catch(e) { console.error(e); /*ignore*/ }
30
-    try { return EC.parseSec1(der); } catch(e) { /*ignore*/ }
31
-  } else if (!rsa && !prv) {
32
-    try { return EC.parseSpki(der); } catch(e) { /*ignore*/ }
33
-  }
34
-
35
-  // Try RSA Private and Public keys
36
-  if (!ec && !pub) {
37
-    try { return RSA.parsePkcs8(der); } catch(e) { /*ignore*/ }
38
-    try { return RSA.parsePkcs1(der); } catch(e) { /*ignore*/ }
39
-  } else if (!ec && !prv) {
40
-    try { return RSA.parseSpki(der); } catch(e) { /*ignore*/ }
41
-    try { return RSA.parsePublicPkcs1(der); } catch(e) { /*ignore*/ }
42
-  }
43
-
44
-  throw new Error("Invalid or Unsupported key:\n"
45
-    + "Tried ASN.1 PEM/DER PKCS1, SEC1, PKCS8, and SPKI/PKIX (RSA and EC Private and Public)");
46
-};

+ 0
- 3
lib/x509-rsa-parser.js View File

@@ -1,3 +0,0 @@
1
-'use strict';
2
-
3
-var RSA = module.exports;

+ 0
- 153
lib/x509.js View File

@@ -1,153 +0,0 @@
1
-'use strict';
2
-
3
-var x509 = module.exports;
4
-var ASN1 = require('./asn1.js');
5
-var Enc = require('./encoding.js');
6
-
7
-x509.guess = function (der, asn1) {
8
-  // accepting der for compatability with other usages
9
-
10
-  var meta = { kty: 'RSA', format: 'pkcs1', public: true };
11
-  //meta.asn1 = ASN1.parse(u8);
12
-
13
-  if (asn1.children.every(function(el) {
14
-    return 0x02 === el.type;
15
-  })) {
16
-    if (2 === asn1.children.length) {
17
-      // rsa pkcs1 public
18
-      return meta;
19
-    } else if (asn1.children.length >= 9) {
20
-      // the standard allows for "otherPrimeInfos", hence at least 9
21
-      meta.public = false;
22
-      // rsa pkcs1 private
23
-      return meta;
24
-    } else {
25
-      throw new Error("not an RSA PKCS#1 public or private key (wrong number of ints)");
26
-    }
27
-  } else {
28
-    meta.format = 'pkcs8';
29
-  }
30
-
31
-  return meta;
32
-};
33
-
34
-x509.parsePkcs1 = function parseRsaPkcs1(buf, asn1, jwk) {
35
-  if (!asn1.children.every(function(el) {
36
-    return 0x02 === el.type;
37
-  })) {
38
-    throw new Error("not an RSA PKCS#1 public or private key (not all ints)");
39
-  }
40
-
41
-  if (2 === asn1.children.length) {
42
-
43
-    jwk.n = Enc.bufToUrlBase64(asn1.children[0].value);
44
-    jwk.e = Enc.bufToUrlBase64(asn1.children[1].value);
45
-    return jwk;
46
-
47
-  } else if (asn1.children.length >= 9) {
48
-    // the standard allows for "otherPrimeInfos", hence at least 9
49
-
50
-    jwk.n = Enc.bufToUrlBase64(asn1.children[1].value);
51
-    jwk.e = Enc.bufToUrlBase64(asn1.children[2].value);
52
-    jwk.d = Enc.bufToUrlBase64(asn1.children[3].value);
53
-    jwk.p = Enc.bufToUrlBase64(asn1.children[4].value);
54
-    jwk.q = Enc.bufToUrlBase64(asn1.children[5].value);
55
-    jwk.dp = Enc.bufToUrlBase64(asn1.children[6].value);
56
-    jwk.dq = Enc.bufToUrlBase64(asn1.children[7].value);
57
-    jwk.qi = Enc.bufToUrlBase64(asn1.children[8].value);
58
-    return jwk;
59
-
60
-  } else {
61
-    throw new Error("not an RSA PKCS#1 public or private key (wrong number of ints)");
62
-  }
63
-};
64
-
65
-x509.parsePkcs8 = function parseRsaPkcs8(buf, asn1, jwk) {
66
-  if (2 === asn1.children.length
67
-    && 0x03 === asn1.children[1].type
68
-    && 0x30 === asn1.children[1].value[0]) {
69
-
70
-    asn1 = ASN1.parse(asn1.children[1].value);
71
-    jwk.n = Enc.bufToUrlBase64(asn1.children[0].value);
72
-    jwk.e = Enc.bufToUrlBase64(asn1.children[1].value);
73
-
74
-  } else if (3 === asn1.children.length
75
-    && 0x04 === asn1.children[2].type
76
-    && 0x30 === asn1.children[2].children[0].type
77
-    && 0x02 === asn1.children[2].children[0].children[0].type) {
78
-
79
-    asn1 = asn1.children[2].children[0];
80
-    jwk.n = Enc.bufToUrlBase64(asn1.children[1].value);
81
-    jwk.e = Enc.bufToUrlBase64(asn1.children[2].value);
82
-    jwk.d = Enc.bufToUrlBase64(asn1.children[3].value);
83
-    jwk.p = Enc.bufToUrlBase64(asn1.children[4].value);
84
-    jwk.q = Enc.bufToUrlBase64(asn1.children[5].value);
85
-    jwk.dp = Enc.bufToUrlBase64(asn1.children[6].value);
86
-    jwk.dq = Enc.bufToUrlBase64(asn1.children[7].value);
87
-    jwk.qi = Enc.bufToUrlBase64(asn1.children[8].value);
88
-
89
-  } else {
90
-    throw new Error("not an RSA PKCS#8 public or private key (wrong format)");
91
-  }
92
-  return jwk;
93
-};
94
-
95
-x509.packPkcs1 = function (jwk) {
96
-  var n = ASN1.UInt(Enc.base64ToHex(jwk.n));
97
-  var e = ASN1.UInt(Enc.base64ToHex(jwk.e));
98
-
99
-  if (!jwk.d) {
100
-    return Enc.hexToBuf(ASN1('30', n, e));
101
-  }
102
-
103
-  return Enc.hexToBuf(ASN1('30'
104
-  , ASN1.UInt('00')
105
-  , n
106
-  , e
107
-  , ASN1.UInt(Enc.base64ToHex(jwk.d))
108
-  , ASN1.UInt(Enc.base64ToHex(jwk.p))
109
-  , ASN1.UInt(Enc.base64ToHex(jwk.q))
110
-  , ASN1.UInt(Enc.base64ToHex(jwk.dp))
111
-  , ASN1.UInt(Enc.base64ToHex(jwk.dq))
112
-  , ASN1.UInt(Enc.base64ToHex(jwk.qi))
113
-  ));
114
-};
115
-
116
-x509.packPkcs8 = function (jwk) {
117
-  if (!jwk.d) {
118
-    // Public RSA
119
-    return Enc.hexToBuf(ASN1('30'
120
-      , ASN1('30'
121
-        , ASN1('06', '2a864886f70d010101')
122
-        , ASN1('05')
123
-      )
124
-      , ASN1.BitStr(ASN1('30'
125
-        , ASN1.UInt(Enc.base64ToHex(jwk.n))
126
-        , ASN1.UInt(Enc.base64ToHex(jwk.e))
127
-      ))
128
-    ));
129
-  }
130
-
131
-  // Private RSA
132
-  return Enc.hexToBuf(ASN1('30'
133
-    , ASN1.UInt('00')
134
-    , ASN1('30'
135
-      , ASN1('06', '2a864886f70d010101')
136
-      , ASN1('05')
137
-    )
138
-    , ASN1('04'
139
-      , ASN1('30'
140
-        , ASN1.UInt('00')
141
-        , ASN1.UInt(Enc.base64ToHex(jwk.n))
142
-        , ASN1.UInt(Enc.base64ToHex(jwk.e))
143
-        , ASN1.UInt(Enc.base64ToHex(jwk.d))
144
-        , ASN1.UInt(Enc.base64ToHex(jwk.p))
145
-        , ASN1.UInt(Enc.base64ToHex(jwk.q))
146
-        , ASN1.UInt(Enc.base64ToHex(jwk.dp))
147
-        , ASN1.UInt(Enc.base64ToHex(jwk.dq))
148
-        , ASN1.UInt(Enc.base64ToHex(jwk.qi))
149
-      )
150
-    )
151
-  ));
152
-};
153
-x509.packSpki = x509.packPkcs8;

+ 18
- 0
package-lock.json View File

@@ -0,0 +1,18 @@
1
+{
2
+  "name": "keypairs",
3
+  "version": "1.0.0",
4
+  "lockfileVersion": 1,
5
+  "requires": true,
6
+  "dependencies": {
7
+    "eckles": {
8
+      "version": "1.4.0",
9
+      "resolved": "https://registry.npmjs.org/eckles/-/eckles-1.4.0.tgz",
10
+      "integrity": "sha512-Bm5dpwhsBuoCHvKCY3gAvP8XFyXH7im8uAu3szykpVNbFBdC+lOuV8vLC8fvTYRZBfFqB+k/P6ud/ZPVO2V2tA=="
11
+    },
12
+    "rasha": {
13
+      "version": "1.2.1",
14
+      "resolved": "https://registry.npmjs.org/rasha/-/rasha-1.2.1.tgz",
15
+      "integrity": "sha512-cs4Hu/rVF3/Qucq+V7lxSz449VfHNMVXJaeajAHno9H5FC1PWlmS4NM6IAX5jPKFF0IC2rOdHdf7iNxQuIWZag=="
16
+    }
17
+  }
18
+}

+ 9
- 6
package.json View File

@@ -1,8 +1,9 @@
1 1
 {
2 2
   "name": "keypairs",
3
-  "version": "0.0.4",
4
-  "description": "Interchangeably use RSA & ECDSA with PEM and JWK for Signing, Verifying, CSR generation and JOSE. Ugh... that was a mouthful.",
5
-  "main": "index.js",
3
+  "version": "1.0.0",
4
+  "description": "Lightweight RSA/ECDSA keypair generation and JWK <-> PEM",
5
+  "main": "keypairs.js",
6
+  "files": [],
6 7
   "scripts": {
7 8
     "test": "echo \"Error: no test specified\" && exit 1"
8 9
   },
@@ -16,9 +17,11 @@
16 17
     "ECDSA",
17 18
     "PEM",
18 19
     "JWK",
19
-    "CSR",
20
-    "JOSE"
21 20
   ],
22 21
   "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
23
-  "license": "(MIT OR Apache-2.0)"
22
+  "license": "MPL-2.0",
23
+  "dependencies": {
24
+    "eckles": "^1.4.0",
25
+    "rasha": "^1.2.1"
26
+  }
24 27
 }

+ 0
- 23
pubkey-cli.js View File

@@ -1,23 +0,0 @@
1
-'use strict';
2
-
3
-var pubkey = require('./pubkey.js');
4
-var pubname = process.argv[2];