Failed Conditions
Push — v7 ( 61ffea...f7e5f1 )
by Florent
04:20
created

ECKey::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace Jose\Component\Core\Util;
15
16
use Assert\Assertion;
17
use Base64Url\Base64Url;
18
use FG\ASN1\ExplicitlyTaggedObject;
19
use FG\ASN1\Universal\BitString;
20
use FG\ASN1\Universal\Integer;
21
use FG\ASN1\Universal\ObjectIdentifier;
22
use FG\ASN1\Universal\OctetString;
23
use FG\ASN1\Universal\Sequence;
24
use Jose\Component\Core\JWK;
25
26
final class ECKey
27
{
28
    /**
29
     * @var null|Sequence
30
     */
31
    private $sequence = null;
32
33
    /**
34
     * @var bool
35
     */
36
    private $private = false;
37
38
    /**
39
     * @var array
40
     */
41
    private $values = [];
42
43
    /**
44
     * ECKey constructor.
45
     *
46
     * @param JWK $data
47
     */
48
    private function __construct(JWK $data)
49
    {
50
        $this->sequence = new Sequence();
51
        $this->loadJWK($data->all());
52
        $this->private = isset($this->values['d']);
53
    }
54
55
    /**
56
     * @param JWK $jwk
57
     *
58
     * @return ECKey
59
     */
60
    public static function createFromJWK(JWK $jwk): ECKey
61
    {
62
        return new self($jwk);
63
    }
64
65
    /**
66
     * @param array $jwk
67
     */
68
    private function loadJWK(array $jwk)
69
    {
70
        Assertion::true(array_key_exists('kty', $jwk), 'JWK is not an Elliptic Curve key');
71
        Assertion::eq($jwk['kty'], 'EC', 'JWK is not an Elliptic Curve key');
72
        Assertion::true(array_key_exists('crv', $jwk), 'Curve parameter is missing');
73
        Assertion::true(array_key_exists('x', $jwk), 'Point parameters are missing');
74
        Assertion::true(array_key_exists('y', $jwk), 'Point parameters are missing');
75
76
        $this->values = $jwk;
77
        if (array_key_exists('d', $jwk)) {
78
            $this->initPrivateKey();
79
        } else {
80
            $this->initPublicKey();
81
        }
82
    }
83
84
    private function initPublicKey()
85
    {
86
        $oid_sequence = new Sequence();
87
        $oid_sequence->addChild(new ObjectIdentifier('1.2.840.10045.2.1'));
88
        $oid_sequence->addChild(new ObjectIdentifier($this->getOID($this->values['crv'])));
89
        $this->sequence->addChild($oid_sequence);
90
91
        $bits = '04';
92
        $bits .= bin2hex(Base64Url::decode($this->values['x']));
93
        $bits .= bin2hex(Base64Url::decode($this->values['y']));
94
        $this->sequence->addChild(new BitString($bits));
95
    }
96
97
    private function initPrivateKey()
98
    {
99
        $this->sequence->addChild(new Integer(1));
100
        $this->sequence->addChild(new OctetString(bin2hex(Base64Url::decode($this->values['d']))));
101
102
        $oid = new ObjectIdentifier($this->getOID($this->values['crv']));
103
        $this->sequence->addChild(new ExplicitlyTaggedObject(0, $oid));
104
105
        $bits = '04';
106
        $bits .= bin2hex(Base64Url::decode($this->values['x']));
107
        $bits .= bin2hex(Base64Url::decode($this->values['y']));
108
        $bit = new BitString($bits);
109
        $this->sequence->addChild(new ExplicitlyTaggedObject(1, $bit));
110
    }
111
112
    /**
113
     * @return string
114
     */
115
    public function toPEM(): string
116
    {
117
        $result = '-----BEGIN '.($this->private ? 'EC PRIVATE' : 'PUBLIC').' KEY-----'.PHP_EOL;
118
        $result .= chunk_split(base64_encode($this->sequence->getBinary()), 64, PHP_EOL);
119
        $result .= '-----END '.($this->private ? 'EC PRIVATE' : 'PUBLIC').' KEY-----'.PHP_EOL;
120
121
        return $result;
122
    }
123
124
    /**
125
     * @param $curve
126
     *
127
     * @return string
128
     */
129
    private function getOID(string $curve): string
130
    {
131
        $curves = $this->getSupportedCurves();
132
        $oid = array_key_exists($curve, $curves) ? $curves[$curve] : null;
133
134
        Assertion::notNull($oid, 'Unsupported curve');
135
136
        return $oid;
137
    }
138
139
    /**
140
     * @param string $oid
141
     *
142
     * @return string
143
     */
144
    private function getCurve(string $oid): string
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
145
    {
146
        $curves = $this->getSupportedCurves();
147
        $curve = array_search($oid, $curves, true);
148
        Assertion::string($curve, 'Unsupported OID');
149
150
        return $curve;
151
    }
152
153
    /**
154
     * @return array
155
     */
156
    private function getSupportedCurves(): array
157
    {
158
        return [
159
            'P-256' => '1.2.840.10045.3.1.7',
160
            'P-384' => '1.3.132.0.34',
161
            'P-521' => '1.3.132.0.35',
162
        ];
163
    }
164
}
165