Buckets:
| import crypto, { isCryptoKey } from './webcrypto.js'; | |
| import invalidKeyInput from '../lib/invalid_key_input.js'; | |
| import { encodeBase64, decodeBase64 } from './base64url.js'; | |
| import formatPEM from '../lib/format_pem.js'; | |
| import { JOSENotSupported } from '../util/errors.js'; | |
| import { types } from './is_key_like.js'; | |
| const genericExport = async (keyType, keyFormat, key) => { | |
| if (!isCryptoKey(key)) { | |
| throw new TypeError(invalidKeyInput(key, ...types)); | |
| } | |
| if (!key.extractable) { | |
| throw new TypeError('CryptoKey is not extractable'); | |
| } | |
| if (key.type !== keyType) { | |
| throw new TypeError(`key is not a ${keyType} key`); | |
| } | |
| return formatPEM(encodeBase64(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`); | |
| }; | |
| export const toSPKI = (key) => { | |
| return genericExport('public', 'spki', key); | |
| }; | |
| export const toPKCS8 = (key) => { | |
| return genericExport('private', 'pkcs8', key); | |
| }; | |
| const findOid = (keyData, oid, from = 0) => { | |
| if (from === 0) { | |
| oid.unshift(oid.length); | |
| oid.unshift(0x06); | |
| } | |
| const i = keyData.indexOf(oid[0], from); | |
| if (i === -1) | |
| return false; | |
| const sub = keyData.subarray(i, i + oid.length); | |
| if (sub.length !== oid.length) | |
| return false; | |
| return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1); | |
| }; | |
| const getNamedCurve = (keyData) => { | |
| switch (true) { | |
| case findOid(keyData, [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]): | |
| return 'P-256'; | |
| case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x22]): | |
| return 'P-384'; | |
| case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x23]): | |
| return 'P-521'; | |
| case findOid(keyData, [0x2b, 0x65, 0x6e]): | |
| return 'X25519'; | |
| case findOid(keyData, [0x2b, 0x65, 0x6f]): | |
| return 'X448'; | |
| case findOid(keyData, [0x2b, 0x65, 0x70]): | |
| return 'Ed25519'; | |
| case findOid(keyData, [0x2b, 0x65, 0x71]): | |
| return 'Ed448'; | |
| default: | |
| throw new JOSENotSupported('Invalid or unsupported EC Key Curve or OKP Key Sub Type'); | |
| } | |
| }; | |
| const genericImport = async (replace, keyFormat, pem, alg, options) => { | |
| let algorithm; | |
| let keyUsages; | |
| const keyData = new Uint8Array(atob(pem.replace(replace, '')) | |
| .split('') | |
| .map((c) => c.charCodeAt(0))); | |
| const isPublic = keyFormat === 'spki'; | |
| switch (alg) { | |
| case 'PS256': | |
| case 'PS384': | |
| case 'PS512': | |
| algorithm = { name: 'RSA-PSS', hash: `SHA-${alg.slice(-3)}` }; | |
| keyUsages = isPublic ? ['verify'] : ['sign']; | |
| break; | |
| case 'RS256': | |
| case 'RS384': | |
| case 'RS512': | |
| algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${alg.slice(-3)}` }; | |
| keyUsages = isPublic ? ['verify'] : ['sign']; | |
| break; | |
| case 'RSA-OAEP': | |
| case 'RSA-OAEP-256': | |
| case 'RSA-OAEP-384': | |
| case 'RSA-OAEP-512': | |
| algorithm = { | |
| name: 'RSA-OAEP', | |
| hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`, | |
| }; | |
| keyUsages = isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey']; | |
| break; | |
| case 'ES256': | |
| algorithm = { name: 'ECDSA', namedCurve: 'P-256' }; | |
| keyUsages = isPublic ? ['verify'] : ['sign']; | |
| break; | |
| case 'ES384': | |
| algorithm = { name: 'ECDSA', namedCurve: 'P-384' }; | |
| keyUsages = isPublic ? ['verify'] : ['sign']; | |
| break; | |
| case 'ES512': | |
| algorithm = { name: 'ECDSA', namedCurve: 'P-521' }; | |
| keyUsages = isPublic ? ['verify'] : ['sign']; | |
| break; | |
| case 'ECDH-ES': | |
| case 'ECDH-ES+A128KW': | |
| case 'ECDH-ES+A192KW': | |
| case 'ECDH-ES+A256KW': { | |
| const namedCurve = getNamedCurve(keyData); | |
| algorithm = namedCurve.startsWith('P-') ? { name: 'ECDH', namedCurve } : { name: namedCurve }; | |
| keyUsages = isPublic ? [] : ['deriveBits']; | |
| break; | |
| } | |
| case 'EdDSA': | |
| algorithm = { name: getNamedCurve(keyData) }; | |
| keyUsages = isPublic ? ['verify'] : ['sign']; | |
| break; | |
| default: | |
| throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value'); | |
| } | |
| return crypto.subtle.importKey(keyFormat, keyData, algorithm, options?.extractable ?? false, keyUsages); | |
| }; | |
| export const fromPKCS8 = (pem, alg, options) => { | |
| return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, 'pkcs8', pem, alg, options); | |
| }; | |
| export const fromSPKI = (pem, alg, options) => { | |
| return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, 'spki', pem, alg, options); | |
| }; | |
| function getElement(seq) { | |
| const result = []; | |
| let next = 0; | |
| while (next < seq.length) { | |
| const nextPart = parseElement(seq.subarray(next)); | |
| result.push(nextPart); | |
| next += nextPart.byteLength; | |
| } | |
| return result; | |
| } | |
| function parseElement(bytes) { | |
| let position = 0; | |
| let tag = bytes[0] & 0x1f; | |
| position++; | |
| if (tag === 0x1f) { | |
| tag = 0; | |
| while (bytes[position] >= 0x80) { | |
| tag = tag * 128 + bytes[position] - 0x80; | |
| position++; | |
| } | |
| tag = tag * 128 + bytes[position] - 0x80; | |
| position++; | |
| } | |
| let length = 0; | |
| if (bytes[position] < 0x80) { | |
| length = bytes[position]; | |
| position++; | |
| } | |
| else if (length === 0x80) { | |
| length = 0; | |
| while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) { | |
| if (length > bytes.byteLength) { | |
| throw new TypeError('invalid indefinite form length'); | |
| } | |
| length++; | |
| } | |
| const byteLength = position + length + 2; | |
| return { | |
| byteLength, | |
| contents: bytes.subarray(position, position + length), | |
| raw: bytes.subarray(0, byteLength), | |
| }; | |
| } | |
| else { | |
| const numberOfDigits = bytes[position] & 0x7f; | |
| position++; | |
| length = 0; | |
| for (let i = 0; i < numberOfDigits; i++) { | |
| length = length * 256 + bytes[position]; | |
| position++; | |
| } | |
| } | |
| const byteLength = position + length; | |
| return { | |
| byteLength, | |
| contents: bytes.subarray(position, byteLength), | |
| raw: bytes.subarray(0, byteLength), | |
| }; | |
| } | |
| function spkiFromX509(buf) { | |
| const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents); | |
| return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw); | |
| } | |
| function getSPKI(x509) { | |
| const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, ''); | |
| const raw = decodeBase64(pem); | |
| return formatPEM(spkiFromX509(raw), 'PUBLIC KEY'); | |
| } | |
| export const fromX509 = (pem, alg, options) => { | |
| let spki; | |
| try { | |
| spki = getSPKI(pem); | |
| } | |
| catch (cause) { | |
| throw new TypeError('Failed to parse the X.509 certificate', { cause }); | |
| } | |
| return fromSPKI(spki, alg, options); | |
| }; | |
Xet Storage Details
- Size:
- 7.17 kB
- Xet hash:
- b2d7c41c0c08d40b81d02c803c7665b24d70e61f7a9e79e05a027c896f3d1b84
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.