Subversion Repositories oidplus

Rev

Rev 1042 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
827 daniel-mar 1
<?php
2
 
3
/**
4
 * PKCS#8 Formatted EC Key Handler
5
 *
6
 * PHP version 5
7
 *
8
 * Processes keys with the following headers:
9
 *
10
 * -----BEGIN ENCRYPTED PRIVATE KEY-----
11
 * -----BEGIN PRIVATE KEY-----
12
 * -----BEGIN PUBLIC KEY-----
13
 *
14
 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
15
 * is specific to private keys it's basically creating a DER-encoded wrapper
16
 * for keys. This just extends that same concept to public keys (much like ssh-keygen)
17
 *
18
 * @author    Jim Wigginton <terrafrost@php.net>
19
 * @copyright 2015 Jim Wigginton
20
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
21
 * @link      http://phpseclib.sourceforge.net
22
 */
23
 
24
namespace phpseclib3\Crypt\EC\Formats\Keys;
25
 
26
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
27
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
28
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
29
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
30
use phpseclib3\Crypt\EC\Curves\Ed25519;
31
use phpseclib3\Crypt\EC\Curves\Ed448;
32
use phpseclib3\Exception\UnsupportedCurveException;
33
use phpseclib3\File\ASN1;
34
use phpseclib3\File\ASN1\Maps;
35
use phpseclib3\Math\BigInteger;
36
 
37
/**
38
 * PKCS#8 Formatted EC Key Handler
39
 *
40
 * @author  Jim Wigginton <terrafrost@php.net>
41
 */
42
abstract class PKCS8 extends Progenitor
43
{
44
    use Common;
45
 
46
    /**
47
     * OID Name
48
     *
49
     * @var array
50
     */
51
    const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448'];
52
 
53
    /**
54
     * OID Value
55
     *
56
     * @var string
57
     */
58
    const OID_VALUE = ['1.2.840.10045.2.1', '1.3.101.112', '1.3.101.113'];
59
 
60
    /**
61
     * Break a public or private key down into its constituent components
62
     *
63
     * @param string $key
64
     * @param string $password optional
65
     * @return array
66
     */
67
    public static function load($key, $password = '')
68
    {
69
        // initialize_static_variables() is defined in both the trait and the parent class
70
        // when it's defined in two places it's the traits one that's called
71
        // the parent one is needed, as well, but the parent one is called by other methods
72
        // in the parent class as needed and in the context of the parent it's the parent
73
        // one that's called
74
        self::initialize_static_variables();
75
 
76
        $key = parent::load($key, $password);
77
 
78
        $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey';
79
 
80
        switch ($key[$type . 'Algorithm']['algorithm']) {
81
            case 'id-Ed25519':
82
            case 'id-Ed448':
83
                return self::loadEdDSA($key);
84
        }
85
 
86
        $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element);
1042 daniel-mar 87
        if (!$decoded) {
88
            throw new \RuntimeException('Unable to decode BER');
89
        }
827 daniel-mar 90
        $params = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
91
        if (!$params) {
92
            throw new \RuntimeException('Unable to decode the parameters using Maps\ECParameters');
93
        }
94
 
95
        $components = [];
96
        $components['curve'] = self::loadCurveByParam($params);
97
 
1114 daniel-mar 98
        if ($type == 'publicKey') {
827 daniel-mar 99
            $components['QA'] = self::extractPoint("\0" . $key['publicKey'], $components['curve']);
100
 
101
            return $components;
102
        }
103
 
104
        $decoded = ASN1::decodeBER($key['privateKey']);
1042 daniel-mar 105
        if (!$decoded) {
106
            throw new \RuntimeException('Unable to decode BER');
107
        }
827 daniel-mar 108
        $key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
109
        if (isset($key['parameters']) && $params != $key['parameters']) {
110
            throw new \RuntimeException('The PKCS8 parameter field does not match the private key parameter field');
111
        }
112
 
113
        $components['dA'] = new BigInteger($key['privateKey'], 256);
114
        $components['curve']->rangeCheck($components['dA']);
115
        $components['QA'] = isset($key['publicKey']) ?
116
            self::extractPoint($key['publicKey'], $components['curve']) :
117
            $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
118
 
119
        return $components;
120
    }
