diff --git a/lib/src/cdsa/cdsa.dart b/lib/src/cdsa/cdsa.dart index 67301502..b7df3c82 100644 --- a/lib/src/cdsa/cdsa.dart +++ b/lib/src/cdsa/cdsa.dart @@ -2,3 +2,4 @@ export 'curve_points.dart'; export 'curve_type.dart'; export 'curves.dart'; export 'ecdsa/ecdsa.dart'; +export 'eddsa/eddsa.dart'; diff --git a/lib/src/cdsa/curve_points.dart b/lib/src/cdsa/curve_points.dart index e93f0325..3df0e387 100644 --- a/lib/src/cdsa/curve_points.dart +++ b/lib/src/cdsa/curve_points.dart @@ -10,4 +10,15 @@ class CurvePoints { z: BigInt.one, ); } + + static EDPoint get generatorED25519 { + return EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('15112221349535400772501151409588531511454012693041857206046113283949847762202'), + y: BigInt.parse('46316835694926478169428394003475163141307993866256225615783033603165251855960'), + z: BigInt.one, + t: BigInt.parse('46827403850823179245072216630277197565144205554125654976674165829533817101731'), + ); + } } diff --git a/lib/src/cdsa/curve_type.dart b/lib/src/cdsa/curve_type.dart index 75a57e2e..156f9fe0 100644 --- a/lib/src/cdsa/curve_type.dart +++ b/lib/src/cdsa/curve_type.dart @@ -1,3 +1,4 @@ enum CurveType { secp256k1, + ed25519, } diff --git a/lib/src/cdsa/curves.dart b/lib/src/cdsa/curves.dart index 0e3879aa..dad7e3b8 100644 --- a/lib/src/cdsa/curves.dart +++ b/lib/src/cdsa/curves.dart @@ -9,4 +9,13 @@ class Curves { p: BigInt.parse('115792089237316195423570985008687907853269984665640564039457584007908834671663'), ); } + + static EDCurve get ed25519 { + return EDCurve( + a: BigInt.from(-1), + d: BigInt.parse('37095705934669439343138083508754565189542113879843219016388785533085940283555'), + h: BigInt.from(8), + p: BigInt.parse('57896044618658097711785492504343953926634992332820282019728792003956564819949'), + ); + } } diff --git a/lib/src/cdsa/eddsa/ed25519/ed25519_private_key.dart b/lib/src/cdsa/eddsa/ed25519/ed25519_private_key.dart new file mode 100644 index 00000000..66077630 --- /dev/null +++ b/lib/src/cdsa/eddsa/ed25519/ed25519_private_key.dart @@ -0,0 +1,37 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; + +/// [ED25519PrivateKey] represents [EDPrivateKey] constructed with specific Curve (ED25519) and chain code. +class ED25519PrivateKey extends ABip32PrivateKey { + /// Returns the length of the private key. + static const int _length = 32; + + /// EdDSA Private key used for cryptographic operations, following the ED25519 curve specification. + final EDPrivateKey edPrivateKey; + + const ED25519PrivateKey({ + required super.metadata, + required this.edPrivateKey, + }); + + /// Returns the private key as a byte array. + @override + Uint8List get bytes => edPrivateKey.bytes; + + /// Returns the length of the private key. + @override + int get length => _length; + + /// Returns the public key derived from the private key. + @override + ED25519PublicKey get publicKey { + return ED25519PublicKey( + edPublicKey: edPrivateKey.edPublicKey, + metadata: metadata, + ); + } + + @override + List get props => [edPrivateKey, metadata]; +} diff --git a/lib/src/cdsa/eddsa/ed25519/ed25519_public_key.dart b/lib/src/cdsa/eddsa/ed25519/ed25519_public_key.dart new file mode 100644 index 00000000..8839c735 --- /dev/null +++ b/lib/src/cdsa/eddsa/ed25519/ed25519_public_key.dart @@ -0,0 +1,33 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; + +/// [ED25519PublicKey] represents [EDPublicKey] constructed with specific Curve (ED25519) and chain code. +class ED25519PublicKey extends ABip32PublicKey { + /// EdDSA public key derived from the corresponding private key, following the ED25519 curve specification. + /// This public key is used in the verification process of digital signatures, allowing others to verify the authenticity + /// of transactions or messages signed with the associated private key, without compromising the private key itself. + final EDPublicKey edPublicKey; + + const ED25519PublicKey({ + required super.metadata, + required this.edPublicKey, + }); + + /// Returns the compressed form of the public key. + @override + Uint8List get compressed => bytes; + + /// Returns the uncompressed form of the public key. + @override + Uint8List get uncompressed => bytes; + + /// Returns the public key as bytes. + Uint8List get bytes => edPublicKey.bytes; + + /// Returns the length of the public key. + int get length => edPublicKey.length; + + @override + List get props => [edPublicKey]; +} diff --git a/lib/src/cdsa/eddsa/ed25519/signer/ed25519_signer.dart b/lib/src/cdsa/eddsa/ed25519/signer/ed25519_signer.dart new file mode 100644 index 00000000..0bc0065e --- /dev/null +++ b/lib/src/cdsa/eddsa/ed25519/signer/ed25519_signer.dart @@ -0,0 +1,69 @@ +// Class was shaped by the influence of several key sources including: +// "blockchain_utils" Copyright (c) 2010 Mohsen +// https://github.com/mrtnetwork/blockchain_utils/. +// +// BSD 3-Clause License +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/cdsa/eddsa/ed25519/signer/ed_signature.dart'; +import 'package:cryptography_utils/src/hash/sha/hash/a_hash.dart'; +import 'package:cryptography_utils/src/utils/big_int_utils.dart'; + +/// This class implements the functionality necessary to generate digital signatures using the EdDSA algorithm. +class ED25519Signer { + /// The hash function used for generating the message digest. + final AHash hashFunction; + + /// The EdDSA private key used for signing the message. + final ED25519PrivateKey privateKey; + + ED25519Signer({ + required this.hashFunction, + required this.privateKey, + }); + + /// Generates a deterministic signature for a given message using [ED25519PrivateKey]. + EDSignature sign(Uint8List message) { + Uint8List publicKey = privateKey.publicKey.bytes; + Uint8List h = hashFunction.convert(privateKey.bytes).byteList; + Uint8List prefix = h.sublist(privateKey.length); + + BigInt r = BigIntUtils.decode(hashFunction.convert([...prefix, ...message]).byteList, order: Endian.little); + Uint8List R = (CurvePoints.generatorED25519 * r).toBytes(); + + BigInt k = BigIntUtils.decode(hashFunction.convert([...R, ...publicKey, ...message]).byteList, order: Endian.little); + k %= CurvePoints.generatorED25519.n; + + BigInt s = (r + k * privateKey.edPrivateKey.a) % CurvePoints.generatorED25519.n; + return EDSignature( + r: R, + s: BigIntUtils.changeToBytes(s, length: privateKey.length, order: Endian.little), + ); + } +} diff --git a/lib/src/cdsa/eddsa/ed25519/signer/ed25519_verifier.dart b/lib/src/cdsa/eddsa/ed25519/signer/ed25519_verifier.dart new file mode 100644 index 00000000..a5968850 --- /dev/null +++ b/lib/src/cdsa/eddsa/ed25519/signer/ed25519_verifier.dart @@ -0,0 +1,68 @@ +// Class was shaped by the influence of several key sources including: +// "blockchain_utils" Copyright (c) 2010 Mohsen +// https://github.com/mrtnetwork/blockchain_utils/. +// +// BSD 3-Clause License +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/cdsa/eddsa/ed25519/signer/ed_signature.dart'; +import 'package:cryptography_utils/src/hash/sha/hash/a_hash.dart'; +import 'package:cryptography_utils/src/utils/big_int_utils.dart'; + +/// This class implements the functionality necessary to verify digital signatures using the EdDSA algorithm. +class ED25519Verifier { + /// The hash function used for generating the message digest. + final AHash hashFunction; + + /// The EdDSA public key used for signing the message. + final ED25519PublicKey publicKey; + + ED25519Verifier({ + required this.hashFunction, + required this.publicKey, + }); + + /// Verifies an ED25519 signature against given message. + bool isSignatureValid(Uint8List message, EDSignature edSignature) { + EDPoint R = EDPoint.fromBytes(CurvePoints.generatorED25519, edSignature.r); + BigInt S = BigIntUtils.decode(edSignature.s, order: Endian.little); + if (S >= CurvePoints.generatorED25519.n) { + throw Exception('Invalid signature'); + } + + Uint8List digest = hashFunction.convert([...R.toBytes(), ...publicKey.bytes, ...message]).byteList; + BigInt k = BigIntUtils.decode(digest, order: Endian.little); + EDPoint gs = (CurvePoints.generatorED25519 * S).scaleToAffineCoordinates(); + EDPoint rka = (publicKey.edPublicKey.A * k + R).scaleToAffineCoordinates(); + if (gs != rka) { + return false; + } + return true; + } +} diff --git a/lib/src/cdsa/eddsa/ed25519/signer/ed_signature.dart b/lib/src/cdsa/eddsa/ed25519/signer/ed_signature.dart new file mode 100644 index 00000000..dff01de8 --- /dev/null +++ b/lib/src/cdsa/eddsa/ed25519/signer/ed_signature.dart @@ -0,0 +1,47 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; + +/// Class representing a digital signature in the EdDSA algorithm +class EDSignature extends ASignature { + /// The part of the signature representing the point on the Edwards curve, typically denoted as 'R'. + /// This is a unique identifier for the signature, generated as part of the signing process. + final Uint8List r; + + /// The part of the signature representing the scalar component, typically denoted as 'S'. + /// This value is computed from the signer's private key, the message's hash, and the point 'R', + /// serving as the proof of the signature's authenticity and integrity. + final Uint8List s; + + /// Constructs an instance of ECSignature with the specified 'r' and 's' components. + const EDSignature({ + required this.r, + required this.s, + }); + + /// Constructs an instance of ECSignature from a base64 string. + factory EDSignature.fromBase64(String base64) { + Uint8List bytes = base64Decode(base64); + return EDSignature.fromBytes(bytes); + } + + /// Constructs an instance of ECSignature from a byte array. + factory EDSignature.fromBytes(Uint8List bytes) { + return EDSignature( + r: bytes.sublist(0, 32), + s: bytes.sublist(32, 64), + ); + } + + /// Returns the signature as a byte array. + @override + Uint8List get bytes => Uint8List.fromList(r + s); + + /// Returns the length of the signature. + @override + int get length => r.length + s.length; + + @override + List get props => [r, s]; +} diff --git a/lib/src/cdsa/eddsa/ed_curve.dart b/lib/src/cdsa/eddsa/ed_curve.dart new file mode 100644 index 00000000..0a75a40d --- /dev/null +++ b/lib/src/cdsa/eddsa/ed_curve.dart @@ -0,0 +1,39 @@ +import 'package:equatable/equatable.dart'; + +/// [EDCurve] represents an Edwards curve used in elliptic curve cryptography (ECC). +/// Edwards curves offer several advantages over other curve types, including faster arithmetic operations +/// and strong security properties, making them suitable for a wide range of cryptographic applications. + +/// In ECC, an [EDCurve] is characterized by its equation, typically of the form x^2 + y^2 = 1 + dx^2y^2 in a finite field. +/// This equation defines the mathematical structure of the Edwards curve, where 'd' is a non-square element in the field, +/// and the curve parameters are chosen to optimize security and performance. + +/// The curve is used to generate public-private key pairs and to perform operations like digital signatures. +/// It operates in a finite field, meaning that the values of the points on the curve are limited to a fixed range. +class EDCurve extends Equatable { + /// A constant in the elliptic curve equation. + /// Determines the specific curve used and impacts the cryptographic problem's difficulty. + final BigInt a; + + /// A constant in the edward curve equation. + /// Defines the curve's shape crucial for ECDSA cryptographic properties. + final BigInt d; + + /// The cofactor of the edward curve, related to the curve's point count, + /// used in calculations for subgroup security enhancement. + final BigInt h; + + /// The prime number that defines the field over which the elliptic curve is defined, + /// crucial for modulo operations in ECDSA. + final BigInt p; + + const EDCurve({ + required this.a, + required this.d, + required this.h, + required this.p, + }); + + @override + List get props => [a, d, h, p]; +} diff --git a/lib/src/cdsa/eddsa/ed_point.dart b/lib/src/cdsa/eddsa/ed_point.dart new file mode 100644 index 00000000..e3b2914b --- /dev/null +++ b/lib/src/cdsa/eddsa/ed_point.dart @@ -0,0 +1,261 @@ +// Class was shaped by the influence of several key sources including: +// "blockchain_utils" Copyright (c) 2010 Mohsen +// https://github.com/mrtnetwork/blockchain_utils/. +// +// BSD 3-Clause License +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/utils/big_int_utils.dart'; +import 'package:cryptography_utils/src/utils/ed25519_utils.dart'; +import 'package:equatable/equatable.dart'; + +/// [EDPoint] represents a point on an Edwards curve in the context of elliptic curve cryptography (ECC). +/// Points on an Edwards curve play a crucial role in cryptographic operations such as key generation, encryption, and digital signatures. +/// +/// An [EDPoint] is typically defined by its coordinates (x, y) on the Edwards curve, adhering to the curve's equation. +/// For efficiency in computations and to support environments with constrained resources, extended coordinates may be used, +/// represented as (X, Y, Z) or more complex forms like (X, Y, Z, T), where T is an auxiliary variable for certain optimizations. +class EDPoint extends Equatable { + /// The elliptic curve that the point is on. + final EDCurve curve; + + /// The order of an Edwards curve group. + /// It is the total number of points on the curve that satisfy the elliptic curve equation (typically a large prime number) + final BigInt n; + + /// The x-coordinate of the point on the Edwards curve. + /// Used for both affine and extended [EDPoint] representation. + final BigInt x; + + /// The y-coordinate of the point on the Edwards curve. + /// Used for both affine and extended [EDPoint] representation. + final BigInt y; + + /// The z-coordinate of the point on the Edwards curve. + /// Used only for extended [EDPoint] representation. In case of affine [EDPoint], the value is always zero. + final BigInt z; + + /// The t auxiliary variable + /// Used only for extended [EDPoint] representation. In case of affine [EDPoint], the value is always zero. + final BigInt t; + + EDPoint({ + required this.curve, + required this.n, + BigInt? x, + BigInt? y, + BigInt? z, + BigInt? t, + }) : x = x ?? BigInt.zero, + y = y ?? BigInt.zero, + z = z ?? BigInt.zero, + t = t ?? ((x ?? BigInt.zero) * (y ?? BigInt.zero)); + + /// Constructs an instance of EDPoint from a byte array. + factory EDPoint.fromBytes(EDPoint generator, Uint8List bytes) { + Uint8List editableBytes = Uint8List.fromList(bytes); + EDCurve curve = generator.curve; + BigInt p = curve.p; + int expLen = (p.bitLength + 8) ~/ 8; + + if (editableBytes.length != expLen) { + throw const FormatException("AffinePoint length doesn't match the curve."); + } + + int x0 = (editableBytes[expLen - 1] & 0x80) >> 7; + editableBytes[expLen - 1] &= 0x80 - 1; + + BigInt y = BigIntUtils.decode(editableBytes, order: Endian.little); + + BigInt x2 = (y * y - BigInt.from(1)) * (curve.d * y * y - curve.a).modInverse(p) % p; + BigInt x = ED25519Utils.findModularSquareRoot(a: x2, p: p); + if (x.isOdd != (x0 == 1)) { + x = (-x) % p; + } + + return EDPoint(curve: curve, n: generator.n, x: x, y: y, z: BigInt.one, t: x * y); + } + + /// Constructs an instance of [EDPoint] representing the point at infinity. + factory EDPoint.infinityFrom(EDPoint edPoint) { + return EDPoint(curve: edPoint.curve, n: edPoint.n); + } + + EDPoint copyWith({ + EDCurve? curve, + BigInt? n, + BigInt? x, + BigInt? y, + BigInt? z, + BigInt? t, + }) { + return EDPoint( + curve: curve ?? this.curve, + n: n ?? this.n, + x: x ?? this.x, + y: y ?? this.y, + z: z ?? this.z, + t: t ?? this.t, + ); + } + + /// Negation operator overload + /// + /// In EdDSA (Edwards Curve Digital Signature Algorithm), point negation refers to the process of inverting the x-coordinate and t auxiliary variable + /// of a point on the Edwards curve, while the y-coordinate and z-coordinate remain the same. + /// + /// If the original point is represented as P(x,y,z,t), the negated point becomes −P(-x,y,z,-t), following the curve's symmetry + EDPoint operator -() { + return copyWith(x: -x, t: -t); + } + + /// Addition operator overload + /// + /// In EdDSA (Edwards Curve Digital Signature Algorithm), point addition involves adding two points on the Edwards curve. + EDPoint operator +(EDPoint other) { + BigInt A = (x * other.x) % curve.p; + BigInt b = (y * other.y) % curve.p; + BigInt c = (z * other.t) % curve.p; + BigInt d = (t * other.z) % curve.p; + BigInt e = d + c; + BigInt f = (((x - y) * (other.x + other.y)) + b - A) % curve.p; + BigInt g = b + (curve.a * A); + BigInt h = d - c; + + if (h == BigInt.zero) { + return _double(); + } + + BigInt x3 = (e * f) % curve.p; + BigInt y3 = (g * h) % curve.p; + BigInt z3 = (f * g) % curve.p; + BigInt t3 = (e * h) % curve.p; + + if (x3 == BigInt.zero || t3 == BigInt.zero) { + return EDPoint.infinityFrom(other); + } + + return EDPoint(n: n, curve: curve, x: x3, y: y3, z: z3, t: t3); + } + + /// Multiplication operator overload + /// + /// In EdDSA (Edwards Curve Digital Signature Algorithm), point multiplication involves multiplying a point on the elliptic curve by a scalar value. + /// If the point is P and the scalar is k, the operation k∗P is essentially adding the point P to itself k times. + /// + /// This implementation utilizes the Double-and-Add algorithm combined with the wNAF (windowed Non-Adjacent Form) method for point multiplication in EdDSA. + /// The Double-and-Add algorithm is an efficient iterative method that doubles the point and adds it to the total in each iteration, with the index decreasing. + /// The incorporation of the wNAF method further optimizes this process by minimizing the number of required point additions. + /// + /// These algorithms significantly reduce the number of computations compared to straightforward addition. + EDPoint operator *(BigInt scalar) { + if (x == BigInt.zero || y == BigInt.zero || scalar == BigInt.zero) { + return EDPoint.infinityFrom(this); + } + + if (scalar == BigInt.one) { + return this; + } + + BigInt modScalar = scalar % (n * BigInt.two); + EDPoint multipliedPoint = copyWith( + x: BigInt.zero, + y: BigInt.one, + z: BigInt.one, + t: BigInt.one, + ); + + List nafList = BigIntUtils.computeNAF(modScalar).reversed.toList(); + for (BigInt i in nafList) { + multipliedPoint = multipliedPoint._double(); + + if (i < BigInt.zero) { + multipliedPoint = multipliedPoint + (-this); + } else if (i > BigInt.zero) { + multipliedPoint = multipliedPoint + this; + } + } + + if (multipliedPoint.x == BigInt.zero || multipliedPoint.t == BigInt.zero) { + return EDPoint.infinityFrom(this); + } + + return multipliedPoint; + } + + /// Returns EDPoint as bytes + Uint8List toBytes() { + EDPoint scaledPoint = scaleToAffineCoordinates(); + int encLen = (curve.p.bitLength + 1 + 7) ~/ 8; + Uint8List yStr = BigIntUtils.changeToBytes(scaledPoint.y, length: encLen, order: Endian.little); + if (scaledPoint.x % BigInt.two == BigInt.one) { + yStr[yStr.length - 1] |= 0x80; + } + return yStr; + } + + /// Scales an Edwards curve point back to affine coordinates from extended coordinates. + /// + /// If the z-coordinate is already one, indicating that the point is in affine coordinates, the method returns the point as is. + /// Otherwise, it scales the x, y coordinates and t auxiliary variable using the inverse of z, and sets z to one, converting the point to affine coordinates. + EDPoint scaleToAffineCoordinates() { + if (z == BigInt.one) { + return this; + } + BigInt p = curve.p; + + BigInt zInv = z.modInverse(p); + BigInt xVal = (x * zInv) % p; + BigInt yVal = (y * zInv) % p; + BigInt tVal = (xVal * yVal) % p; + + return EDPoint(curve: curve, n: n, x: xVal, y: yVal, z: BigInt.one, t: tVal); + } + + /// Doubles a point in on an Edwards curve + EDPoint _double() { + BigInt A = (x * x) % curve.p; + BigInt B = (y * y) % curve.p; + BigInt C = (z * z * BigInt.two) % curve.p; + BigInt D = (curve.a * A) % curve.p; + BigInt E = (((x + y) * (x + y)) - A - B) % curve.p; + BigInt G = D + B; + BigInt F = G - C; + BigInt H = D - B; + BigInt x3 = (E * F) % curve.p; + BigInt y3 = (G * H) % curve.p; + BigInt t3 = (E * H) % curve.p; + BigInt z3 = (F * G) % curve.p; + + return copyWith(x: x3, y: y3, z: z3, t: t3); + } + + @override + List get props => [curve, n, x, y, z, t]; +} diff --git a/lib/src/cdsa/eddsa/ed_private_key.dart b/lib/src/cdsa/eddsa/ed_private_key.dart new file mode 100644 index 00000000..e6a020cd --- /dev/null +++ b/lib/src/cdsa/eddsa/ed_private_key.dart @@ -0,0 +1,63 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/utils/big_int_utils.dart'; +import 'package:equatable/equatable.dart'; + +/// Represents an EdDSA private key and provides methods for key operations. +class EDPrivateKey extends Equatable { + /// Generator point on the Edwards curve that serves as a starting point + /// for generating public keys and performing cryptographic operations. + final EDPoint G; + + /// Represents the private key in EdDSA, essential for signing messages and deriving the public key. + final Uint8List _privateKey; + + EDPrivateKey.fromBytes(this._privateKey, {EDPoint? G}) : G = G ?? CurvePoints.generatorED25519; + + /// Returns the private key as bytes. + Uint8List get bytes => _privateKey; + + /// Returns the public key derived from the private key. + EDPublicKey get edPublicKey { + EDPublicKey edPublicKey = EDPublicKey(G * a); + return edPublicKey; + } + + /// Returns the pruned private key as a BigInt. + BigInt get a { + Uint8List h = Sha512().convert(_privateKey).byteList; + Uint8List a = h.sublist(0, _baseLength); + Uint8List sBytes = _prunePrivateKey(a); + return BigIntUtils.decode(sBytes, order: Endian.little); + } + + /// Returns the length of the private key in bytes. + int get _baseLength => (G.curve.p.bitLength + 8) ~/ 8; + + /// Prunes the key to achieve improved security. + Uint8List _prunePrivateKey(Uint8List privateKey) { + BigInt h = G.curve.h; + int hLog; + if (h == BigInt.from(4)) { + hLog = 2; + } else if (h == BigInt.from(8)) { + hLog = 3; + } else { + throw Exception('Pruning of EdDSA private keys is supported only on curves with cofactors 4 or 8'); + } + privateKey[0] &= ~((1 << hLog) - 1); + + int l = G.curve.p.bitLength; + if (l % 8 == 0) { + privateKey[privateKey.length - 1] = 0; + privateKey[privateKey.length - 2] |= 0x80; + } else { + privateKey[privateKey.length - 1] = privateKey[privateKey.length - 1] & ((1 << (l % 8)) - 1) | (1 << (l % 8) - 1); + } + return privateKey; + } + + @override + List get props => [_privateKey]; +} diff --git a/lib/src/cdsa/eddsa/ed_public_key.dart b/lib/src/cdsa/eddsa/ed_public_key.dart new file mode 100644 index 00000000..5912e009 --- /dev/null +++ b/lib/src/cdsa/eddsa/ed_public_key.dart @@ -0,0 +1,22 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:equatable/equatable.dart'; + +/// Represents an EdDSA public key in the Edwards curve format. +class EDPublicKey extends Equatable { + /// Represents the public key derived from the private key through the edwards curve point multiplication + /// operation Q = dG, where `d`is the secret exponent (private key) and `G` is the generator point of the curve. + final EDPoint A; + + const EDPublicKey(this.A); + + /// Returns public key as bytes. + Uint8List get bytes => A.toBytes(); + + /// Returns length of the public key in bytes. + int get length => (A.curve.p.bitLength + 8) ~/ 8; + + @override + List get props => [A]; +} diff --git a/lib/src/cdsa/eddsa/eddsa.dart b/lib/src/cdsa/eddsa/eddsa.dart new file mode 100644 index 00000000..48888119 --- /dev/null +++ b/lib/src/cdsa/eddsa/eddsa.dart @@ -0,0 +1,6 @@ +export 'ed25519/ed25519_private_key.dart'; +export 'ed25519/ed25519_public_key.dart'; +export 'ed_curve.dart'; +export 'ed_point.dart'; +export 'ed_private_key.dart'; +export 'ed_public_key.dart'; diff --git a/lib/src/utils/ed25519_utils.dart b/lib/src/utils/ed25519_utils.dart new file mode 100644 index 00000000..0eb6c983 --- /dev/null +++ b/lib/src/utils/ed25519_utils.dart @@ -0,0 +1,160 @@ +// Class was shaped by the influence of several key sources including: +// "blockchain_utils" Copyright (c) 2010 Mohsen +// https://github.com/mrtnetwork/blockchain_utils/. +// +// BSD 3-Clause License +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +class ED25519Utils { + /// Calculate the modular square root of [a] modulo prime [p] using algorithms + /// from the Handbook of Applied Cryptography (3.34 to 3.39). + static BigInt findModularSquareRoot({required BigInt a, required BigInt p}) { + BigInt jacobiSymbol = _calcJacobiSymbol(a, p); + + if (jacobiSymbol == BigInt.from(-1)) { + throw Exception('$a has no square root modulo $p'); + } + + if (p % BigInt.from(4) == BigInt.from(3)) { + return a.modPow((p + BigInt.one) ~/ BigInt.from(4), p); + } + + if (p % BigInt.from(8) == BigInt.from(5)) { + BigInt d = a.modPow((p - BigInt.one) ~/ BigInt.from(4), p); + if (d == BigInt.one) { + return a.modPow((p + BigInt.from(3)) ~/ BigInt.from(8), p); + } + return (BigInt.from(2) * a * (BigInt.from(4) * a).modPow((p - BigInt.from(5)) ~/ BigInt.from(8), p)) % p; + } + + for (BigInt b = BigInt.from(2); b < p; b += BigInt.one) { + if (_calcJacobiSymbol(b * b - BigInt.from(4) * a, p) == BigInt.from(-1)) { + List quadraticForm = [a, -b, BigInt.one]; + List result = _calcModularPolynomialExponentiation([BigInt.zero, BigInt.one], (p + BigInt.one) ~/ BigInt.from(2), quadraticForm, p); + if (result[1] != BigInt.zero) { + throw Exception('p is not prime'); + } + return result[0]; + } + } + + throw Exception("No suitable 'b' found."); + } + + /// Calculates the Jacobi symbol (a/n) for given integers [a] and [n]. + static BigInt _calcJacobiSymbol(BigInt a, BigInt n) { + BigInt tmpa = a; + if (!(n >= BigInt.from(3))) { + throw const FormatException('n must be larger than 2'); + } + if (!(n % BigInt.two == BigInt.one)) { + throw const FormatException('n must be odd'); + } + + tmpa = tmpa % n; + if (tmpa == BigInt.zero) { + return BigInt.zero; + } + if (tmpa == BigInt.one) { + return BigInt.one; + } + + BigInt a1 = tmpa, e = BigInt.zero; + while (a1 % BigInt.two == BigInt.zero) { + a1 = a1 ~/ BigInt.two; + e = e + BigInt.one; + } + + BigInt s = BigInt.one; + + if (e % BigInt.two == BigInt.zero || n % BigInt.from(8) == BigInt.one || n % BigInt.from(8) == BigInt.from(7)) { + // s remains 1 + } else { + s = BigInt.from(-1); + } + + if (a1 == BigInt.one) { + return s; + } + + if (n % BigInt.from(4) == BigInt.from(3) && a1 % BigInt.from(4) == BigInt.from(3)) { + s = -s; + } + + return s * _calcJacobiSymbol(n % a1, a1); + } + + /// Computes the modular exponentiation of a polynomial represented by [base] + /// to the power of [exponent], using the specified [polymod] and modulus [p]. + static List _calcModularPolynomialExponentiation(List base, BigInt exponent, List polymod, BigInt p) { + if (exponent == BigInt.zero) { + return [BigInt.one]; + } + + List G = List.from(base); + BigInt k = exponent; + List s = (k % BigInt.two == BigInt.one) ? List.from(G) : [BigInt.one]; + + while (k > BigInt.one) { + k = k ~/ BigInt.two; + G = _multiplyModularPolynomial(G, G, polymod, p); + if (k % BigInt.two == BigInt.one) { + s = _multiplyModularPolynomial(G, s, polymod, p); + } + } + + return s; + } + + /// Multiply two polynomials represented by lists [m1] and [m2], reducing modulo [polymod] and prime [p]. + static List _multiplyModularPolynomial(List m1, List m2, List polymod, BigInt p) { + List prod = List.filled(m1.length + m2.length - 1, BigInt.zero); + + for (int i = 0; i < m1.length; i++) { + for (int j = 0; j < m2.length; j++) { + prod[i + j] = (prod[i + j] + m1[i] * m2[j]) % p; + } + } + + return _reduceModularPolynomial(prod, polymod, p); + } + + /// Reduce a polynomial [poly] modulo [polymod] using prime [p]. + static List _reduceModularPolynomial(List poly, List polymod, BigInt p) { + List tmpPoly = List.from(poly); + while (tmpPoly.length >= polymod.length) { + if (tmpPoly.last != BigInt.zero) { + for (int i = 2; i <= polymod.length; i++) { + tmpPoly[tmpPoly.length - i] = (tmpPoly[tmpPoly.length - i] - tmpPoly.last * polymod[polymod.length - i]) % p; + } + } + tmpPoly.removeLast(); + } + + return tmpPoly; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 2b937bd1..66cbaa93 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: cryptography_utils description: "Dart package containing utility methods for common cryptographic and blockchain-specific operations" publish_to: none -version: 0.0.25 +version: 0.0.26 environment: sdk: ">=3.2.6" diff --git a/test/cdsa/eddsa/ed25519/ed25519_private_key_test.dart b/test/cdsa/eddsa/ed25519/ed25519_private_key_test.dart new file mode 100644 index 00000000..71a181e7 --- /dev/null +++ b/test/cdsa/eddsa/ed25519/ed25519_private_key_test.dart @@ -0,0 +1,133 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:test/test.dart'; + +void main() { + group('Tests of ED25519PrivateKey.bytes getter', () { + test('Should [return private key as bytes] from ED25519PrivateKey', () { + // Arrange + ED25519PrivateKey actualED25519PrivateKey = ED25519PrivateKey( + metadata: Bip32KeyMetadata( + depth: 5, + shiftedIndex: 0, + fingerprint: BigInt.parse('2837893204'), + parentFingerprint: BigInt.parse('162080603'), + masterFingerprint: BigInt.parse('83580899'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + ), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M=')), + ); + + // Act + Uint8List actualPrivateKeyBytes = actualED25519PrivateKey.bytes; + + // Assert + Uint8List expectedPrivateKeyBytes = base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M='); + + expect(actualPrivateKeyBytes, expectedPrivateKeyBytes); + }); + }); + + group('Tests of ED25519PrivateKey.metadata getter', () { + test('Should [return Bip32KeyMetadata] from ED25519PrivateKey', () { + // Arrange + ED25519PrivateKey actualED25519PrivateKey = ED25519PrivateKey( + metadata: Bip32KeyMetadata( + depth: 5, + shiftedIndex: 0, + fingerprint: BigInt.parse('2837893204'), + parentFingerprint: BigInt.parse('162080603'), + masterFingerprint: BigInt.parse('83580899'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + ), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M=')), + ); + + // Act + Bip32KeyMetadata actualBip32KeyMetadata = actualED25519PrivateKey.metadata; + + // Assert + Bip32KeyMetadata expectedBip32KeyMetadata = Bip32KeyMetadata( + depth: 5, + shiftedIndex: 0, + fingerprint: BigInt.parse('2837893204'), + parentFingerprint: BigInt.parse('162080603'), + masterFingerprint: BigInt.parse('83580899'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + ); + + expect(actualBip32KeyMetadata, expectedBip32KeyMetadata); + }); + }); + + group('Tests of ED25519PrivateKey.length getter', () { + test('Should [return length of private key] from ED25519PrivateKey', () { + // Arrange + ED25519PrivateKey actualED25519PrivateKey = ED25519PrivateKey( + metadata: Bip32KeyMetadata( + depth: 5, + shiftedIndex: 0, + fingerprint: BigInt.parse('2837893204'), + parentFingerprint: BigInt.parse('162080603'), + masterFingerprint: BigInt.parse('83580899'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + ), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M=')), + ); + + // Act + int actualPrivateKeyLength = actualED25519PrivateKey.length; + + // Assert + int expectedPrivateKeyLength = 32; + + expect(actualPrivateKeyLength, expectedPrivateKeyLength); + }); + }); + + group('Tests of ED25519PrivateKey.publicKey getter', () { + test('Should [return ED25519PublicKey] constructed from ED25519PrivateKey', () { + // Arrange + ED25519PrivateKey actualED25519PrivateKey = ED25519PrivateKey( + metadata: Bip32KeyMetadata( + depth: 5, + shiftedIndex: 0, + fingerprint: BigInt.parse('2837893204'), + parentFingerprint: BigInt.parse('162080603'), + masterFingerprint: BigInt.parse('83580899'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + ), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M=')), + ); + + // Act + ED25519PublicKey actualED25519PublicKey = actualED25519PrivateKey.publicKey; + + // Assert + ED25519PublicKey expectedED25519PublicKey = ED25519PublicKey( + metadata: Bip32KeyMetadata( + depth: 5, + shiftedIndex: 0, + fingerprint: BigInt.parse('2837893204'), + parentFingerprint: BigInt.parse('162080603'), + masterFingerprint: BigInt.parse('83580899'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + ), + edPublicKey: EDPublicKey( + EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ), + ), + ); + + expect(actualED25519PublicKey, expectedED25519PublicKey); + }); + }); +} diff --git a/test/cdsa/eddsa/ed25519/ed25519_public_key_test.dart b/test/cdsa/eddsa/ed25519/ed25519_public_key_test.dart new file mode 100644 index 00000000..f9c4f8e9 --- /dev/null +++ b/test/cdsa/eddsa/ed25519/ed25519_public_key_test.dart @@ -0,0 +1,75 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:test/test.dart'; + +void main() { + group('Tests of ED25519PublicKey.bytes getter', () { + test('Should [return bytes] representing ED25519PublicKey', () { + // Arrange + ED25519PublicKey actualED25519PublicKey = ED25519PublicKey( + metadata: Bip32KeyMetadata( + depth: 5, + shiftedIndex: 0, + fingerprint: BigInt.parse('2837893204'), + parentFingerprint: BigInt.parse('162080603'), + masterFingerprint: BigInt.parse('83580899'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + ), + edPublicKey: EDPublicKey( + EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ), + ), + ); + + // Act + Uint8List actualCompressedPublicKey = actualED25519PublicKey.bytes; + + // Assert + Uint8List expectedCompressedPublicKey = base64Decode('z7Dbiwy9pzyyNc8VrMNqmdssCM0tlKxB9Yk8umwbYwc='); + + expect(actualCompressedPublicKey, expectedCompressedPublicKey); + }); + }); + + group('Tests of ED25519PublicKey.length getter', () { + test('Should [return integer] representing base length of ED25519PublicKey', () { + // Arrange + ED25519PublicKey actualED25519PublicKey = ED25519PublicKey( + metadata: Bip32KeyMetadata( + depth: 5, + shiftedIndex: 0, + fingerprint: BigInt.parse('2837893204'), + parentFingerprint: BigInt.parse('162080603'), + masterFingerprint: BigInt.parse('83580899'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + ), + edPublicKey: EDPublicKey( + EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ), + ), + ); + + // Act + int actualPublicKeyLength = actualED25519PublicKey.length; + + // Assert + int expectedPublicKeyLength = 32; + + expect(actualPublicKeyLength, expectedPublicKeyLength); + }); + }); +} diff --git a/test/cdsa/eddsa/ed25519/signer/ed25519_signer_test.dart b/test/cdsa/eddsa/ed25519/signer/ed25519_signer_test.dart new file mode 100644 index 00000000..f6920e1a --- /dev/null +++ b/test/cdsa/eddsa/ed25519/signer/ed25519_signer_test.dart @@ -0,0 +1,42 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/cdsa/eddsa/ed25519/signer/ed25519_signer.dart'; +import 'package:cryptography_utils/src/cdsa/eddsa/ed25519/signer/ed_signature.dart'; +import 'package:test/test.dart'; + +void main() { + ED25519Signer actualED25519Signer = ED25519Signer( + hashFunction: Sha512(), + privateKey: ED25519PrivateKey( + metadata: Bip32KeyMetadata( + depth: 5, + shiftedIndex: 0, + fingerprint: BigInt.parse('2837893204'), + parentFingerprint: BigInt.parse('162080603'), + masterFingerprint: BigInt.parse('83580899'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + ), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M=')), + ), + ); + + group('Tests of ED25519Signer.sign()', () { + test('Should [return EDSignature] generated for given message', () { + // Assert + Uint8List actualMessage = utf8.encode('Hello, World!'); + + // Act + EDSignature actualSignature = actualED25519Signer.sign(actualMessage); + + // Assert + EDSignature expectedSignature = EDSignature( + r: base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='), + s: base64Decode('PzP+qMlZFKIuxxdoUKRtuHN22GtKwVuSdSHDZmjiowQ='), + ); + + expect(actualSignature, expectedSignature); + }); + }); +} diff --git a/test/cdsa/eddsa/ed25519/signer/ed25519_verifier_test.dart b/test/cdsa/eddsa/ed25519/signer/ed25519_verifier_test.dart new file mode 100644 index 00000000..235a9c33 --- /dev/null +++ b/test/cdsa/eddsa/ed25519/signer/ed25519_verifier_test.dart @@ -0,0 +1,95 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/cdsa/eddsa/ed25519/signer/ed25519_verifier.dart'; +import 'package:cryptography_utils/src/cdsa/eddsa/ed25519/signer/ed_signature.dart'; +import 'package:test/test.dart'; + +void main() { + ED25519Verifier actualED25519Verifier = ED25519Verifier( + hashFunction: Sha512(), + publicKey: ED25519PublicKey( + metadata: Bip32KeyMetadata( + depth: 5, + shiftedIndex: 0, + fingerprint: BigInt.parse('2837893204'), + parentFingerprint: BigInt.parse('162080603'), + masterFingerprint: BigInt.parse('83580899'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + ), + edPublicKey: EDPublicKey( + EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ), + ), + ), + ); + + group('Tests of ED25519Verifier.isSignatureValid()', () { + test('Should [return TRUE] if [signature VALID] for given public key', () { + // Arrange + Uint8List actualMessage = utf8.encode('Hello, World!'); + EDSignature actualSignature = EDSignature( + r: base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='), + s: base64Decode('PzP+qMlZFKIuxxdoUKRtuHN22GtKwVuSdSHDZmjiowQ='), + ); + + // Act + bool actualSignatureValidBool = actualED25519Verifier.isSignatureValid(actualMessage, actualSignature); + + // Assert + expect(actualSignatureValidBool, true); + }); + + test('Should [return FALSE] if [signature INVALID] for given public key', () { + // Arrange + Uint8List actualMessage = utf8.encode('Hello, World!'); + EDSignature actualSignature = EDSignature( + r: base64Decode('pQCXZYSu7MdCWso7jgZn9PG1Kx6eWfCP47Flf2GKYBg='), + s: base64Decode('FdUwxjlNiVx6Kg1cTbH4w3cJlLGDKzTttd5bTzvXSgs='), + ); + + // Act + bool actualSignatureValidBool = actualED25519Verifier.isSignatureValid(actualMessage, actualSignature); + + // Assert + expect(actualSignatureValidBool, false); + }); + + test('Should [throw Exception] if [signature INCORRECT] [signature S > G.n]', () { + // Arrange + Uint8List actualMessage = utf8.encode('Hello, World!'); + EDSignature actualSignature = EDSignature( + r: base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='), + s: base64Decode('7237005577332262213973186563042994240857116359379907606001950938285454250989'), // S >= G.n + ); + + // Act & Assert + expect( + () => actualED25519Verifier.isSignatureValid(actualMessage, actualSignature), + throwsException, + ); + }); + + test('Should [throw Exception] if [signature INCORRECT] [signature S == G.n]', () { + // Arrange + Uint8List actualMessage = utf8.encode('Hello, World!'); + EDSignature actualSignature = EDSignature( + r: base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='), + s: base64Decode('EAAAAAAAAAAAAAAAAAAAABTe+d6i95zWWBJjGlz10+0='), + ); + + // Act & Assert + expect( + () => actualED25519Verifier.isSignatureValid(actualMessage, actualSignature), + throwsException, + ); + }); + }); +} diff --git a/test/cdsa/eddsa/ed25519/signer/ed_signature_test.dart b/test/cdsa/eddsa/ed25519/signer/ed_signature_test.dart new file mode 100644 index 00000000..6c4919aa --- /dev/null +++ b/test/cdsa/eddsa/ed25519/signer/ed_signature_test.dart @@ -0,0 +1,118 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/cdsa/eddsa/ed25519/signer/ed_signature.dart'; +import 'package:test/test.dart'; + +void main() { + group('Tests of EDSignature.fromBase64() constructor', () { + test('Should [return EDSignature] constructed from base64 String', () { + // Arrange + String actualSignature = 'abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA/M/6oyVkUoi7HF2hQpG24c3bYa0rBW5J1IcNmaOKjBA=='; + + // Act + EDSignature actualEDSignature = EDSignature.fromBase64(actualSignature); + + // Assert + EDSignature expectedEDSignature = EDSignature( + r: base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='), + s: base64Decode('PzP+qMlZFKIuxxdoUKRtuHN22GtKwVuSdSHDZmjiowQ='), + ); + + expect(actualEDSignature, expectedEDSignature); + }); + }); + + group('Tests of EDSignature.fromBytes() constructor', () { + test('Should [return EDSignature] constructed from bytes', () { + // Arrange + String actualSignature = 'abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA/M/6oyVkUoi7HF2hQpG24c3bYa0rBW5J1IcNmaOKjBA=='; + Uint8List actualSignatureBytes = base64Decode(actualSignature); + + // Act + EDSignature actualEDSignature = EDSignature.fromBytes(actualSignatureBytes); + + // Assert + EDSignature expectedEDSignature = EDSignature( + r: base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='), + s: base64Decode('PzP+qMlZFKIuxxdoUKRtuHN22GtKwVuSdSHDZmjiowQ='), + ); + + expect(actualEDSignature, expectedEDSignature); + }); + }); + + group('Tests of EDSignature.bytes getter', () { + test('Should [return List] representing signature as bytes', () { + // Arrange + EDSignature actualEDSignature = EDSignature( + r: base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='), + s: base64Decode('PzP+qMlZFKIuxxdoUKRtuHN22GtKwVuSdSHDZmjiowQ='), + ); + + // Act + List actualSignatureBytes = actualEDSignature.bytes; + + // Assert + String expectedSignature = 'abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA/M/6oyVkUoi7HF2hQpG24c3bYa0rBW5J1IcNmaOKjBA=='; + Uint8List expectedSignatureBytes = base64Decode(expectedSignature); + + expect(actualSignatureBytes, expectedSignatureBytes); + }); + }); + + group('Tests of EDSignature.base64 getter', () { + test('Should [return String] representing signature in base64 format', () { + // Arrange + EDSignature actualEDSignature = EDSignature( + r: base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='), + s: base64Decode('PzP+qMlZFKIuxxdoUKRtuHN22GtKwVuSdSHDZmjiowQ='), + ); + + // Act + String actualSignature = actualEDSignature.base64; + + // Assert + String expectedSignature = 'abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA/M/6oyVkUoi7HF2hQpG24c3bYa0rBW5J1IcNmaOKjBA=='; + + expect(actualSignature, expectedSignature); + }); + }); + + group('Tests of EDSignature.hex getter', () { + test('Should [return HEX] representing signature in HEX format', () { + // Arrange + EDSignature actualEDSignature = EDSignature( + r: base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='), + s: base64Decode('PzP+qMlZFKIuxxdoUKRtuHN22GtKwVuSdSHDZmjiowQ='), + ); + + // Act + String actualSignature = actualEDSignature.hex; + + // Assert + String expectedSignature = + '0x69b20c87adad9bf33266639054dcfbb8d76a7c8801affc9ee68ea7f10ca606303f33fea8c95914a22ec7176850a46db87376d86b4ac15b927521c36668e2a304'; + + expect(actualSignature, expectedSignature); + }); + }); + + group('Tests of EDSignature.length getter', () { + test('Should [return integer] representing signature length', () { + // Arrange + EDSignature actualEDSignature = EDSignature( + r: base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='), + s: base64Decode('PzP+qMlZFKIuxxdoUKRtuHN22GtKwVuSdSHDZmjiowQ='), + ); + + // Act + int actualSignatureLength = actualEDSignature.length; + + // Assert + int expectedSignatureLength = 64; + + expect(actualSignatureLength, expectedSignatureLength); + }); + }); +} diff --git a/test/cdsa/eddsa/ed_point_test.dart b/test/cdsa/eddsa/ed_point_test.dart new file mode 100644 index 00000000..1680e841 --- /dev/null +++ b/test/cdsa/eddsa/ed_point_test.dart @@ -0,0 +1,349 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:test/test.dart'; + +void main() { + group('Tests of EDPoint.fromBytes() constructor', () { + test('Should [return EDPoint] from given bytes', () { + // Arrange + Uint8List actualBytes = base64Decode('abIMh62tm/MyZmOQVNz7uNdqfIgBr/ye5o6n8QymBjA='); + + // Act + EDPoint actualEDPoint = EDPoint.fromBytes(CurvePoints.generatorED25519, actualBytes); + + // Assert + EDPoint expectedEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('36296584640568705578765211864789903326726128726082628951800433550187951947162'), + y: BigInt.parse('21722763853247575927517928451216332729805032358530396811928860500080504648297'), + z: BigInt.one, + t: BigInt.parse( + '788462136826487035517522181628529984945326118901391734773631962347443138612680071187232142979650154875261951270767071733362165107888869070753013137283114'), + ); + + expect(actualEDPoint, expectedEDPoint); + }); + + test('Should [throw Exception] if [private key bytes length != 32] (length < 32)', () { + // Arrange + Uint8List actualBytes = Uint8List(31); + + // Assert + expect( + () => EDPoint.fromBytes(CurvePoints.generatorED25519, actualBytes), + throwsA(isA()), + ); + }); + + test('Should [throw Exception] if [private key bytes length != 32] (length > 32)', () { + // Arrange + Uint8List actualBytes = Uint8List(33); + + // Assert + expect( + () => EDPoint.fromBytes(CurvePoints.generatorED25519, actualBytes), + throwsA(isA()), + ); + }); + }); + + group('Tests of EDPoint.infinityFrom() constructor', () { + test('Should [return infinity EDPoint] basing on other EDPoint params (curve and order)', () { + // Arrange + EDPoint actualEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + // Act + EDPoint actualInfinityEDPoint = EDPoint.infinityFrom(actualEDPoint); + + // Assert + EDPoint expectedInfinityEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.zero, + y: BigInt.zero, + z: BigInt.zero, + t: BigInt.zero, + ); + + expect(actualInfinityEDPoint, expectedInfinityEDPoint); + }); + }); + + group('Tests of EDPoint - (negation) operator overload', () { + test('Should [return EDPoint] with negated y coordinate', () { + // Arrange + EDPoint actualEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + // Act + EDPoint actualNegatedEDPoint = -actualEDPoint; + + // Assert + EDPoint expectedNegatedEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('-19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('-18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + expect(actualNegatedEDPoint, expectedNegatedEDPoint); + }); + }); + + group('Tests of EDPoint + (addition) operator overload', () { + test('Should [return EDPoint] sum of given EDPoint', () { + // Arrange + EDPoint actualFirstEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + EDPoint actualSecondEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('6096909168845075289895766140667470516872676772040305287224409768025240085365'), + y: BigInt.parse('26827353303075333452492487487417802419667623484019508509947057840019676335990'), + z: BigInt.parse('34167211617719735327647555959431506547296161705475459783750089478168360658532'), + t: BigInt.parse('46277596158329281260802852912934565824330399740884467667771483970460031540457'), + ); + + // Act + EDPoint actualSumEDPoint = actualFirstEDPoint + actualSecondEDPoint; + + // Assert + EDPoint expectedSumEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('31526783872771802866845889330755663428311874124493638537576632137069217592509'), + y: BigInt.parse('48629464835436797222907152574229008341263093550785796383013624914972494502600'), + z: BigInt.parse('333800734235119114968985188976990598100047827976359596122448946415405085008'), + t: BigInt.parse('36042969637043635775808115931322878445759837817669436253876818904907009351091'), + ); + + expect(actualSumEDPoint, expectedSumEDPoint); + }); + }); + + group('Tests of EDPoint * (multiplication) operator overload', () { + test('Should [return EDPoint] multiplied by given scalar', () { + // Arrange + EDPoint actualEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + // Act + EDPoint actualMultipliedEDPoint = actualEDPoint * BigInt.parse('52296706698976945379478339393009008515096998632529786687805880390877129570304'); + + // Assert + EDPoint expectedMultipliedEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('51244919994351172387417348510608637925235608054940772592337648463674609760537'), + y: BigInt.parse('35449132413827682982386728877599892602830155330348740731324917440722313036655'), + z: BigInt.parse('7168729421692682075928873541290169440201662592510102281021726379451957383384'), + t: BigInt.parse('27233844124704309868588251123744664512193349282150838965396361685047483783056'), + ); + + expect(actualMultipliedEDPoint, expectedMultipliedEDPoint); + }); + + test('Should [return infinity EDPoint] if given [scalar == 0]', () { + // Arrange + EDPoint actualEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + // Act + EDPoint actualMultipliedEDPoint = actualEDPoint * BigInt.zero; + + // Assert + EDPoint expectedMultipliedEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.zero, + y: BigInt.zero, + z: BigInt.zero, + t: BigInt.zero, + ); + + expect(actualMultipliedEDPoint, expectedMultipliedEDPoint); + }); + + test('Should [return infinity EDPoint] if multiplied EDPoint has [x == 0]', () { + // Arrange + EDPoint actualEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.zero, + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + // Act + EDPoint actualMultipliedEDPoint = actualEDPoint * BigInt.parse('52296706698976945379478339393009008515096998632529786687805880390877129570304'); + + // Assert + EDPoint expectedMultipliedEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.zero, + y: BigInt.zero, + z: BigInt.zero, + t: BigInt.zero, + ); + + expect(actualMultipliedEDPoint, expectedMultipliedEDPoint); + }); + + test('Should [return infinity EDPoint] if multiplied EDPoint has [y == 0]', () { + // Arrange + EDPoint actualEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.zero, + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + // Act + EDPoint actualMultipliedEDPoint = actualEDPoint * BigInt.parse('52296706698976945379478339393009008515096998632529786687805880390877129570304'); + + // Assert + EDPoint expectedMultipliedEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.zero, + y: BigInt.zero, + z: BigInt.zero, + t: BigInt.zero, + ); + + expect(actualMultipliedEDPoint, expectedMultipliedEDPoint); + }); + + test('Should [return EDPoint] without changes if given [scalar == 1]', () { + // Arrange + EDPoint actualEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + // Act + EDPoint actualMultipliedEDPoint = actualEDPoint * BigInt.one; + + // Assert + EDPoint expectedMultipliedEDPoint = actualEDPoint; + + expect(actualMultipliedEDPoint, expectedMultipliedEDPoint); + }); + }); + + group('Tests of EDPoint.toBytes()', () { + test('Should [return bytes] from given EDPoint', () { + // Arrange + EDPoint actualEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + // Act + Uint8List actualBytes = actualEDPoint.toBytes(); + + // Assert + Uint8List expectedBytes = base64Decode('z7Dbiwy9pzyyNc8VrMNqmdssCM0tlKxB9Yk8umwbYwc='); + + expect(actualBytes, expectedBytes); + }); + }); + + group('Tests of EDPoint.scaleToAffineCoordinates()', () { + test('Should [return EDPoint] scaled to affine coordinates', () { + // Arrange + EDPoint actualEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ); + + // Act + EDPoint actualAffineEDPoint = actualEDPoint.scaleToAffineCoordinates(); + + // Assert + EDPoint expectedAffineEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('54065565874754690464541746979170565753169877194888053393545030920551520745988'), + y: BigInt.parse('3341297077934518673457142734077350879420471371059900657313532910747302736079'), + z: BigInt.one, + t: BigInt.parse('17889545404307743854678190178496636156195764849150499252736625982701909100111'), + ); + + expect(actualAffineEDPoint, expectedAffineEDPoint); + }); + + test('Should [return EDPoint] without changes if given point is already affine point (z == 1)', () { + // Arrange + EDPoint actualEDPoint = EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('54065565874754690464541746979170565753169877194888053393545030920551520745988'), + y: BigInt.parse('3341297077934518673457142734077350879420471371059900657313532910747302736079'), + z: BigInt.one, + t: BigInt.parse('17889545404307743854678190178496636156195764849150499252736625982701909100111'), + ); + + // Act + EDPoint actualScaledEDPoint = actualEDPoint.scaleToAffineCoordinates(); + + // Assert + EDPoint expectedScaledEDPoint = actualEDPoint; + + expect(actualScaledEDPoint, expectedScaledEDPoint); + }); + }); +} diff --git a/test/cdsa/eddsa/ed_private_key_test.dart b/test/cdsa/eddsa/ed_private_key_test.dart new file mode 100644 index 00000000..d0783d2b --- /dev/null +++ b/test/cdsa/eddsa/ed_private_key_test.dart @@ -0,0 +1,79 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:test/test.dart'; + +void main() { + group('Tests of EDPrivateKey.bytes getter', () { + test('Should [return private key bytes] from EDPrivateKey', () { + // Arrange + EDPrivateKey actualEDPrivateKey = EDPrivateKey.fromBytes(base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M=')); + + // Act + Uint8List actualPrivateKeyBytes = actualEDPrivateKey.bytes; + + // Assert + Uint8List expectedPrivateKeyBytes = base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M='); + + expect(actualPrivateKeyBytes, expectedPrivateKeyBytes); + }); + }); + + group('Tests of EDPrivateKey.edPublicKey getter', () { + test('Should [return EDPublicKey] from ECPrivateKey', () { + // Arrange + EDPrivateKey actualEDPrivateKey = EDPrivateKey.fromBytes(base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M=')); + + // Act + EDPublicKey actualEDPublicKey = actualEDPrivateKey.edPublicKey; + + // Assert + EDPublicKey expectedEDPublicKey = EDPublicKey( + EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ), + ); + + expect(actualEDPublicKey, expectedEDPublicKey); + }); + }); + + group('Tests of EDPrivateKey.a getter', () { + test('Should [return a (pruned private key)] from EDPrivateKey', () { + // Arrange + EDPrivateKey actualEDPrivateKey = EDPrivateKey.fromBytes(base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M=')); + + // Act + BigInt actualA = actualEDPrivateKey.a; + + // Assert + BigInt expectedA = BigInt.parse('52296706698976945379478339393009008515096998632529786687805880390877129570304'); + + expect(actualA, expectedA); + }); + + test('Should [throw Exception] if cofactor h is not 4 or 8', () { + // Arrange + EDPrivateKey actualEDPrivateKey = EDPrivateKey.fromBytes( + base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M='), + G: CurvePoints.generatorED25519.copyWith( + curve: EDCurve( + a: BigInt.from(-1), + d: BigInt.parse('37095705934669439343138083508754565189542113879843219016388785533085940283555'), + h: BigInt.zero, + p: BigInt.parse('57896044618658097711785492504343953926634992332820282019728792003956564819949'), + ), + ), + ); + + // Act & Assert + expect(() => actualEDPrivateKey.a, throwsException); + }); + }); +} diff --git a/test/cdsa/eddsa/ed_public_key_test.dart b/test/cdsa/eddsa/ed_public_key_test.dart new file mode 100644 index 00000000..918fc603 --- /dev/null +++ b/test/cdsa/eddsa/ed_public_key_test.dart @@ -0,0 +1,55 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:test/test.dart'; + +void main() { + group('Tests of EDPublicKey.bytes getter', () { + test('Should [return bytes] representing ECPublicKey', () { + // Arrange + EDPublicKey actualEDPublicKey = EDPublicKey( + EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ), + ); + + // Act + Uint8List actualBytes = actualEDPublicKey.bytes; + + // Assert + Uint8List expectedBytes = base64Decode('z7Dbiwy9pzyyNc8VrMNqmdssCM0tlKxB9Yk8umwbYwc='); + + expect(actualBytes, expectedBytes); + }); + }); + + group('Tests of EDPublicKey.length getter', () { + test('Should [return integer] representing length of EDPublicKey', () { + // Arrange + EDPublicKey actualEDPublicKey = EDPublicKey( + EDPoint( + curve: Curves.ed25519, + n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), + x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), + y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), + z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), + t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + ), + ); + + // Act + int actualBaselen = actualEDPublicKey.length; + + // Assert + int expectedBaselen = 32; + + expect(actualBaselen, expectedBaselen); + }); + }); +} diff --git a/test/utils/ed25519_utils_test.dart b/test/utils/ed25519_utils_test.dart new file mode 100644 index 00000000..776f2403 --- /dev/null +++ b/test/utils/ed25519_utils_test.dart @@ -0,0 +1,130 @@ +import 'package:cryptography_utils/src/utils/ed25519_utils.dart'; +import 'package:test/test.dart'; + +void main() { + group('Tests of ED25519Utils.findModularSquareRoot()', () { + test('Should [find square root] where [p % 4 = 3]', () { + // Act + BigInt actualModularSquareRoot = ED25519Utils.findModularSquareRoot( + a: BigInt.parse('4'), + p: BigInt.parse('7'), + ); + + // Assert + BigInt expectedModularSquareRoot = BigInt.parse('2'); + + expect(actualModularSquareRoot, expectedModularSquareRoot); + }); + + test('Should [find square root] where [p % 8 = 5]', () { + // Act + BigInt actualModularSquareRoot = ED25519Utils.findModularSquareRoot( + a: BigInt.parse('10'), + p: BigInt.parse('13'), + ); + + // Assert + BigInt expectedModularSquareRoot = BigInt.parse('7'); + + expect(actualModularSquareRoot, expectedModularSquareRoot); + }); + + test('Should [find square root] where [x^2 = a mod p]', () { + // Act + BigInt actualModularSquareRoot = ED25519Utils.findModularSquareRoot( + a: BigInt.parse('25'), + p: BigInt.parse('41'), + ); + + // Assert + BigInt expectedModularSquareRoot = BigInt.parse('36'); + + expect(actualModularSquareRoot, expectedModularSquareRoot); + }); + + test('Should [find square root] where [a = 0]', () { + // Act + BigInt actualModularSquareRoot = ED25519Utils.findModularSquareRoot( + a: BigInt.parse('0'), + p: BigInt.parse('11'), + ); + + // Assert + BigInt expectedModularSquareRoot = BigInt.parse('0'); + + expect(actualModularSquareRoot, expectedModularSquareRoot); + }); + + test('Should [find square root] where [a = p - 1]', () { + // Act + BigInt actualModularSquareRoot = ED25519Utils.findModularSquareRoot( + a: BigInt.parse('16'), + p: BigInt.parse('17'), + ); + + // Assert + BigInt expectedModularSquareRoot = BigInt.parse('13'); + + expect(actualModularSquareRoot, expectedModularSquareRoot); + }); + + test('Should [find square root] where parameters are large numbers', () { + // Act + BigInt actualModularSquareRoot = ED25519Utils.findModularSquareRoot( + a: BigInt.parse('123456789'), + p: BigInt.parse('1000000007'), + ); + + // Assert + BigInt expectedModularSquareRoot = BigInt.parse('151347102'); + + expect(actualModularSquareRoot, expectedModularSquareRoot); + }); + + test('Should [find square root] where parameters are large numbers', () { + // Act + BigInt actualModularSquareRoot = ED25519Utils.findModularSquareRoot( + a: BigInt.parse('17732832541123001036019990679150092111822642384727112811704228629262802952098'), + p: BigInt.parse('57896044618658097711785492504343953926634992332820282019728792003956564819949'), + ); + + // Assert + BigInt expectedModularSquareRoot = BigInt.parse('35747017703550611212035021907870961965630890711630954962468208729293799748073'); + + expect(actualModularSquareRoot, expectedModularSquareRoot); + }); + + test('Should [throw Exception] if [jacobi symbol NOT EXISTS] (p is odd)', () { + // Assert + expect( + () => ED25519Utils.findModularSquareRoot( + a: BigInt.parse('3'), + p: BigInt.parse('8'), + ), + throwsException, + ); + }); + + test('Should [throw Exception] if [jacobi symbol NOT EXISTS] (p is smaller than 2)', () { + // Assert + expect( + () => ED25519Utils.findModularSquareRoot( + a: BigInt.parse('9'), + p: BigInt.parse('1'), + ), + throwsException, + ); + }); + + test('Should [throw Exception] if [solution NOT EXISTS]', () { + // Assert + expect( + () => ED25519Utils.findModularSquareRoot( + a: BigInt.parse('5'), + p: BigInt.parse('13'), + ), + throwsException, + ); + }); + }); +}