99 lines
2.5 KiB
TypeScript
99 lines
2.5 KiB
TypeScript
import logger from 'log'
|
|
import {a2b64, b642a} from 'misc'
|
|
|
|
const log = logger('crypto:secret-wrap')
|
|
|
|
export class SecretWrap {
|
|
constructor(
|
|
private readonly cipher: Uint8Array,
|
|
private readonly algorithm: KeyAlgorithm,
|
|
private readonly usages: KeyUsage[],
|
|
private readonly type: 'raw' | 'pkcs8',
|
|
private readonly iv: Uint8Array
|
|
) {}
|
|
|
|
public static gen_key(extractable: boolean = true): Promise<CryptoKey> {
|
|
log.trace('generate key')
|
|
return crypto.subtle.generateKey(
|
|
{
|
|
name: 'AES-GCM',
|
|
length: 256
|
|
},
|
|
extractable,
|
|
['wrapKey', 'unwrapKey']
|
|
)
|
|
}
|
|
|
|
private static format(type: KeyType): 'raw' | 'pkcs8' {
|
|
switch (type) {
|
|
case 'private':
|
|
return 'pkcs8'
|
|
case 'secret':
|
|
return 'raw'
|
|
default:
|
|
throw "Don't wrap public keys please..."
|
|
}
|
|
}
|
|
|
|
public static async encrypt(data: CryptoKey, key: CryptoKey): Promise<SecretWrap> {
|
|
log.trace('encrypt')
|
|
const format = this.format(data.type)
|
|
const iv = crypto.getRandomValues(new Uint8Array(12))
|
|
const box = await crypto.subtle.wrapKey(format, data, key, {
|
|
name: 'AES-GCM',
|
|
iv
|
|
})
|
|
return new SecretWrap(new Uint8Array(box), data.algorithm, data.usages, format, iv)
|
|
}
|
|
public async decrypt(key: CryptoKey): Promise<CryptoKey> {
|
|
log.trace('decrypt')
|
|
return crypto.subtle.unwrapKey(
|
|
this.type,
|
|
this.cipher,
|
|
key,
|
|
{
|
|
name: 'AES-GCM',
|
|
iv: this.iv
|
|
},
|
|
this.algorithm,
|
|
true,
|
|
this.usages
|
|
)
|
|
}
|
|
|
|
public toString(): string {
|
|
log.trace('toString')
|
|
return JSON.stringify({
|
|
cipher: a2b64(this.cipher),
|
|
iv: a2b64(this.iv),
|
|
algorithm: this.algorithm,
|
|
usages: this.usages,
|
|
type: this.type
|
|
})
|
|
}
|
|
public static fromString(data: string): SecretWrap | null {
|
|
log.trace('fromString')
|
|
|
|
const obj = this.parseJSON(data)
|
|
if (obj === null) return null
|
|
|
|
const cipher = b642a(obj.cipher)
|
|
if (cipher.is_err()) return null
|
|
const iv = b642a(obj.iv)
|
|
if (iv.is_err()) return null
|
|
|
|
return new SecretWrap(cipher.unwrap(), obj.algorithm, obj.usages, obj.type, iv.unwrap())
|
|
}
|
|
|
|
private static parseJSON(data: string): {cipher: string, iv: string, algorithm: KeyAlgorithm, usages: KeyUsage[], type: 'raw' | 'pkcs8'} | null {
|
|
log.trace('parseJSON')
|
|
try {
|
|
// TODO : Check the fields
|
|
return JSON.parse(data)
|
|
} catch (e) {
|
|
log.warn('Failed to parse JSON')
|
|
return null
|
|
}
|
|
}
|
|
}
|