121
 
122
    /**
123
     * Break a public or private EdDSA key down into its constituent components
124
     *
125
     * @return array
126
     */
127
    private static function loadEdDSA(array $key)
128
    {
129
        $components = [];
130
 
131
        if (isset($key['privateKey'])) {
132
            $components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448();
133
 
134
            // 0x04 == octet string
135
            // 0x20 == length (32 bytes)
136
            if (substr($key['privateKey'], 0, 2) != "\x04\x20") {
137
                throw new \RuntimeException('The first two bytes of the private key field should be 0x0420');
138
            }
1042 daniel-mar 139
            $arr = $components['curve']->extractSecret(substr($key['privateKey'], 2));
140
            $components['dA'] = $arr['dA'];
141
            $components['secret'] = $arr['secret'];
827 daniel-mar 142
        }
143
 
144
        if (isset($key['publicKey'])) {
145
            if (!isset($components['curve'])) {
146
                $components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448();
147
            }
148
 
149
            $components['QA'] = self::extractPoint($key['publicKey'], $components['curve']);
150
        }
151
 
152
        if (isset($key['privateKey']) && !isset($components['QA'])) {
153
            $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
154
        }
155
 
156
        return $components;
157
    }
158
 
159
    /**
160
     * Convert an EC public key to the appropriate format
161
     *
162
     * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
163
     * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
164
     * @param array $options optional
165
     * @return string
166
     */
167
    public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = [])
168
    {
169
        self::initialize_static_variables();
170
 
171
        if ($curve instanceof MontgomeryCurve) {
172
            throw new UnsupportedCurveException('Montgomery Curves are not supported');
173
        }
174
 
175
        if ($curve instanceof TwistedEdwardsCurve) {
176
            return self::wrapPublicKey(
177
                $curve->encodePoint($publicKey),
178
                null,
179
                $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448'
180
            );
181
        }
182
 
183
        $params = new ASN1\Element(self::encodeParameters($curve, false, $options));
184
 
185
        $key = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
186
 
187
        return self::wrapPublicKey($key, $params, 'id-ecPublicKey');
188
    }
189
 
190
    /**
191
     * Convert a private key to the appropriate format.
192
     *
193
     * @param \phpseclib3\Math\BigInteger $privateKey
194
     * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
195
     * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
1042 daniel-mar 196
     * @param string $secret optional
827 daniel-mar 197
     * @param string $password optional
198
     * @param array $options optional
199
     * @return string
200
     */
1042 daniel-mar 201
    public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = [])
827 daniel-mar 202
    {
203
        self::initialize_static_variables();
204
 
205
        if ($curve instanceof MontgomeryCurve) {
206
            throw new UnsupportedCurveException('Montgomery Curves are not supported');
207
        }
208
 
209
        if ($curve instanceof TwistedEdwardsCurve) {
210
            return self::wrapPrivateKey(
1042 daniel-mar 211
                "\x04\x20" . $secret,
827 daniel-mar 212
                [],
213
                null,
214
                $password,
215
                $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448'
216
            );
217
        }
218
 
219
        $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
220
 
221
        $params = new ASN1\Element(self::encodeParameters($curve, false, $options));
222
 
223
        $key = [
224
            'version' => 'ecPrivkeyVer1',
225
            'privateKey' => $privateKey->toBytes(),
226
            //'parameters' => $params,
227
            'publicKey' => "\0" . $publicKey
228
        ];
229
 
230
        $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);
231
 
232
        return self::wrapPrivateKey($key, [], $params, $password, 'id-ecPublicKey', '', $options);
233
    }
234
}