@libp2p/crypto 0.0.0 → 0.22.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/LICENSE +2 -0
  2. package/README.md +314 -0
  3. package/dist/src/aes/cipher-mode.d.ts +2 -0
  4. package/dist/src/aes/cipher-mode.d.ts.map +1 -0
  5. package/dist/src/aes/cipher-mode.js +13 -0
  6. package/dist/src/aes/cipher-mode.js.map +1 -0
  7. package/dist/src/aes/ciphers-browser.d.ts +8 -0
  8. package/dist/src/aes/ciphers-browser.d.ts.map +1 -0
  9. package/dist/src/aes/ciphers-browser.js +26 -0
  10. package/dist/src/aes/ciphers-browser.js.map +1 -0
  11. package/dist/src/aes/ciphers.d.ts +5 -0
  12. package/dist/src/aes/ciphers.d.ts.map +1 -0
  13. package/dist/src/aes/ciphers.js +4 -0
  14. package/dist/src/aes/ciphers.js.map +1 -0
  15. package/dist/src/aes/index.d.ts +6 -0
  16. package/dist/src/aes/index.d.ts.map +1 -0
  17. package/dist/src/aes/index.js +17 -0
  18. package/dist/src/aes/index.js.map +1 -0
  19. package/dist/src/ciphers/aes-gcm.browser.d.ts +3 -0
  20. package/dist/src/ciphers/aes-gcm.browser.d.ts.map +1 -0
  21. package/dist/src/ciphers/aes-gcm.browser.js +61 -0
  22. package/dist/src/ciphers/aes-gcm.browser.js.map +1 -0
  23. package/dist/src/ciphers/aes-gcm.d.ts +3 -0
  24. package/dist/src/ciphers/aes-gcm.d.ts.map +1 -0
  25. package/dist/src/ciphers/aes-gcm.js +83 -0
  26. package/dist/src/ciphers/aes-gcm.js.map +1 -0
  27. package/dist/src/ciphers/interface.d.ts +14 -0
  28. package/dist/src/ciphers/interface.d.ts.map +1 -0
  29. package/dist/src/ciphers/interface.js +2 -0
  30. package/dist/src/ciphers/interface.js.map +1 -0
  31. package/dist/src/hmac/index-browser.d.ts +5 -0
  32. package/dist/src/hmac/index-browser.d.ts.map +1 -0
  33. package/dist/src/hmac/index-browser.js +25 -0
  34. package/dist/src/hmac/index-browser.js.map +1 -0
  35. package/dist/src/hmac/index.d.ts +5 -0
  36. package/dist/src/hmac/index.d.ts.map +1 -0
  37. package/dist/src/hmac/index.js +14 -0
  38. package/dist/src/hmac/index.js.map +1 -0
  39. package/dist/src/hmac/lengths.d.ts +7 -0
  40. package/dist/src/hmac/lengths.d.ts.map +1 -0
  41. package/dist/src/hmac/lengths.js +6 -0
  42. package/dist/src/hmac/lengths.js.map +1 -0
  43. package/dist/src/index.d.ts +11 -0
  44. package/dist/src/index.d.ts.map +1 -0
  45. package/dist/src/index.js +11 -0
  46. package/dist/src/index.js.map +1 -0
  47. package/dist/src/keys/ecdh-browser.d.ts +3 -0
  48. package/dist/src/keys/ecdh-browser.d.ts.map +1 -0
  49. package/dist/src/keys/ecdh-browser.js +97 -0
  50. package/dist/src/keys/ecdh-browser.js.map +1 -0
  51. package/dist/src/keys/ecdh.d.ts +3 -0
  52. package/dist/src/keys/ecdh.d.ts.map +1 -0
  53. package/dist/src/keys/ecdh.js +26 -0
  54. package/dist/src/keys/ecdh.js.map +1 -0
  55. package/dist/src/keys/ed25519-class.d.ts +39 -0
  56. package/dist/src/keys/ed25519-class.d.ts.map +1 -0
  57. package/dist/src/keys/ed25519-class.js +119 -0
  58. package/dist/src/keys/ed25519-class.js.map +1 -0
  59. package/dist/src/keys/ed25519.d.ts +18 -0
  60. package/dist/src/keys/ed25519.d.ts.map +1 -0
  61. package/dist/src/keys/ed25519.js +52 -0
  62. package/dist/src/keys/ed25519.js.map +1 -0
  63. package/dist/src/keys/ephemeral-keys.d.ts +9 -0
  64. package/dist/src/keys/ephemeral-keys.d.ts.map +1 -0
  65. package/dist/src/keys/ephemeral-keys.js +9 -0
  66. package/dist/src/keys/ephemeral-keys.js.map +1 -0
  67. package/dist/src/keys/exporter.d.ts +7 -0
  68. package/dist/src/keys/exporter.d.ts.map +1 -0
  69. package/dist/src/keys/exporter.js +13 -0
  70. package/dist/src/keys/exporter.js.map +1 -0
  71. package/dist/src/keys/importer.d.ts +7 -0
  72. package/dist/src/keys/importer.d.ts.map +1 -0
  73. package/dist/src/keys/importer.js +13 -0
  74. package/dist/src/keys/importer.js.map +1 -0
  75. package/dist/src/keys/index.d.ts +33 -0
  76. package/dist/src/keys/index.d.ts.map +1 -0
  77. package/dist/src/keys/index.js +111 -0
  78. package/dist/src/keys/index.js.map +1 -0
  79. package/dist/src/keys/interface.d.ts +17 -0
  80. package/dist/src/keys/interface.d.ts.map +1 -0
  81. package/dist/src/keys/interface.js +2 -0
  82. package/dist/src/keys/interface.js.map +1 -0
  83. package/dist/src/keys/jwk2pem.d.ts +4 -0
  84. package/dist/src/keys/jwk2pem.d.ts.map +1 -0
  85. package/dist/src/keys/jwk2pem.js +14 -0
  86. package/dist/src/keys/jwk2pem.js.map +1 -0
  87. package/dist/src/keys/key-stretcher.d.ts +17 -0
  88. package/dist/src/keys/key-stretcher.d.ts.map +1 -0
  89. package/dist/src/keys/key-stretcher.js +65 -0
  90. package/dist/src/keys/key-stretcher.js.map +1 -0
  91. package/dist/src/keys/keys.d.ts +225 -0
  92. package/dist/src/keys/keys.d.ts.map +1 -0
  93. package/dist/src/keys/keys.js +345 -0
  94. package/dist/src/keys/keys.js.map +1 -0
  95. package/dist/src/keys/rsa-browser.d.ts +17 -0
  96. package/dist/src/keys/rsa-browser.d.ts.map +1 -0
  97. package/dist/src/keys/rsa-browser.js +99 -0
  98. package/dist/src/keys/rsa-browser.js.map +1 -0
  99. package/dist/src/keys/rsa-class.d.ts +42 -0
  100. package/dist/src/keys/rsa-class.d.ts.map +1 -0
  101. package/dist/src/keys/rsa-class.js +126 -0
  102. package/dist/src/keys/rsa-class.js.map +1 -0
  103. package/dist/src/keys/rsa-utils.d.ts +7 -0
  104. package/dist/src/keys/rsa-utils.d.ts.map +1 -0
  105. package/dist/src/keys/rsa-utils.js +65 -0
  106. package/dist/src/keys/rsa-utils.js.map +1 -0
  107. package/dist/src/keys/rsa.d.ts +13 -0
  108. package/dist/src/keys/rsa.d.ts.map +1 -0
  109. package/dist/src/keys/rsa.js +58 -0
  110. package/dist/src/keys/rsa.js.map +1 -0
  111. package/dist/src/keys/secp256k1-class.d.ts +36 -0
  112. package/dist/src/keys/secp256k1-class.d.ts.map +1 -0
  113. package/dist/src/keys/secp256k1-class.js +95 -0
  114. package/dist/src/keys/secp256k1-class.js.map +1 -0
  115. package/dist/src/keys/secp256k1.d.ts +17 -0
  116. package/dist/src/keys/secp256k1.d.ts.map +1 -0
  117. package/dist/src/keys/secp256k1.js +65 -0
  118. package/dist/src/keys/secp256k1.js.map +1 -0
  119. package/dist/src/pbkdf2.d.ts +5 -0
  120. package/dist/src/pbkdf2.d.ts.map +1 -0
  121. package/dist/src/pbkdf2.js +30 -0
  122. package/dist/src/pbkdf2.js.map +1 -0
  123. package/dist/src/random-bytes.d.ts +2 -0
  124. package/dist/src/random-bytes.d.ts.map +1 -0
  125. package/dist/src/random-bytes.js +9 -0
  126. package/dist/src/random-bytes.js.map +1 -0
  127. package/dist/src/util.d.ts +9 -0
  128. package/dist/src/util.d.ts.map +1 -0
  129. package/dist/src/util.js +37 -0
  130. package/dist/src/util.js.map +1 -0
  131. package/dist/src/webcrypto.d.ts +5 -0
  132. package/dist/src/webcrypto.d.ts.map +1 -0
  133. package/dist/src/webcrypto.js +17 -0
  134. package/dist/src/webcrypto.js.map +1 -0
  135. package/package.json +178 -4
  136. package/src/aes/cipher-mode.ts +15 -0
  137. package/src/aes/ciphers-browser.ts +28 -0
  138. package/src/aes/ciphers.ts +4 -0
  139. package/src/aes/index.ts +25 -0
  140. package/src/ciphers/aes-gcm.browser.ts +74 -0
  141. package/src/ciphers/aes-gcm.ts +102 -0
  142. package/src/ciphers/interface.ts +15 -0
  143. package/src/hmac/index-browser.ts +35 -0
  144. package/src/hmac/index.ts +15 -0
  145. package/src/hmac/lengths.ts +6 -0
  146. package/src/index.ts +11 -0
  147. package/src/keys/ecdh-browser.ts +138 -0
  148. package/src/keys/ecdh.ts +33 -0
  149. package/src/keys/ed25519-class.ts +145 -0
  150. package/src/keys/ed25519.ts +63 -0
  151. package/src/keys/ephemeral-keys.ts +9 -0
  152. package/src/keys/exporter.ts +13 -0
  153. package/src/keys/importer.ts +13 -0
  154. package/src/keys/index.ts +126 -0
  155. package/src/keys/interface.ts +20 -0
  156. package/src/keys/jwk2pem.ts +16 -0
  157. package/src/keys/key-stretcher.ts +77 -0
  158. package/src/keys/keys.d.ts +146 -0
  159. package/src/keys/keys.js +366 -0
  160. package/src/keys/keys.proto +15 -0
  161. package/src/keys/rsa-browser.ts +156 -0
  162. package/src/keys/rsa-class.ts +155 -0
  163. package/src/keys/rsa-utils.ts +74 -0
  164. package/src/keys/rsa.ts +69 -0
  165. package/src/keys/secp256k1-class.ts +118 -0
  166. package/src/keys/secp256k1.ts +69 -0
  167. package/src/pbkdf2.ts +39 -0
  168. package/src/random-bytes.ts +9 -0
  169. package/src/util.ts +42 -0
  170. package/src/webcrypto.ts +24 -0
