blob: 160f92043f262b4e7f537d2f508de343d19aa5e6 [file] [log] [blame] [edit]
// Copyright 2021 Google LLC
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
// A small part is based on a JavaScript implementation of RSA by Tom Wu
// but re-written in dart.
import 'dart:typed_data';
import 'package:meta/meta.dart';
/// Represents integers obtained while creating a Public/Private key pair.
final class RSAPrivateKey {
/// First prime number.
final BigInt p;
/// Second prime number.
final BigInt q;
/// Modulus for public and private keys. Satisfies `n=p*q`.
final BigInt n;
/// Public key exponent. Satisfies `d*e=1 mod phi(n)`.
final BigInt e;
/// Private key exponent. Satisfies `d*e=1 mod phi(n)`.
final BigInt d;
/// Different form of [p]. Satisfies `dmp1=d mod (p-1)`.
final BigInt dmp1;
/// Different form of [p]. Satisfies `dmq1=d mod (q-1)`.
final BigInt dmq1;
/// A coefficient which satisfies `coeff=q^-1 mod p`.
final BigInt coeff;
/// The number of bits used for the modulus. Usually 1024, 2048 or 4096 bits.
int get bitLength => n.bitLength;
RSAPrivateKey(
this.n,
this.e,
this.d,
this.p,
this.q,
this.dmp1,
this.dmq1,
this.coeff,
);
}
/// Performs the private key operation (signing) on [bytes] with the private
/// [key].
///
/// Others who have access to the public key will be able to verify this
/// the result.
///
/// The [intendedLength] argument specifies the number of bytes in which the
/// result should be encoded. Zero bytes will be used for padding.
@internal
Uint8List rawSign(RSAPrivateKey key, List<int> bytes, int intendedLength) {
final message = bytes2BigInt(bytes);
final encryptedMessage = _encryptInteger(key, message);
return integer2Bytes(encryptedMessage, intendedLength);
}
BigInt _encryptInteger(RSAPrivateKey key, BigInt x) {
// The following is equivalent to `(x % key.n).modPow(key.d, key.n)` but is
// much more efficient. It exploits the fact that we have dmp1/dmq1.
var xp = (x % key.p).modPow(key.dmp1, key.p);
final xq = (x % key.q).modPow(key.dmq1, key.q);
while (xp < xq) {
xp += key.p;
}
return ((((xp - xq) * key.coeff) % key.p) * key.q) + xq;
}
@internal
BigInt bytes2BigInt(List<int> bytes) {
var number = BigInt.zero;
for (var i = 0; i < bytes.length; i++) {
number = (number << 8) | BigInt.from(bytes[i]);
}
return number;
}
@visibleForTesting
Uint8List integer2Bytes(BigInt integer, int intendedLength) {
if (integer < BigInt.one) {
throw ArgumentError('Only positive integers are supported.');
}
final bytes = Uint8List(intendedLength);
for (var i = bytes.length - 1; i >= 0; i--) {
bytes[i] = (integer & _bigIntFF).toInt();
integer >>= 8;
}
return bytes;
}
final _bigIntFF = BigInt.from(0xff);