unwrapConfigLink static method

String unwrapConfigLink({
  1. required String sbmmLink,
  2. required String passphrase,
})

Implementation

static String unwrapConfigLink({
  required String sbmmLink,
  required String passphrase,
}) {
  final String normalized = sbmmLink.trim();
  if (!isSbmmLink(normalized)) {
    throw const FormatException('Input is not an sbmm:// link.');
  }
  final String normalizedPassphrase = passphrase.trim();
  if (normalizedPassphrase.isEmpty) {
    throw const FormatException('sbmm passphrase cannot be empty.');
  }

  final String token = _extractEnvelopeToken(normalized);
  final Uint8List envelopeBytes = _decodeBase64UrlNoPadding(token);
  final Object? envelopeRaw = _tryJsonDecode(utf8.decode(envelopeBytes));
  if (envelopeRaw is! Map<Object?, Object?>) {
    throw const FormatException('Invalid sbmm envelope JSON.');
  }
  final Map<String, Object?> envelope = <String, Object?>{};
  envelopeRaw.forEach((Object? key, Object? value) {
    if (key != null) {
      envelope[key.toString()] = value;
    }
  });

  final int version = _readInt(envelope['v']);
  if (version != currentVersion) {
    throw FormatException('Unsupported sbmm envelope version: $version.');
  }
  final int iterations = _readInt(envelope['iter']);
  if (iterations < 100000) {
    throw const FormatException('Invalid sbmm PBKDF2 iteration count.');
  }

  final Uint8List salt = _decodeBase64UrlNoPadding(
    _readString(envelope['salt'], field: 'salt'),
  );
  final Uint8List nonce = _decodeBase64UrlNoPadding(
    _readString(envelope['nonce'], field: 'nonce'),
  );
  final Uint8List cipherText = _decodeBase64UrlNoPadding(
    _readString(envelope['ct'], field: 'ct'),
  );

  if (salt.length != _saltLengthBytes) {
    throw const FormatException('Invalid sbmm salt length.');
  }
  if (nonce.length != _nonceLengthBytes) {
    throw const FormatException('Invalid sbmm nonce length.');
  }

  final Uint8List key = _deriveKey(
    passphrase: normalizedPassphrase,
    salt: salt,
    iterations: iterations,
  );

  final Uint8List plainBytes = _decryptAesGcm(
    cipherText: cipherText,
    key: key,
    nonce: nonce,
    aad: Uint8List.fromList(utf8.encode(_aad)),
  );

  final String plain = utf8.decode(plainBytes).trim();
  if (plain.isEmpty) {
    throw const FormatException('sbmm payload decrypted to empty config.');
  }
  return plain;
}