173 lines
4.7 KiB
Dart
173 lines
4.7 KiB
Dart
// See file LICENSE for more information.
|
|
|
|
library impl.asymmetric_block_cipher.pkcs1;
|
|
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:pointycastle/api.dart';
|
|
import 'package:pointycastle/random/fortuna_random.dart';
|
|
import 'package:pointycastle/src/impl/base_asymmetric_block_cipher.dart';
|
|
import 'package:pointycastle/src/platform_check/platform_check.dart';
|
|
import 'package:pointycastle/src/registry/registry.dart';
|
|
|
|
class PKCS1Encoding extends BaseAsymmetricBlockCipher {
|
|
/// Intended for internal use.
|
|
static final FactoryConfig factoryConfig = DynamicFactoryConfig.suffix(
|
|
AsymmetricBlockCipher,
|
|
'/PKCS1',
|
|
(_, final Match match) => () {
|
|
var underlyingCipher = AsymmetricBlockCipher(match.group(1)!);
|
|
return PKCS1Encoding(underlyingCipher);
|
|
});
|
|
|
|
static const _HEADER_LENGTH = 10;
|
|
|
|
final AsymmetricBlockCipher _engine;
|
|
|
|
late SecureRandom _random;
|
|
late bool _forEncryption;
|
|
late bool _forPrivateKey;
|
|
|
|
PKCS1Encoding(this._engine);
|
|
|
|
@override
|
|
String get algorithmName => '${_engine.algorithmName}/PKCS1';
|
|
|
|
@override
|
|
void reset() {}
|
|
|
|
Uint8List _seed() {
|
|
return Platform.instance.platformEntropySource().getBytes(32);
|
|
}
|
|
|
|
@override
|
|
void init(bool forEncryption, CipherParameters params) {
|
|
AsymmetricKeyParameter akparams;
|
|
|
|
if (params is ParametersWithRandom) {
|
|
var paramswr = params;
|
|
|
|
_random = paramswr.random;
|
|
akparams = paramswr.parameters as AsymmetricKeyParameter<AsymmetricKey>;
|
|
} else {
|
|
_random = FortunaRandom();
|
|
_random.seed(KeyParameter(_seed()));
|
|
akparams = params as AsymmetricKeyParameter<AsymmetricKey>;
|
|
}
|
|
|
|
_engine.init(forEncryption, akparams);
|
|
|
|
_forPrivateKey = akparams.key is PrivateKey;
|
|
_forEncryption = forEncryption;
|
|
}
|
|
|
|
@override
|
|
int get inputBlockSize {
|
|
var baseBlockSize = _engine.inputBlockSize;
|
|
|
|
if (_forEncryption) {
|
|
return baseBlockSize - _HEADER_LENGTH;
|
|
} else {
|
|
return baseBlockSize;
|
|
}
|
|
}
|
|
|
|
@override
|
|
int get outputBlockSize {
|
|
var baseBlockSize = _engine.outputBlockSize;
|
|
|
|
if (_forEncryption) {
|
|
return baseBlockSize;
|
|
} else {
|
|
return baseBlockSize - _HEADER_LENGTH;
|
|
}
|
|
}
|
|
|
|
@override
|
|
int processBlock(
|
|
Uint8List inp, int inpOff, int len, Uint8List out, int outOff) {
|
|
if (_forEncryption) {
|
|
return _encodeBlock(inp, inpOff, len, out, outOff);
|
|
} else {
|
|
return _decodeBlock(inp, inpOff, len, out, outOff);
|
|
}
|
|
}
|
|
|
|
int _encodeBlock(
|
|
Uint8List inp, int inpOff, int inpLen, Uint8List out, int outOff) {
|
|
if (inpLen > inputBlockSize) {
|
|
throw ArgumentError('Input data too large');
|
|
}
|
|
|
|
var block = Uint8List(_engine.inputBlockSize);
|
|
var padLength = block.length - inpLen - 1;
|
|
|
|
if (_forPrivateKey) {
|
|
block[0] = 0x01; // type code 1
|
|
block.fillRange(1, padLength, 0xFF);
|
|
} else {
|
|
block[0] = 0x02; // type code 2
|
|
block.setRange(1, padLength, _random.nextBytes(padLength - 1));
|
|
|
|
// a zero byte marks the end of the padding, so all
|
|
// the pad bytes must be non-zero.
|
|
for (var i = 1; i < padLength; i++) {
|
|
while (block[i] == 0) {
|
|
block[i] = _random.nextUint8();
|
|
}
|
|
}
|
|
}
|
|
|
|
block[padLength] = 0x00; // mark the end of the padding
|
|
block.setRange(padLength + 1, block.length, inp.sublist(inpOff));
|
|
|
|
return _engine.processBlock(block, 0, block.length, out, outOff);
|
|
}
|
|
|
|
int _decodeBlock(
|
|
Uint8List inp, int inpOff, int inpLen, Uint8List out, int outOff) {
|
|
var block = Uint8List(_engine.inputBlockSize);
|
|
var len = _engine.processBlock(inp, inpOff, inpLen, block, 0);
|
|
block = block.sublist(0, len);
|
|
|
|
if (block.length < outputBlockSize) {
|
|
throw ArgumentError('Block truncated');
|
|
}
|
|
|
|
var type = block[0];
|
|
|
|
if (_forPrivateKey && (type != 2)) {
|
|
throw ArgumentError('Unsupported block type for private key: $type');
|
|
}
|
|
if (!_forPrivateKey && (type != 1)) {
|
|
throw ArgumentError('Unsupported block type for public key: $type');
|
|
}
|
|
if (block.length != _engine.outputBlockSize) {
|
|
throw ArgumentError('Block size is incorrect: ${block.length}');
|
|
}
|
|
|
|
// find and extract the message block.
|
|
int start;
|
|
|
|
for (start = 1; start < block.length; start++) {
|
|
var pad = block[start];
|
|
|
|
if (pad == 0) {
|
|
break;
|
|
}
|
|
if (type == 1 && (pad != 0xFF)) {
|
|
throw ArgumentError('Incorrect block padding');
|
|
}
|
|
}
|
|
|
|
start++; // data should start at the next byte
|
|
|
|
if ((start > block.length) || (start < _HEADER_LENGTH)) {
|
|
throw ArgumentError('No data found in block, only padding');
|
|
}
|
|
|
|
var rlen = block.length - start;
|
|
out.setRange(outOff, outOff + rlen, block.sublist(start));
|
|
return rlen;
|
|
}
|
|
}
|