pssht  latest
SSH server library written in PHP
ED25519.php
1 <?php
2 
3 /*
4 * This file is part of pssht.
5 *
6 * (c) François Poirotte <clicky@erebot.net>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 
12 namespace fpoirotte\Pssht\Key\SSH;
13 
25 class ED25519 implements
27  \fpoirotte\Pssht\Algorithms\AvailabilityInterface
28 {
30  protected $pk;
31 
33  protected $sk;
34 
35 
48  public function __construct($pk, $sk = null)
49  {
50  if (strlen($pk) !== 32 || ($sk !== null && strlen($sk) !== 32)) {
51  throw new \InvalidArgumentException();
52  }
53  $this->pk = $pk;
54  $this->sk = $sk;
55  }
56 
57  public static function getName()
58  {
59  return 'ssh-ed25519';
60  }
61 
62  public function serialize(\fpoirotte\Pssht\Wire\Encoder $encoder)
63  {
64  $encoder->encodeString(self::getName());
65  $encoder->encodeString($this->pk);
66  }
67 
68  public static function unserialize(\fpoirotte\Pssht\Wire\Decoder $decoder, $private = null)
69  {
70  $pk = $decoder->decodeString();
71  if ($pk === null) {
72  throw new \InvalidArgumentException();
73  }
74  return new static($pk, $private);
75  }
76 
77 
78  public static function isAvailable()
79  {
80  return function_exists('hash_algos') &&
81  function_exists('hash') &&
82  in_array('sha512', hash_algos());
83  }
84 
85  protected static function encodeint($y)
86  {
87  $t = str_pad(gmp_strval($y, 16), 64, 0, STR_PAD_LEFT);
88  $res = strrev(pack('H*', $t));
89  return $res;
90  }
91 
92  protected static function encodepoint($P)
93  {
94  list($x, $y) = $P;
95  $t = gmp_or(
96  gmp_and($y, gmp_sub(gmp_pow(2, 256), 1)),
97  gmp_mul(gmp_and($x, 1), gmp_pow(2, 255))
98  );
99  $t = str_pad(gmp_strval($t, 16), 64, 0, STR_PAD_LEFT);
100  $res = strrev(pack('H*', $t));
101  return $res;
102  }
103 
104  protected static function hashint($m)
105  {
106  $h = hash('sha512', $m, true);
107  $res = gmp_init(bin2hex(strrev($h)), 16);
108  return $res;
109  }
110 
111  protected static function decodeint($s)
112  {
113  return gmp_init(bin2hex(strrev($s)), 16);
114  }
115 
116  protected static function decodepoint($s)
117  {
118  $curve = \fpoirotte\Pssht\ECC\ED25519::getInstance();
119  $y = gmp_and(
120  gmp_init(bin2hex(strrev($s)), 16),
121  gmp_sub(gmp_pow(2, 255), 1)
122  );
123  $x = $curve->xrecover($y);
124  if (gmp_cmp(gmp_and($x, 1), (ord(substr($s, -1)) >> 7) & 1)) {
125  $x = gmp_sub($curve->q, $x);
126  }
127  $P = array($x, $y);
128  if (!static::isOnCurve($P)) {
129  return null;
130  }
131  return $P;
132  }
133 
134  protected static function isOnCurve($P)
135  {
136  $curve = \fpoirotte\Pssht\ECC\ED25519::getInstance();
137  list($x, $y) = $P;
138  $x2 = gmp_mul($x, $x);
139  $y2 = gmp_mul($y, $y);
140  $t = gmp_mod(
141  gmp_sub(
142  gmp_sub(gmp_add(gmp_neg($x2), $y2), 1),
143  gmp_mul($curve->d, gmp_mul($x2, $y2))
144  ),
145  $curve->q
146  );
147  return !gmp_cmp($t, 0);
148  }
149 
150  public function sign($message)
151  {
152  if ($this->sk === null) {
153  throw new \RuntimeException();
154  }
155 
156  $curve = \fpoirotte\Pssht\ECC\ED25519::getInstance();
157  $h = hash('sha512', $this->sk, true);
158  $a = gmp_add(
159  gmp_pow(2, 256-2),
160  gmp_and(
161  gmp_init(bin2hex(strrev($h)), 16),
162  gmp_sub(gmp_pow(2, 254), 8)
163  )
164  );
165  $r = static::hashint(substr($h, 32) . $message);
166  $R = $curve->scalarmult($curve->B, $r);
167  $t = static::encodepoint($R) . $this->pk . $message;
168  $S = gmp_mod(
169  gmp_add(
170  $r,
171  gmp_mul(
172  static::hashint($t),
173  $a
174  )
175  ),
176  $curve->l
177  );
178  return static::encodepoint($R) . static::encodeint($S);
179  }
180 
181  public function check($message, $signature)
182  {
183  $curve = \fpoirotte\Pssht\ECC\ED25519::getInstance();
184  if (strlen($signature) !== 64) {
185  throw new \InvalidArgumentException();
186  }
187 
188  $R = static::decodepoint(substr($signature, 0, 32));
189  if ($R === null) {
190  return false;
191  }
192 
193  $A = static::decodepoint($this->pk);
194  if ($A === null) {
195  return false;
196  }
197  $S = static::decodeint(substr($signature, 32, 64));
198  $h = static::hashint(static::encodepoint($R) . $this->pk . $message);
199  $res1 = $curve->scalarmult($curve->B, $S);
200  $res2 = $curve->edwards($R, $curve->scalarmult($A, $h));
201  return (!gmp_cmp($res1[0], $res2[0]) &&
202  !gmp_cmp($res1[1], $res2[1]));
203  }
204 }
serialize(\fpoirotte\Pssht\Wire\Encoder $encoder)
Definition: ED25519.php:62
__construct($pk, $sk=null)
Definition: ED25519.php:48
check($message, $signature)
Definition: ED25519.php:181
static getName()
Return the name of the algorithm.
Definition: ED25519.php:57
$sk
Private (secret) key.
Definition: ED25519.php:33