xref: /dokuwiki/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php (revision 850e662095111529d7d330745fee3207907c4aee)
1<?php
2
3/**
4 * PKCS#8 Formatted RSA-PSS Key Handler
5 *
6 * PHP version 5
7 *
8 * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
9 *
10 * Processes keys with the following headers:
11 *
12 * -----BEGIN ENCRYPTED PRIVATE KEY-----
13 * -----BEGIN PRIVATE KEY-----
14 * -----BEGIN PUBLIC KEY-----
15 *
16 * Analogous to "openssl genpkey -algorithm rsa-pss".
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
24namespace phpseclib3\Crypt\RSA\Formats\Keys;
25
26use phpseclib3\Common\Functions\Strings;
27use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
28use phpseclib3\File\ASN1;
29use phpseclib3\File\ASN1\Maps;
30use phpseclib3\Math\BigInteger;
31
32/**
33 * PKCS#8 Formatted RSA-PSS Key Handler
34 *
35 * @author  Jim Wigginton <terrafrost@php.net>
36 */
37abstract class PSS extends Progenitor
38{
39    /**
40     * OID Name
41     *
42     * @var string
43     */
44    const OID_NAME = 'id-RSASSA-PSS';
45
46    /**
47     * OID Value
48     *
49     * @var string
50     */
51    const OID_VALUE = '1.2.840.113549.1.1.10';
52
53    /**
54     * OIDs loaded
55     *
56     * @var bool
57     */
58    private static $oidsLoaded = false;
59
60    /**
61     * Child OIDs loaded
62     *
63     * @var bool
64     */
65    protected static $childOIDsLoaded = false;
66
67    /**
68     * Initialize static variables
69     */
70    private static function initialize_static_variables()
71    {
72        if (!self::$oidsLoaded) {
73            ASN1::loadOIDs([
74                'md2' => '1.2.840.113549.2.2',
75                'md4' => '1.2.840.113549.2.4',
76                'md5' => '1.2.840.113549.2.5',
77                'id-sha1' => '1.3.14.3.2.26',
78                'id-sha256' => '2.16.840.1.101.3.4.2.1',
79                'id-sha384' => '2.16.840.1.101.3.4.2.2',
80                'id-sha512' => '2.16.840.1.101.3.4.2.3',
81                'id-sha224' => '2.16.840.1.101.3.4.2.4',
82                'id-sha512/224' => '2.16.840.1.101.3.4.2.5',
83                'id-sha512/256' => '2.16.840.1.101.3.4.2.6',
84
85                'id-mgf1' => '1.2.840.113549.1.1.8'
86            ]);
87            self::$oidsLoaded = true;
88        }
89    }
90
91    /**
92     * Break a public or private key down into its constituent components
93     *
94     * @param string $key
95     * @param string $password optional
96     * @return array
97     */
98    public static function load($key, $password = '')
99    {
100        self::initialize_static_variables();
101
102        if (!Strings::is_stringable($key)) {
103            throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
104        }
105
106        $components = ['isPublicKey' => strpos($key, 'PUBLIC') !== false];
107
108        $key = parent::load($key, $password);
109
110        $type = isset($key['privateKey']) ? 'private' : 'public';
111
112        $result = $components + PKCS1::load($key[$type . 'Key']);
113
114        if (isset($key[$type . 'KeyAlgorithm']['parameters'])) {
115            $decoded = ASN1::decodeBER($key[$type . 'KeyAlgorithm']['parameters']);
116            if ($decoded === false) {
117                throw new \UnexpectedValueException('Unable to decode parameters');
118            }
119            $params = ASN1::asn1map($decoded[0], Maps\RSASSA_PSS_params::MAP);
120        } else {
121            $params = [];
122        }
123
124        if (isset($params['maskGenAlgorithm']['parameters'])) {
125            $decoded = ASN1::decodeBER($params['maskGenAlgorithm']['parameters']);
126            if ($decoded === false) {
127                throw new \UnexpectedValueException('Unable to decode parameters');
128            }
129            $params['maskGenAlgorithm']['parameters'] = ASN1::asn1map($decoded[0], Maps\HashAlgorithm::MAP);
130        } else {
131            $params['maskGenAlgorithm'] = [
132                'algorithm' => 'id-mgf1',
133                'parameters' => ['algorithm' => 'id-sha1']
134            ];
135        }
136
137        if (!isset($params['hashAlgorithm']['algorithm'])) {
138            $params['hashAlgorithm']['algorithm'] = 'id-sha1';
139        }
140
141        $result['hash'] = str_replace('id-', '', $params['hashAlgorithm']['algorithm']);
142        $result['MGFHash'] = str_replace('id-', '', $params['maskGenAlgorithm']['parameters']['algorithm']);
143        if (isset($params['saltLength'])) {
144            $result['saltLength'] = (int) $params['saltLength']->toString();
145        }
146
147        if (isset($key['meta'])) {
148            $result['meta'] = $key['meta'];
149        }
150
151        return $result;
152    }
153
154    /**
155     * Convert a private key to the appropriate format.
156     *
157     * @param BigInteger $n
158     * @param BigInteger $e
159     * @param BigInteger $d
160     * @param array $primes
161     * @param array $exponents
162     * @param array $coefficients
163     * @param string $password optional
164     * @param array $options optional
165     * @return string
166     */
167    public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = [])
168    {
169        self::initialize_static_variables();
170
171        $key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
172        $key = ASN1::extractBER($key);
173        $params = self::savePSSParams($options);
174        return self::wrapPrivateKey($key, [], $params, $password, null, '', $options);
175    }
176
177    /**
178     * Convert a public key to the appropriate format
179     *
180     * @param BigInteger $n
181     * @param BigInteger $e
182     * @param array $options optional
183     * @return string
184     */
185    public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = [])
186    {
187        self::initialize_static_variables();
188
189        $key = PKCS1::savePublicKey($n, $e);
190        $key = ASN1::extractBER($key);
191        $params = self::savePSSParams($options);
192        return self::wrapPublicKey($key, $params);
193    }
194
195    /**
196     * Encodes PSS parameters
197     *
198     * @param array $options
199     * @return string
200     */
201    public static function savePSSParams(array $options)
202    {
203        /*
204         The trailerField field is an integer.  It provides
205         compatibility with IEEE Std 1363a-2004 [P1363A].  The value
206         MUST be 1, which represents the trailer field with hexadecimal
207         value 0xBC.  Other trailer fields, including the trailer field
208         composed of HashID concatenated with 0xCC that is specified in
209         IEEE Std 1363a, are not supported.  Implementations that
210         perform signature generation MUST omit the trailerField field,
211         indicating that the default trailer field value was used.
212         Implementations that perform signature validation MUST
213         recognize both a present trailerField field with value 1 and an
214         absent trailerField field.
215
216         source: https://tools.ietf.org/html/rfc4055#page-9
217        */
218        $params = [
219            'trailerField' => new BigInteger(1)
220        ];
221        if (isset($options['hash'])) {
222            $params['hashAlgorithm']['algorithm'] = 'id-' . $options['hash'];
223        }
224        if (isset($options['MGFHash'])) {
225            $temp = ['algorithm' => 'id-' . $options['MGFHash']];
226            $temp = ASN1::encodeDER($temp, Maps\HashAlgorithm::MAP);
227            $params['maskGenAlgorithm'] = [
228                'algorithm' => 'id-mgf1',
229                'parameters' => new ASN1\Element($temp)
230            ];
231        }
232        if (isset($options['saltLength'])) {
233            $params['saltLength'] = new BigInteger($options['saltLength']);
234        }
235
236        return new ASN1\Element(ASN1::encodeDER($params, Maps\RSASSA_PSS_params::MAP));
237    }
238}
239