Ed25519::unpackneg()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 45
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 29
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 45
ccs 29
cts 29
cp 1
crap 4
rs 9.456
1
<?php
2
3
/**
4
 * Copyright (c) 2020 UMI
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in all
14
 * copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24
25
declare(strict_types=1);
26
27
namespace UmiTop\UmiCore\Util\Ed25519;
28
29
/**
30
 * Class Ed25519
31
 * Implementation derived from TweetNaCl version 20140427.
32
 * @see http://tweetnacl.cr.yp.to/
33
 * @package UmiTop\UmiCore\Util\Ed25519
34
 * @SuppressWarnings(PHPMD.ShortMethodName)
35
 * @SuppressWarnings(PHPMD.ShortVariable)
36
 */
37
class Ed25519 extends AbstractEd25519
38
{
39
    /**
40
     * @param string $seed
41
     * @return string
42
     */
43 5
    public function secretKeyFromSeed(string $seed): string
44
    {
45 5
        $p = array_fill(0, 4, array_fill(0, 16, 0));
46
47 5
        $d = hash('sha512', $seed, true);
48 5
        $d[0] = chr(ord($d[0]) & 248);   // d[0] &= 248;
49 5
        $d[31] = chr(ord($d[31]) & 127); // d[31] &= 127;
50 5
        $d[31] = chr(ord($d[31]) | 64);  // d[31] |= 64;
51
52 5
        $pub = str_repeat("\x0", 32);
53
54 5
        $this->scalarbase($p, $d);
55 5
        $this->pack($pub, $p);
56
57 5
        return $seed . $pub;
58
    }
59
60
    /**
61
     * @param string $message
62
     * @param string $secretKey
63
     * @return string
64
     */
65 3
    public function sign(string $message, string $secretKey): string
66
    {
67
        // хэшируем приватный ключик (32байта)
68 3
        $d = hash('sha512', substr($secretKey, 0, 32), true);
69 3
        $d[0] = chr(ord($d[0]) & 248);   // d[0] &= 248
70 3
        $d[31] = chr(ord($d[31]) & 127); // d[31] &= 127
71 3
        $d[31] = chr(ord($d[31]) | 64);  // d[31] |= 64
72
73 3
        $sm = str_repeat("\x0", 32) . substr($d, 32, 32) . $message;
74
75 3
        $r = hash('sha512', substr($sm, 32), true);
76 3
        $this->reduce($r);
77
78 3
        $p = array_fill(0, 4, array_fill(0, 16, 0));
79 3
        $this->scalarbase($p, $r);
80 3
        $this->pack($sm, $p);
81
82 3
        $sm = substr_replace($sm, substr($secretKey, 32, 32), 32, 32);
83
84 3
        $h = hash('sha512', $sm, true);
85 3
        $this->reduce($h);
86
87 3
        $x = array_fill(0, 64, 0);
88 3
        for ($i = 0; $i < 32; $i++) {
89 3
            $x[$i] = ord($r[$i]);
90
        }
91 3
        for ($i = 0; $i < 32; $i++) {
92 3
            for ($j = 0; $j < 32; $j++) {
93 3
                $x[$i + $j] += ord($h[$i]) * ord($d[$j]);
94
            }
95
        }
96
97 3
        $sm2 = substr($sm, 32);
98 3
        $this->modL($sm2, $x);
99
100 3
        return substr($sm, 0, 32) . substr($sm2, 0, 32);
101
    }
102
103
    /**
104
     * @param string $signature
105
     * @param string $message
106
     * @param string $publicKey
107
     * @return bool
108
     */
109 6
    public function verify(string $signature, string $message, string $publicKey): bool
110
    {
111 6
        $q = array_fill(0, 4, array_fill(0, 16, 0));
112 6
        if (!$this->unpackneg($q, $publicKey)) {
113
            return false; // @codeCoverageIgnore
114
        }
115
116 6
        $sm = $signature . $message;
117 6
        $m = substr_replace($sm, substr($publicKey, 0, 32), 32, 32);
118
119 6
        $h = hash('sha512', $m, true);
120 6
        $this->reduce($h);
121
122 6
        $p = array_fill(0, 4, array_fill(0, 16, 0));
123 6
        $this->scalarmult($p, $q, $h);
124 6
        $this->scalarbase($q, substr($sm, 32));
125 6
        $this->add($p, $q);
126
127 6
        $t = str_repeat("\x0", 32);
128 6
        $this->pack($t, $p);
129
130 6
        return $this->cryptoVerify32($sm, $t);
131
    }
132
133
    /**
134
     * @param array<int, array<int, int>> $p
135
     * @param array<int, array<int, int>> $q
136
     * @return void
137
     */
138 12
    private function add(array &$p, array $q): void
139
    {
140 12
        $a = $b = $c = $d = $t = $e = $f = $g = $h = array_fill(0, 16, 0);
141
142 12
        $this->fnZ($a, $p[1], $p[0]);
143 12
        $this->fnZ($t, $q[1], $q[0]);
144 12
        $this->fnM($a, $a, $t);
145 12
        $this->fnA($b, $p[0], $p[1]);
146 12
        $this->fnA($t, $q[0], $q[1]);
147 12
        $this->fnM($b, $b, $t);
148 12
        $this->fnM($c, $p[3], $q[3]);
149 12
        $this->fnM($c, $c, $this->D2);
150 12
        $this->fnM($d, $p[2], $q[2]);
151 12
        $this->fnA($d, $d, $d);
152 12
        $this->fnZ($e, $b, $a);
153 12
        $this->fnZ($f, $d, $c);
154 12
        $this->fnA($g, $d, $c);
155 12
        $this->fnA($h, $b, $a);
156
157 12
        $this->fnM($p[0], $e, $f);
158 12
        $this->fnM($p[1], $h, $g);
159 12
        $this->fnM($p[2], $g, $f);
160 12
        $this->fnM($p[3], $e, $h);
161 12
    }
162
163
    /**
164
     * @param string $r
165
     * @param array<int, int> $x
166
     * @return void
167
     */
168 8
    private function modL(string &$r, array &$x): void
169
    {
170 8
        for ($i = 63; $i >= 32; --$i) {
171 8
            $carry = 0;
172 8
            for ($j = $i - 32; $j < $i - 12; ++$j) {
173 8
                $x[$j] += $carry - 16 * $x[$i] * $this->L[$j - ($i - 32)];
174 8
                $carry = ($x[$j] + 128) >> 8;
175 8
                $x[$j] -= $carry << 8;
176
            }
177 8
            $x[$j] += $carry;
178 8
            $x[$i] = 0;
179
        }
180
181 8
        $carry = 0;
182 8
        for ($j = 0; $j < 32; $j++) {
183 8
            $x[$j] += $carry - ($x[31] >> 4) * $this->L[$j];
184 8
            $carry = $x[$j] >> 8;
185 8
            $x[$j] &= 255;
186
        }
187
188 8
        for ($j = 0; $j < 32; $j++) {
189 8
            $x[$j] -= $carry * $this->L[$j];
190
        }
191
192 8
        for ($i = 0; $i < 32; $i++) {
193 8
            $x[$i + 1] += $x[$i] >> 8;
194 8
            $r[$i] = chr($x[$i] & 255);
195
        }
196 8
    }
197
198
    /**
199
     * @param string $r
200
     * @param array<int, array<int, int>> $p
201
     * @return void
202
     */
203 12
    private function pack(string &$r, array $p): void
204
    {
205 12
        $tx = $ty = $zi = array_fill(0, 16, 0);
206
207 12
        $this->inv25519($zi, $p[2]);
208 12
        $this->fnM($tx, $p[0], $zi);
209 12
        $this->fnM($ty, $p[1], $zi);
210 12
        $this->pack25519($r, $ty);
211
212 12
        $r[31] = chr(ord($r[31]) ^ $this->par25519($tx) << 7); // r[31] ^= par25519(tx) << 7;
213 12
    }
214
215
    /**
216
     * @param string $r
217
     * @return void
218
     */
219 8
    private function reduce(string &$r): void
220
    {
221 8
        $x = array_fill(0, 64, 0);
222
223 8
        for ($i = 0; $i < 64; $i++) {
224 8
            $x[$i] = ord($r[$i]);
225
        }
226
227 8
        for ($i = 0; $i < 64; $i++) {
228 8
            $r[$i] = chr(0);
229
        }
230
231 8
        $this->modL($r, $x);
232 8
    }
233
234
    /**
235
     * @param array<int, array<int, int>> $p
236
     * @param string $s
237
     * @return void
238
     */
239 12
    private function scalarbase(array &$p, string $s): void
240
    {
241 12
        $q = array_fill(0, 4, array_fill(0, 16, 0));
242 12
        $this->set25519($q[0], $this->X);
243 12
        $this->set25519($q[1], $this->Y);
244 12
        $this->set25519($q[2], $this->gf1);
245 12
        $this->fnM($q[3], $this->X, $this->Y);
246 12
        $this->scalarmult($p, $q, $s);
247 12
    }
248
249
    /**
250
     * @param array<int, array<int, int>> $p
251
     * @param array<int, array<int, int>> $q
252
     * @param string $s
253
     * @return void
254
     */
255 12
    private function scalarmult(array &$p, array &$q, string $s): void
256
    {
257 12
        $this->set25519($p[0], $this->gf0);
258 12
        $this->set25519($p[1], $this->gf1);
259 12
        $this->set25519($p[2], $this->gf1);
260 12
        $this->set25519($p[3], $this->gf0);
261
262 12
        for ($i = 255; $i >= 0; --$i) {
263 12
            $b = (ord($s[(int)($i / 8)]) >> ($i & 7)) & 1;
264 12
            $this->cswap($p, $q, $b);
265 12
            $this->add($q, $p);
266 12
            $this->add($p, $p);
267 12
            $this->cswap($p, $q, $b);
268
        }
269 12
    }
270
271
    /**
272
     * @param array<int, array<int, int>> $r
273
     * @param string $p
274
     * @return bool
275
     */
276 6
    private function unpackneg(array &$r, string $p): bool
277
    {
278 6
        $t = $chk = $num = $den = $den2 = $den4 = $den6 = array_fill(0, 16, 0);
279
280 6
        $this->set25519($r[2], $this->gf1);
281 6
        $this->unpack25519($r[1], $p);
282
283 6
        $this->fnM($num, $r[1], $r[1]);
284 6
        $this->fnM($den, $num, $this->D);
285 6
        $this->fnZ($num, $num, $r[2]);
286 6
        $this->fnA($den, $r[2], $den);
287
288 6
        $this->fnM($den2, $den, $den);
289 6
        $this->fnM($den4, $den2, $den2);
290 6
        $this->fnM($den6, $den4, $den2);
291 6
        $this->fnM($t, $den6, $num);
292 6
        $this->fnM($t, $t, $den);
293
294 6
        $this->pow2523($t, $t);
295 6
        $this->fnM($t, $t, $num);
296 6
        $this->fnM($t, $t, $den);
297 6
        $this->fnM($t, $t, $den);
298 6
        $this->fnM($r[0], $t, $den);
299
300 6
        $this->fnM($chk, $r[0], $r[0]);
301 6
        $this->fnM($chk, $chk, $den);
302
303 6
        if (!$this->neq25519($chk, $num)) {
304 3
            $this->fnM($r[0], $r[0], $this->I);
305
        }
306
307 6
        $this->fnM($chk, $r[0], $r[0]);
308 6
        $this->fnM($chk, $chk, $den);
309
310 6
        if (!$this->neq25519($chk, $num)) {
311
            return false; // @codeCoverageIgnore
312
        }
313
314 6
        if ($this->par25519($r[0]) === (ord($p[31]) >> 7)) {
315 4
            $this->fnZ($r[0], $this->gf0, $r[0]);
316
        }
317
318 6
        $this->fnM($r[3], $r[0], $r[1]);
319
320 6
        return true;
321
    }
322
}
323