unseal static method

Future<K4LocalKey> unseal(
  1. String serialized,
  2. K4SecretKey secretKey
)

Implementation

static Future<K4LocalKey> unseal(
  String serialized,
  K4SecretKey secretKey,
) async {
  if (!serialized.startsWith(PaserkKey.k4SealPrefix)) {
    throw ArgumentError('Invalid k4.seal format');
  }

  final data = Uint8List.fromList(
    SafeBase64.decode(serialized.substring(PaserkKey.k4SealPrefix.length)),
  );

  if (data.length !=
      tagLength + ephemeralPublicKeyLength + K4LocalKey.keyLength) {
    throw ArgumentError('Invalid sealed key length');
  }

  final tag = data.sublist(0, tagLength);
  final epk = data.sublist(tagLength, tagLength + ephemeralPublicKeyLength);
  final encrypted = data.sublist(tagLength + ephemeralPublicKeyLength);

  final xsk = await _ed25519SecretToX25519(secretKey.rawBytes);
  final xpk = _ed25519PublicToX25519(secretKey.publicKeyBytes);
  final headerBytes = utf8.encode(PaserkKey.k4SealPrefix);

  final x25519 = X25519();
  final x25519KeyPair = SimpleKeyPairData(
    xsk,
    publicKey: SimplePublicKey(xpk, type: KeyPairType.x25519),
    type: KeyPairType.x25519,
  );

  final sharedSecret = await x25519.sharedSecretKey(
    keyPair: x25519KeyPair,
    remotePublicKey: SimplePublicKey(epk, type: KeyPairType.x25519),
  );
  final xk = Uint8List.fromList(await sharedSecret.extractBytes());

  final authKey = _derivePkeKey(0x02, headerBytes, xk, epk, xpk);
  final expectedTag = _computeSealTag(headerBytes, epk, encrypted, authKey);

  if (!_constantTimeEquals(tag, expectedTag)) {
    throw ArgumentError('Invalid authentication tag');
  }

  final encKey = _derivePkeKey(0x01, headerBytes, xk, epk, xpk);
  final nonce = _deriveNonce(epk, xpk);
  final decrypted = _decrypt(encKey, nonce, encrypted);

  if (decrypted.length != K4LocalKey.keyLength) {
    throw ArgumentError('Decrypted key has invalid length');
  }

  return K4LocalKey(decrypted);
}