Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f73a273
refactor: upgrade major dependencies (#120)
alfonsobries Jul 16, 2024
761ddd2
chore: update documentation link in readme (#137)
Nov 25, 2024
d3e19e9
chore: minor update dependencies (#182)
alexbarnsley Jul 2, 2025
5b52e60
style: resolve style guide violations
ItsANameToo Jul 2, 2025
b77b1c5
feat: add BLS implementation
shahin-hq Jun 11, 2026
9d97386
feat: add BLS implementation
shahin-hq Jun 11, 2026
6b704ff
feat: add BLS implementation
shahin-hq Jun 11, 2026
57df336
feat: add BLS implementation
shahin-hq Jun 11, 2026
ca56d01
feat: add BLS implementation
shahin-hq Jun 11, 2026
b09a9b2
feat: add BLS implementation
shahin-hq Jun 11, 2026
12b123a
style: resolve style guide violations
shahin-hq Jun 12, 2026
96a50c7
feat: add BLS implementation
shahin-hq Jun 12, 2026
3ed2227
feat: add BLS implementation
shahin-hq Jun 12, 2026
de231aa
feat: add BLS implementation
shahin-hq Jun 12, 2026
656d872
feat: add BLS implementation
shahin-hq Jun 12, 2026
b282351
feat: add BLS implementation
shahin-hq Jun 12, 2026
bd3773a
style: resolve style guide violations
shahin-hq Jun 12, 2026
e04fa7f
feat: add BLS implementation
shahin-hq Jun 12, 2026
a4cdc7c
style: resolve style guide violations
shahin-hq Jun 12, 2026
0fa317c
Merge branch 'feat/mainsail' into feat/add-bls-passphrase
shahin-hq Jun 15, 2026
6d58243
style: resolve style guide violations
shahin-hq Jun 15, 2026
bae9fbd
feat: add BLS implementation
shahin-hq Jun 15, 2026
a4c89d1
Merge remote-tracking branch 'origin/feat/add-bls-passphrase' into fe…
shahin-hq Jun 15, 2026
4a24a03
feat: add BLS implementation
shahin-hq Jun 15, 2026
aba0a21
style: resolve style guide violations
shahin-hq Jun 15, 2026
87609b2
feat: add BLS implementation
shahin-hq Jun 16, 2026
25bab3e
Merge remote-tracking branch 'origin/feat/add-bls-passphrase' into fe…
shahin-hq Jun 16, 2026
da808fb
style: resolve style guide violations
shahin-hq Jun 16, 2026
04aca8a
feat: add BLS implementation
shahin-hq Jun 16, 2026
411c94a
Merge remote-tracking branch 'origin/feat/add-bls-passphrase' into fe…
shahin-hq Jun 16, 2026
4cd94bc
style: resolve style guide violations
shahin-hq Jun 16, 2026
1774168
Merge branch 'feat/mainsail' into feat/add-bls-passphrase
shahin-hq Jun 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .php-cs-fixer.cache

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"brick/math": "^0.14.2",
"kornrunner/keccak": "^1.1",
"protonlabs/bitcoin": "^1.0",
"simplito/elliptic-php": "^1.0"
"simplito/elliptic-php": "^1.0",
"ext-gmp": "*"

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protonlabs/bitcoin has shanecurran/phpecc dependency which requires ext-gmp

https://github.com/shanecurran/phpecc/blob/master/composer.json#L33

},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.59",
Expand Down
193 changes: 193 additions & 0 deletions src/BLS/Curves/G1.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<?php

declare(strict_types=1);

namespace ArkEcosystem\Crypto\BLS\Curves;

use ArkEcosystem\Crypto\BLS\Fields\Fp;

/**
* BLS12-381 G1 curve: y² = x³ + 4 over Fp.
* Points stored in Jacobian projective coordinates (X:Y:Z), affine = (X/Z², Y/Z³).
* Identity = (0:1:0).
*/
final class G1
{
// Generator point (affine)
public const GX = '17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb';

public const GY = '08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1';

private readonly Fp $x;

private readonly Fp $y;

private readonly Fp $z; // z = zero means identity

private function __construct(Fp $x, Fp $y, Fp $z)
{
$this->x = $x;
$this->y = $y;
$this->z = $z;
}

public static function generator(): self
{
return new self(Fp::fromHex(self::GX), Fp::fromHex(self::GY), Fp::one());
}

public static function identity(): self
{
return new self(Fp::zero(), Fp::one(), Fp::zero());
}

public function isIdentity(): bool
{
return $this->z->isZero();
}

// -------------------------------------------------------------------------
// Point doubling (dbl-2009-l, a=0)
// -------------------------------------------------------------------------

public function double(): self
{
if ($this->isIdentity()) {
return self::identity();
}

$X = $this->x;
$Y = $this->y;
$Z = $this->z;

$A = $X->square();
$B = $Y->square();
$C = $B->square();
$D = $X->add($B)->square()->sub($A)->sub($C)->mul(Fp::fromInt(2));
$E = $A->mul(Fp::fromInt(3));
$F = $E->square();
$X3 = $F->sub($D->mul(Fp::fromInt(2)));
$Y3 = $E->mul($D->sub($X3))->sub($C->mul(Fp::fromInt(8)));
$Z3 = $Y->mul($Z)->mul(Fp::fromInt(2));

return new self($X3, $Y3, $Z3);
}

// -------------------------------------------------------------------------
// Point addition (add-2007-bl, handles mixed/projective)
// -------------------------------------------------------------------------

public function add(self $other): self
{
if ($this->isIdentity()) {
return $other;
}
if ($other->isIdentity()) {
return $this;
}

$X1 = $this->x;
$Y1 = $this->y;
$Z1 = $this->z;
$X2 = $other->x;
$Y2 = $other->y;
$Z2 = $other->z;

$Z1Z1 = $Z1->square();
$Z2Z2 = $Z2->square();
$U1 = $X1->mul($Z2Z2);
$U2 = $X2->mul($Z1Z1);
$S1 = $Y1->mul($Z2)->mul($Z2Z2);
$S2 = $Y2->mul($Z1)->mul($Z1Z1);
$H = $U2->sub($U1);
$r = $S2->sub($S1)->mul(Fp::fromInt(2));

// If H == 0 and r == 0: both points are equal, use double
if ($H->isZero()) {
if ($r->isZero()) {
return $this->double();
}

return self::identity(); // opposite points
}

$I = $H->mul(Fp::fromInt(2))->square();
$J = $H->mul($I);
$V = $U1->mul($I);
$X3 = $r->square()->sub($J)->sub($V->mul(Fp::fromInt(2)));
$Y3 = $r->mul($V->sub($X3))->sub($S1->mul($J)->mul(Fp::fromInt(2)));
$Z3 = $Z1->add($Z2)->square()->sub($Z1Z1)->sub($Z2Z2)->mul($H);

return new self($X3, $Y3, $Z3);
}

public function neg(): self
{
return new self($this->x, $this->y->neg(), $this->z);
}

// -------------------------------------------------------------------------
// Scalar multiplication (LSB double-and-add)
// -------------------------------------------------------------------------

public function scalarMul(\GMP $k): self
{
if (gmp_sign($k) === 0) {
return self::identity();
}

$result = self::identity();
$addend = $this;
$n = gmp_abs($k);

while (gmp_cmp($n, gmp_init(0)) > 0) {
if (gmp_testbit($n, 0)) {
$result = $result->add($addend);
}
$addend = $addend->double();
$n = gmp_div($n, gmp_init(2));
}

if (gmp_sign($k) < 0) {
return $result->neg();
}

return $result;
}

// -------------------------------------------------------------------------
// Compressed serialization (ZCash encoding, 48 bytes)
// -------------------------------------------------------------------------

public function toCompressedBytes(): string
{
if ($this->isIdentity()) {
$bytes = str_repeat("\x00", 48);
$bytes[0] = chr(0xc0); // 0x80 (compressed) | 0x40 (infinity)

return $bytes;
}

// Convert to affine
$zinv = $this->z->inv();
$zinv2 = $zinv->square();
$zinv3 = $zinv2->mul($zinv);
$ax = $this->x->mul($zinv2);
$ay = $this->y->mul($zinv3);

$bytes = $ax->toBytes(); // 48 bytes, top 3 bits of first byte are 0
// Bit 7 (0x80): compressed flag
$bytes[0] = chr(ord($bytes[0]) | 0x80);
// Bit 5 (0x20): sort/sign flag, set if y > (p-1)/2
if ($ay->isNegative()) {
$bytes[0] = chr(ord($bytes[0]) | 0x20);
}

return $bytes;
}

public function toHex(): string
{
return bin2hex($this->toCompressedBytes());
}
}
Loading
Loading