package/package.json CHANGED
@@ -1,7 +1,181 @@
1
1
  {
2
2
  "name": "@libp2p/crypto",
3
- "version": "0.0.0",
4
- "description": "",
5
- "main": "index.js",
6
- "license": "(Apache-2.0 OR MIT)"
3
+ "version": "0.22.3",
4
+ "description": "Crypto primitives for libp2p",
5
+ "type": "module",
6
+ "types": "./dist/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/src/index.js"
10
+ },
11
+ "./aes": {
12
+ "import": "./dist/src/aes/index.js"
13
+ },
14
+ "./ciphers": {
15
+ "import": "./dist/src/ciphers/index.js"
16
+ },
17
+ "./hmac": {
18
+ "import": "./dist/src/hmac/index.js"
19
+ },
20
+ "./keys": {
21
+ "import": "./dist/src/keys/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "src",
26
+ "dist/src",
27
+ "!dist/test",
28
+ "!**/*.tsbuildinfo"
29
+ ],
30
+ "eslintConfig": {
31
+ "extends": "ipfs",
32
+ "parserOptions": {
33
+ "sourceType": "module"
34
+ },
35
+ "ignorePatterns": [
36
+ "src/*.d.ts"
37
+ ]
38
+ },
39
+ "scripts": {
40
+ "lint": "aegir lint",
41
+ "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js",
42
+ "build": "tsc",
43
+ "pretest": "npm run build",
44
+ "test": "aegir test -f ./dist/test/**/*.js",
45
+ "test:chrome": "npm run test -- -t browser",
46
+ "test:chrome-webworker": "npm run test -- -t webworker",
47
+ "test:firefox": "npm run test -- -t browser -- --browser firefox",
48
+ "test:firefox-webworker": "npm run test -- -t webworker -- --browser firefox",
49
+ "test:node": "npm run test -- -t node --cov",
50
+ "test:electron-main": "npm run test -- -t electron-main",
51
+ "release": "semantic-release",
52
+ "build:proto": "npm run build:proto:js && npm run build:proto:types",
53
+ "build:proto:js": "pbjs -t static-module -w es6 --es6 -r libp2p-crypto-keys --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/keys/keys.js ./src/keys/keys.proto",
54
+ "build:proto:types": "pbts -o src/keys/keys.d.ts src/keys/keys.js"
55
+ },
56
+ "browser": {
57
+ "./dist/src/aes/ciphers.js": "./dist/src/aes/ciphers-browser.js",
58
+ "./dist/src/ciphers/aes-gcm.js": "./dist/src/ciphers/aes-gcm.browser.js",
59
+ "./dist/src/hmac/index.js": "./dist/src/hmac/index-browser.js",
60
+ "./dist/src/keys/ecdh.js": "./dist/src/keys/ecdh-browser.js",
61
+ "./dist/src/keys/rsa.js": "./dist/src/keys/rsa-browser.js"
62
+ },
63
+ "keywords": [
64
+ "IPFS",
65
+ "libp2p",
66
+ "crypto",
67
+ "rsa",
68
+ "secp256k1"
69
+ ],
70
+ "license": "Apache-2.0 OR MIT",
71
+ "dependencies": {
72
+ "@noble/ed25519": "^1.3.3",
73
+ "@noble/secp256k1": "^1.3.4",
74
+ "err-code": "^3.0.1",
75
+ "iso-random-stream": "^2.0.0",
76
+ "multiformats": "^9.4.5",
77
+ "node-forge": "^0.10.0",
78
+ "protobufjs": "^6.11.2",
79
+ "uint8arrays": "^3.0.0"
80
+ },
81
+ "devDependencies": {
82
+ "@types/mocha": "^9.0.0",
83
+ "aegir": "^36.1.2",
84
+ "benchmark": "^2.1.4",
85
+ "sinon": "^12.0.1",
86
+ "util": "^0.12.3",
87
+ "wherearewe": "^1.0.0"
88
+ },
89
+ "engines": {
90
+ "node": ">=15.0.0"
91
+ },
92
+ "repository": {
93
+ "type": "git",
94
+ "url": "https://github.com/libp2p/js-libp2p-crypto.git"
95
+ },
96
+ "bugs": {
97
+ "url": "https://github.com/libp2p/js-libp2p-crypto/issues"
98
+ },
99
+ "homepage": "https://github.com/libp2p/js-libp2p-crypto",
100
+ "release": {
101
+ "branches": [
102
+ "master"
103
+ ],
104
+ "plugins": [
105
+ [
106
+ "@semantic-release/commit-analyzer",
107
+ {
108
+ "preset": "conventionalcommits",
109
+ "releaseRules": [
110
+ {
111
+ "breaking": true,
112
+ "release": "major"
113
+ },
114
+ {
115
+ "revert": true,
116
+ "release": "patch"
117
+ },
118
+ {
119
+ "type": "feat",
120
+ "release": "minor"
121
+ },
122
+ {
123
+ "type": "fix",
124
+ "release": "patch"
125
+ },
126
+ {
127
+ "type": "chore",
128
+ "release": "patch"
129
+ },
130
+ {
131
+ "type": "docs",
132
+ "release": "patch"
133
+ },
134
+ {
135
+ "type": "test",
136
+ "release": "patch"
137
+ },
138
+ {
139
+ "scope": "no-release",
140
+ "release": false
141
+ }
142
+ ]
143
+ }
144
+ ],
145
+ [
146
+ "@semantic-release/release-notes-generator",
147
+ {
148
+ "preset": "conventionalcommits",
149
+ "presetConfig": {
150
+ "types": [
151
+ {
152
+ "type": "feat",
153
+ "section": "Features"
154
+ },
155
+ {
156
+ "type": "fix",
157
+ "section": "Bug Fixes"
158
+ },
159
+ {
160
+ "type": "chore",
161
+ "section": "Trivial Changes"
162
+ },
163
+ {
164
+ "type": "docs",
165
+ "section": "Trivial Changes"
166
+ },
167
+ {
168
+ "type": "test",
169
+ "section": "Tests"
170
+ }
171
+ ]
172
+ }
173
+ }
174
+ ],
175
+ "@semantic-release/changelog",
176
+ "@semantic-release/npm",
177
+ "@semantic-release/github",
178
+ "@semantic-release/git"
179
+ ]
180
+ }
7
181
  }
