balmet.com

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

Ed25519.php (15618B)


      1 <?php
      2 
      3 if (class_exists('ParagonIE_Sodium_Core32_Ed25519', false)) {
      4     return;
      5 }
      6 
      7 /**
      8  * Class ParagonIE_Sodium_Core32_Ed25519
      9  */
     10 abstract class ParagonIE_Sodium_Core32_Ed25519 extends ParagonIE_Sodium_Core32_Curve25519
     11 {
     12     const KEYPAIR_BYTES = 96;
     13     const SEED_BYTES = 32;
     14 
     15     /**
     16      * @internal You should not use this directly from another application
     17      *
     18      * @return string (96 bytes)
     19      * @throws Exception
     20      * @throws SodiumException
     21      * @throws TypeError
     22      */
     23     public static function keypair()
     24     {
     25         $seed = random_bytes(self::SEED_BYTES);
     26         $pk = '';
     27         $sk = '';
     28         self::seed_keypair($pk, $sk, $seed);
     29         return $sk . $pk;
     30     }
     31 
     32     /**
     33      * @internal You should not use this directly from another application
     34      *
     35      * @param string $pk
     36      * @param string $sk
     37      * @param string $seed
     38      * @return string
     39      * @throws SodiumException
     40      * @throws TypeError
     41      */
     42     public static function seed_keypair(&$pk, &$sk, $seed)
     43     {
     44         if (self::strlen($seed) !== self::SEED_BYTES) {
     45             throw new RangeException('crypto_sign keypair seed must be 32 bytes long');
     46         }
     47 
     48         /** @var string $pk */
     49         $pk = self::publickey_from_secretkey($seed);
     50         $sk = $seed . $pk;
     51         return $sk;
     52     }
     53 
     54     /**
     55      * @internal You should not use this directly from another application
     56      *
     57      * @param string $keypair
     58      * @return string
     59      * @throws TypeError
     60      */
     61     public static function secretkey($keypair)
     62     {
     63         if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
     64             throw new RangeException('crypto_sign keypair must be 96 bytes long');
     65         }
     66         return self::substr($keypair, 0, 64);
     67     }
     68 
     69     /**
     70      * @internal You should not use this directly from another application
     71      *
     72      * @param string $keypair
     73      * @return string
     74      * @throws RangeException
     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($pk);
    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 
    134         # fe_1(x);
    135         # fe_add(x, x, A.Y);
    136         # fe_mul(x, x, one_minus_y);
    137         $x = self::fe_mul(
    138             self::fe_add(self::fe_1(), $A->Y),
    139             $one_minux_y
    140         );
    141 
    142         # fe_tobytes(curve25519_pk, x);
    143         return self::fe_tobytes($x);
    144     }
    145 
    146     /**
    147      * @internal You should not use this directly from another application
    148      *
    149      * @param string $sk
    150      * @return string
    151      * @throws SodiumException
    152      * @throws TypeError
    153      */
    154     public static function sk_to_pk($sk)
    155     {
    156         return self::ge_p3_tobytes(
    157             self::ge_scalarmult_base(
    158                 self::substr($sk, 0, 32)
    159             )
    160         );
    161     }
    162 
    163     /**
    164      * @internal You should not use this directly from another application
    165      *
    166      * @param string $message
    167      * @param string $sk
    168      * @return string
    169      * @throws SodiumException
    170      * @throws TypeError
    171      */
    172     public static function sign($message, $sk)
    173     {
    174         /** @var string $signature */
    175         $signature = self::sign_detached($message, $sk);
    176         return $signature . $message;
    177     }
    178 
    179     /**
    180      * @internal You should not use this directly from another application
    181      *
    182      * @param string $message A signed message
    183      * @param string $pk      Public key
    184      * @return string         Message (without signature)
    185      * @throws SodiumException
    186      * @throws TypeError
    187      */
    188     public static function sign_open($message, $pk)
    189     {
    190         /** @var string $signature */
    191         $signature = self::substr($message, 0, 64);
    192 
    193         /** @var string $message */
    194         $message = self::substr($message, 64);
    195 
    196         if (self::verify_detached($signature, $message, $pk)) {
    197             return $message;
    198         }
    199         throw new SodiumException('Invalid signature');
    200     }
    201 
    202     /**
    203      * @internal You should not use this directly from another application
    204      *
    205      * @param string $message
    206      * @param string $sk
    207      * @return string
    208      * @throws SodiumException
    209      * @throws TypeError
    210      * @psalm-suppress PossiblyInvalidArgument
    211      */
    212     public static function sign_detached($message, $sk)
    213     {
    214         # crypto_hash_sha512(az, sk, 32);
    215         $az =  hash('sha512', self::substr($sk, 0, 32), true);
    216 
    217         # az[0] &= 248;
    218         # az[31] &= 63;
    219         # az[31] |= 64;
    220         $az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
    221         $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
    222 
    223         # crypto_hash_sha512_init(&hs);
    224         # crypto_hash_sha512_update(&hs, az + 32, 32);
    225         # crypto_hash_sha512_update(&hs, m, mlen);
    226         # crypto_hash_sha512_final(&hs, nonce);
    227         $hs = hash_init('sha512');
    228         self::hash_update($hs, self::substr($az, 32, 32));
    229         self::hash_update($hs, $message);
    230         $nonceHash = hash_final($hs, true);
    231 
    232         # memmove(sig + 32, sk + 32, 32);
    233         $pk = self::substr($sk, 32, 32);
    234 
    235         # sc_reduce(nonce);
    236         # ge_scalarmult_base(&R, nonce);
    237         # ge_p3_tobytes(sig, &R);
    238         $nonce = self::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
    239         $sig = self::ge_p3_tobytes(
    240             self::ge_scalarmult_base($nonce)
    241         );
    242 
    243         # crypto_hash_sha512_init(&hs);
    244         # crypto_hash_sha512_update(&hs, sig, 64);
    245         # crypto_hash_sha512_update(&hs, m, mlen);
    246         # crypto_hash_sha512_final(&hs, hram);
    247         $hs = hash_init('sha512');
    248         self::hash_update($hs, self::substr($sig, 0, 32));
    249         self::hash_update($hs, self::substr($pk, 0, 32));
    250         self::hash_update($hs, $message);
    251         $hramHash = hash_final($hs, true);
    252 
    253         # sc_reduce(hram);
    254         # sc_muladd(sig + 32, hram, az, nonce);
    255         $hram = self::sc_reduce($hramHash);
    256         $sigAfter = self::sc_muladd($hram, $az, $nonce);
    257         $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
    258 
    259         try {
    260             ParagonIE_Sodium_Compat::memzero($az);
    261         } catch (SodiumException $ex) {
    262             $az = null;
    263         }
    264         return $sig;
    265     }
    266 
    267     /**
    268      * @internal You should not use this directly from another application
    269      *
    270      * @param string $sig
    271      * @param string $message
    272      * @param string $pk
    273      * @return bool
    274      * @throws SodiumException
    275      * @throws TypeError
    276      */
    277     public static function verify_detached($sig, $message, $pk)
    278     {
    279         if (self::strlen($sig) < 64) {
    280             throw new SodiumException('Signature is too short');
    281         }
    282         if ((self::chrToInt($sig[63]) & 240) && self::check_S_lt_L(self::substr($sig, 32, 32))) {
    283             throw new SodiumException('S < L - Invalid signature');
    284         }
    285         if (self::small_order($sig)) {
    286             throw new SodiumException('Signature is on too small of an order');
    287         }
    288         if ((self::chrToInt($sig[63]) & 224) !== 0) {
    289             throw new SodiumException('Invalid signature');
    290         }
    291         $d = 0;
    292         for ($i = 0; $i < 32; ++$i) {
    293             $d |= self::chrToInt($pk[$i]);
    294         }
    295         if ($d === 0) {
    296             throw new SodiumException('All zero public key');
    297         }
    298 
    299         /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
    300         $orig = ParagonIE_Sodium_Compat::$fastMult;
    301 
    302         // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
    303         ParagonIE_Sodium_Compat::$fastMult = true;
    304 
    305         /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */
    306         $A = self::ge_frombytes_negate_vartime($pk);
    307 
    308         /** @var string $hDigest */
    309         $hDigest = hash(
    310             'sha512',
    311             self::substr($sig, 0, 32) .
    312             self::substr($pk, 0, 32) .
    313             $message,
    314             true
    315         );
    316 
    317         /** @var string $h */
    318         $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32);
    319 
    320         /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */
    321         $R = self::ge_double_scalarmult_vartime(
    322             $h,
    323             $A,
    324             self::substr($sig, 32)
    325         );
    326 
    327         /** @var string $rcheck */
    328         $rcheck = self::ge_tobytes($R);
    329 
    330         // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
    331         ParagonIE_Sodium_Compat::$fastMult = $orig;
    332 
    333         return self::verify_32($rcheck, self::substr($sig, 0, 32));
    334     }
    335 
    336     /**
    337      * @internal You should not use this directly from another application
    338      *
    339      * @param string $S
    340      * @return bool
    341      * @throws SodiumException
    342      * @throws TypeError
    343      */
    344     public static function check_S_lt_L($S)
    345     {
    346         if (self::strlen($S) < 32) {
    347             throw new SodiumException('Signature must be 32 bytes');
    348         }
    349         static $L = array(
    350             0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
    351             0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
    352             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    353             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
    354         );
    355         /** @var array<int, int> $L */
    356         $c = 0;
    357         $n = 1;
    358         $i = 32;
    359 
    360         do {
    361             --$i;
    362             $x = self::chrToInt($S[$i]);
    363             $c |= (
    364                 (($x - $L[$i]) >> 8) & $n
    365             );
    366             $n &= (
    367                 (($x ^ $L[$i]) - 1) >> 8
    368             );
    369         } while ($i !== 0);
    370 
    371         return $c === 0;
    372     }
    373 
    374     /**
    375      * @param string $R
    376      * @return bool
    377      * @throws SodiumException
    378      * @throws TypeError
    379      */
    380     public static function small_order($R)
    381     {
    382         static $blocklist = array(
    383             /* 0 (order 4) */
    384             array(
    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                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    389             ),
    390             /* 1 (order 1) */
    391             array(
    392                 0x01, 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                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    396             ),
    397             /* 2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
    398             array(
    399                 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
    400                 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
    401                 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
    402                 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05
    403             ),
    404             /* 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
    405             array(
    406                 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
    407                 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
    408                 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
    409                 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a
    410             ),
    411             /* p-1 (order 2) */
    412             array(
    413                 0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
    414                 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
    415                 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
    416                 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85
    417             ),
    418             /* p (order 4) */
    419             array(
    420                 0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
    421                 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
    422                 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
    423                 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa
    424             ),
    425             /* p+1 (order 1) */
    426             array(
    427                 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    428                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    429                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    430                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
    431             ),
    432             /* p+2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
    433             array(
    434                 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    435                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    436                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    437                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
    438             ),
    439             /* p+55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
    440             array(
    441                 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    442                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    443                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    444                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
    445             ),
    446             /* 2p-1 (order 2) */
    447             array(
    448                 0xd9, 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                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    452             ),
    453             /* 2p (order 4) */
    454             array(
    455                 0xda, 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                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    459             ),
    460             /* 2p+1 (order 1) */
    461             array(
    462                 0xdb, 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                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    466             )
    467         );
    468         /** @var array<int, array<int, int>> $blocklist */
    469         $countBlocklist = count($blocklist);
    470 
    471         for ($i = 0; $i < $countBlocklist; ++$i) {
    472             $c = 0;
    473             for ($j = 0; $j < 32; ++$j) {
    474                 $c |= self::chrToInt($R[$j]) ^ $blocklist[$i][$j];
    475             }
    476             if ($c === 0) {
    477                 return true;
    478             }
    479         }
    480         return false;
    481     }
    482 }