import type { Branded } from '../framework/types/branded.js' import type { DeviceId } from './device.js' import type { EncryptionKeypair, SigningKeypair, SigningPublicKey, } from './keypair.js' import { decodeBase64, encodeBase64 } from '../framework/base64/mod.js' import { BaseError } from '../framework/error/mod.js' import { isError, makeError, makeSuccess, unwrapResult, } from '../framework/types/result.js' import { decodeJson, encodeJson } from '../framework/json/mod.js' export class DecodeDeviceRegistrationManifestError extends BaseError { public readonly _tag = 'DecodeDeviceRegistrationManifestError' } export class EncodeDeviceRegistrationManifestError extends BaseError { public readonly _tag = 'EncodeDeviceRegistrationManifestError' } export interface DecodedDeviceRegistrationManifest { readonly _tag: 'DeviceRegistrationManifest' readonly encryption: { readonly keypair: EncryptionKeypair } readonly signing: { readonly keypair: SigningKeypair } readonly registration: EncryptedDeviceRegistration } export type DeviceRegistrationManifest = Branded< string, 'DeviceRegistrationManifest' > export interface DeviceRegistration { readonly device: { readonly id: DeviceId } readonly license: { readonly jwt: string } readonly signing: { readonly publicKey: SigningPublicKey } } export type EncryptedDeviceRegistration = Branded< string, 'EncryptedDeviceRegistration' > const MANIFEST_DELIMITER = '.' export function encodeDeviceRegistrationManifest( manifest: DecodedDeviceRegistrationManifest, ) { const resultOfJsonEncodingEncryptionKeypair = encodeJson( manifest.encryption.keypair, ) if (isError(resultOfJsonEncodingEncryptionKeypair)) { return makeError( new EncodeDeviceRegistrationManifestError( `failed to encode the device registration manifest: ${resultOfJsonEncodingEncryptionKeypair.error}`, ), ) } const resultOfBase64EncodingEncryptionKeypair = encodeBase64( resultOfJsonEncodingEncryptionKeypair.value, ) if (isError(resultOfBase64EncodingEncryptionKeypair)) { return makeError( new EncodeDeviceRegistrationManifestError( `failed to encode the device registration manifest: ${resultOfBase64EncodingEncryptionKeypair.error}`, ), ) } const resultOfJsonEncodingSingingKeypair = encodeJson( manifest.signing.keypair, ) if (isError(resultOfJsonEncodingSingingKeypair)) { return makeError( new EncodeDeviceRegistrationManifestError( `failed to encode the device registration manifest: ${resultOfJsonEncodingSingingKeypair.error}`, ), ) } const resultOfBase64EncodingSigningKeypair = encodeBase64( resultOfJsonEncodingSingingKeypair.value, ) if (isError(resultOfBase64EncodingSigningKeypair)) { return makeError( new EncodeDeviceRegistrationManifestError( `failed to encode the device registration manifest: ${resultOfBase64EncodingSigningKeypair.error}`, ), ) } const resultOfBase64EncodingEncryptedDeviceRegistration = encodeBase64( manifest.registration, ) if (isError(resultOfBase64EncodingEncryptedDeviceRegistration)) { return makeError( new EncodeDeviceRegistrationManifestError( `failed to encode the device registration manifest: ${resultOfBase64EncodingEncryptedDeviceRegistration.error}`, ), ) } const encodedEncryptionKeypair = unwrapResult( resultOfBase64EncodingEncryptionKeypair, ) const encodedSigningKeypair = unwrapResult( resultOfBase64EncodingSigningKeypair, ) const encodedEncryptedDeviceRegistration = unwrapResult( resultOfBase64EncodingEncryptedDeviceRegistration, ) const drm = [ encodedEncryptionKeypair, encodedSigningKeypair, encodedEncryptedDeviceRegistration, ].join(MANIFEST_DELIMITER) as DeviceRegistrationManifest return makeSuccess(drm) } function decodeBase64AndDecodeJson(encoded: string) { const resultOfBase64Decoding = decodeBase64(encoded) if (isError(resultOfBase64Decoding)) { return makeError( new DecodeDeviceRegistrationManifestError( 'failed to decode the device registration manifest, because it is malformed.', ), ) } const jsonEncoded = unwrapResult(resultOfBase64Decoding) const resultOfJsonDecoding = decodeJson(jsonEncoded) if (isError(resultOfJsonDecoding)) { return makeError( new DecodeDeviceRegistrationManifestError( 'failed to decode the device registration manifest, because it is malformed.', ), ) } return resultOfJsonDecoding } export function decodeDeviceRegistrationManifest( encodedManifest: DeviceRegistrationManifest, ) { const manifestParts = encodedManifest.split(MANIFEST_DELIMITER) if (manifestParts.length !== 3) { return makeError( new DecodeDeviceRegistrationManifestError( 'failed to decode the device registration manifest, because it is malformed.', ), ) } const [ encodedEncryptionKeypair, encodedSigningKeypair, encodedEncryptedDeviceRegistration, ] = manifestParts if ( !encodedEncryptionKeypair || !encodedSigningKeypair || !encodedEncryptedDeviceRegistration ) { return makeError( new DecodeDeviceRegistrationManifestError( 'failed to decode the device registration manifest, because it is malformed.', ), ) } const resultEncryptionKeypair = decodeBase64AndDecodeJson( encodedEncryptionKeypair, ) const resultSigningKeypair = decodeBase64AndDecodeJson( encodedSigningKeypair, ) const resultEncryptedDeviceRegistration = decodeBase64( encodedEncryptedDeviceRegistration, ) if ( isError(resultEncryptionKeypair) || isError(resultSigningKeypair) || isError(resultEncryptedDeviceRegistration) ) { return makeError( new DecodeDeviceRegistrationManifestError( 'failed to decode the device registration manifest, because it is malformed.', ), ) } const encryptionKeypair = unwrapResult(resultEncryptionKeypair) const signingKeypair = unwrapResult(resultSigningKeypair) const encryptedDeviceRegistration = unwrapResult( resultEncryptedDeviceRegistration, ) const manifest: DecodedDeviceRegistrationManifest = { _tag: 'DeviceRegistrationManifest', encryption: { keypair: encryptionKeypair, }, signing: { keypair: signingKeypair, }, registration: encryptedDeviceRegistration as EncryptedDeviceRegistration, } return makeSuccess(manifest) }