// See file LICENSE for more information. library impl.asymmetric_block_cipher.rsa; import 'dart:typed_data'; import 'package:pointycastle/api.dart'; import 'package:pointycastle/asymmetric/api.dart'; import 'package:pointycastle/src/impl/base_asymmetric_block_cipher.dart'; import 'package:pointycastle/src/registry/registry.dart'; import 'package:pointycastle/src/utils.dart' as utils; class RSAEngine extends BaseAsymmetricBlockCipher { static final FactoryConfig factoryConfig = StaticFactoryConfig(AsymmetricBlockCipher, 'RSA', () => RSAEngine()); late bool _forEncryption; RSAAsymmetricKey? _key; late BigInt _dP; late BigInt _dQ; late BigInt _qInv; @override String get algorithmName => 'RSA'; @override int get inputBlockSize { if (_key == null) { throw StateError( 'Input block size cannot be calculated until init() called'); } var bitSize = _key!.modulus!.bitLength; if (_forEncryption) { return ((bitSize + 7) ~/ 8) - 1; } else { return (bitSize + 7) ~/ 8; } } @override int get outputBlockSize { if (_key == null) { throw StateError( 'Output block size cannot be calculated until init() called'); } var bitSize = _key!.modulus!.bitLength; if (_forEncryption) { return (bitSize + 7) ~/ 8; } else { return ((bitSize + 7) ~/ 8) - 1; } } @override void reset() {} @override void init(bool forEncryption, covariant AsymmetricKeyParameter params) { _forEncryption = forEncryption; _key = params.key; if (_key is RSAPrivateKey) { var privKey = _key as RSAPrivateKey; var pSub1 = privKey.p! - BigInt.one; var qSub1 = privKey.q! - BigInt.one; _dP = privKey.privateExponent!.remainder(pSub1); _dQ = privKey.privateExponent!.remainder(qSub1); _qInv = privKey.q!.modInverse(privKey.p!); } } @override Uint8List process(Uint8List data) { // Expand the output block size by an extra byte to handle cases where // the output is larger than expected. var out = Uint8List(outputBlockSize + 1); var len = processBlock(data, 0, data.length, out, 0); return out.sublist(0, len); } @override int processBlock( Uint8List inp, int inpOff, int len, Uint8List out, int outOff) { var input = _convertInput(inp, inpOff, len); var output = _processBigInteger(input); return _convertOutput(output, out, outOff); } BigInt _convertInput(Uint8List inp, int inpOff, int len) { var inpLen = inp.length; if (inpLen < inpOff + len) { throw ArgumentError.value(inpOff, 'inpOff', 'Not enough data for RSA cipher (length=$len, available=$inpLen)'); } if (inputBlockSize + 1 < len) { throw ArgumentError.value(len, 'len', 'Too large for maximum RSA cipher input block size ($inputBlockSize)'); } var res = utils.decodeBigIntWithSign(1, inp.sublist(inpOff, inpOff + len)); if (res >= _key!.modulus!) { throw ArgumentError('Input block too large for RSA key size'); } return res; } int _convertOutput(BigInt result, Uint8List out, int outOff) { final output = utils.encodeBigInt(result); if (_forEncryption) { if ((output[0] == 0) && (output.length > outputBlockSize)) { // have ended up with an extra zero byte, copy down. var len = output.length - 1; out.setRange(outOff, outOff + len, output.sublist(1)); return len; } if (output.length < outputBlockSize) { // have ended up with less bytes than normal, lengthen var len = outputBlockSize; out.setRange(outOff + len - output.length, outOff + len, output); return len; } } else { if (output[0] == 0) { // have ended up with an extra zero byte, copy down. var len = output.length - 1; out.setRange(outOff, outOff + len, output.sublist(1)); return len; } } out.setAll(outOff, output); return output.length; } BigInt _processBigInteger(BigInt input) { if (_key is RSAPrivateKey) { var privKey = _key as RSAPrivateKey; BigInt mP, mQ, h, m; mP = input.remainder(privKey.p!).modPow(_dP, privKey.p!); mQ = input.remainder(privKey.q!).modPow(_dQ, privKey.q!); h = mP - mQ; h = h * _qInv; h = h % privKey.p!; m = h * privKey.q!; m = m + mQ; return m; } else { return input.modPow(_key!.exponent!, _key!.modulus!); } } }