blob: c11d34db3af274caca899eda4b5b30154d497cad [file] [log] [blame] [edit]
// See file LICENSE for more information.
library impl.ecc.ecc_base;
//TODO I think this stuff might be moved to src/impl
import 'dart:typed_data';
import 'package:pointycastle/ecc/api.dart';
import 'package:pointycastle/src/utils.dart' as utils;
/// Implementation of [ECDomainParameters]
class ECDomainParametersImpl implements ECDomainParameters {
@override
final String domainName;
@override
final ECCurve curve;
@override
final List<int>? seed;
@override
final ECPoint G;
@override
final BigInt n;
BigInt? _h;
ECDomainParametersImpl(this.domainName, this.curve, this.G, this.n,
[this._h, this.seed]) {
_h ??= BigInt.one;
}
BigInt? get h => _h;
}
/// Base implementation for [ECFieldElement]
abstract class ECFieldElementBase implements ECFieldElement {
@override
BigInt? toBigInteger();
@override
String get fieldName;
@override
int get fieldSize;
@override
int get byteLength => (fieldSize + 7) ~/ 8;
@override
ECFieldElementBase operator +(covariant ECFieldElementBase b);
@override
ECFieldElementBase operator -(covariant ECFieldElementBase b);
@override
ECFieldElementBase operator *(covariant ECFieldElementBase b);
@override
ECFieldElementBase operator /(covariant ECFieldElementBase b);
@override
ECFieldElementBase operator -();
@override
ECFieldElementBase invert();
@override
ECFieldElementBase square();
@override
ECFieldElementBase? sqrt();
@override
String toString() => toBigInteger().toString();
}
/// Base implementation for [ECPoint]
abstract class ECPointBase implements ECPoint {
@override
final ECCurveBase curve;
@override
final ECFieldElementBase? x;
@override
final ECFieldElementBase? y;
@override
final bool isCompressed;
final ECMultiplier _multiplier;
PreCompInfo? _preCompInfo;
ECPointBase(this.curve, this.x, this.y, this.isCompressed,
[this._multiplier = _fpNafMultiplier]);
@override
bool get isInfinity => x == null && y == null;
set preCompInfo(PreCompInfo preCompInfo) {
_preCompInfo = preCompInfo;
}
@override
bool operator ==(Object other) {
if (other is ECPointBase) {
if (isInfinity) {
return other.isInfinity;
}
return x == other.x && y == other.y;
}
return false;
}
@override
String toString() => '($x,$y)';
@override
int get hashCode {
if (isInfinity) {
return 0;
}
return x.hashCode ^ y.hashCode;
}
@override
Uint8List getEncoded([bool compressed = true]);
@override
ECPointBase? operator +(covariant ECPointBase? b);
@override
ECPointBase? operator -(covariant ECPointBase b);
@override
ECPointBase operator -();
@override
ECPointBase? twice();
/// Multiplies this <code>ECPoint</code> by the given number.
/// @param k The multiplicator.
/// @return <code>k * this</code>.
@override
ECPointBase? operator *(BigInt? k) {
if (k!.sign < 0) {
throw ArgumentError('The multiplicator cannot be negative');
}
if (isInfinity) {
return this;
}
if (k.sign == 0) {
return curve.infinity;
}
return _multiplier(this, k, _preCompInfo);
}
}
/// Base implementation for [ECCurve]
abstract class ECCurveBase implements ECCurve {
ECFieldElementBase? _a;
ECFieldElementBase? _b;
ECCurveBase(BigInt? a, BigInt? b) {
_a = fromBigInteger(a);
_b = fromBigInteger(b);
}
@override
ECFieldElementBase? get a => _a;
@override
ECFieldElementBase? get b => _b;
@override
int get fieldSize;
@override
ECPointBase? get infinity;
@override
ECFieldElementBase fromBigInteger(BigInt? x);
@override
ECPointBase createPoint(BigInt x, BigInt y, [bool withCompression = false]);
@override
ECPointBase decompressPoint(int yTilde, BigInt x1);
/// Decode a point on this curve from its ASN.1 encoding. The different
/// encodings are taken account of, including point compression for
/// <code>F<sub>p</sub></code> (X9.62 s 4.2.1 pg 17).
/// @return The decoded point.
@override
ECPointBase? decodePoint(List<int> encoded) {
ECPointBase? p;
var expectedLength = (fieldSize + 7) ~/ 8;
switch (encoded[0]) {
case 0x00: // infinity
if (encoded.length != 1) {
throw ArgumentError('Incorrect length for infinity encoding');
}
p = infinity;
break;
case 0x02: // compressed
case 0x03: // compressed
if (encoded.length != (expectedLength + 1)) {
throw ArgumentError('Incorrect length for compressed encoding');
}
var yTilde = encoded[0] & 1;
var x1 = _fromArray(encoded, 1, expectedLength);
p = decompressPoint(yTilde, x1);
break;
case 0x04: // uncompressed
case 0x06: // hybrid
case 0x07: // hybrid
if (encoded.length != (2 * expectedLength + 1)) {
throw ArgumentError(
'Incorrect length for uncompressed/hybrid encoding');
}
var x1 = _fromArray(encoded, 1, expectedLength);
var y1 = _fromArray(encoded, 1 + expectedLength, expectedLength);
p = createPoint(x1, y1, false);
break;
default:
throw ArgumentError(
'Invalid point encoding 0x${encoded[0].toRadixString(16)}');
}
return p;
}
BigInt _fromArray(List<int> buf, int off, int length) {
return utils.decodeBigIntWithSign(1, buf.sublist(off, off + length));
}
}
/// Interface for classes storing precomputation data for multiplication algorithms.
abstract class PreCompInfo {}
/// Interface for functions encapsulating a point multiplication algorithm for [ECPointBase]. Multiplies [p] by [k], i.e. [p] is
/// added [k] times to itself.
typedef ECMultiplier = ECPointBase? Function(
ECPointBase p, BigInt? k, PreCompInfo? preCompInfo);
bool _testBit(BigInt i, int n) {
return i & (BigInt.one << n) != BigInt.zero;
}
/// Function implementing the NAF (Non-Adjacent Form) multiplication algorithm.
ECPointBase? _fpNafMultiplier(
ECPointBase p, BigInt? k, PreCompInfo? preCompInfo) {
// TODO Probably should try to add this
// BigInt e = k.mod(n); // n == order of p
var e = k;
var h = e! * BigInt.from(3);
var neg = -p;
ECPointBase? R = p;
for (var i = h.bitLength - 2; i > 0; --i) {
R = R!.twice();
var hBit = _testBit(h, i);
var eBit = _testBit(e, i);
if (hBit != eBit) {
R = R! + (hBit ? p : neg);
}
}
return R;
}