| // See file LICENSE for more information. |
| |
| library impl.signer.pss_signer; |
| |
| import 'dart:typed_data'; |
| |
| import 'package:pointycastle/api.dart'; |
| import 'package:pointycastle/asymmetric/api.dart'; |
| import 'package:pointycastle/asymmetric/rsa.dart'; |
| import 'package:pointycastle/src/registry/registry.dart'; |
| |
| import '../src/utils.dart'; |
| |
| class PSSSigner implements Signer { |
| /// Intended for internal use. |
| static final FactoryConfig factoryConfig = |
| DynamicFactoryConfig.suffix(Signer, '/PSS', (_, Match match) { |
| final digestName = match.group(1); |
| return () => PSSSigner( |
| RSAEngine(), |
| Digest(digestName!), |
| Digest(digestName), |
| ); |
| }); |
| |
| static const int TRAILER_IMPLICIT = 0xBC; |
| |
| final Digest _contentDigest; |
| final Digest _mgfDigest; |
| final AsymmetricBlockCipher _cipher; |
| final int _hLen; |
| final int _mgfhLen; |
| final int _trailer; |
| |
| late bool _sSet; |
| late int _sLen; |
| late Uint8List _salt; |
| late SecureRandom _random; |
| |
| late int _emBits; |
| late Uint8List _block; |
| late Uint8List _mDash; |
| |
| late bool _forSigning; |
| |
| PSSSigner(this._cipher, this._contentDigest, this._mgfDigest, |
| {int trailer = TRAILER_IMPLICIT}) |
| : _hLen = _contentDigest.digestSize, |
| _mgfhLen = _mgfDigest.digestSize, |
| _trailer = trailer; |
| |
| @override |
| String get algorithmName => '${_mgfDigest.algorithmName}/PSS'; |
| |
| @override |
| void init(bool forSigning, CipherParameters params) { |
| _forSigning = forSigning; |
| |
| AsymmetricKeyParameter akparams; |
| if (params is ParametersWithSaltConfiguration) { |
| akparams = params.parameters as AsymmetricKeyParameter<AsymmetricKey>; |
| _random = params.random; |
| _sSet = false; |
| _sLen = params.saltLength; |
| _salt = Uint8List(_sLen); |
| } else if (params is ParametersWithSalt) { |
| akparams = params.parameters as AsymmetricKeyParameter<AsymmetricKey>; |
| _sSet = true; |
| _salt = params.salt; |
| _sLen = _salt.length; |
| } else { |
| throw ArgumentError( |
| 'Unsupported parameters type ${params.runtimeType}: should be ParametersWithSaltConfiguration or ParametersWithSalt'); |
| } |
| |
| var k = akparams.key as RSAAsymmetricKey; |
| |
| if (forSigning && (k is! RSAPrivateKey)) { |
| throw ArgumentError('Signing requires private key'); |
| } |
| |
| if (!forSigning && (k is! RSAPublicKey)) { |
| throw ArgumentError('Verification requires public key'); |
| } |
| |
| _emBits = k.modulus!.bitLength - 1; |
| |
| if (_emBits < (8 * _hLen + 8 * _sLen + 9)) { |
| throw ArgumentError('Key too small for specified hash and salt lengths'); |
| } |
| |
| _mDash = Uint8List(8 + _sLen + _contentDigest.digestSize); |
| |
| _cipher.init(forSigning, akparams); |
| |
| _block = Uint8List((_emBits + 7) ~/ 8); |
| |
| reset(); |
| } |
| |
| /// Clear possibly sensitive data. |
| void _clearBlock(Uint8List block) { |
| for (var i = 0; i != block.length; i++) { |
| block[i] = 0; |
| } |
| } |
| |
| @override |
| void reset() { |
| _contentDigest.reset(); |
| } |
| |
| @override |
| PSSSignature generateSignature(Uint8List message) { |
| if (!_forSigning) { |
| throw StateError('Signer was not initialised for signature generation'); |
| } |
| |
| _contentDigest.reset(); |
| _contentDigest.update(message, 0, message.length); |
| _contentDigest.doFinal(_mDash, _mDash.length - _hLen - _sLen); |
| |
| if (_sLen != 0) { |
| if (!_sSet) { |
| _salt = _random.nextBytes(_sLen); |
| } |
| |
| arrayCopy(_salt, 0, _mDash, _mDash.length - _sLen, _sLen); |
| } |
| |
| var h = Uint8List(_hLen); |
| |
| _contentDigest.update(_mDash, 0, _mDash.length); |
| |
| _contentDigest.doFinal(h, 0); |
| |
| _block[_block.length - _sLen - 1 - _hLen - 1] = 0x01; |
| arrayCopy(_salt, 0, _block, _block.length - _sLen - _hLen - 1, _sLen); |
| |
| var dbMask = |
| _maskGeneratorFunction1(h, 0, h.length, _block.length - _hLen - 1); |
| for (var i = 0; i != dbMask.length; i++) { |
| _block[i] ^= dbMask[i]; |
| } |
| |
| arrayCopy(h, 0, _block, _block.length - _hLen - 1, _hLen); |
| |
| var firstByteMask = 0xff >> ((_block.length * 8) - _emBits); |
| |
| _block[0] &= firstByteMask; |
| _block[_block.length - 1] = _trailer; |
| |
| var b = _cipher.process(_block); |
| |
| _clearBlock(_block); |
| |
| return PSSSignature(b); |
| } |
| |
| @override |
| bool verifySignature(Uint8List message, covariant PSSSignature signature) { |
| if (_forSigning) { |
| throw StateError('Signer was not initialised for signature verification'); |
| } |
| |
| _contentDigest.reset(); |
| _contentDigest.update(message, 0, message.length); |
| _contentDigest.doFinal(_mDash, _mDash.length - _hLen - _sLen); |
| |
| var b = _cipher.process(signature.bytes); |
| _block.fillRange(0, _block.length - b.length, 0); |
| arrayCopy(b, 0, _block, _block.length - b.length, b.length); |
| |
| var firstByteMask = 0xFF >> ((_block.length * 8) - _emBits); |
| |
| if (_block[0] != (_block[0] & firstByteMask) || |
| _block[_block.length - 1] != _trailer) { |
| _clearBlock(_block); |
| return false; |
| } |
| |
| var dbMask = _maskGeneratorFunction1( |
| _block, _block.length - _hLen - 1, _hLen, _block.length - _hLen - 1); |
| |
| for (var i = 0; i != dbMask.length; i++) { |
| _block[i] ^= dbMask[i]; |
| } |
| |
| _block[0] &= firstByteMask; |
| |
| for (var i = 0; i != _block.length - _hLen - _sLen - 2; i++) { |
| if (_block[i] != 0) { |
| _clearBlock(_block); |
| return false; |
| } |
| } |
| |
| if (_block[_block.length - _hLen - _sLen - 2] != 0x01) { |
| _clearBlock(_block); |
| return false; |
| } |
| |
| if (_sSet) { |
| arrayCopy(_salt, 0, _mDash, _mDash.length - _sLen, _sLen); |
| } else { |
| arrayCopy(_block, _block.length - _sLen - _hLen - 1, _mDash, |
| _mDash.length - _sLen, _sLen); |
| } |
| |
| _contentDigest.update(_mDash, 0, _mDash.length); |
| _contentDigest.doFinal(_mDash, _mDash.length - _hLen); |
| |
| for (var i = _block.length - _hLen - 1, j = _mDash.length - _hLen; |
| j != _mDash.length; |
| i++, j++) { |
| if ((_block[i] ^ _mDash[j]) != 0) { |
| _clearBlock(_mDash); |
| _clearBlock(_block); |
| return false; |
| } |
| } |
| |
| _clearBlock(_mDash); |
| _clearBlock(_block); |
| return true; |
| } |
| |
| /// Convert int to octet string. |
| void _intToOSP(int i, Uint8List sp) { |
| sp[0] = i >> 24; |
| sp[1] = i >> 16; |
| sp[2] = i >> 8; |
| sp[3] = i >> 0; |
| } |
| |
| Uint8List _maskGeneratorFunction1( |
| Uint8List Z, int zOff, int zLen, int length) { |
| var mask = Uint8List(length); |
| var hashBuf = Uint8List(_mgfhLen); |
| var C = Uint8List(4); |
| var counter = 0; |
| |
| _mgfDigest.reset(); |
| |
| while (counter < (length ~/ _mgfhLen)) { |
| _intToOSP(counter, C); |
| |
| _mgfDigest.update(Z, zOff, zLen); |
| _mgfDigest.update(C, 0, C.length); |
| _mgfDigest.doFinal(hashBuf, 0); |
| |
| arrayCopy(hashBuf, 0, mask, counter * _mgfhLen, _mgfhLen); |
| counter++; |
| } |
| |
| if ((counter * _mgfhLen) < length) { |
| _intToOSP(counter, C); |
| |
| _mgfDigest.update(Z, zOff, zLen); |
| _mgfDigest.update(C, 0, C.length); |
| _mgfDigest.doFinal(hashBuf, 0); |
| |
| arrayCopy(hashBuf, 0, mask, counter * _mgfhLen, |
| mask.length - (counter * _mgfhLen)); |
| } |
| |
| return mask; |
| } |
| } |