Failed Conditions
Push — v7 ( 19cd68...092ee9 )
by Florent
01:43
created

ECKey::getCurve()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
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
     * @return array
141
     */
142
    private function getSupportedCurves(): array
143
    {
144
        return [
145
            'P-256' => '1.2.840.10045.3.1.7',
146
            'P-384' => '1.3.132.0.34',
147
            'P-521' => '1.3.132.0.35',
148
        ];
149
    }
150
}
151