@@ -0,0 +1,15 @@
1
+ import errcode from 'err-code'
2
+
3
+ const CIPHER_MODES = {
4
+ 16: 'aes-128-ctr',
5
+ 32: 'aes-256-ctr'
6
+ }
7
+
8
+ export function cipherMode (key: Uint8Array) {
9
+ if (key.length === 16 || key.length === 32) {
10
+ return CIPHER_MODES[key.length]
11
+ }
12
+
13
+ const modes = Object.entries(CIPHER_MODES).map(([k, v]) => `${k} (${v})`).join(' / ')
14
+ throw errcode(new Error(`Invalid key length ${key.length} bytes. Must be ${modes}`), 'ERR_INVALID_KEY_LENGTH')
15
+ }
@@ -0,0 +1,28 @@
1
+
2
+ import 'node-forge/lib/aes.js'
3
+ // @ts-expect-error types are missing
4
+ import forge from 'node-forge/lib/forge.js'
5
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
6
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
7
+
8
+ export function createCipheriv (mode: any, key: Uint8Array, iv: Uint8Array) {
9
+ const cipher2 = forge.cipher.createCipher('AES-CTR', uint8ArrayToString(key, 'ascii'))
10
+ cipher2.start({ iv: uint8ArrayToString(iv, 'ascii') })
11
+ return {
12
+ update: (data: Uint8Array) => {
13
+ cipher2.update(forge.util.createBuffer(uint8ArrayToString(data, 'ascii')))
14
+ return uint8ArrayFromString(cipher2.output.getBytes(), 'ascii')
15
+ }
16
+ }
17
+ }
18
+
19
+ export function createDecipheriv (mode: any, key: Uint8Array, iv: Uint8Array) {
20
+ const cipher2 = forge.cipher.createDecipher('AES-CTR', uint8ArrayToString(key, 'ascii'))
21
+ cipher2.start({ iv: uint8ArrayToString(iv, 'ascii') })
22
+ return {
23
+ update: (data: Uint8Array) => {
24
+ cipher2.update(forge.util.createBuffer(uint8ArrayToString(data, 'ascii')))
25
+ return uint8ArrayFromString(cipher2.output.getBytes(), 'ascii')
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,4 @@
1
+ import crypto from 'crypto'
2
+
3
+ export const createCipheriv = crypto.createCipheriv
4
+ export const createDecipheriv = crypto.createDecipheriv
@@ -0,0 +1,25 @@
1
+ import * as ciphers from './ciphers.js'
2
+ import { cipherMode } from './cipher-mode.js'
3
+
4
+ export interface AESCipher {
5
+ encrypt: (data: Uint8Array) => Promise<Uint8Array>
6
+ decrypt: (data: Uint8Array) => Promise<Uint8Array>
7
+ }
8
+
9
+ export async function create (key: Uint8Array, iv: Uint8Array) { // eslint-disable-line require-await
10
+ const mode = cipherMode(key)
11
+ const cipher = ciphers.createCipheriv(mode, key, iv)
12
+ const decipher = ciphers.createDecipheriv(mode, key, iv)
13
+
14
+ const res: AESCipher = {
15
+ async encrypt (data) { // eslint-disable-line require-await
16
+ return cipher.update(data)
17
+ },
18
+
19
+ async decrypt (data) { // eslint-disable-line require-await
20
+ return decipher.update(data)
21
+ }
22
+ }
23
+
24
+ return res
25
+ }
@@ -0,0 +1,74 @@
1
+ import { concat } from 'uint8arrays/concat'
2
+ import { fromString } from 'uint8arrays/from-string'
3
+ import webcrypto from '../webcrypto.js'
4
+ import type { CreateOptions, AESCipher } from './interface.js'
5
+
6
+ // Based off of code from https://github.com/luke-park/SecureCompatibleEncryptionExamples
7
+
8
+ export function create (opts?: CreateOptions) {
9
+ const algorithm = opts?.algorithm ?? 'AES-GCM'
10
+ let keyLength = opts?.keyLength ?? 16
11
+ const nonceLength = opts?.nonceLength ?? 12
12
+ const digest = opts?.digest ?? 'SHA-256'
13
+ const saltLength = opts?.saltLength ?? 16
14
+ const iterations = opts?.iterations ?? 32767
15
+
16
+ const crypto = webcrypto.get()
17
+ keyLength *= 8 // Browser crypto uses bits instead of bytes
18
+
19
+ /**
20
+ * Uses the provided password to derive a pbkdf2 key. The key
21
+ * will then be used to encrypt the data.
22
+ */
23
+ async function encrypt (data: Uint8Array, password: string | Uint8Array) { // eslint-disable-line require-await
24
+ const salt = crypto.getRandomValues(new Uint8Array(saltLength))
25
+ const nonce = crypto.getRandomValues(new Uint8Array(nonceLength))
26
+ const aesGcm = { name: algorithm, iv: nonce }
27
+
28
+ if (typeof password === 'string') {
29
+ password = fromString(password)
30
+ }
31
+
32
+ // Derive a key using PBKDF2.
33
+ const deriveParams = { name: 'PBKDF2', salt, iterations, hash: { name: digest } }
34
+ const rawKey = await crypto.subtle.importKey('raw', password, { name: 'PBKDF2' }, false, ['deriveKey', 'deriveBits'])
35
+ const cryptoKey = await crypto.subtle.deriveKey(deriveParams, rawKey, { name: algorithm, length: keyLength }, true, ['encrypt'])
36
+
37
+ // Encrypt the string.
38
+ const ciphertext = await crypto.subtle.encrypt(aesGcm, cryptoKey, data)
39
+ return concat([salt, aesGcm.iv, new Uint8Array(ciphertext)])
40
+ }
41
+
42
+ /**
43
+ * Uses the provided password to derive a pbkdf2 key. The key
44
+ * will then be used to decrypt the data. The options used to create
45
+ * this decryption cipher must be the same as those used to create
46
+ * the encryption cipher.
47
+ */
48
+ async function decrypt (data: Uint8Array, password: string | Uint8Array) {
49
+ const salt = data.slice(0, saltLength)
50
+ const nonce = data.slice(saltLength, saltLength + nonceLength)
51
+ const ciphertext = data.slice(saltLength + nonceLength)
52
+ const aesGcm = { name: algorithm, iv: nonce }
53
+
54
+ if (typeof password === 'string') {
55
+ password = fromString(password)
56
+ }
57
+
58
+ // Derive the key using PBKDF2.
59
+ const deriveParams = { name: 'PBKDF2', salt, iterations, hash: { name: digest } }
60
+ const rawKey = await crypto.subtle.importKey('raw', password, { name: 'PBKDF2' }, false, ['deriveKey', 'deriveBits'])
61
+ const cryptoKey = await crypto.subtle.deriveKey(deriveParams, rawKey, { name: algorithm, length: keyLength }, true, ['decrypt'])
62
+
63
+ // Decrypt the string.
64
+ const plaintext = await crypto.subtle.decrypt(aesGcm, cryptoKey, ciphertext)
65
+ return new Uint8Array(plaintext)
66
+ }
67
+
68
+ const cipher: AESCipher = {
69
+ encrypt,
70
+ decrypt
71
+ }
72
+
73
+ return cipher
74
+ }
@@ -0,0 +1,102 @@
1
+ import crypto from 'crypto'
2
+ import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
3
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
4
+ import type { CreateOptions, AESCipher } from './interface.js'
5
+
6
+ // Based off of code from https://github.com/luke-park/SecureCompatibleEncryptionExamples
7
+
8
+ export function create (opts?: CreateOptions) {
9
+ const algorithm = opts?.algorithm ?? 'aes-128-gcm'
10
+ const keyLength = opts?.keyLength ?? 16
11
+ const nonceLength = opts?.nonceLength ?? 12
12
+ const digest = opts?.digest ?? 'sha256'
13
+ const saltLength = opts?.saltLength ?? 16
14
+ const iterations = opts?.iterations ?? 32767
15
+ const algorithmTagLength = opts?.algorithmTagLength ?? 16
16
+
17
+ async function encryptWithKey (data: Uint8Array, key: Uint8Array) { // eslint-disable-line require-await
18
+ const nonce = crypto.randomBytes(nonceLength)
19
+
20
+ // Create the cipher instance.
21
+ const cipher = crypto.createCipheriv(algorithm, key, nonce)
22
+
23
+ // Encrypt and prepend nonce.
24
+ const ciphertext = uint8ArrayConcat([cipher.update(data), cipher.final()])
25
+
26
+ // @ts-expect-error getAuthTag is not a function
27
+ return uint8ArrayConcat([nonce, ciphertext, cipher.getAuthTag()])
28
+ }
29
+
30
+ /**
31
+ * Uses the provided password to derive a pbkdf2 key. The key
32
+ * will then be used to encrypt the data.
33
+ */
34
+ async function encrypt (data: Uint8Array, password: string | Uint8Array) { // eslint-disable-line require-await
35
+ // Generate a 128-bit salt using a CSPRNG.
36
+ const salt = crypto.randomBytes(saltLength)
37
+
38
+ if (typeof password === 'string') {
39
+ password = uint8ArrayFromString(password)
40
+ }
41
+
42
+ // Derive a key using PBKDF2.
43
+ const key = crypto.pbkdf2Sync(password, salt, iterations, keyLength, digest)
44
+
45
+ // Encrypt and prepend salt.
46
+ return uint8ArrayConcat([salt, await encryptWithKey(Uint8Array.from(data), key)])
47
+ }
48
+
49
+ /**
50
+ * Decrypts the given cipher text with the provided key. The `key` should
51
+ * be a cryptographically safe key and not a plaintext password. To use
52
+ * a plaintext password, use `decrypt`. The options used to create
53
+ * this decryption cipher must be the same as those used to create
54
+ * the encryption cipher.
55
+ */
56
+ async function decryptWithKey (ciphertextAndNonce: Uint8Array, key: Uint8Array) { // eslint-disable-line require-await
57
+ // Create Uint8Arrays of nonce, ciphertext and tag.
58
+ const nonce = ciphertextAndNonce.slice(0, nonceLength)
59
+ const ciphertext = ciphertextAndNonce.slice(nonceLength, ciphertextAndNonce.length - algorithmTagLength)
60
+ const tag = ciphertextAndNonce.slice(ciphertext.length + nonceLength)
61
+
62
+ // Create the cipher instance.
63
+ const cipher = crypto.createDecipheriv(algorithm, key, nonce)
64
+
65
+ // Decrypt and return result.
66
+ // @ts-expect-error getAuthTag is not a function
67
+ cipher.setAuthTag(tag)
68
+ return uint8ArrayConcat([cipher.update(ciphertext), cipher.final()])
69
+ }
70
+
71
+ /**
72
+ * Uses the provided password to derive a pbkdf2 key. The key
73
+ * will then be used to decrypt the data. The options used to create
74
+ * this decryption cipher must be the same as those used to create
75
+ * the encryption cipher.
76
+ *
77
+ * @param {Uint8Array} data - The data to decrypt
78
+ * @param {string|Uint8Array} password - A plain password
79
+ */
80
+ async function decrypt (data: Uint8Array, password: string | Uint8Array) { // eslint-disable-line require-await
81
+ // Create Uint8Arrays of salt and ciphertextAndNonce.
82
+ const salt = data.slice(0, saltLength)
83
+ const ciphertextAndNonce = data.slice(saltLength)
84
+
85
+ if (typeof password === 'string') {
86
+ password = uint8ArrayFromString(password)
87
+ }
88
+
89
+ // Derive the key using PBKDF2.
90
+ const key = crypto.pbkdf2Sync(password, salt, iterations, keyLength, digest)
91
+
92
+ // Decrypt and return result.
93
+ return await decryptWithKey(ciphertextAndNonce, key)
94
+ }
95
+
96
+ const cipher: AESCipher = {
97
+ encrypt,
98
+ decrypt
99
+ }
100
+
101
+ return cipher
102
+ }
@@ -0,0 +1,15 @@
1
+
2
+ export interface CreateOptions {
3
+ algorithm?: string
4
+ nonceLength?: number
5
+ keyLength?: number
6
+ digest?: string
7
+ saltLength?: number
8
+ iterations?: number
9
+ algorithmTagLength?: number
10
+ }
11
+
12
+ export interface AESCipher {
13
+ encrypt: (data: Uint8Array, password: string | Uint8Array) => Promise<Uint8Array>
14
+ decrypt: (data: Uint8Array, password: string | Uint8Array) => Promise<Uint8Array>
15
+ }
@@ -0,0 +1,35 @@
1
+ import webcrypto from '../webcrypto.js'
2
+ import lengths from './lengths.js'
3
+
4
+ const hashTypes = {
5
+ SHA1: 'SHA-1',
6
+ SHA256: 'SHA-256',
7
+ SHA512: 'SHA-512'
8
+ }
9
+
10
+ const sign = async (key: CryptoKey, data: Uint8Array) => {
11
+ const buf = await webcrypto.get().subtle.sign({ name: 'HMAC' }, key, data)
12
+ return new Uint8Array(buf, 0, buf.byteLength)
13
+ }
14
+
15
+ export async function create (hashType: 'SHA1' | 'SHA256' | 'SHA512', secret: Uint8Array) {
16
+ const hash = hashTypes[hashType]
17
+
18
+ const key = await webcrypto.get().subtle.importKey(
19
+ 'raw',
20
+ secret,
21
+ {
22
+ name: 'HMAC',
23
+ hash: { name: hash }
24
+ },
25
+ false,
26
+ ['sign']
27
+ )
28
+
29
+ return {
30
+ async digest (data: Uint8Array) { // eslint-disable-line require-await
31
+ return await sign(key, data)
32
+ },
33
+ length: lengths[hashType]
34
+ }
35
+ }
@@ -0,0 +1,15 @@
1
+ import crypto from 'crypto'
2
+ import lengths from './lengths.js'
3
+
4
+ export async function create (hash: 'SHA1' | 'SHA256' | 'SHA512', secret: Uint8Array) { // eslint-disable-line require-await
5
+ const res = {
6
+ async digest (data: Uint8Array) { // eslint-disable-line require-await
7
+ const hmac = crypto.createHmac(hash.toLowerCase(), secret)
8
+ hmac.update(data)
9
+ return hmac.digest()
10
+ },
11
+ length: lengths[hash]
12
+ }
13
+
14
+ return res
15
+ }
@@ -0,0 +1,6 @@
1
+
2
+ export default {
3
+ SHA1: 20,
4
+ SHA256: 32,
5
+ SHA512: 64
6
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ import * as hmac from './hmac/index.js'
2
+ import * as aes from './aes/index.js'
3
+ import * as keys from './keys/index.js'
4
+ import randomBytes from './random-bytes.js'
5
+ import pbkdf2 from './pbkdf2.js'
6
+
7
+ export { aes }
8
+ export { hmac }
9
+ export { keys }
10
+ export { randomBytes }
11
+ export { pbkdf2 }
@@ -0,0 +1,138 @@
1
+ import errcode from 'err-code'
2
+ import webcrypto from '../webcrypto.js'
3
+ import { base64urlToBuffer } from '../util.js'
4
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
5
+ import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
6
+ import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
7
+ import type { ECDHKey, ECDHKeyPair } from './interface.js'
8
+
9
+ const bits = {
10
+ 'P-256': 256,
11
+ 'P-384': 384,
12
+ 'P-521': 521
13
+ }
14
+
15
+ const curveTypes = Object.keys(bits)
16
+ const names = curveTypes.join(' / ')
17
+
18
+ export async function generateEphmeralKeyPair (curve: string) {
19
+ if (curve !== 'P-256' && curve !== 'P-384' && curve !== 'P-521') {
20
+ throw errcode(new Error(`Unknown curve: ${curve}. Must be ${names}`), 'ERR_INVALID_CURVE')
21
+ }
22
+
23
+ const pair = await webcrypto.get().subtle.generateKey(
24
+ {
25
+ name: 'ECDH',
26
+ namedCurve: curve
27
+ },
28
+ true,
29
+ ['deriveBits']
30
+ )
31
+
32
+ // forcePrivate is used for testing only
33
+ const genSharedKey = async (theirPub: Uint8Array, forcePrivate?: ECDHKeyPair) => {
34
+ let privateKey
35
+
36
+ if (forcePrivate != null) {
37
+ privateKey = await webcrypto.get().subtle.importKey(
38
+ 'jwk',
39
+ unmarshalPrivateKey(curve, forcePrivate),
40
+ {
41
+ name: 'ECDH',
42
+ namedCurve: curve
43
+ },
44
+ false,
45
+ ['deriveBits']
46
+ )
47
+ } else {
48
+ privateKey = pair.privateKey
49
+ }
50
+
51
+ const key = await webcrypto.get().subtle.importKey(
52
+ 'jwk',
53
+ unmarshalPublicKey(curve, theirPub),
54
+ {
55
+ name: 'ECDH',
56
+ namedCurve: curve
57
+ },
58
+ false,
59
+ []
60
+ )
61
+
62
+ const buffer = await webcrypto.get().subtle.deriveBits(
63
+ {
64
+ name: 'ECDH',
65
+ // @ts-expect-error namedCurve is missing from the types
66
+ namedCurve: curve,
67
+ public: key
68
+ },
69
+ privateKey,
70
+ bits[curve]
71
+ )
72
+
73
+ return new Uint8Array(buffer, 0, buffer.byteLength)
74
+ }
75
+
76
+ // @ts-expect-error jwk is missing from the types
77
+ const publicKey = await webcrypto.get().subtle.exportKey('jwk', pair.publicKey)
78
+
79
+ const ecdhKey: ECDHKey = {
80
+ key: marshalPublicKey(publicKey),
81
+ genSharedKey
82
+ }
83
+
84
+ return ecdhKey
85
+ }
86
+
87
+ const curveLengths = {
88
+ 'P-256': 32,
89
+ 'P-384': 48,
90
+ 'P-521': 66
91
+ }
92
+
93
+ // Marshal converts a jwk encoded ECDH public key into the
94
+ // form specified in section 4.3.6 of ANSI X9.62. (This is the format
95
+ // go-ipfs uses)
96
+ function marshalPublicKey (jwk: JsonWebKey) {
97
+ if (jwk.crv == null || jwk.x == null || jwk.y == null) {
98
+ throw errcode(new Error('JWK was missing components'), 'ERR_INVALID_PARAMETERS')
99
+ }
100
+
101
+ if (jwk.crv !== 'P-256' && jwk.crv !== 'P-384' && jwk.crv !== 'P-521') {
102
+ throw errcode(new Error(`Unknown curve: ${jwk.crv}. Must be ${names}`), 'ERR_INVALID_CURVE')
103
+ }
104
+
105
+ const byteLen = curveLengths[jwk.crv]
106
+
107
+ return uint8ArrayConcat([
108
+ Uint8Array.from([4]), // uncompressed point
109
+ base64urlToBuffer(jwk.x, byteLen),
110
+ base64urlToBuffer(jwk.y, byteLen)
111
+ ], 1 + byteLen * 2)
112
+ }
113
+
114
+ // Unmarshal converts a point, serialized by Marshal, into an jwk encoded key
115
+ function unmarshalPublicKey (curve: string, key: Uint8Array) {
116
+ if (curve !== 'P-256' && curve !== 'P-384' && curve !== 'P-521') {
117
+ throw errcode(new Error(`Unknown curve: ${curve}. Must be ${names}`), 'ERR_INVALID_CURVE')
118
+ }
119
+
120
+ const byteLen = curveLengths[curve]
121
+
122
+ if (!uint8ArrayEquals(key.slice(0, 1), Uint8Array.from([4]))) {
123
+ throw errcode(new Error('Cannot unmarshal public key - invalid key format'), 'ERR_INVALID_KEY_FORMAT')
124
+ }
125
+
126
+ return {
127
+ kty: 'EC',
128
+ crv: curve,
129
+ x: uint8ArrayToString(key.slice(1, byteLen + 1), 'base64url'),
130
+ y: uint8ArrayToString(key.slice(1 + byteLen), 'base64url'),
131
+ ext: true
132
+ }
133
+ }
134
+
135
+ const unmarshalPrivateKey = (curve: string, key: ECDHKeyPair) => ({
136
+ ...unmarshalPublicKey(curve, key.public),
137
+ d: uint8ArrayToString(key.private, 'base64url')
138
+ })