Subversion Repositories oidplus

Rev

Rev 846 | 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
 * "PKCS1" (RFC5915) Formatted EC Key Handler
5
 *
6
 * PHP version 5
7
 *
8
 * Used by File/X509.php
9
 *
10
 * Processes keys with the following headers:
11
 *
12
 * -----BEGIN EC PRIVATE KEY-----
13
 * -----BEGIN EC PARAMETERS-----
14
 *
15
 * Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to describe
16
 * DSA, whose format isn't really formally described anywhere, so might as well
17
 * use it to describe this, too. PKCS1 is easier to remember than RFC5915, after
18
 * all. I suppose this could also be named IETF but idk
19
 *
874 daniel-mar 20
 * @category  Crypt
21
 * @package   EC
827 daniel-mar 22
 * @author    Jim Wigginton <terrafrost@php.net>
23
 * @copyright 2015 Jim Wigginton
24
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
25
 * @link      http://phpseclib.sourceforge.net
26
 */
27
 
28
namespace phpseclib3\Crypt\EC\Formats\Keys;
29
 
30
use ParagonIE\ConstantTime\Base64;
31
use phpseclib3\Common\Functions\Strings;
32
use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
33
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
34
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
35
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
36
use phpseclib3\Exception\UnsupportedCurveException;
37
use phpseclib3\File\ASN1;
38
use phpseclib3\File\ASN1\Maps;
39
use phpseclib3\Math\BigInteger;
40
 
41
/**
42
 * "PKCS1" (RFC5915) Formatted EC Key Handler
43
 *
874 daniel-mar 44
 * @package EC
827 daniel-mar 45
 * @author  Jim Wigginton <terrafrost@php.net>
874 daniel-mar 46
 * @access  public
827 daniel-mar 47
 */
48
abstract class PKCS1 extends Progenitor
49
{
50
    use Common;
51
 
52
    /**
53
     * Break a public or private key down into its constituent components
54
     *
874 daniel-mar 55
     * @access public
827 daniel-mar 56
     * @param string $key
57
     * @param string $password optional
58
     * @return array
59
     */
60
    public static function load($key, $password = '')
61
    {
62
        self::initialize_static_variables();
63
 
64
        if (!Strings::is_stringable($key)) {
65
            throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
66
        }
67
 
68
        if (strpos($key, 'BEGIN EC PARAMETERS') && strpos($key, 'BEGIN EC PRIVATE KEY')) {
69
            $components = [];
70
 
71
            preg_match('#-*BEGIN EC PRIVATE KEY-*[^-]*-*END EC PRIVATE KEY-*#s', $key, $matches);
72
            $decoded = parent::load($matches[0], $password);
73
            $decoded = ASN1::decodeBER($decoded);
74
            if (empty($decoded)) {
75
                throw new \RuntimeException('Unable to decode BER');
76
            }
77
 
78
            $ecPrivate = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
79
            if (!is_array($ecPrivate)) {
80
                throw new \RuntimeException('Unable to perform ASN1 mapping');
81
            }
82
 
83
            if (isset($ecPrivate['parameters'])) {
84
                $components['curve'] = self::loadCurveByParam($ecPrivate['parameters']);
85
            }
86
 
87
            preg_match('#-*BEGIN EC PARAMETERS-*[^-]*-*END EC PARAMETERS-*#s', $key, $matches);
88
            $decoded = parent::load($matches[0], '');
89
            $decoded = ASN1::decodeBER($decoded);
90
            if (empty($decoded)) {
91
                throw new \RuntimeException('Unable to decode BER');
92
            }
93
            $ecParams = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
94
            if (!is_array($ecParams)) {
95
                throw new \RuntimeException('Unable to perform ASN1 mapping');
96
            }
97
            $ecParams = self::loadCurveByParam($ecParams);
98
 
99
            // comparing $ecParams and $components['curve'] directly won't work because they'll have different Math\Common\FiniteField classes
100
            // even if the modulo is the same
101
            if (isset($components['curve']) && self::encodeParameters($ecParams, false, []) != self::encodeParameters($components['curve'], false, [])) {
102
                throw new \RuntimeException('EC PARAMETERS does not correspond to EC PRIVATE KEY');
103
            }
104
 
105
            if (!isset($components['curve'])) {
106
                $components['curve'] = $ecParams;
107
            }
108
 
109
            $components['dA'] = new BigInteger($ecPrivate['privateKey'], 256);
110
            $components['curve']->rangeCheck($components['dA']);
111
            $components['QA'] = isset($ecPrivate['publicKey']) ?
112
                self::extractPoint($ecPrivate['publicKey'], $components['curve']) :
113
                $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
114
 
115
            return $components;
116
        }
117
 
118
        $key = parent::load($key, $password);
119
 
120
        $decoded = ASN1::decodeBER($key);
121
        if (empty($decoded)) {
122
            throw new \RuntimeException('Unable to decode BER');
123
        }
124
 
125
        $key = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
126
        if (is_array($key)) {
127
            return ['curve' => self::loadCurveByParam($key)];
128
        }
129
 
130
        $key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
131
        if (!is_array($key)) {
132
            throw new \RuntimeException('Unable to perform ASN1 mapping');
133
        }
134
        if (!isset($key['parameters'])) {
135
            throw new \RuntimeException('Key cannot be loaded without parameters');
136
        }
137
 
138
        $components = [];
139
        $components['curve'] = self::loadCurveByParam($key['parameters']);
140
        $components['dA'] = new BigInteger($key['privateKey'], 256);
141
        $components['QA'] = isset($ecPrivate['publicKey']) ?
142
            self::extractPoint($ecPrivate['publicKey'], $components['curve']) :
143
            $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
144
 
145
        return $components;
146
    }
147
 
148
    /**
149
     * Convert EC parameters to the appropriate format
150
     *
874 daniel-mar 151
     * @access public
827 daniel-mar 152
     * @return string
153
     */
154
    public static function saveParameters(BaseCurve $curve, array $options = [])
155
    {
156
        self::initialize_static_variables();
157
 
158
        if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
159
            throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
160
        }
161
 
162
        $key = self::encodeParameters($curve, false, $options);
163
 
164
        return "-----BEGIN EC PARAMETERS-----\r\n" .
165
               chunk_split(Base64::encode($key), 64) .
166
               "-----END EC PARAMETERS-----\r\n";
167
    }
168
 
169
    /**
170
     * Convert a private key to the appropriate format.
171
     *
874 daniel-mar 172
     * @access public
827 daniel-mar 173
     * @param \phpseclib3\Math\BigInteger $privateKey
174
     * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
175
     * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
176
     * @param string $password optional
177
     * @param array $options optional
178
     * @return string
179
     */
180
    public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $password = '', array $options = [])
181
    {
182
        self::initialize_static_variables();
183
 
184
        if ($curve instanceof TwistedEdwardsCurve  || $curve instanceof MontgomeryCurve) {
185
            throw new UnsupportedCurveException('TwistedEdwards Curves are not supported');
186
        }
187
 
188
        $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
189
 
190
        $key = [
191
            'version' => 'ecPrivkeyVer1',
192
            'privateKey' => $privateKey->toBytes(),
193
            'parameters' => new ASN1\Element(self::encodeParameters($curve)),
194
            'publicKey' => "\0" . $publicKey
195
        ];
196
 
197
        $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);
198
 
199
        return self::wrapPrivateKey($key, 'EC', $password, $options);
200
    }
201
}