163 lines
4.5 KiB
Dart
163 lines
4.5 KiB
Dart
// 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!);
|
|
}
|
|
}
|
|
}
|