blob: 0d00be444d1f867ced0e08d5e4d622cca8c9a50d [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// A small part is based on a JavaScript implementation of RSA by Tom Wu
// but re-written in dart.
import 'dart:typed_data';
/// Represents integers obtained while creating a Public/Private key pair.
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,
);
}
// ignore: avoid_classes_with_only_static_members
/// Provides a [encrypt] method for encrypting messages with a [RSAPrivateKey].
abstract class RSAAlgorithm {
/// Performs the encryption of [bytes] with the private [key].
/// Others who have access to the public key will be able to decrypt 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.
static List<int> encrypt(
RSAPrivateKey key,
List<int> bytes,
int intendedLength,
) {
final message = bytes2BigInt(bytes);
final encryptedMessage = _encryptInteger(key, message);
return integer2Bytes(encryptedMessage, intendedLength);
}
static BigInt _encryptInteger(RSAPrivateKey key, BigInt x) {
// The following is equivalent to `_modPow(x, key.d, key.n) but is much
// more efficient. It exploits the fact that we have dmp1/dmq1.
var xp = _modPow(x % key.p, key.dmp1, key.p);
final xq = _modPow(x % key.q, key.dmq1, key.q);
while (xp < xq) {
xp += key.p;
}
return ((((xp - xq) * key.coeff) % key.p) * key.q) + xq;
}
static BigInt _modPow(BigInt b, BigInt e, BigInt m) {
if (e < BigInt.one) {
return BigInt.one;
}
if (b < BigInt.zero || b > m) {
b = b % m;
}
var r = BigInt.one;
while (e > BigInt.zero) {
if ((e & BigInt.one) > BigInt.zero) {
r = (r * b) % m;
}
e >>= 1;
b = (b * b) % m;
}
return r;
}
static 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;
}
static List<int> 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);