blob: 2e5cd94dfdd42a9e06cd921e15651a7fc0a39e11 [file] [log] [blame]
// 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<RSAAsymmetricKey> 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!);
}
}
}