angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

Ed25519.php (17362B)


      1 <?php
      2 
      3 if (class_exists('ParagonIE_Sodium_Core_Ed25519', false)) {
      4     return;
      5 }
      6 
      7 /**
      8  * Class ParagonIE_Sodium_Core_Ed25519
      9  */
     10 abstract class ParagonIE_Sodium_Core_Ed25519 extends ParagonIE_Sodium_Core_Curve25519
     11 {
     12     const KEYPAIR_BYTES = 96;
     13     const SEED_BYTES = 32;
     14     const SCALAR_BYTES = 32;
     15 
     16     /**
     17      * @internal You should not use this directly from another application
     18      *
     19      * @return string (96 bytes)
     20      * @throws Exception
     21      * @throws SodiumException
     22      * @throws TypeError
     23      */
     24     public static function keypair()
     25     {
     26         $seed = random_bytes(self::SEED_BYTES);
     27         $pk = '';
     28         $sk = '';
     29         self::seed_keypair($pk, $sk, $seed);
     30         return $sk . $pk;
     31     }
     32 
     33     /**
     34      * @internal You should not use this directly from another application
     35      *
     36      * @param string $pk
     37      * @param string $sk
     38      * @param string $seed
     39      * @return string
     40      * @throws SodiumException
     41      * @throws TypeError
     42      */
     43     public static function seed_keypair(&$pk, &$sk, $seed)
     44     {
     45         if (self::strlen($seed) !== self::SEED_BYTES) {
     46             throw new RangeException('crypto_sign keypair seed must be 32 bytes long');
     47         }
     48 
     49         /** @var string $pk */
     50         $pk = self::publickey_from_secretkey($seed);
     51         $sk = $seed . $pk;
     52         return $sk;
     53     }
     54 
     55     /**
     56      * @internal You should not use this directly from another application
     57      *
     58      * @param string $keypair
     59      * @return string
     60      * @throws TypeError
     61      */
     62     public static function secretkey($keypair)
     63     {
     64         if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
     65             throw new RangeException('crypto_sign keypair must be 96 bytes long');
     66         }
     67         return self::substr($keypair, 0, 64);
     68     }
     69 
     70     /**
     71      * @internal You should not use this directly from another application
     72      *
     73      * @param string $keypair
     74      * @return string
     75      * @throws TypeError
     76      */
     77     public static function publickey($keypair)
     78     {
     79         if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
     80             throw new RangeException('crypto_sign keypair must be 96 bytes long');
     81         }
     82         return self::substr($keypair, 64, 32);
     83     }
     84 
     85     /**
     86      * @internal You should not use this directly from another application
     87      *
     88      * @param string $sk
     89      * @return string
     90      * @throws SodiumException
     91      * @throws TypeError
     92      */
     93     public static function publickey_from_secretkey($sk)
     94     {
     95         /** @var string $sk */
     96         $sk = hash('sha512', self::substr($sk, 0, 32), true);
     97         $sk[0] = self::intToChr(
     98             self::chrToInt($sk[0]) & 248
     99         );
    100         $sk[31] = self::intToChr(
    101             (self::chrToInt($sk[31]) & 63) | 64
    102         );
    103         return self::sk_to_pk($sk);
    104     }
    105 
    106     /**
    107      * @param string $pk
    108      * @return string
    109      * @throws SodiumException
    110      * @throws TypeError
    111      */
    112     public static function pk_to_curve25519($pk)
    113     {
    114         if (self::small_order($pk)) {
    115             throw new SodiumException('Public key is on a small order');
    116         }
    117         $A = self::ge_frombytes_negate_vartime(self::substr($pk, 0, 32));
    118         $p1 = self::ge_mul_l($A);
    119         if (!self::fe_isnonzero($p1->X)) {
    120             throw new SodiumException('Unexpected zero result');
    121         }
    122 
    123         # fe_1(one_minus_y);
    124         # fe_sub(one_minus_y, one_minus_y, A.Y);
    125         # fe_invert(one_minus_y, one_minus_y);
    126         $one_minux_y = self::fe_invert(
    127             self::fe_sub(
    128                 self::fe_1(),
    129                 $A->Y
    130             )
    131         );
    132 
    133         # fe_1(x);
    134         # fe_add(x, x, A.Y);
    135         # fe_mul(x, x, one_minus_y);
    136         $x = self::fe_mul(
    137             self::fe_add(self::fe_1(), $A->Y),
    138             $one_minux_y
    139         );
    140 
    141         # fe_tobytes(curve25519_pk, x);
    142         return self::fe_tobytes($x);
    143     }
    144 
    145     /**
    146      * @internal You should not use this directly from another application
    147      *
    148      * @param string $sk
    149      * @return string
    150      * @throws SodiumException
    151      * @throws TypeError
    152      */
    153     public static function sk_to_pk($sk)
    154     {
    155         return self::ge_p3_tobytes(
    156             self::ge_scalarmult_base(
    157                 self::substr($sk, 0, 32)
    158             )
    159         );
    160     }
    161 
    162     /**
    163      * @internal You should not use this directly from another application
    164      *
    165      * @param string $message
    166      * @param string $sk
    167      * @return string
    168      * @throws SodiumException
    169      * @throws TypeError
    170      */
    171     public static function sign($message, $sk)
    172     {
    173         /** @var string $signature */
    174         $signature = self::sign_detached($message, $sk);
    175         return $signature . $message;
    176     }
    177 
    178     /**
    179      * @internal You should not use this directly from another application
    180      *
    181      * @param string $message A signed message
    182      * @param string $pk      Public key
    183      * @return string         Message (without signature)
    184      * @throws SodiumException
    185      * @throws TypeError
    186      */
    187     public static function sign_open($message, $pk)
    188     {
    189         /** @var string $signature */
    190         $signature = self::substr($message, 0, 64);
    191 
    192         /** @var string $message */
    193         $message = self::substr($message, 64);
    194 
    195         if (self::verify_detached($signature, $message, $pk)) {
    196             return $message;
    197         }
    198         throw new SodiumException('Invalid signature');
    199     }
    200 
    201     /**
    202      * @internal You should not use this directly from another application
    203      *
    204      * @param string $message
    205      * @param string $sk
    206      * @return string
    207      * @throws SodiumException
    208      * @throws TypeError
    209      */
    210     public static function sign_detached($message, $sk)
    211     {
    212         # crypto_hash_sha512(az, sk, 32);
    213         $az =  hash('sha512', self::substr($sk, 0, 32), true);
    214 
    215         # az[0] &= 248;
    216         # az[31] &= 63;
    217         # az[31] |= 64;
    218         $az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
    219         $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
    220 
    221         # crypto_hash_sha512_init(&hs);
    222         # crypto_hash_sha512_update(&hs, az + 32, 32);
    223         # crypto_hash_sha512_update(&hs, m, mlen);
    224         # crypto_hash_sha512_final(&hs, nonce);
    225         $hs = hash_init('sha512');
    226         hash_update($hs, self::substr($az, 32, 32));
    227         hash_update($hs, $message);
    228         $nonceHash = hash_final($hs, true);
    229 
    230         # memmove(sig + 32, sk + 32, 32);
    231         $pk = self::substr($sk, 32, 32);
    232 
    233         # sc_reduce(nonce);
    234         # ge_scalarmult_base(&R, nonce);
    235         # ge_p3_tobytes(sig, &R);
    236         $nonce = self::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
    237         $sig = self::ge_p3_tobytes(
    238             self::ge_scalarmult_base($nonce)
    239         );
    240 
    241         # crypto_hash_sha512_init(&hs);
    242         # crypto_hash_sha512_update(&hs, sig, 64);
    243         # crypto_hash_sha512_update(&hs, m, mlen);
    244         # crypto_hash_sha512_final(&hs, hram);
    245         $hs = hash_init('sha512');
    246         hash_update($hs, self::substr($sig, 0, 32));
    247         hash_update($hs, self::substr($pk, 0, 32));
    248         hash_update($hs, $message);
    249         $hramHash = hash_final($hs, true);
    250 
    251         # sc_reduce(hram);
    252         # sc_muladd(sig + 32, hram, az, nonce);
    253         $hram = self::sc_reduce($hramHash);
    254         $sigAfter = self::sc_muladd($hram, $az, $nonce);
    255         $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
    256 
    257         try {
    258             ParagonIE_Sodium_Compat::memzero($az);
    259         } catch (SodiumException $ex) {
    260             $az = null;
    261         }
    262         return $sig;
    263     }
    264 
    265     /**
    266      * @internal You should not use this directly from another application
    267      *
    268      * @param string $sig
    269      * @param string $message
    270      * @param string $pk
    271      * @return bool
    272      * @throws SodiumException
    273      * @throws TypeError
    274      */
    275     public static function verify_detached($sig, $message, $pk)
    276     {
    277         if (self::strlen($sig) < 64) {
    278             throw new SodiumException('Signature is too short');
    279         }
    280         if ((self::chrToInt($sig[63]) & 240) && self::check_S_lt_L(self::substr($sig, 32, 32))) {
    281             throw new SodiumException('S < L - Invalid signature');
    282         }
    283         if (self::small_order($sig)) {
    284             throw new SodiumException('Signature is on too small of an order');
    285         }
    286         if ((self::chrToInt($sig[63]) & 224) !== 0) {
    287             throw new SodiumException('Invalid signature');
    288         }
    289         $d = 0;
    290         for ($i = 0; $i < 32; ++$i) {
    291             $d |= self::chrToInt($pk[$i]);
    292         }
    293         if ($d === 0) {
    294             throw new SodiumException('All zero public key');
    295         }
    296 
    297         /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
    298         $orig = ParagonIE_Sodium_Compat::$fastMult;
    299 
    300         // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
    301         ParagonIE_Sodium_Compat::$fastMult = true;
    302 
    303         /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */
    304         $A = self::ge_frombytes_negate_vartime($pk);
    305 
    306         /** @var string $hDigest */
    307         $hDigest = hash(
    308             'sha512',
    309             self::substr($sig, 0, 32) .
    310                 self::substr($pk, 0, 32) .
    311                 $message,
    312             true
    313         );
    314 
    315         /** @var string $h */
    316         $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32);
    317 
    318         /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */
    319         $R = self::ge_double_scalarmult_vartime(
    320             $h,
    321             $A,
    322             self::substr($sig, 32)
    323         );
    324 
    325         /** @var string $rcheck */
    326         $rcheck = self::ge_tobytes($R);
    327 
    328         // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
    329         ParagonIE_Sodium_Compat::$fastMult = $orig;
    330 
    331         return self::verify_32($rcheck, self::substr($sig, 0, 32));
    332     }
    333 
    334     /**
    335      * @internal You should not use this directly from another application
    336      *
    337      * @param string $S
    338      * @return bool
    339      * @throws SodiumException
    340      * @throws TypeError
    341      */
    342     public static function check_S_lt_L($S)
    343     {
    344         if (self::strlen($S) < 32) {
    345             throw new SodiumException('Signature must be 32 bytes');
    346         }
    347         $L = array(
    348             0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
    349             0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
    350             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    351             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
    352         );
    353         $c = 0;
    354         $n = 1;
    355         $i = 32;
    356 
    357         /** @var array<int, int> $L */
    358         do {
    359             --$i;
    360             $x = self::chrToInt($S[$i]);
    361             $c |= (
    362                 (($x - $L[$i]) >> 8) & $n
    363             );
    364             $n &= (
    365                 (($x ^ $L[$i]) - 1) >> 8
    366             );
    367         } while ($i !== 0);
    368 
    369         return $c === 0;
    370     }
    371 
    372     /**
    373      * @param string $R
    374      * @return bool
    375      * @throws SodiumException
    376      * @throws TypeError
    377      */
    378     public static function small_order($R)
    379     {
    380         /** @var array<int, array<int, int>> $blocklist */
    381         $blocklist = array(
    382             /* 0 (order 4) */
    383             array(
    384                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    385                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    386                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    387                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    388             ),
    389             /* 1 (order 1) */
    390             array(
    391                 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    392                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    393                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    394                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    395             ),
    396             /* 2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
    397             array(
    398                 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
    399                 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
    400                 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
    401                 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05
    402             ),
    403             /* 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
    404             array(
    405                 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
    406                 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
    407                 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
    408                 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a
    409             ),
    410             /* p-1 (order 2) */
    411             array(
    412                 0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
    413                 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
    414                 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
    415                 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85
    416             ),
    417             /* p (order 4) */
    418             array(
    419                 0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
    420                 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
    421                 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
    422                 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa
    423             ),
    424             /* p+1 (order 1) */
    425             array(
    426                 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    427                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    428                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    429                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
    430             ),
    431             /* p+2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
    432             array(
    433                 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    434                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    435                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    436                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
    437             ),
    438             /* p+55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
    439             array(
    440                 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    441                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    442                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    443                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
    444             ),
    445             /* 2p-1 (order 2) */
    446             array(
    447                 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    448                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    449                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    450                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    451             ),
    452             /* 2p (order 4) */
    453             array(
    454                 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    455                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    456                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    457                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    458             ),
    459             /* 2p+1 (order 1) */
    460             array(
    461                 0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    462                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    463                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    464                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    465             )
    466         );
    467         /** @var int $countBlocklist */
    468         $countBlocklist = count($blocklist);
    469 
    470         for ($i = 0; $i < $countBlocklist; ++$i) {
    471             $c = 0;
    472             for ($j = 0; $j < 32; ++$j) {
    473                 $c |= self::chrToInt($R[$j]) ^ (int) $blocklist[$i][$j];
    474             }
    475             if ($c === 0) {
    476                 return true;
    477             }
    478         }
    479         return false;
    480     }
    481 
    482     /**
    483      * @param string $s
    484      * @return string
    485      * @throws SodiumException
    486      */
    487     public static function scalar_complement($s)
    488     {
    489         $t_ = self::L . str_repeat("\x00", 32);
    490         sodium_increment($t_);
    491         $s_ = $s . str_repeat("\x00", 32);
    492         ParagonIE_Sodium_Compat::sub($t_, $s_);
    493         return self::sc_reduce($t_);
    494     }
    495 
    496     /**
    497      * @return string
    498      * @throws SodiumException
    499      */
    500     public static function scalar_random()
    501     {
    502         do {
    503             $r = ParagonIE_Sodium_Compat::randombytes_buf(self::SCALAR_BYTES);
    504             $r[self::SCALAR_BYTES - 1] = self::intToChr(
    505                 self::chrToInt($r[self::SCALAR_BYTES - 1]) & 0x1f
    506             );
    507         } while (
    508             !self::check_S_lt_L($r) || ParagonIE_Sodium_Compat::is_zero($r)
    509         );
    510         return $r;
    511     }
    512 
    513     /**
    514      * @param string $s
    515      * @return string
    516      * @throws SodiumException
    517      */
    518     public static function scalar_negate($s)
    519     {
    520         $t_ = self::L . str_repeat("\x00", 32) ;
    521         $s_ = $s . str_repeat("\x00", 32) ;
    522         ParagonIE_Sodium_Compat::sub($t_, $s_);
    523         return self::sc_reduce($t_);
    524     }
    525 
    526     /**
    527      * @param string $a
    528      * @param string $b
    529      * @return string
    530      * @throws SodiumException
    531      */
    532     public static function scalar_add($a, $b)
    533     {
    534         $a_ = $a . str_repeat("\x00", 32);
    535         $b_ = $b . str_repeat("\x00", 32);
    536         ParagonIE_Sodium_Compat::add($a_, $b_);
    537         return self::sc_reduce($a_);
    538     }
    539 
    540     /**
    541      * @param string $x
    542      * @param string $y
    543      * @return string
    544      * @throws SodiumException
    545      */
    546     public static function scalar_sub($x, $y)
    547     {
    548         $yn = self::scalar_negate($y);
    549         return self::scalar_add($x, $yn);
    550     }
    551 }