build method

  1. @override
JsonWebEncryption build()
override

Implementation

@override
JsonWebEncryption build() {
  if (encryptionAlgorithm == null) {
    throw StateError('No encryption algorithm set');
  }
  if (encryptionAlgorithm == 'none') {
    throw StateError('Encryption algorithm cannot be `none`');
  }
  if (recipients.isEmpty) {
    throw StateError('Need at least one recipient');
  }
  var payload = this.payload;
  if (payload == null) {
    throw StateError('No payload set');
  }

  var compact = recipients.length == 1 && additionalAuthenticatedData == null;

  var cek = JsonWebKey.generate(encryptionAlgorithm);
  var sharedUnprotectedHeaderParams = <String, dynamic>{
    'enc': encryptionAlgorithm
  };

  var recipientsMapped = recipients.map((r) {
    var key = r['_jwk'] as JsonWebKey;
    var algorithm = r['alg'] ?? key.algorithmForOperation('wrapKey') ?? 'dir';
    if (algorithm == 'dir') {
      if (recipients.length > 1) {
        throw StateError(
            'JWE can only have one recipient when using direct encryption with a shared symmetric key.');
      }
      final k = JsonWebKey.fromJson({
        'alg': encryptionAlgorithm,
        ...key.toJson(),
      });
      if (k == null) {
        throw UnimplementedError('Unkown key.');
      }
      cek = k;
    }
    var encryptedKey = algorithm == 'dir'
        ? const <int>[]
        : key.wrapKey(
            cek,
            algorithm: algorithm,
          );

    var unprotectedHeaderParams = <String, dynamic>{'alg': algorithm};
    if (key.keyId != null) {
      unprotectedHeaderParams['kid'] = key.keyId;
    }
    if (compact) {
      sharedUnprotectedHeaderParams.addAll(unprotectedHeaderParams);
    }

    return _JweRecipient._(
        encryptedKey: encryptedKey,
        header: compact ? null : JsonObject.from(unprotectedHeaderParams));
  }).toList();

  var protectedHeader = payload.protectedHeader;
  if (compact) {
    protectedHeader = JsonObject.from(safeUnion(
        [protectedHeader?.toJson(), sharedUnprotectedHeaderParams]));
  }
  var aad = protectedHeader!.toBase64EncodedString();
  if (additionalAuthenticatedData != null) {
    aad += '.${String.fromCharCodes(additionalAuthenticatedData!)}';
  }

  // RFC 7518 requires that a 96 bit iv is used with AESGCM and cjose insists
  var iv = (encryptionAlgorithm != null &&
          encryptionAlgorithm!.contains(RegExp('A[0-9][0-9][0-9]GCM')))
      ? Uint8List.fromList(
          List.generate(12, (_) => Random.secure().nextInt(256)))
      : null;

  var encryptedData = cek.encrypt(data!,
      initializationVector: iv,
      additionalAuthenticatedData: Uint8List.fromList(aad.codeUnits));
  return JsonWebEncryption._(encryptedData.data, recipientsMapped,
      protectedHeader: protectedHeader,
      unprotectedHeader:
          compact ? null : JsonObject.from(sharedUnprotectedHeaderParams),
      initializationVector: encryptedData.initializationVector!,
      authenticationTag: encryptedData.authenticationTag!,
      additionalAuthenticatedData: additionalAuthenticatedData);
}