constructor()

in modules/raw-rsa-keyring-browser/src/raw_rsa_keyring_web_crypto.ts [49:170]


  constructor(input: RawRsaKeyringWebCryptoInput) {
    super()

    const { publicKey, privateKey, keyName, keyNamespace } = input
    /* Precondition: RsaKeyringWebCrypto needs either a public or a private key to operate. */
    needs(publicKey || privateKey, 'No Key provided.')
    /* Precondition: RsaKeyringWebCrypto needs identifying information for encrypt and decrypt. */
    needs(keyName && keyNamespace, 'Identifying information must be defined.')

    const wrappingAlgorithm = getWrappingAlgorithm(publicKey, privateKey)

    const _wrapKey = async (material: WebCryptoEncryptionMaterial) => {
      /* Precondition: I must have a publicKey to wrap. */
      if (!publicKey)
        throw new Error('No publicKey configured, encrypt not supported.')

      // The nonZero backend is used because some browsers support Subtle Crypto
      // but do not support Zero Byte AES-GCM. I want to use the native
      // browser implementation of wrapKey
      const subtle = getNonZeroByteBackend(await getWebCryptoBackend())
      /* Can not use importCryptoKey as `wrapKey` requires extractable = true
       * In web crypto `wrapKey` is a composition of `export` and `encrypt` and
       * so the cryptoKey must have `extractable = true`.
       */
      const extractable = true
      const { encryption } = material.suite
      const importFormat = 'jwk'
      const keyUsages: KeyUsage[] = ['wrapKey'] // limit the use of this key (*not* decrypt, encrypt, deriveKey)
      const jwk = bytes2JWK(unwrapDataKey(material.getUnencryptedDataKey()))
      const cryptoKey = await subtle.importKey(
        importFormat,
        jwk,
        encryption,
        extractable,
        keyUsages
      )

      const wrapFormat = 'raw'
      const encryptedArrayBuffer = await subtle.wrapKey(
        wrapFormat,
        cryptoKey,
        publicKey,
        wrappingAlgorithm
      )

      // Can the extractable setting of cryptoKey be changed to false here?  If so, do it.
      const edk = new EncryptedDataKey({
        providerId: keyNamespace,
        providerInfo: keyName,
        encryptedDataKey: new Uint8Array(encryptedArrayBuffer),
      })

      return material.addEncryptedDataKey(
        edk,
        KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY
      )
    }

    /* returns either an array of 1 CryptoKey or an array of both from MixedBackendCryptoKey e.g.
     * [privateKey] || [nonZeroByteCryptoKey, zeroByteCryptoKey]
     */
    const privateKeys = flattenMixedCryptoKey(privateKey)

    const _unwrapKey = async (
      material: WebCryptoDecryptionMaterial,
      edk: EncryptedDataKey
    ) => {
      /* Precondition: I must have a privateKey to unwrap. */
      if (!privateKey)
        throw new Error('No privateKey configured, decrypt not supported.')
      const backend = await getWebCryptoBackend()
      const { suite } = material

      const trace: KeyringTrace = {
        keyName: this.keyName,
        keyNamespace: this.keyNamespace,
        flags: KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY,
      }

      const format = 'raw'
      const extractable = false
      const algorithm = suite.kdf ? suite.kdf : suite.encryption
      const keyUsages = [keyUsageForMaterial(material)]

      const importArgs: Parameters<SubtleCrypto['unwrapKey']> = [
        format,
        edk.encryptedDataKey,
        privateKeys[0],
        wrappingAlgorithm,
        algorithm,
        extractable,
        keyUsages,
      ]

      /* This is superior to importForWebCryptoDecryptionMaterial.
       * Here I use `subtle.unwrap` and bring the unencrypted data key into the WebCrypto world
       * without ever exposing the unencrypted data key to JavaScript.
       */
      if (isFullSupportWebCryptoBackend(backend)) {
        const cryptoKey = await backend.subtle.unwrapKey(...importArgs)
        return material.setCryptoKey(cryptoKey, trace)
      } else {
        const importZeroBackend = [...importArgs] as Parameters<
          SubtleCrypto['unwrapKey']
        >
        importZeroBackend[2] = privateKeys[1]
        const mixedDataKey: MixedBackendCryptoKey = await Promise.all([
          backend.nonZeroByteSubtle.unwrapKey(...importArgs),
          backend.zeroByteSubtle.unwrapKey(...importZeroBackend),
        ]).then(([nonZeroByteCryptoKey, zeroByteCryptoKey]) => ({
          nonZeroByteCryptoKey,
          zeroByteCryptoKey,
        }))
        return material.setCryptoKey(mixedDataKey, trace)
      }
    }

    readOnlyProperty(this, 'keyName', keyName)
    readOnlyProperty(this, 'keyNamespace', keyNamespace)
    readOnlyProperty(this, '_wrapKey', _wrapKey)
    readOnlyProperty(this, '_unwrapKey', _unwrapKey)
  }