├── .github ├── CONTRIBUTING.md ├── FUNDING.yml └── PULL_REQUEST_TEMPLATE.md ├── Curve.php ├── EcDH.php ├── LICENSE ├── Math.php ├── ModularArithmetic.php ├── NistCurve.php ├── Point.php ├── PrivateKey.php ├── PublicKey.php ├── README.md └── composer.json /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. 4 | Please do not submit any Pull Requests here. It will be automatically closed. 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: FlorentMorselli 2 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please do not submit any Pull Requests here. It will be automatically closed. 2 | 3 | You should submit it here: https://github.com/web-token/jwt-framework/pulls 4 | -------------------------------------------------------------------------------- /Curve.php: -------------------------------------------------------------------------------- 1 | getA()) . ', ' . Math::toString($this->getB()) . ', ' . Math::toString( 29 | $this->getPrime() 30 | ) . ')'; 31 | } 32 | 33 | public function getA(): BigInteger 34 | { 35 | return $this->a; 36 | } 37 | 38 | public function getB(): BigInteger 39 | { 40 | return $this->b; 41 | } 42 | 43 | public function getPrime(): BigInteger 44 | { 45 | return $this->prime; 46 | } 47 | 48 | public function getSize(): int 49 | { 50 | return $this->size; 51 | } 52 | 53 | public function getPoint(BigInteger $x, BigInteger $y, ?BigInteger $order = null): Point 54 | { 55 | if (! $this->contains($x, $y)) { 56 | throw new RuntimeException('Curve ' . $this->__toString() . ' does not contain point (' . Math::toString( 57 | $x 58 | ) . ', ' . Math::toString($y) . ')'); 59 | } 60 | $point = Point::create($x, $y, $order); 61 | if ($order !== null) { 62 | $mul = $this->mul($point, $order); 63 | if (! $mul->isInfinity()) { 64 | throw new RuntimeException('SELF * ORDER MUST EQUAL INFINITY.'); 65 | } 66 | } 67 | 68 | return $point; 69 | } 70 | 71 | public function getPublicKeyFrom(BigInteger $x, BigInteger $y): PublicKey 72 | { 73 | $zero = BigInteger::zero(); 74 | if ($x->compareTo($zero) < 0 || $y->compareTo($zero) < 0 || $this->generator->getOrder()->compareTo( 75 | $x 76 | ) <= 0 || $this->generator->getOrder() 77 | ->compareTo($y) <= 0) { 78 | throw new RuntimeException('Generator point has x and y out of range.'); 79 | } 80 | $point = $this->getPoint($x, $y); 81 | 82 | return new PublicKey($point); 83 | } 84 | 85 | public function contains(BigInteger $x, BigInteger $y): bool 86 | { 87 | return Math::equals( 88 | ModularArithmetic::sub( 89 | $y->power(2), 90 | Math::add(Math::add($x->power(3), $this->getA()->multipliedBy($x)), $this->getB()), 91 | $this->getPrime() 92 | ), 93 | BigInteger::zero() 94 | ); 95 | } 96 | 97 | public function add(Point $one, Point $two): Point 98 | { 99 | if ($two->isInfinity()) { 100 | return clone $one; 101 | } 102 | 103 | if ($one->isInfinity()) { 104 | return clone $two; 105 | } 106 | 107 | if ($two->getX()->isEqualTo($one->getX())) { 108 | if ($two->getY()->isEqualTo($one->getY())) { 109 | return $this->getDouble($one); 110 | } 111 | 112 | return Point::infinity(); 113 | } 114 | 115 | $slope = ModularArithmetic::div( 116 | $two->getY() 117 | ->minus($one->getY()), 118 | $two->getX() 119 | ->minus($one->getX()), 120 | $this->getPrime() 121 | ); 122 | 123 | $xR = ModularArithmetic::sub($slope->power(2)->minus($one->getX()), $two->getX(), $this->getPrime()); 124 | 125 | $yR = ModularArithmetic::sub( 126 | $slope->multipliedBy($one->getX()->minus($xR)), 127 | $one->getY(), 128 | $this->getPrime() 129 | ); 130 | 131 | return $this->getPoint($xR, $yR, $one->getOrder()); 132 | } 133 | 134 | public function mul(Point $one, BigInteger $n): Point 135 | { 136 | if ($one->isInfinity()) { 137 | return Point::infinity(); 138 | } 139 | 140 | /** @var BigInteger $zero */ 141 | $zero = BigInteger::zero(); 142 | if ($one->getOrder()->compareTo($zero) > 0) { 143 | $n = $n->mod($one->getOrder()); 144 | } 145 | 146 | if ($n->isEqualTo($zero)) { 147 | return Point::infinity(); 148 | } 149 | 150 | /** @var Point[] $r */ 151 | $r = [Point::infinity(), clone $one]; 152 | 153 | $k = $this->getSize(); 154 | $n1 = str_pad(Math::baseConvert(Math::toString($n), 10, 2), $k, '0', STR_PAD_LEFT); 155 | 156 | for ($i = 0; $i < $k; ++$i) { 157 | $j = $n1[$i]; 158 | Point::cswap($r[0], $r[1], $j ^ 1); 159 | $r[0] = $this->add($r[0], $r[1]); 160 | $r[1] = $this->getDouble($r[1]); 161 | Point::cswap($r[0], $r[1], $j ^ 1); 162 | } 163 | 164 | $this->validate($r[0]); 165 | 166 | return $r[0]; 167 | } 168 | 169 | public function cmp(self $other): int 170 | { 171 | $equal = $this->getA() 172 | ->isEqualTo($other->getA()) 173 | && $this->getB() 174 | ->isEqualTo($other->getB()) 175 | && $this->getPrime() 176 | ->isEqualTo($other->getPrime()) 177 | ; 178 | 179 | return $equal ? 0 : 1; 180 | } 181 | 182 | public function equals(self $other): bool 183 | { 184 | return $this->cmp($other) === 0; 185 | } 186 | 187 | public function getDouble(Point $point): Point 188 | { 189 | if ($point->isInfinity()) { 190 | return Point::infinity(); 191 | } 192 | 193 | $a = $this->getA(); 194 | $threeX2 = BigInteger::of(3)->multipliedBy($point->getX()->power(2)); 195 | 196 | $tangent = ModularArithmetic::div( 197 | $threeX2->plus($a), 198 | BigInteger::of(2)->multipliedBy($point->getY()), 199 | $this->getPrime() 200 | ); 201 | 202 | $x3 = ModularArithmetic::sub( 203 | $tangent->power(2), 204 | BigInteger::of(2)->multipliedBy($point->getX()), 205 | $this->getPrime() 206 | ); 207 | 208 | $y3 = ModularArithmetic::sub( 209 | $tangent->multipliedBy($point->getX()->minus($x3)), 210 | $point->getY(), 211 | $this->getPrime() 212 | ); 213 | 214 | return $this->getPoint($x3, $y3, $point->getOrder()); 215 | } 216 | 217 | public function createPrivateKey(): PrivateKey 218 | { 219 | return PrivateKey::create($this->generate()); 220 | } 221 | 222 | public function createPublicKey(PrivateKey $privateKey): PublicKey 223 | { 224 | $point = $this->mul($this->generator, $privateKey->getSecret()); 225 | 226 | return new PublicKey($point); 227 | } 228 | 229 | public function getGenerator(): Point 230 | { 231 | return $this->generator; 232 | } 233 | 234 | private function validate(Point $point): void 235 | { 236 | if (! $point->isInfinity() && ! $this->contains($point->getX(), $point->getY())) { 237 | throw new RuntimeException('Invalid point'); 238 | } 239 | } 240 | 241 | private function generate(): BigInteger 242 | { 243 | $max = $this->generator->getOrder(); 244 | $numBits = $this->bnNumBits($max); 245 | $numBytes = (int) ceil($numBits / 8); 246 | // Generate an integer of size >= $numBits 247 | $bytes = BigInteger::randomBits($numBytes); 248 | $mask = BigInteger::of(2)->power($numBits)->minus(1); 249 | 250 | return $bytes->and($mask); 251 | } 252 | 253 | /** 254 | * Returns the number of bits used to store this number. Non-significant upper bits are not counted. 255 | * 256 | * @see https://www.openssl.org/docs/crypto/BN_num_bytes.html 257 | */ 258 | private function bnNumBits(BigInteger $x): int 259 | { 260 | $zero = BigInteger::of(0); 261 | if ($x->isEqualTo($zero)) { 262 | return 0; 263 | } 264 | $log2 = 0; 265 | while (! $x->isEqualTo($zero)) { 266 | $x = $x->shiftedRight(1); 267 | ++$log2; 268 | } 269 | 270 | return $log2; 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /EcDH.php: -------------------------------------------------------------------------------- 1 | mul($publicKey->getPoint(), $privateKey->getSecret()) 41 | ->getX() 42 | ; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2019 Spomky-Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Math.php: -------------------------------------------------------------------------------- 1 | isEqualTo($other); 18 | } 19 | 20 | public static function add(BigInteger $augend, BigInteger $addend): BigInteger 21 | { 22 | return $augend->plus($addend); 23 | } 24 | 25 | public static function toString(BigInteger $value): string 26 | { 27 | return $value->toBase(10); 28 | } 29 | 30 | public static function inverseMod(BigInteger $a, BigInteger $m): BigInteger 31 | { 32 | return CoreBigInteger::createFromBigInteger($a)->modInverse(CoreBigInteger::createFromBigInteger($m))->get(); 33 | } 34 | 35 | public static function baseConvert(string $number, int $from, int $to): string 36 | { 37 | return BigInteger::fromBase($number, $from)->toBase($to); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ModularArithmetic.php: -------------------------------------------------------------------------------- 1 | minus($subtrahend) 17 | ->mod($modulus) 18 | ; 19 | } 20 | 21 | public static function mul(BigInteger $multiplier, BigInteger $muliplicand, BigInteger $modulus): BigInteger 22 | { 23 | return $multiplier->multipliedBy($muliplicand) 24 | ->mod($modulus) 25 | ; 26 | } 27 | 28 | public static function div(BigInteger $dividend, BigInteger $divisor, BigInteger $modulus): BigInteger 29 | { 30 | return self::mul($dividend, Math::inverseMod($divisor, $modulus), $modulus); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /NistCurve.php: -------------------------------------------------------------------------------- 1 | infinity; 55 | } 56 | 57 | public function getOrder(): BigInteger 58 | { 59 | return $this->order; 60 | } 61 | 62 | public function getX(): BigInteger 63 | { 64 | return $this->x; 65 | } 66 | 67 | public function getY(): BigInteger 68 | { 69 | return $this->y; 70 | } 71 | 72 | public static function cswap(self $a, self $b, int $cond): void 73 | { 74 | self::cswapBigInteger($a->x, $b->x, $cond); 75 | self::cswapBigInteger($a->y, $b->y, $cond); 76 | self::cswapBigInteger($a->order, $b->order, $cond); 77 | self::cswapBoolean($a->infinity, $b->infinity, $cond); 78 | } 79 | 80 | private static function cswapBoolean(bool &$a, bool &$b, int $cond): void 81 | { 82 | $sa = BigInteger::of((int) $a); 83 | $sb = BigInteger::of((int) $b); 84 | 85 | self::cswapBigInteger($sa, $sb, $cond); 86 | 87 | $a = (bool) $sa->toBase(10); 88 | $b = (bool) $sb->toBase(10); 89 | } 90 | 91 | private static function cswapBigInteger(BigInteger &$sa, BigInteger &$sb, int $cond): void 92 | { 93 | $size = max(mb_strlen($sa->toBase(2), '8bit'), mb_strlen($sb->toBase(2), '8bit')); 94 | $mask = (string) (1 - $cond); 95 | $mask = str_pad('', $size, $mask, STR_PAD_LEFT); 96 | $mask = BigInteger::fromBase($mask, 2); 97 | $taA = $sa->and($mask); 98 | $taB = $sb->and($mask); 99 | $sa = $sa->xor($sb) 100 | ->xor($taB) 101 | ; 102 | $sb = $sa->xor($sb) 103 | ->xor($taA) 104 | ; 105 | $sa = $sa->xor($sb) 106 | ->xor($taB) 107 | ; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /PrivateKey.php: -------------------------------------------------------------------------------- 1 | secret; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /PublicKey.php: -------------------------------------------------------------------------------- 1 | point; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ECC Tools For JWT-Framework 2 | ============================ 3 | 4 | This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. 5 | 6 | **Please do not submit any Pull Request here.** 7 | You should go to [the main repository](https://github.com/web-token/jwt-framework) instead. 8 | 9 | # Documentation 10 | 11 | The official documentation is available as https://web-token.spomky-labs.com/ 12 | 13 | # Licence 14 | 15 | This software is release under [MIT licence](LICENSE). 16 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-token/jwt-util-ecc", 3 | "description": "ECC Tools for the JWT Framework.", 4 | "type": "library", 5 | "license": "MIT", 6 | "keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"], 7 | "homepage": "https://github.com/web-token", 8 | "authors": [ 9 | { 10 | "name": "Florent Morselli", 11 | "homepage": "https://github.com/Spomky" 12 | },{ 13 | "name": "All contributors", 14 | "homepage": "https://github.com/web-token/jwt-framework/contributors" 15 | } 16 | ], 17 | "autoload": { 18 | "psr-4": { 19 | "Jose\\Component\\Core\\Util\\Ecc\\": "" 20 | } 21 | }, 22 | "require": { 23 | "php": ">=8.1", 24 | "brick/math": "^0.9" 25 | }, 26 | "suggest": { 27 | "ext-gmp": "GMP or BCMath is highly recommended to improve the library performance", 28 | "ext-bcmath": "GMP or BCMath is highly recommended to improve the library performance" 29 | } 30 | } 31 | --------------------------------------------------------------------------------