SecretWrap : Simplify serialization

This commit is contained in:
2024-09-18 19:45:53 +02:00
parent 53dccfc4cc
commit 17c0b1b414
+55 -48
View File
@@ -1,16 +1,21 @@
import logger from 'log'
import {a2b64, b642a} from 'misc'
import {BIT_LENGTH, ENCRYPTION_ALGORITHM} from './const'
import {BIT_LENGTH, ECDH_PARAMETERS, ENCRYPTION_ALGORITHM, SIGNATURE_KEY} from './const'
const log = logger('crypto:secret-wrap')
enum Type {
SecretBox, // secret, decrypt, encrypt
SecretWrap, // secret, wrap, unwrap
Private, // private, deriveBits
signature // private, sign
}
export default class SecretWrap {
constructor(
private readonly cipher: Uint8Array,
private readonly algorithm: KeyAlgorithm,
private readonly usages: KeyUsage[],
private readonly type: 'raw' | 'pkcs8',
private readonly iv: Uint8Array
private readonly iv: Uint8Array,
private readonly type: Type
) {}
public static async gen(extractable: boolean = true): Promise<CryptoKey> {
@@ -25,25 +30,30 @@ export default class SecretWrap {
)
}
private static format(type: KeyType): 'raw' | 'pkcs8' | null {
switch (type) {
case 'private':
return 'pkcs8'
case 'secret':
return 'raw'
default:
log.warn('Trying to wrap public key')
return null
private static identify(data: CryptoKey): Type {
if (data.type === 'secret') {
if (data.usages.includes('wrapKey')) return Type.SecretWrap
else return Type.SecretBox
} else {
if (data.usages.includes('sign')) return Type.signature
else return Type.Private
}
}
private information(): {format: KeyFormat, algorithm: AesKeyAlgorithm | EcKeyImportParams, usages: KeyUsage[]} {
switch (this.type) {
case Type.SecretWrap: return {algorithm: {name: ENCRYPTION_ALGORITHM, length: BIT_LENGTH()}, format: 'raw', usages: ['wrapKey', 'unwrapKey']}
case Type.SecretBox: return {algorithm: {name: ENCRYPTION_ALGORITHM, length: BIT_LENGTH()}, format: 'raw', usages: ['encrypt', 'decrypt']}
case Type.signature: return {algorithm: SIGNATURE_KEY(), format: 'pkcs8', usages: ['sign']}
case Type.Private: return {algorithm: ECDH_PARAMETERS(), format: 'pkcs8', usages: ['deriveBits']}
}
}
public static async wrap(data: CryptoKey, key: CryptoKey): Promise<SecretWrap | null> {
log.trace('wrap')
const format = SecretWrap.format(data.type)
if (format === null) return null
const iv = crypto.getRandomValues(new Uint8Array(12))
const type = this.identify(data)
const format = (type === Type.SecretBox || type === Type.SecretWrap) ? 'raw' : 'pkcs8'
let box: ArrayBuffer
try {
@@ -54,19 +64,22 @@ export default class SecretWrap {
return null
}
return new SecretWrap(new Uint8Array(box), data.algorithm, data.usages, format, iv)
return new SecretWrap(new Uint8Array(box), iv, type)
}
public async unwrap(key: CryptoKey): Promise<CryptoKey | null> {
log.trace('unwrap')
const info = this.information()
try {
return await crypto.subtle.unwrapKey(
this.type,
info.format,
this.cipher,
key,
{name: ENCRYPTION_ALGORITHM, iv: this.iv},
this.algorithm,
info.algorithm,
true,
this.usages
info.usages
)
} catch (e) {
log.warn('Failed to unwrap')
@@ -77,45 +90,39 @@ export default class SecretWrap {
public toString(): string {
log.trace('toString')
const cipher = a2b64(this.cipher)
const iv = a2b64(this.iv)
return JSON.stringify({
cipher: a2b64(this.cipher),
algorithm: this.algorithm,
usages: this.usages,
type: this.type,
iv
})
return `${this.type}.${iv}.${cipher}`
}
public static fromString(data: string): SecretWrap | null {
log.trace('fromString')
const obj = SecretWrap.parseJSON(data)
if (obj === null) return null
const parts = data.split('.')
if (parts.length !== 3) {
log.warn(`Part count doesn't match : ${parts.length} != 3`)
return null
}
let type: Type
try {
const cipher = b642a(obj.cipher)
if (cipher === null) return null
const iv = b642a(obj.iv)
if (iv === null) return null
return new SecretWrap(cipher, obj.algorithm, obj.usages, obj.type, iv)
type = parseInt(parts[0])
} catch (e) {
log.warn('Probable access to undefined field')
log.warn('Failed to parse type')
log.debug('Type :', parts[0])
log.debug('Error :', e)
return null
}
}
private static parseJSON(
data: string
): {cipher: string; iv: string; algorithm: KeyAlgorithm; usages: KeyUsage[]; type: 'raw' | 'pkcs8'} | null {
log.trace('parseJSON')
try {
return JSON.parse(data)
} catch (e) {
log.warn('Failed to parse JSON')
if (!(type in Type)) {
log.warn('Unknown type', type)
return null
}
const iv = b642a(parts[1])
if (iv === null) return null
const cipher = b642a(parts[2])
if (cipher === null) return null
return new this(cipher, iv, type)
}
}