GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

ECPublicKey::fromPEM()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 3
nop 1
dl 0
loc 13
ccs 9
cts 9
cp 1
crap 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\CryptoTypes\Asymmetric\EC;
6
7
use Sop\ASN1\Type\Primitive\Integer;
8
use Sop\ASN1\Type\Primitive\OctetString;
9
use Sop\CryptoEncoding\PEM;
10
use Sop\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifier;
11
use Sop\CryptoTypes\AlgorithmIdentifier\Asymmetric\ECPublicKeyAlgorithmIdentifier;
12
use Sop\CryptoTypes\AlgorithmIdentifier\Feature\AlgorithmIdentifierType;
13
use Sop\CryptoTypes\Asymmetric\PublicKey;
14
use Sop\CryptoTypes\Asymmetric\PublicKeyInfo;
15
16
/**
17
 * Implements elliptic curve public key type as specified by RFC 5480.
18
 *
19
 * @see https://tools.ietf.org/html/rfc5480#section-2.2
20
 */
21
class ECPublicKey extends PublicKey
22
{
23
    /**
24
     * Elliptic curve public key.
25
     *
26
     * @var string
27
     */
28
    protected $_ecPoint;
29
30
    /**
31
     * Named curve OID.
32
     *
33
     * Named curve is not a part of ECPublicKey, but it's stored as a hint
34
     * for the purpose of PublicKeyInfo creation.
35
     *
36
     * @var null|string
37
     */
38
    protected $_namedCurve;
39
40
    /**
41
     * Constructor.
42
     *
43
     * @param string      $ec_point    ECPoint
44
     * @param null|string $named_curve Named curve OID
45
     *
46
     * @throws \InvalidArgumentException If ECPoint is invalid
47
     */
48 12
    public function __construct(string $ec_point, ?string $named_curve = null)
49
    {
50
        // first octet must be 0x04 for uncompressed form, and 0x02 or 0x03
51
        // for compressed form.
52 12
        if (!strlen($ec_point) || !in_array(ord($ec_point[0]), [2, 3, 4])) {
53 1
            throw new \InvalidArgumentException('Invalid ECPoint.');
54
        }
55 11
        $this->_ecPoint = $ec_point;
56 11
        $this->_namedCurve = $named_curve;
57 11
    }
58
59
    /**
60
     * Initialize from curve point coordinates.
61
     *
62
     * @param int|string  $x           X coordinate as a base10 number
63
     * @param int|string  $y           Y coordinate as a base10 number
64
     * @param null|string $named_curve Named curve OID
65
     * @param null|int    $bits        Size of *p* in bits
66
     *
67
     * @return self
68
     */
69 2
    public static function fromCoordinates($x, $y,
70
        ?string $named_curve = null, ?int $bits = null): ECPublicKey
71
    {
72
        // if bitsize is not explicitly set, check from supported curves
73 2
        if (!isset($bits) && isset($named_curve)) {
74 2
            $bits = self::_curveSize($named_curve);
75
        }
76 2
        $mlen = null;
77 2
        if (isset($bits)) {
78 1
            $mlen = (int) ceil($bits / 8);
79
        }
80 2
        $x_os = ECConversion::integerToOctetString(new Integer($x), $mlen)->string();
81 2
        $y_os = ECConversion::integerToOctetString(new Integer($y), $mlen)->string();
82 2
        $ec_point = "\x4{$x_os}{$y_os}";
83 2
        return new self($ec_point, $named_curve);
84
    }
85
86
    /**
87
     * @see PublicKey::fromPEM()
88
     *
89
     * @param PEM $pem
90
     *
91
     * @throws \UnexpectedValueException
92
     *
93
     * @return self
94
     */
95 4
    public static function fromPEM(PEM $pem): ECPublicKey
96
    {
97 4
        if (PEM::TYPE_PUBLIC_KEY !== $pem->type()) {
98 1
            throw new \UnexpectedValueException('Not a public key.');
99
        }
100 3
        $pki = PublicKeyInfo::fromDER($pem->data());
101 3
        $algo = $pki->algorithmIdentifier();
102 3
        if (AlgorithmIdentifier::OID_EC_PUBLIC_KEY !== $algo->oid() ||
103 3
            !($algo instanceof ECPublicKeyAlgorithmIdentifier)) {
104 1
            throw new \UnexpectedValueException('Not an elliptic curve key.');
105
        }
106
        // ECPoint is directly mapped into public key data
107 2
        return new self($pki->publicKeyData(), $algo->namedCurve());
108
    }
109
110
    /**
111
     * Get ECPoint value.
112
     *
113
     * @return string
114
     */
115 1
    public function ECPoint(): string
116
    {
117 1
        return $this->_ecPoint;
118
    }
119
120
    /**
121
     * Get curve point coordinates.
122
     *
123
     * @return string[] Tuple of X and Y coordinates as base-10 numbers
124
     */
125 2
    public function curvePoint(): array
126
    {
127 2
        return array_map(
128
            function ($str) {
129 1
                return ECConversion::octetsToNumber($str);
130 2
            }, $this->curvePointOctets());
131
    }
132
133
    /**
134
     * Get curve point coordinates in octet string representation.
135
     *
136
     * @return string[] tuple of X and Y field elements as a string
137
     */
138 2
    public function curvePointOctets(): array
139
    {
140 2
        if ($this->isCompressed()) {
141 1
            throw new \RuntimeException('EC point compression not supported.');
142
        }
143 1
        $str = substr($this->_ecPoint, 1);
144 1
        [$x, $y] = str_split($str, (int) floor(strlen($str) / 2));
145 1
        return [$x, $y];
146
    }
147
148
    /**
149
     * Whether ECPoint is in compressed form.
150
     *
151
     * @return bool
152
     */
153 2
    public function isCompressed(): bool
154
    {
155 2
        $c = ord($this->_ecPoint[0]);
156 2
        return 4 !== $c;
157
    }
158
159
    /**
160
     * Whether named curve is present.
161
     *
162
     * @return bool
163
     */
164 7
    public function hasNamedCurve(): bool
165
    {
166 7
        return isset($this->_namedCurve);
167
    }
168
169
    /**
170
     * Get named curve OID.
171
     *
172
     * @throws \LogicException
173
     *
174
     * @return string
175
     */
176 6
    public function namedCurve(): string
177
    {
178 6
        if (!$this->hasNamedCurve()) {
179 2
            throw new \LogicException('namedCurve not set.');
180
        }
181 4
        return $this->_namedCurve;
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 4
    public function algorithmIdentifier(): AlgorithmIdentifierType
188
    {
189 4
        return new ECPublicKeyAlgorithmIdentifier($this->namedCurve());
190
    }
191
192
    /**
193
     * Generate ASN.1 element.
194
     *
195
     * @return OctetString
196
     */
197 1
    public function toASN1(): OctetString
198
    {
199 1
        return new OctetString($this->_ecPoint);
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205 1
    public function toDER(): string
206
    {
207 1
        return $this->toASN1()->toDER();
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     *
213
     * @see https://tools.ietf.org/html/rfc5480#section-2.2
214
     */
215 3
    public function subjectPublicKeyData(): string
216
    {
217
        // ECPoint is directly mapped to subjectPublicKey
218 3
        return $this->_ecPoint;
219
    }
220
221
    /**
222
     * Get the curve size *p* in bits.
223
     *
224
     * @param string $oid Curve OID
225
     *
226
     * @return null|int
227
     */
228 2
    private static function _curveSize(string $oid): ?int
229
    {
230 2
        if (!array_key_exists($oid, ECPublicKeyAlgorithmIdentifier::MAP_CURVE_TO_SIZE)) {
231 1
            return null;
232
        }
233 1
        return ECPublicKeyAlgorithmIdentifier::MAP_CURVE_TO_SIZE[$oid];
234
    }
235
}
236