import { isMsWindow } from '@aws-crypto/ie11-detection' ; import { supportsWebCrypto , supportsSubtleCrypto , supportsZeroByteGCM , } from '@aws-crypto/supports-web-crypto' ; import { generateSynchronousRandomValues } from './synchronous_random_values' ; import promisifyMsSubtleCrypto from './promisify-ms-crypto' ; type MaybeSubtleCrypto = SubtleCrypto | false ; export type WebCryptoBackend = FullSupportWebCryptoBackend | MixedSupportWebCryptoBackend ; export type FullSupportWebCryptoBackend = { subtle : SubtleCrypto ; randomValues : ( byteLength : number ) => Uint8Array ; } ; export type MixedSupportWebCryptoBackend = { zeroByteSubtle : SubtleCrypto ; nonZeroByteSubtle : SubtleCrypto ; randomValues : ( byteLength : number ) => Uint8Array ; } ; export function webCryptoBackendFactory ( window ) { const fallbackRequiredPromise = windowRequiresFallback ( window ) ; const randomValues = generateSynchronousRandomValues ( window ) ; let webCryptoFallbackPromise = false ; return { getWebCryptoBackend , configureFallback } ; async function getWebCryptoBackend ( ) { try { randomValues ( 1 ) ; } catch ( ex ) { throw new Error ( 'No supported secure random' ) ; } const fallbackRequired = await fallbackRequiredPromise ; const subtle = pluckSubtleCrypto ( window ) ; const webCryptoFallback = await webCryptoFallbackPromise ; if ( subtle && fallbackRequired && ! webCryptoFallback ) { throw new Error ( 'A Fallback is required for zero byte AES-GCM operations.' ) ; } if ( ! subtle && ! webCryptoFallback ) { throw new Error ( 'A Fallback is required because no subtle backend exists.' ) ; } if ( ! fallbackRequired && subtle ) { return { subtle , randomValues } ; } if ( fallbackRequired && subtle && webCryptoFallback ) { return { nonZeroByteSubtle : subtle , randomValues , zeroByteSubtle : webCryptoFallback , } ; } if ( fallbackRequired && ! subtle && webCryptoFallback ) { return { subtle : webCryptoFallback , randomValues } ; } throw new Error ( 'unknown error' ) ; } async function configureFallback ( fallback ) { const fallbackRequired = await fallbackRequiredPromise ; if ( ! fallbackRequired ) { return ; } if ( webCryptoFallbackPromise ) throw new Error ( 'Fallback reconfiguration denied' ) ; if ( ! supportsSubtleCrypto ( fallback ) ) throw new Error ( 'Fallback does not support WebCrypto' ) ; webCryptoFallbackPromise = supportsZeroByteGCM ( fallback ) . then ( ( zeroByteGCMSupport ) => { if ( ! zeroByteGCMSupport ) throw new Error ( 'Fallback does not support zero byte AES-GCM' ) ; return fallback ; } ) ; return webCryptoFallbackPromise ; } } export function getNonZeroByteBackend ( backend ) { if ( ! backend ) throw new Error ( 'No supported backend.' ) ; return ( ( backend as FullSupportWebCryptoBackend ) . subtle || ( backend as MixedSupportWebCryptoBackend ) . nonZeroByteSubtle ) ; } export function getZeroByteSubtle ( backend ) { if ( ! backend ) throw new Error ( 'No supported backend.' ) ; return ( ( backend as FullSupportWebCryptoBackend ) . subtle || ( backend as MixedSupportWebCryptoBackend ) . zeroByteSubtle ) ; } export async function windowRequiresFallback ( window ) { const subtle = pluckSubtleCrypto ( window ) ; if ( ! subtle ) return true ; const zeroByteSupport = await supportsZeroByteGCM ( subtle ) ; return ! zeroByteSupport ; } export function pluckSubtleCrypto ( window ) { if ( supportsWebCrypto ( window ) ) return window . crypto . subtle ; if ( isMsWindow ( window ) ) return promisifyMsSubtleCrypto ( window . msCrypto . subtle ) ; return false ; } export function isFullSupportWebCryptoBackend ( backend ) { return ! ! ( backend as FullSupportWebCryptoBackend ) . subtle ; }