47 const LIBRARY_VERSION_MAJOR = 9; |
47 const LIBRARY_VERSION_MAJOR = 9; |
48 const LIBRARY_VERSION_MINOR = 1; |
48 const LIBRARY_VERSION_MINOR = 1; |
49 const VERSION_STRING = 'polyfill-1.0.8'; |
49 const VERSION_STRING = 'polyfill-1.0.8'; |
50 |
50 |
51 // From libsodium |
51 // From libsodium |
|
52 const BASE64_VARIANT_ORIGINAL = 1; |
|
53 const BASE64_VARIANT_ORIGINAL_NO_PADDING = 3; |
|
54 const BASE64_VARIANT_URLSAFE = 5; |
|
55 const BASE64_VARIANT_URLSAFE_NO_PADDING = 7; |
52 const CRYPTO_AEAD_AES256GCM_KEYBYTES = 32; |
56 const CRYPTO_AEAD_AES256GCM_KEYBYTES = 32; |
53 const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0; |
57 const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0; |
54 const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12; |
58 const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12; |
55 const CRYPTO_AEAD_AES256GCM_ABYTES = 16; |
59 const CRYPTO_AEAD_AES256GCM_ABYTES = 16; |
56 const CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32; |
60 const CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32; |
72 const CRYPTO_BOX_PUBLICKEYBYTES = 32; |
76 const CRYPTO_BOX_PUBLICKEYBYTES = 32; |
73 const CRYPTO_BOX_KEYPAIRBYTES = 64; |
77 const CRYPTO_BOX_KEYPAIRBYTES = 64; |
74 const CRYPTO_BOX_MACBYTES = 16; |
78 const CRYPTO_BOX_MACBYTES = 16; |
75 const CRYPTO_BOX_NONCEBYTES = 24; |
79 const CRYPTO_BOX_NONCEBYTES = 24; |
76 const CRYPTO_BOX_SEEDBYTES = 32; |
80 const CRYPTO_BOX_SEEDBYTES = 32; |
|
81 const CRYPTO_KDF_BYTES_MIN = 16; |
|
82 const CRYPTO_KDF_BYTES_MAX = 64; |
|
83 const CRYPTO_KDF_CONTEXTBYTES = 8; |
|
84 const CRYPTO_KDF_KEYBYTES = 32; |
77 const CRYPTO_KX_BYTES = 32; |
85 const CRYPTO_KX_BYTES = 32; |
|
86 const CRYPTO_KX_PRIMITIVE = 'x25519blake2b'; |
78 const CRYPTO_KX_SEEDBYTES = 32; |
87 const CRYPTO_KX_SEEDBYTES = 32; |
|
88 const CRYPTO_KX_KEYPAIRBYTES = 64; |
79 const CRYPTO_KX_PUBLICKEYBYTES = 32; |
89 const CRYPTO_KX_PUBLICKEYBYTES = 32; |
80 const CRYPTO_KX_SECRETKEYBYTES = 32; |
90 const CRYPTO_KX_SECRETKEYBYTES = 32; |
|
91 const CRYPTO_KX_SESSIONKEYBYTES = 32; |
81 const CRYPTO_GENERICHASH_BYTES = 32; |
92 const CRYPTO_GENERICHASH_BYTES = 32; |
82 const CRYPTO_GENERICHASH_BYTES_MIN = 16; |
93 const CRYPTO_GENERICHASH_BYTES_MIN = 16; |
83 const CRYPTO_GENERICHASH_BYTES_MAX = 64; |
94 const CRYPTO_GENERICHASH_BYTES_MAX = 64; |
84 const CRYPTO_GENERICHASH_KEYBYTES = 32; |
95 const CRYPTO_GENERICHASH_KEYBYTES = 32; |
85 const CRYPTO_GENERICHASH_KEYBYTES_MIN = 16; |
96 const CRYPTO_GENERICHASH_KEYBYTES_MIN = 16; |
86 const CRYPTO_GENERICHASH_KEYBYTES_MAX = 64; |
97 const CRYPTO_GENERICHASH_KEYBYTES_MAX = 64; |
87 const CRYPTO_PWHASH_SALTBYTES = 16; |
98 const CRYPTO_PWHASH_SALTBYTES = 16; |
88 const CRYPTO_PWHASH_STRPREFIX = '$argon2i$'; |
99 const CRYPTO_PWHASH_STRPREFIX = '$argon2id$'; |
89 const CRYPTO_PWHASH_ALG_ARGON2I13 = 1; |
100 const CRYPTO_PWHASH_ALG_ARGON2I13 = 1; |
90 const CRYPTO_PWHASH_ALG_ARGON2ID13 = 2; |
101 const CRYPTO_PWHASH_ALG_ARGON2ID13 = 2; |
91 const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 33554432; |
102 const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 33554432; |
92 const CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE = 4; |
103 const CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE = 4; |
93 const CRYPTO_PWHASH_MEMLIMIT_MODERATE = 134217728; |
104 const CRYPTO_PWHASH_MEMLIMIT_MODERATE = 134217728; |
105 const CRYPTO_SHORTHASH_BYTES = 8; |
116 const CRYPTO_SHORTHASH_BYTES = 8; |
106 const CRYPTO_SHORTHASH_KEYBYTES = 16; |
117 const CRYPTO_SHORTHASH_KEYBYTES = 16; |
107 const CRYPTO_SECRETBOX_KEYBYTES = 32; |
118 const CRYPTO_SECRETBOX_KEYBYTES = 32; |
108 const CRYPTO_SECRETBOX_MACBYTES = 16; |
119 const CRYPTO_SECRETBOX_MACBYTES = 16; |
109 const CRYPTO_SECRETBOX_NONCEBYTES = 24; |
120 const CRYPTO_SECRETBOX_NONCEBYTES = 24; |
|
121 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES = 17; |
|
122 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES = 24; |
|
123 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES = 32; |
|
124 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH = 0; |
|
125 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PULL = 1; |
|
126 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY = 2; |
|
127 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL = 3; |
|
128 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX = 0x3fffffff80; |
110 const CRYPTO_SIGN_BYTES = 64; |
129 const CRYPTO_SIGN_BYTES = 64; |
111 const CRYPTO_SIGN_SEEDBYTES = 32; |
130 const CRYPTO_SIGN_SEEDBYTES = 32; |
112 const CRYPTO_SIGN_PUBLICKEYBYTES = 32; |
131 const CRYPTO_SIGN_PUBLICKEYBYTES = 32; |
113 const CRYPTO_SIGN_SECRETKEYBYTES = 64; |
132 const CRYPTO_SIGN_SECRETKEYBYTES = 64; |
114 const CRYPTO_SIGN_KEYPAIRBYTES = 96; |
133 const CRYPTO_SIGN_KEYPAIRBYTES = 96; |
115 const CRYPTO_STREAM_KEYBYTES = 32; |
134 const CRYPTO_STREAM_KEYBYTES = 32; |
116 const CRYPTO_STREAM_NONCEBYTES = 24; |
135 const CRYPTO_STREAM_NONCEBYTES = 24; |
|
136 |
|
137 /** |
|
138 * Add two numbers (little-endian unsigned), storing the value in the first |
|
139 * parameter. |
|
140 * |
|
141 * This mutates $val. |
|
142 * |
|
143 * @param string $val |
|
144 * @param string $addv |
|
145 * @return void |
|
146 * @throws SodiumException |
|
147 */ |
|
148 public static function add(&$val, $addv) |
|
149 { |
|
150 $val_len = ParagonIE_Sodium_Core_Util::strlen($val); |
|
151 $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv); |
|
152 if ($val_len !== $addv_len) { |
|
153 throw new SodiumException('values must have the same length'); |
|
154 } |
|
155 $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val); |
|
156 $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv); |
|
157 |
|
158 $c = 0; |
|
159 for ($i = 0; $i < $val_len; $i++) { |
|
160 $c += ($A[$i] + $B[$i]); |
|
161 $A[$i] = ($c & 0xff); |
|
162 $c >>= 8; |
|
163 } |
|
164 $val = ParagonIE_Sodium_Core_Util::intArrayToString($A); |
|
165 } |
|
166 |
|
167 /** |
|
168 * @param string $encoded |
|
169 * @param int $variant |
|
170 * @param string $ignore |
|
171 * @return string |
|
172 * @throws SodiumException |
|
173 */ |
|
174 public static function base642bin($encoded, $variant, $ignore = '') |
|
175 { |
|
176 /* Type checks: */ |
|
177 ParagonIE_Sodium_Core_Util::declareScalarType($encoded, 'string', 1); |
|
178 |
|
179 /** @var string $encoded */ |
|
180 $encoded = (string) $encoded; |
|
181 if (ParagonIE_Sodium_Core_Util::strlen($encoded) === 0) { |
|
182 return ''; |
|
183 } |
|
184 |
|
185 // Just strip before decoding |
|
186 if (!empty($ignore)) { |
|
187 $encoded = str_replace($ignore, '', $encoded); |
|
188 } |
|
189 |
|
190 try { |
|
191 switch ($variant) { |
|
192 case self::BASE64_VARIANT_ORIGINAL: |
|
193 return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, true); |
|
194 case self::BASE64_VARIANT_ORIGINAL_NO_PADDING: |
|
195 return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, false); |
|
196 case self::BASE64_VARIANT_URLSAFE: |
|
197 return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, true); |
|
198 case self::BASE64_VARIANT_URLSAFE_NO_PADDING: |
|
199 return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, false); |
|
200 default: |
|
201 throw new SodiumException('invalid base64 variant identifier'); |
|
202 } |
|
203 } catch (Exception $ex) { |
|
204 if ($ex instanceof SodiumException) { |
|
205 throw $ex; |
|
206 } |
|
207 throw new SodiumException('invalid base64 string'); |
|
208 } |
|
209 } |
|
210 |
|
211 /** |
|
212 * @param string $decoded |
|
213 * @param int $variant |
|
214 * @return string |
|
215 * @throws SodiumException |
|
216 */ |
|
217 public static function bin2base64($decoded, $variant) |
|
218 { |
|
219 /* Type checks: */ |
|
220 ParagonIE_Sodium_Core_Util::declareScalarType($decoded, 'string', 1); |
|
221 /** @var string $decoded */ |
|
222 $decoded = (string) $decoded; |
|
223 if (ParagonIE_Sodium_Core_Util::strlen($decoded) === 0) { |
|
224 return ''; |
|
225 } |
|
226 |
|
227 switch ($variant) { |
|
228 case self::BASE64_VARIANT_ORIGINAL: |
|
229 return ParagonIE_Sodium_Core_Base64_Original::encode($decoded); |
|
230 case self::BASE64_VARIANT_ORIGINAL_NO_PADDING: |
|
231 return ParagonIE_Sodium_Core_Base64_Original::encodeUnpadded($decoded); |
|
232 case self::BASE64_VARIANT_URLSAFE: |
|
233 return ParagonIE_Sodium_Core_Base64_UrlSafe::encode($decoded); |
|
234 case self::BASE64_VARIANT_URLSAFE_NO_PADDING: |
|
235 return ParagonIE_Sodium_Core_Base64_UrlSafe::encodeUnpadded($decoded); |
|
236 default: |
|
237 throw new SodiumException('invalid base64 variant identifier'); |
|
238 } |
|
239 } |
117 |
240 |
118 /** |
241 /** |
119 * Cache-timing-safe implementation of bin2hex(). |
242 * Cache-timing-safe implementation of bin2hex(). |
120 * |
243 * |
121 * @param string $string A string (probably raw binary) |
244 * @param string $string A string (probably raw binary) |
1378 } |
1510 } |
1379 return ParagonIE_Sodium_Crypto::generichash_init($key, $length); |
1511 return ParagonIE_Sodium_Crypto::generichash_init($key, $length); |
1380 } |
1512 } |
1381 |
1513 |
1382 /** |
1514 /** |
|
1515 * Initialize a BLAKE2b hashing context, for use in a streaming interface. |
|
1516 * |
|
1517 * @param string|null $key If specified must be a string between 16 and 64 bytes |
|
1518 * @param int $length The size of the desired hash output |
|
1519 * @param string $salt Salt (up to 16 bytes) |
|
1520 * @param string $personal Personalization string (up to 16 bytes) |
|
1521 * @return string A BLAKE2 hashing context, encoded as a string |
|
1522 * (To be 100% compatible with ext/libsodium) |
|
1523 * @throws SodiumException |
|
1524 * @throws TypeError |
|
1525 * @psalm-suppress MixedArgument |
|
1526 */ |
|
1527 public static function crypto_generichash_init_salt_personal( |
|
1528 $key = '', |
|
1529 $length = self::CRYPTO_GENERICHASH_BYTES, |
|
1530 $salt = '', |
|
1531 $personal = '' |
|
1532 ) { |
|
1533 /* Type checks: */ |
|
1534 if (is_null($key)) { |
|
1535 $key = ''; |
|
1536 } |
|
1537 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1); |
|
1538 ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2); |
|
1539 ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3); |
|
1540 ParagonIE_Sodium_Core_Util::declareScalarType($personal, 'string', 4); |
|
1541 $salt = str_pad($salt, 16, "\0", STR_PAD_RIGHT); |
|
1542 $personal = str_pad($personal, 16, "\0", STR_PAD_RIGHT); |
|
1543 |
|
1544 /* Input validation: */ |
|
1545 if (!empty($key)) { |
|
1546 /* |
|
1547 if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) { |
|
1548 throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.'); |
|
1549 } |
|
1550 */ |
|
1551 if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) { |
|
1552 throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.'); |
|
1553 } |
|
1554 } |
|
1555 if (PHP_INT_SIZE === 4) { |
|
1556 return ParagonIE_Sodium_Crypto32::generichash_init_salt_personal($key, $length, $salt, $personal); |
|
1557 } |
|
1558 return ParagonIE_Sodium_Crypto::generichash_init_salt_personal($key, $length, $salt, $personal); |
|
1559 } |
|
1560 |
|
1561 /** |
1383 * Update a BLAKE2b hashing context with additional data. |
1562 * Update a BLAKE2b hashing context with additional data. |
1384 * |
1563 * |
1385 * @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init(). |
1564 * @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init(). |
1386 * $ctx is passed by reference and gets updated in-place. |
1565 * $ctx is passed by reference and gets updated in-place. |
1387 * @param-out string $ctx |
1566 * @param-out string $ctx |
1420 * @throws Error |
1599 * @throws Error |
1421 */ |
1600 */ |
1422 public static function crypto_generichash_keygen() |
1601 public static function crypto_generichash_keygen() |
1423 { |
1602 { |
1424 return random_bytes(self::CRYPTO_GENERICHASH_KEYBYTES); |
1603 return random_bytes(self::CRYPTO_GENERICHASH_KEYBYTES); |
|
1604 } |
|
1605 |
|
1606 /** |
|
1607 * @param int $subkey_len |
|
1608 * @param int $subkey_id |
|
1609 * @param string $context |
|
1610 * @param string $key |
|
1611 * @return string |
|
1612 * @throws SodiumException |
|
1613 */ |
|
1614 public static function crypto_kdf_derive_from_key( |
|
1615 $subkey_len, |
|
1616 $subkey_id, |
|
1617 $context, |
|
1618 $key |
|
1619 ) { |
|
1620 ParagonIE_Sodium_Core_Util::declareScalarType($subkey_len, 'int', 1); |
|
1621 ParagonIE_Sodium_Core_Util::declareScalarType($subkey_id, 'int', 2); |
|
1622 ParagonIE_Sodium_Core_Util::declareScalarType($context, 'string', 3); |
|
1623 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); |
|
1624 $subkey_id = (int) $subkey_id; |
|
1625 $subkey_len = (int) $subkey_len; |
|
1626 $context = (string) $context; |
|
1627 $key = (string) $key; |
|
1628 |
|
1629 if ($subkey_len < self::CRYPTO_KDF_BYTES_MIN) { |
|
1630 throw new SodiumException('subkey cannot be smaller than SODIUM_CRYPTO_KDF_BYTES_MIN'); |
|
1631 } |
|
1632 if ($subkey_len > self::CRYPTO_KDF_BYTES_MAX) { |
|
1633 throw new SodiumException('subkey cannot be larger than SODIUM_CRYPTO_KDF_BYTES_MAX'); |
|
1634 } |
|
1635 if ($subkey_id < 0) { |
|
1636 throw new SodiumException('subkey_id cannot be negative'); |
|
1637 } |
|
1638 if (ParagonIE_Sodium_Core_Util::strlen($context) !== self::CRYPTO_KDF_CONTEXTBYTES) { |
|
1639 throw new SodiumException('context should be SODIUM_CRYPTO_KDF_CONTEXTBYTES bytes'); |
|
1640 } |
|
1641 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_KDF_KEYBYTES) { |
|
1642 throw new SodiumException('key should be SODIUM_CRYPTO_KDF_KEYBYTES bytes'); |
|
1643 } |
|
1644 |
|
1645 $salt = ParagonIE_Sodium_Core_Util::store64_le($subkey_id); |
|
1646 $state = self::crypto_generichash_init_salt_personal( |
|
1647 $key, |
|
1648 $subkey_len, |
|
1649 $salt, |
|
1650 $context |
|
1651 ); |
|
1652 return self::crypto_generichash_final($state, $subkey_len); |
|
1653 } |
|
1654 |
|
1655 /** |
|
1656 * @return string |
|
1657 * @throws Exception |
|
1658 * @throws Error |
|
1659 */ |
|
1660 public static function crypto_kdf_keygen() |
|
1661 { |
|
1662 return random_bytes(self::CRYPTO_KDF_KEYBYTES); |
1425 } |
1663 } |
1426 |
1664 |
1427 /** |
1665 /** |
1428 * Perform a key exchange, between a designated client and a server. |
1666 * Perform a key exchange, between a designated client and a server. |
1429 * |
1667 * |
1509 $server_public |
1747 $server_public |
1510 ); |
1748 ); |
1511 } |
1749 } |
1512 |
1750 |
1513 /** |
1751 /** |
|
1752 * @param string $seed |
|
1753 * @return string |
|
1754 * @throws SodiumException |
|
1755 */ |
|
1756 public static function crypto_kx_seed_keypair($seed) |
|
1757 { |
|
1758 ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1); |
|
1759 |
|
1760 $seed = (string) $seed; |
|
1761 |
|
1762 if (ParagonIE_Sodium_Core_Util::strlen($seed) !== self::CRYPTO_KX_SEEDBYTES) { |
|
1763 throw new SodiumException('seed must be SODIUM_CRYPTO_KX_SEEDBYTES bytes'); |
|
1764 } |
|
1765 |
|
1766 $sk = self::crypto_generichash($seed, '', self::CRYPTO_KX_SECRETKEYBYTES); |
|
1767 $pk = self::crypto_scalarmult_base($sk); |
|
1768 return $sk . $pk; |
|
1769 } |
|
1770 |
|
1771 /** |
|
1772 * @return string |
|
1773 * @throws Exception |
|
1774 */ |
|
1775 public static function crypto_kx_keypair() |
|
1776 { |
|
1777 $sk = self::randombytes_buf(self::CRYPTO_KX_SECRETKEYBYTES); |
|
1778 $pk = self::crypto_scalarmult_base($sk); |
|
1779 return $sk . $pk; |
|
1780 } |
|
1781 |
|
1782 /** |
|
1783 * @param string $keypair |
|
1784 * @param string $serverPublicKey |
|
1785 * @return array{0: string, 1: string} |
|
1786 * @throws SodiumException |
|
1787 */ |
|
1788 public static function crypto_kx_client_session_keys($keypair, $serverPublicKey) |
|
1789 { |
|
1790 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); |
|
1791 ParagonIE_Sodium_Core_Util::declareScalarType($serverPublicKey, 'string', 2); |
|
1792 |
|
1793 $keypair = (string) $keypair; |
|
1794 $serverPublicKey = (string) $serverPublicKey; |
|
1795 |
|
1796 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) { |
|
1797 throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes'); |
|
1798 } |
|
1799 if (ParagonIE_Sodium_Core_Util::strlen($serverPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) { |
|
1800 throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes'); |
|
1801 } |
|
1802 |
|
1803 $sk = self::crypto_kx_secretkey($keypair); |
|
1804 $pk = self::crypto_kx_publickey($keypair); |
|
1805 $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2); |
|
1806 self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $serverPublicKey)); |
|
1807 self::crypto_generichash_update($h, $pk); |
|
1808 self::crypto_generichash_update($h, $serverPublicKey); |
|
1809 $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2); |
|
1810 return array( |
|
1811 ParagonIE_Sodium_Core_Util::substr( |
|
1812 $sessionKeys, |
|
1813 0, |
|
1814 self::CRYPTO_KX_SESSIONKEYBYTES |
|
1815 ), |
|
1816 ParagonIE_Sodium_Core_Util::substr( |
|
1817 $sessionKeys, |
|
1818 self::CRYPTO_KX_SESSIONKEYBYTES, |
|
1819 self::CRYPTO_KX_SESSIONKEYBYTES |
|
1820 ) |
|
1821 ); |
|
1822 } |
|
1823 |
|
1824 /** |
|
1825 * @param string $keypair |
|
1826 * @param string $clientPublicKey |
|
1827 * @return array{0: string, 1: string} |
|
1828 * @throws SodiumException |
|
1829 */ |
|
1830 public static function crypto_kx_server_session_keys($keypair, $clientPublicKey) |
|
1831 { |
|
1832 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); |
|
1833 ParagonIE_Sodium_Core_Util::declareScalarType($clientPublicKey, 'string', 2); |
|
1834 |
|
1835 $keypair = (string) $keypair; |
|
1836 $clientPublicKey = (string) $clientPublicKey; |
|
1837 |
|
1838 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) { |
|
1839 throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes'); |
|
1840 } |
|
1841 if (ParagonIE_Sodium_Core_Util::strlen($clientPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) { |
|
1842 throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes'); |
|
1843 } |
|
1844 |
|
1845 $sk = self::crypto_kx_secretkey($keypair); |
|
1846 $pk = self::crypto_kx_publickey($keypair); |
|
1847 $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2); |
|
1848 self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $clientPublicKey)); |
|
1849 self::crypto_generichash_update($h, $clientPublicKey); |
|
1850 self::crypto_generichash_update($h, $pk); |
|
1851 $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2); |
|
1852 return array( |
|
1853 ParagonIE_Sodium_Core_Util::substr( |
|
1854 $sessionKeys, |
|
1855 self::CRYPTO_KX_SESSIONKEYBYTES, |
|
1856 self::CRYPTO_KX_SESSIONKEYBYTES |
|
1857 ), |
|
1858 ParagonIE_Sodium_Core_Util::substr( |
|
1859 $sessionKeys, |
|
1860 0, |
|
1861 self::CRYPTO_KX_SESSIONKEYBYTES |
|
1862 ) |
|
1863 ); |
|
1864 } |
|
1865 |
|
1866 /** |
|
1867 * @param string $kp |
|
1868 * @return string |
|
1869 * @throws SodiumException |
|
1870 */ |
|
1871 public static function crypto_kx_secretkey($kp) |
|
1872 { |
|
1873 return ParagonIE_Sodium_Core_Util::substr( |
|
1874 $kp, |
|
1875 0, |
|
1876 self::CRYPTO_KX_SECRETKEYBYTES |
|
1877 ); |
|
1878 } |
|
1879 |
|
1880 /** |
|
1881 * @param string $kp |
|
1882 * @return string |
|
1883 * @throws SodiumException |
|
1884 */ |
|
1885 public static function crypto_kx_publickey($kp) |
|
1886 { |
|
1887 return ParagonIE_Sodium_Core_Util::substr( |
|
1888 $kp, |
|
1889 self::CRYPTO_KX_SECRETKEYBYTES, |
|
1890 self::CRYPTO_KX_PUBLICKEYBYTES |
|
1891 ); |
|
1892 } |
|
1893 |
|
1894 /** |
1514 * @param int $outlen |
1895 * @param int $outlen |
1515 * @param string $passwd |
1896 * @param string $passwd |
1516 * @param string $salt |
1897 * @param string $salt |
1517 * @param int $opslimit |
1898 * @param int $opslimit |
1518 * @param int $memlimit |
1899 * @param int $memlimit |
1588 } |
1969 } |
1589 // This is the best we can do. |
1970 // This is the best we can do. |
1590 throw new SodiumException( |
1971 throw new SodiumException( |
1591 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP' |
1972 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP' |
1592 ); |
1973 ); |
|
1974 } |
|
1975 |
|
1976 /** |
|
1977 * Do we need to rehash this password? |
|
1978 * |
|
1979 * @param string $hash |
|
1980 * @param int $opslimit |
|
1981 * @param int $memlimit |
|
1982 * @return bool |
|
1983 * @throws SodiumException |
|
1984 */ |
|
1985 public static function crypto_pwhash_str_needs_rehash($hash, $opslimit, $memlimit) |
|
1986 { |
|
1987 ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 1); |
|
1988 ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2); |
|
1989 ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3); |
|
1990 |
|
1991 // Just grab the first 4 pieces. |
|
1992 $pieces = explode('$', (string) $hash); |
|
1993 $prefix = implode('$', array_slice($pieces, 0, 4)); |
|
1994 |
|
1995 // Rebuild the expected header. |
|
1996 /** @var int $ops */ |
|
1997 $ops = (int) $opslimit; |
|
1998 /** @var int $mem */ |
|
1999 $mem = (int) $memlimit >> 10; |
|
2000 $encoded = self::CRYPTO_PWHASH_STRPREFIX . 'v=19$m=' . $mem . ',t=' . $ops . ',p=1'; |
|
2001 |
|
2002 // Do they match? If so, we don't need to rehash, so return false. |
|
2003 return !ParagonIE_Sodium_Core_Util::hashEquals($encoded, $prefix); |
1593 } |
2004 } |
1594 |
2005 |
1595 /** |
2006 /** |
1596 * @param string $passwd |
2007 * @param string $passwd |
1597 * @param string $hash |
2008 * @param string $hash |
1986 } |
2397 } |
1987 return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key); |
2398 return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key); |
1988 } |
2399 } |
1989 |
2400 |
1990 /** |
2401 /** |
|
2402 * @param string $key |
|
2403 * @return array<int, string> Returns a state and a header. |
|
2404 * @throws Exception |
|
2405 * @throws SodiumException |
|
2406 */ |
|
2407 public static function crypto_secretstream_xchacha20poly1305_init_push($key) |
|
2408 { |
|
2409 if (PHP_INT_SIZE === 4) { |
|
2410 return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_push($key); |
|
2411 } |
|
2412 return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_push($key); |
|
2413 } |
|
2414 |
|
2415 /** |
|
2416 * @param string $header |
|
2417 * @param string $key |
|
2418 * @return string Returns a state. |
|
2419 * @throws Exception |
|
2420 */ |
|
2421 public static function crypto_secretstream_xchacha20poly1305_init_pull($header, $key) |
|
2422 { |
|
2423 if (ParagonIE_Sodium_Core_Util::strlen($header) < self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) { |
|
2424 throw new SodiumException( |
|
2425 'header size should be SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES bytes' |
|
2426 ); |
|
2427 } |
|
2428 if (PHP_INT_SIZE === 4) { |
|
2429 return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_pull($key, $header); |
|
2430 } |
|
2431 return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_pull($key, $header); |
|
2432 } |
|
2433 |
|
2434 /** |
|
2435 * @param string $state |
|
2436 * @param string $msg |
|
2437 * @param string $aad |
|
2438 * @param int $tag |
|
2439 * @return string |
|
2440 * @throws SodiumException |
|
2441 */ |
|
2442 public static function crypto_secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0) |
|
2443 { |
|
2444 if (PHP_INT_SIZE === 4) { |
|
2445 return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_push( |
|
2446 $state, |
|
2447 $msg, |
|
2448 $aad, |
|
2449 $tag |
|
2450 ); |
|
2451 } |
|
2452 return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_push( |
|
2453 $state, |
|
2454 $msg, |
|
2455 $aad, |
|
2456 $tag |
|
2457 ); |
|
2458 } |
|
2459 |
|
2460 /** |
|
2461 * @param string $state |
|
2462 * @param string $msg |
|
2463 * @param string $aad |
|
2464 * @return bool|array{0: string, 1: int} |
|
2465 * @throws SodiumException |
|
2466 */ |
|
2467 public static function crypto_secretstream_xchacha20poly1305_pull(&$state, $msg, $aad = '') |
|
2468 { |
|
2469 if (PHP_INT_SIZE === 4) { |
|
2470 return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_pull( |
|
2471 $state, |
|
2472 $msg, |
|
2473 $aad |
|
2474 ); |
|
2475 } |
|
2476 return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_pull( |
|
2477 $state, |
|
2478 $msg, |
|
2479 $aad |
|
2480 ); |
|
2481 } |
|
2482 |
|
2483 /** |
|
2484 * @return string |
|
2485 * @throws Exception |
|
2486 */ |
|
2487 public static function crypto_secretstream_xchacha20poly1305_keygen() |
|
2488 { |
|
2489 return random_bytes(self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES); |
|
2490 } |
|
2491 |
|
2492 /** |
|
2493 * @param string $state |
|
2494 * @return void |
|
2495 * @throws SodiumException |
|
2496 */ |
|
2497 public static function crypto_secretstream_xchacha20poly1305_rekey(&$state) |
|
2498 { |
|
2499 if (PHP_INT_SIZE === 4) { |
|
2500 ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_rekey($state); |
|
2501 } else { |
|
2502 ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_rekey($state); |
|
2503 } |
|
2504 } |
|
2505 |
|
2506 /** |
1991 * Calculates a SipHash-2-4 hash of a message for a given key. |
2507 * Calculates a SipHash-2-4 hash of a message for a given key. |
1992 * |
2508 * |
1993 * @param string $message Input message |
2509 * @param string $message Input message |
1994 * @param string $key SipHash-2-4 key |
2510 * @param string $key SipHash-2-4 key |
1995 * @return string Hash |
2511 * @return string Hash |
2132 } |
2648 } |
2133 if (PHP_INT_SIZE === 4) { |
2649 if (PHP_INT_SIZE === 4) { |
2134 return ParagonIE_Sodium_Core32_Ed25519::keypair(); |
2650 return ParagonIE_Sodium_Core32_Ed25519::keypair(); |
2135 } |
2651 } |
2136 return ParagonIE_Sodium_Core_Ed25519::keypair(); |
2652 return ParagonIE_Sodium_Core_Ed25519::keypair(); |
|
2653 } |
|
2654 |
|
2655 /** |
|
2656 * @param string $sk |
|
2657 * @param string $pk |
|
2658 * @return string |
|
2659 * @throws SodiumException |
|
2660 */ |
|
2661 public static function crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk) |
|
2662 { |
|
2663 ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1); |
|
2664 ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1); |
|
2665 $sk = (string) $sk; |
|
2666 $pk = (string) $pk; |
|
2667 |
|
2668 if (ParagonIE_Sodium_Core_Util::strlen($sk) !== self::CRYPTO_SIGN_SECRETKEYBYTES) { |
|
2669 throw new SodiumException('secretkey should be SODIUM_CRYPTO_SIGN_SECRETKEYBYTES bytes'); |
|
2670 } |
|
2671 if (ParagonIE_Sodium_Core_Util::strlen($pk) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) { |
|
2672 throw new SodiumException('publickey should be SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES bytes'); |
|
2673 } |
|
2674 |
|
2675 if (self::useNewSodiumAPI()) { |
|
2676 return sodium_crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk); |
|
2677 } |
|
2678 return $sk . $pk; |
2137 } |
2679 } |
2138 |
2680 |
2139 /** |
2681 /** |
2140 * Generate an Ed25519 keypair from a seed. |
2682 * Generate an Ed25519 keypair from a seed. |
2141 * |
2683 * |
2664 // This is the best we can do. |
3209 // This is the best we can do. |
2665 throw new SodiumException( |
3210 throw new SodiumException( |
2666 'This is not implemented in sodium_compat, as it is not possible to securely wipe memory from PHP. ' . |
3211 'This is not implemented in sodium_compat, as it is not possible to securely wipe memory from PHP. ' . |
2667 'To fix this error, make sure libsodium is installed and the PHP extension is enabled.' |
3212 'To fix this error, make sure libsodium is installed and the PHP extension is enabled.' |
2668 ); |
3213 ); |
|
3214 } |
|
3215 |
|
3216 /** |
|
3217 * @param string $unpadded |
|
3218 * @param int $blockSize |
|
3219 * @param bool $dontFallback |
|
3220 * @return string |
|
3221 * @throws SodiumException |
|
3222 */ |
|
3223 public static function pad($unpadded, $blockSize, $dontFallback = false) |
|
3224 { |
|
3225 /* Type checks: */ |
|
3226 ParagonIE_Sodium_Core_Util::declareScalarType($unpadded, 'string', 1); |
|
3227 ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2); |
|
3228 |
|
3229 $unpadded = (string) $unpadded; |
|
3230 $blockSize = (int) $blockSize; |
|
3231 |
|
3232 if (self::useNewSodiumAPI() && !$dontFallback) { |
|
3233 return (string) sodium_pad($unpadded, $blockSize); |
|
3234 } |
|
3235 |
|
3236 if ($blockSize <= 0) { |
|
3237 throw new SodiumException( |
|
3238 'block size cannot be less than 1' |
|
3239 ); |
|
3240 } |
|
3241 $unpadded_len = ParagonIE_Sodium_Core_Util::strlen($unpadded); |
|
3242 $xpadlen = ($blockSize - 1); |
|
3243 if (($blockSize & ($blockSize - 1)) === 0) { |
|
3244 $xpadlen -= $unpadded_len & ($blockSize - 1); |
|
3245 } else { |
|
3246 $xpadlen -= $unpadded_len % $blockSize; |
|
3247 } |
|
3248 |
|
3249 $xpadded_len = $unpadded_len + $xpadlen; |
|
3250 $padded = str_repeat("\0", $xpadded_len - 1); |
|
3251 if ($unpadded_len > 0) { |
|
3252 $st = 1; |
|
3253 $i = 0; |
|
3254 $k = $unpadded_len; |
|
3255 for ($j = 0; $j <= $xpadded_len; ++$j) { |
|
3256 $i = (int) $i; |
|
3257 $k = (int) $k; |
|
3258 $st = (int) $st; |
|
3259 if ($j >= $unpadded_len) { |
|
3260 $padded[$j] = "\0"; |
|
3261 } else { |
|
3262 $padded[$j] = $unpadded[$j]; |
|
3263 } |
|
3264 /** @var int $k */ |
|
3265 $k -= $st; |
|
3266 $st = (int) (~( |
|
3267 ( |
|
3268 ( |
|
3269 ($k >> 48) |
|
3270 | |
|
3271 ($k >> 32) |
|
3272 | |
|
3273 ($k >> 16) |
|
3274 | |
|
3275 $k |
|
3276 ) - 1 |
|
3277 ) >> 16 |
|
3278 ) |
|
3279 ) & 1; |
|
3280 $i += $st; |
|
3281 } |
|
3282 } |
|
3283 |
|
3284 $mask = 0; |
|
3285 $tail = $xpadded_len; |
|
3286 for ($i = 0; $i < $blockSize; ++$i) { |
|
3287 # barrier_mask = (unsigned char) |
|
3288 # (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT)); |
|
3289 $barrier_mask = (($i ^ $xpadlen) -1) >> ((PHP_INT_SIZE << 3) - 1); |
|
3290 # tail[-i] = (tail[-i] & mask) | (0x80 & barrier_mask); |
|
3291 $padded[$tail - $i] = ParagonIE_Sodium_Core_Util::intToChr( |
|
3292 (ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]) & $mask) |
|
3293 | |
|
3294 (0x80 & $barrier_mask) |
|
3295 ); |
|
3296 # mask |= barrier_mask; |
|
3297 $mask |= $barrier_mask; |
|
3298 } |
|
3299 return $padded; |
|
3300 } |
|
3301 |
|
3302 /** |
|
3303 * @param string $padded |
|
3304 * @param int $blockSize |
|
3305 * @param bool $dontFallback |
|
3306 * @return string |
|
3307 * @throws SodiumException |
|
3308 */ |
|
3309 public static function unpad($padded, $blockSize, $dontFallback = false) |
|
3310 { |
|
3311 /* Type checks: */ |
|
3312 ParagonIE_Sodium_Core_Util::declareScalarType($padded, 'string', 1); |
|
3313 ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2); |
|
3314 |
|
3315 $padded = (string) $padded; |
|
3316 $blockSize = (int) $blockSize; |
|
3317 |
|
3318 if (self::useNewSodiumAPI() && !$dontFallback) { |
|
3319 return (string) sodium_unpad($padded, $blockSize); |
|
3320 } |
|
3321 if ($blockSize <= 0) { |
|
3322 throw new SodiumException('block size cannot be less than 1'); |
|
3323 } |
|
3324 $padded_len = ParagonIE_Sodium_Core_Util::strlen($padded); |
|
3325 if ($padded_len < $blockSize) { |
|
3326 throw new SodiumException('invalid padding'); |
|
3327 } |
|
3328 |
|
3329 # tail = &padded[padded_len - 1U]; |
|
3330 $tail = $padded_len - 1; |
|
3331 |
|
3332 $acc = 0; |
|
3333 $valid = 0; |
|
3334 $pad_len = 0; |
|
3335 |
|
3336 $found = 0; |
|
3337 for ($i = 0; $i < $blockSize; ++$i) { |
|
3338 # c = tail[-i]; |
|
3339 $c = ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]); |
|
3340 |
|
3341 # is_barrier = |
|
3342 # (( (acc - 1U) & (pad_len - 1U) & ((c ^ 0x80) - 1U) ) >> 8) & 1U; |
|
3343 $is_barrier = ( |
|
3344 ( |
|
3345 ($acc - 1) & ($pad_len - 1) & (($c ^ 80) - 1) |
|
3346 ) >> 7 |
|
3347 ) & 1; |
|
3348 $is_barrier &= ~$found; |
|
3349 $found |= $is_barrier; |
|
3350 |
|
3351 # acc |= c; |
|
3352 $acc |= $c; |
|
3353 |
|
3354 # pad_len |= i & (1U + ~is_barrier); |
|
3355 $pad_len |= $i & (1 + ~$is_barrier); |
|
3356 |
|
3357 # valid |= (unsigned char) is_barrier; |
|
3358 $valid |= ($is_barrier & 0xff); |
|
3359 } |
|
3360 # unpadded_len = padded_len - 1U - pad_len; |
|
3361 $unpadded_len = $padded_len - 1 - $pad_len; |
|
3362 if ($valid !== 1) { |
|
3363 throw new SodiumException('invalid padding'); |
|
3364 } |
|
3365 return ParagonIE_Sodium_Core_Util::substr($padded, 0, $unpadded_len); |
2669 } |
3366 } |
2670 |
3367 |
2671 /** |
3368 /** |
2672 * Will sodium_compat run fast on the current hardware and PHP configuration? |
3369 * Will sodium_compat run fast on the current hardware and PHP configuration? |
2673 * |
3370 * |