const crypto = require('.')
const errors = require('./lib/errors')
const CryptoKey = require('./lib/web/crypto-key')
const hmac = require('./lib/web/algorithm/hmac')
const pbkdf2 = require('./lib/web/algorithm/pbkdf2')
const ed25519 = require('./lib/web/algorithm/ed25519')
const sha = require('./lib/web/algorithm/sha')
exports.CryptoKey = CryptoKey
// https://w3c.github.io/webcrypto/#Crypto-method-getRandomValues
exports.getRandomValues = function getRandomValues(array) {
return crypto.randomFillSync(array)
}
// https://w3c.github.io/webcrypto/#dfn-Crypto-method-randomUUID
exports.randomUUID = crypto.randomUUID
// https://w3c.github.io/webcrypto/#subtlecrypto-interface
exports.SubtleCrypto = class SubtleCrypto {
// https://w3c.github.io/webcrypto/#SubtleCrypto-method-generateKey
async generateKey(algorithm, extractable, usages) {
if (typeof algorithm === 'string') algorithm = { name: algorithm }
switch (algorithm.name.toLowerCase()) {
case 'hmac':
return hmac.generateKey(algorithm, extractable, usages)
case 'ed25519':
return ed25519.generateKey(algorithm, extractable, usages)
default:
throw errors.NOT_SUPPORTED(
`Algorithm '${algorithm.name}' does not support the generateKey() operation`
)
}
}
// https://w3c.github.io/webcrypto/#SubtleCrypto-method-importKey
async importKey(format, keyData, algorithm, extractable, usages) {
if (typeof algorithm === 'string') algorithm = { name: algorithm }
switch (format) {
case 'raw':
case 'pkcs8':
case 'spki':
if (ArrayBuffer.isView(keyData)) {
keyData = Buffer.from(keyData)
} else {
keyData = Buffer.from(keyData.slice())
}
break
}
switch (algorithm.name.toLowerCase()) {
case 'hmac':
return hmac.importKey(format, keyData, algorithm, extractable, usages)
case 'ed25519':
return ed25519.importKey(format, keyData, algorithm, extractable, usages)
case 'pbkdf2':
return pbkdf2.importKey(format, keyData, algorithm, extractable, usages)
default:
throw errors.NOT_SUPPORTED(
`Algorithm '${algorithm.name}' does not support the importKey() operation`
)
}
}
// https://w3c.github.io/webcrypto/#SubtleCrypto-method-exportKey
async exportKey(format, key) {
if (!key.extractable) {
throw errors.INVALID_ACCESS('Key is not extractable')
}
switch (key.algorithm.name.toLowerCase()) {
case 'hmac':
return hmac.exportKey(format, key)
case 'ed25519':
return ed25519.exportKey(format, key)
default:
throw errors.NOT_SUPPORTED(
`Algorithm '${key.algorithm.name}' does not support the exportKey() operation`
)
}
}
// https://w3c.github.io/webcrypto/#SubtleCrypto-method-sign
async sign(algorithm, key, data) {
if (typeof algorithm === 'string') algorithm = { name: algorithm }
if (algorithm.name.toLowerCase() !== key.algorithm.name.toLowerCase()) {
throw errors.INVALID_ACCESS(`Algorithm '${algorithm.name}' does not match key'`)
}
if (!key.usages.includes('sign')) {
throw errors.INVALID_ACCESS('Key cannot be used for signing')
}
switch (algorithm.name.toLowerCase()) {
case 'hmac':
return hmac.sign(algorithm, key, data)
case 'ed25519':
return ed25519.sign(algorithm, key, data)
default:
throw errors.NOT_SUPPORTED(
`Algorithm '${algorithm.name}' does not support the sign() operation`
)
}
}
// https://w3c.github.io/webcrypto/#SubtleCrypto-method-verify
async verify(algorithm, key, signature, data) {
if (typeof algorithm === 'string') algorithm = { name: algorithm }
if (algorithm.name.toLowerCase() !== key.algorithm.name.toLowerCase()) {
throw errors.INVALID_ACCESS(`Algorithm '${algorithm.name}' does not match key'`)
}
if (!key.usages.includes('verify')) {
throw errors.INVALID_ACCESS('Key cannot be used for verification')
}
switch (algorithm.name.toLowerCase()) {
case 'hmac':
return hmac.verify(algorithm, key, signature, data)
case 'ed25519':
return ed25519.verify(algorithm, key, signature, data)
default:
throw errors.NOT_SUPPORTED(
`Algorithm '${algorithm.name}' does not support the verify() operation`
)
}
}
// https://w3c.github.io/webcrypto/#SubtleCrypto-method-deriveBits
async deriveBits(algorithm, key, length) {
if (typeof algorithm === 'string') algorithm = { name: algorithm }
if (algorithm.name.toLowerCase() !== key.algorithm.name.toLowerCase()) {
throw errors.INVALID_ACCESS(`Algorithm '${algorithm.name}' does not match key'`)
}
if (!key.usages.includes('deriveBits')) {
throw errors.INVALID_ACCESS('Key cannot be used to derive bits')
}
switch (algorithm.name.toLowerCase()) {
case 'pbkdf2':
return pbkdf2.deriveBits(algorithm, key, length)
default:
throw errors.NOT_SUPPORTED(
`Algorithm '${algorithm.name}' does not support the deriveBits() operation`
)
}
}
// https://w3c.github.io/webcrypto/#SubtleCrypto-method-deriveKey
async deriveKey(algorithm, baseKey, derivedKeyType, extractable, usages) {
if (typeof algorithm === 'string') algorithm = { name: algorithm }
if (typeof derivedKeyType === 'string') {
derivedKeyType = { name: derivedKeyType }
}
if (algorithm.name.toLowerCase() !== baseKey.algorithm.name.toLowerCase()) {
throw errors.INVALID_ACCESS(`Algorithm '${algorithm.name}' does not match key'`)
}
if (!baseKey.usages.includes('deriveKey')) {
throw errors.INVALID_ACCESS('Key cannot be used to derive key')
}
let length
switch (derivedKeyType.name.toLowerCase()) {
case 'hmac':
length = hmac.getKeyLength(derivedKeyType)
break
default:
throw errors.NOT_SUPPORTED(
`Algorithm '${derivedKeyType.name}' does not support the getKeyLength() operation`
)
}
let secret
switch (algorithm.name.toLowerCase()) {
case 'pbkdf2':
secret = pbkdf2.deriveBits(algorithm, baseKey, length)
break
default:
throw errors.NOT_SUPPORTED(
`Algorithm '${algorithm.name}' does not support the deriveBits() operation`
)
}
return this.importKey('raw', secret, derivedKeyType, extractable, usages)
}
// https://w3c.github.io/webcrypto/#SubtleCrypto-method-digest
async digest(algorithm, data) {
if (typeof algorithm === 'string') algorithm = { name: algorithm }
switch (algorithm.name.toLowerCase()) {
case 'sha-1':
return sha.digest(crypto.constants.hash.SHA1, data)
case 'sha-256':
return sha.digest(crypto.constants.hash.SHA256, data)
case 'sha-384':
return sha.digest(crypto.constants.hash.SHA384, data)
case 'sha-512':
return sha.digest(crypto.constants.hash.SHA512, data)
default:
throw errors.NOT_SUPPORTED(
`Algorithm '${algorithm.name}' does not support the digest() operation`
)
}
}
}
exports.subtle = new exports.SubtleCrypto()
// https://w3c.github.io/webcrypto/#crypto-interface
exports.Crypto = class Crypto {
get subtle() {
return exports.subtle
}
getRandomValues(array) {
return exports.getRandomValues(array)
}
randomUUID() {
return exports.randomUUID()
}
}