Subversion Repositories oidplus

Rev

Rev 846 | Rev 1042 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
827 daniel-mar 1
<?php
2
 
3
/**
4
 * EC Public Key
5
 *
874 daniel-mar 6
 * @category  Crypt
7
 * @package   EC
827 daniel-mar 8
 * @author    Jim Wigginton <terrafrost@php.net>
9
 * @copyright 2015 Jim Wigginton
10
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
11
 * @link      http://phpseclib.sourceforge.net
12
 */
13
 
14
namespace phpseclib3\Crypt\EC;
15
 
16
use phpseclib3\Common\Functions\Strings;
17
use phpseclib3\Crypt\Common;
18
use phpseclib3\Crypt\EC;
19
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
20
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
21
use phpseclib3\Crypt\EC\Curves\Ed25519;
22
use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
23
use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
24
use phpseclib3\Crypt\Hash;
25
use phpseclib3\Exception\UnsupportedOperationException;
26
use phpseclib3\Math\BigInteger;
27
 
28
/**
29
 * EC Public Key
30
 *
874 daniel-mar 31
 * @package EC
827 daniel-mar 32
 * @author  Jim Wigginton <terrafrost@php.net>
874 daniel-mar 33
 * @access  public
827 daniel-mar 34
 */
35
class PublicKey extends EC implements Common\PublicKey
36
{
37
    use Common\Traits\Fingerprint;
38
 
39
    /**
40
     * Verify a signature
41
     *
42
     * @see self::verify()
874 daniel-mar 43
     * @access public
827 daniel-mar 44
     * @param string $message
45
     * @param string $signature
46
     * @return mixed
47
     */
48
    public function verify($message, $signature)
49
    {
50
        if ($this->curve instanceof MontgomeryCurve) {
51
            throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
52
        }
53
 
54
        $shortFormat = $this->shortFormat;
55
        $format = $this->sigFormat;
56
        if ($format === false) {
57
            return false;
58
        }
59
 
60
        $order = $this->curve->getOrder();
61
 
62
        if ($this->curve instanceof TwistedEdwardsCurve) {
63
            if ($shortFormat == 'SSH2') {
64
                list(, $signature) = Strings::unpackSSH2('ss', $signature);
65
            }
66
 
67
            if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) {
68
                return sodium_crypto_sign_verify_detached($signature, $message, $this->toString('libsodium'));
69
            }
70
 
71
            $curve = $this->curve;
72
            if (strlen($signature) != 2 * $curve::SIZE) {
73
                return false;
74
            }
75
 
76
            $R = substr($signature, 0, $curve::SIZE);
77
            $S = substr($signature, $curve::SIZE);
78
 
79
            try {
80
                $R = PKCS1::extractPoint($R, $curve);
81
                $R = $this->curve->convertToInternal($R);
82
            } catch (\Exception $e) {
83
                return false;
84
            }
85
 
86
            $S = strrev($S);
87
            $S = new BigInteger($S, 256);
88
 
89
            if ($S->compare($order) >= 0) {
90
                return false;
91
            }
92
 
93
            $A = $curve->encodePoint($this->QA);
94
 
95
            if ($curve instanceof Ed25519) {
96
                $dom2 = !isset($this->context) ? '' :
97
                    'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context;
98
            } else {
99
                $context = isset($this->context) ? $this->context : '';
100
                $dom2 = 'SigEd448' . "\0" . chr(strlen($context)) . $context;
101
            }
102
 
103
            $hash = new Hash($curve::HASH);
104
            $k = $hash->hash($dom2 . substr($signature, 0, $curve::SIZE) . $A . $message);
105
            $k = strrev($k);
106
            $k = new BigInteger($k, 256);
107
            list(, $k) = $k->divide($order);
108
 
109
            $qa = $curve->convertToInternal($this->QA);
110
 
111
            $lhs = $curve->multiplyPoint($curve->getBasePoint(), $S);
112
            $rhs = $curve->multiplyPoint($qa, $k);
113
            $rhs = $curve->addPoint($rhs, $R);
114
            $rhs = $curve->convertToAffine($rhs);
115
 
116
            return $lhs[0]->equals($rhs[0]) && $lhs[1]->equals($rhs[1]);
117
        }
118
 
119
        $params = $format::load($signature);
120
        if ($params === false || count($params) != 2) {
121
            return false;
122
        }
123
        extract($params);
124
 
125
        if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
126
            $sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
127
 
128
            $result = openssl_verify($message, $sig, $this->toString('PKCS8', ['namedCurve' => false]), $this->hash->getHash());
129
 
130
            if ($result != -1) {
131
                return (bool) $result;
132
            }
133
        }
134
 
135
        $n_1 = $order->subtract(self::$one);
136
        if (!$r->between(self::$one, $n_1) || !$s->between(self::$one, $n_1)) {
137
            return false;
138
        }
139
 
140
        $e = $this->hash->hash($message);
141
        $e = new BigInteger($e, 256);
142
 
143
        $Ln = $this->hash->getLength() - $order->getLength();
144
        $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;
145
 
146
        $w = $s->modInverse($order);
147
        list(, $u1) = $z->multiply($w)->divide($order);
148
        list(, $u2) = $r->multiply($w)->divide($order);
149
 
150
        $u1 = $this->curve->convertInteger($u1);
151
        $u2 = $this->curve->convertInteger($u2);
152
 
153
        list($x1, $y1) = $this->curve->multiplyAddPoints(
154
            [$this->curve->getBasePoint(), $this->QA],
155
            [$u1, $u2]
156
        );
157
 
158
        $x1 = $x1->toBigInteger();
159
        list(, $x1) = $x1->divide($order);
160
 
161
        return $x1->equals($r);
162
    }
163
 
164
    /**
165
     * Returns the public key
166
     *
167
     * @param string $type
168
     * @param array $options optional
169
     * @return string
170
     */
171
    public function toString($type, array $options = [])
172
    {
173
        $type = self::validatePlugin('Keys', $type, 'savePublicKey');
174
 
175
        return $type::savePublicKey($this->curve, $this->QA, $options);
176
    }
177
}