Skip to content

Instantly share code, notes, and snippets.

@devi
Created May 7, 2014 17:38
Show Gist options
  • Select an option

  • Save devi/c245de0d2e8f38511f4f to your computer and use it in GitHub Desktop.

Select an option

Save devi/c245de0d2e8f38511f4f to your computer and use it in GitHub Desktop.
Curve25519 for PHP
<?php
class Curve25519 {
const CRYPTO_SCALARBYTES = 32;
protected $minusp = null;
public function __construct() {
$this->minusp = new SplFixedArray(32);
$this->minusp[0] = 19;
$this->minusp[31] = 128;
}
protected function add($outv, $outvoffset, $a, $aoffset, $b, $boffset) {
$u = 0;
for ($j = 0; $j < 31; ++$j) {
$u += $a[$aoffset + $j] + $b[$boffset + $j];
$outv[$outvoffset + $j] = $u & 255;
$u >>= 8;
}
$outv[$outvoffset + 31] = $u + $a[$aoffset + 31] + $b[$boffset + 31];
}
protected function sub($outv, $outvoffset, $a, $aoffset, $b, $boffset) {
$u = 218;
for ($j = 0; $j < 31; ++$j) {
$u += $a[$aoffset + $j] + 65280 - $b[$boffset + $j];
$outv[$outvoffset + $j] = $u & 255;
$u >>= 8;
}
$outv[$outvoffset + 31] = $u + $a[$aoffset + 31] - $b[$boffset + 31];
}
protected function squeeze($a, $aoffset) {
$u = 0;
$a31 = $aoffset + 31;
for ($j = 0; $j < 31; ++$j) {
$u += $a[$aoffset + $j];
$a[$aoffset + $j] = $u & 255;
$u >>= 8;
}
$u += $a[$a31];
$a[$a31] = $u & 127;
$u = 19 * ($u >> 7);
for ($j = 0; $j < 31; ++$j) {
$u += $a[$aoffset + $j];
$a[$aoffset + $j] = $u & 255;
$u >>= 8;
}
$a[$a31] += $u;
}
protected function freeze($a, $aoffset) {
$aorig = new SplFixedArray(32);
for ($j = 0; $j < 32; ++$j)
$aorig[$j] = $a[$aoffset + $j];
$minuspp = $this->minusp;
$this->add($a, 0, $a, 0, $minuspp, 0);
$negative = -(($a[$aoffset + 31] >> 7) & 1);
for ($j = 0; $j < 32; ++$j)
$a[$aoffset + $j] ^= $negative & ($aorig[$j] ^ $a[$aoffset + $j]);
}
protected function mult($outv, $outvoffset, $a, $aoffset, $b, $boffset) {
$j = 0;
for ($i = 0; $i < 32; ++$i) {
$u = 0;
$b1 = $boffset + $i;
for ($j = 0; $j <= $i; ++$j)
$u += $a[$aoffset + $j] * $b[$b1 - $j];
$i1 = $i + 1;
$b1 += 32;
for ($j = $i1; $j < 32; ++$j)
$u += 38 * $a[$aoffset + $j] * $b[$b1 - $j];
$outv[$outvoffset + $i] = $u;
}
$this->squeeze($outv, $outvoffset);
}
protected function mult121665($outv, $a) {
$j = 0;
$u = 0;
for ($j = 0; $j < 31; ++$j) {
$u += 121665 * $a[$j];
$outv[$j] = $u & 255;
$u >>= 8;
}
$u += 121665 * $a[31];
$outv[31] = $u & 127;
$u = 19 * ($u >> 7);
for ($j = 0; $j < 31; ++$j) {
$u += $outv[$j];
$outv[$j] = $u & 255;
$u >>= 8;
}
$outv[$j] += $u;
}
protected function square($outv, $outvoffset, $a, $aoffset) {
$j = 0;
for ($i = 0; $i < 32; ++$i) {
$u = 0;
$a1 = $aoffset + $i;
$a2 = $aoffset + $i / 2;
for ($j = 0; $j < $i - $j; ++$j)
$u += $a[$aoffset + $j] * $a[$a1 - $j];
$i1 = $i + 1;
$i2 = $i + 32;
$a1 += 32;
for ($j = $i1; $j < $i2 - $j; ++$j)
$u += 38 * $a[$aoffset + $j] * $a[$a1 - $j];
$u *= 2;
if (($i & 1) === 0) {
$u += $a[$a2] * $a[$a2];
$u += 38 * $a[$a2 + 16] * $a[$a2 + 16];
}
$outv[$outvoffset + $i] = $u;
}
$this->squeeze($outv, $outvoffset);
}
protected function select($p, $q, $r, $s, $b) {
$b1 = $b - 1;
for ($j = 0; $j < 64; ++$j) {
$x = $b1 & ($r[$j] ^ $s[$j]);
$p[$j] = $s[$j] ^ $x;
$q[$j] = $r[$j] ^ $x;
}
}
protected function mainloop($work, $e) {
$xzm1 = new SplFixedArray(64);
$xzm = new SplFixedArray(64);
$xzmb = new SplFixedArray(64);
$xzm1b = new SplFixedArray(64);
$xznb = new SplFixedArray(64);
$xzn1b = new SplFixedArray(64);
$a0 = new SplFixedArray(64);
$a1 = new SplFixedArray(64);
$b0 = new SplFixedArray(64);
$b1 = new SplFixedArray(64);
$c1 = new SplFixedArray(64);
$r = new SplFixedArray(32);
$s = new SplFixedArray(32);
$t = new SplFixedArray(32);
$u = new SplFixedArray(32);
for ($j = 0; $j < 32; ++$j)
$xzm1[$j] = $work[$j];
$xzm1[32] = 1;
$xzm[0] = 1;
$xzmbp = $xzmb;
$a0p = $a0;
$xzm1bp = $xzm1b;
$a1p = $a1;
$b0p = $b0;
$b1p = $b1;
$c1p = $c1;
$xznbp = $xznb;
$up = $u;
$xzn1bp = $xzn1b;
$workp = $work;
$sp = $s;
$rp = $r;
for ($pos = 254; $pos >= 0; $pos--) {
$b = $e[$pos/8] >> ($pos & 7);
$b &= 1;
$this->select($xzmb, $xzm1b, $xzm, $xzm1, $b);
$this->add($a0, 0, $xzmb, 0, $xzmbp, 32);
$this->sub($a0p, 32, $xzmb, 0, $xzmbp, 32);
$this->add($a1, 0, $xzm1b, 0, $xzm1bp, 32);
$this->sub($a1p, 32, $xzm1b, 0, $xzm1bp, 32);
$this->square($b0p, 0, $a0p, 0);
$this->square($b0p, 32, $a0p, 32);
$this->mult($b1p, 0, $a1p, 0, $a0p, 32);
$this->mult($b1p, 32, $a1p, 32, $a0p, 0);
$this->add($c1, 0, $b1, 0, $b1p, 32);
$this->sub($c1p, 32, $b1, 0, $b1p, 32);
$this->square($rp, 0, $c1p, 32);
$this->sub($sp, 0, $b0, 0, $b0p, 32);
$this->mult121665($t, $s);
$this->add($u, 0, $t, 0, $b0p, 0);
$this->mult($xznbp, 0, $b0p, 0, $b0p, 32);
$this->mult($xznbp, 32, $sp, 0, $up, 0);
$this->square($xzn1bp, 0, $c1p, 0);
$this->mult($xzn1bp, 32, $rp, 0, $workp, 0);
$this->select($xzm, $xzm1, $xznb, $xzn1b, $b);
}
for ($j = 0; $j < 64; ++$j)
$work[$j] = $xzm[$j];
}
protected function recip($outv, $outvoffset, $z, $zoffset) {
$z2 = new SplFixedArray(32);
$z9 = new SplFixedArray(32);
$z11 = new SplFixedArray(32);
$z2_5_0 = new SplFixedArray(32);
$z2_10_0 = new SplFixedArray(32);
$z2_20_0 = new SplFixedArray(32);
$z2_50_0 = new SplFixedArray(32);
$z2_100_0 = new SplFixedArray(32);
$t0 = new SplFixedArray(32);
$t1 = new SplFixedArray(32);
$z2p = $z2;
$this->square($z2p, 0, $z, $zoffset);
$this->square($t1, 0, $z2, 0);
$this->square($t0, 0, $t1, 0);
$z9p = $z9;
$t0p = $t0;
$this->mult($z9p, 0, $t0p, 0, $z, $zoffset);
$this->mult($z11, 0, $z9, 0, $z2, 0);
$this->square($t0, 0, $z11, 0);
$this->mult($z2_5_0, 0, $t0, 0, $z9, 0);
$this->square($t0, 0, $z2_5_0, 0);
$this->square($t1, 0, $t0, 0);
$this->square($t0, 0, $t1, 0);
$this->square($t1, 0, $t0, 0);
$this->square($t0, 0, $t1, 0);
$this->mult($z2_10_0, 0, $t0, 0, $z2_5_0, 0);
$this->square($t0, 0, $z2_10_0, 0);
$this->square($t1, 0, $t0, 0);
for ($i = 2; $i < 10; $i += 2) {
$this->square($t0, 0, $t1, 0);
$this->square($t1, 0, $t0, 0);
}
$this->mult($z2_20_0, 0, $t1, 0, $z2_10_0, 0);
$this->square($t0, 0, $z2_20_0, 0);
$this->square($t1, 0, $t0, 0);
for ($i = 2; $i < 20; $i += 2) {
$this->square($t0, 0, $t1, 0);
$this->square($t1, 0, $t0, 0);
}
$this->mult($t0, 0, $t1, 0, $z2_20_0, 0);
$this->square($t1, 0, $t0, 0);
$this->square($t0, 0, $t1, 0);
for ($i = 2; $i < 10; $i += 2) {
$this->square($t1, 0, $t0, 0);
$this->square($t0, 0, $t1, 0);
}
$this->mult($z2_50_0, 0, $t0, 0, $z2_10_0, 0);
$this->square($t0, 0, $z2_50_0, 0);
$this->square($t1, 0, $t0, 0);
for ($i = 2; $i < 50; $i += 2) {
$this->square($t0, 0, $t1, 0);
$this->square($t1, 0, $t0, 0);
}
$this->mult($z2_100_0, 0, $t1, 0, $z2_50_0, 0);
$this->square($t1, 0, $z2_100_0, 0);
$this->square($t0, 0, $t1, 0);
for ($i = 2; $i < 100; $i += 2) {
$this->square($t1, 0, $t0, 0);
$this->square($t0, 0, $t1, 0);
}
$this->mult($t1, 0, $t0, 0, $z2_100_0, 0);
$this->square($t0, 0, $t1, 0);
$this->square($t1, 0, $t0, 0);
for ($i = 2; $i < 50; $i += 2) {
$this->square($t0, 0, $t1, 0);
$this->square($t1, 0, $t0, 0);
}
$this->mult($t0, 0, $t1, 0, $z2_50_0, 0);
$this->square($t1, 0, $t0, 0);
$this->square($t0, 0, $t1, 0);
$this->square($t1, 0, $t0, 0);
$this->square($t0, 0, $t1, 0);
$this->square($t1, 0, $t0, 0);
$t1p = $t1;
$z11p = $z11;
$this->mult($outv, $outvoffset, $t1p, 0, $z11p, 0);
}
public function scalarMult($n, $p) {
if (count($n) !== self::CRYPTO_SCALARBYTES || count($p) !== self::CRYPTO_SCALARBYTES) {
return false;
}
$work = new SplFixedArray(96);
$e = new SplFixedArray(32);
for ($i = 0; $i < 32; ++$i) $e[$i] = $n[$i];
$e[0] &= 248;
$e[31] &= 127;
$e[31] |= 64;
for ($i = 0; $i < 32; ++$i)
$work[$i] = $p[$i] & 0xFF;
$this->mainloop($work, $e);
$this->recip($work, 32, $work, 32);
$this->mult($work, 64, $work, 0, $work, 32);
$this->freeze($work, 64);
$q = new SplFixedArray(32);
for ($i = 0; $i < 32; ++$i)
$q[$i] = $work[64 + $i];
return $q;
}
public function scalarMultBase($priv) {
$bp = new SplFixedArray(32);
$bp[0] = 9;
return $this->scalarMult($priv, $bp);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment