Failed Conditions
Push — v7 ( e9e51b...d9c0af )
by Florent
03:20
created

ECDHES::checkKey()   C

Complexity

Conditions 12
Paths 41

Size

Total Lines 33
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 5.1612
c 0
b 0
f 0
cc 12
eloc 22
nc 41
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Encryption\Algorithm\KeyEncryption;
15
16
use Base64Url\Base64Url;
17
use Jose\Component\Core\JWK;
18
use Jose\Component\Encryption\Util\ConcatKDF;
19
use Jose\Component\KeyManagement\JWKFactory;
20
use Mdanter\Ecc\Crypto\EcDH\EcDH;
21
use Mdanter\Ecc\EccFactory;
22
23
/**
24
 * Class ECDHES.
25
 */
26
final class ECDHES implements KeyAgreementInterface
27
{
28
    /**
29
     * {@inheritdoc}
30
     */
31
    public function getAgreementKey(int $encryption_key_length, string $algorithm, JWK $recipient_key, array $complete_header = [], array &$additional_header_values = []): string
32
    {
33
        if ($recipient_key->has('d')) {
34
            $this->checkKey($recipient_key, true);
35
            $private_key = $recipient_key;
36
            $public_key = $this->getPublicKey($complete_header);
37
        } else {
38
            $this->checkKey($recipient_key, false);
39
            $public_key = $recipient_key;
40
            switch ($public_key->get('crv')) {
41
                case 'P-256':
42
                case 'P-384':
43
                case 'P-521':
44
                    $private_key = JWKFactory::createECKey($public_key->get('crv'));
45
                    break;
46
                case 'X25519':
47
                    $private_key = JWKFactory::createOKPKey('X25519');
48
                    break;
49
                default:
50
                    throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported', $public_key->get('crv')));
51
            }
52
            $epk = $private_key->toPublic()->all();
53
            $additional_header_values = array_merge($additional_header_values, [
54
                'epk' => $epk,
55
            ]);
56
        }
57
        if ($private_key->get('crv') !== $public_key->get('crv')) {
58
            throw new \InvalidArgumentException('Curves are different');
59
        }
60
61
        $agreed_key = $this->calculateAgreementKey($private_key, $public_key);
62
63
        $apu = array_key_exists('apu', $complete_header) ? $complete_header['apu'] : '';
64
        $apv = array_key_exists('apv', $complete_header) ? $complete_header['apv'] : '';
65
66
        return ConcatKDF::generate($agreed_key, $algorithm, $encryption_key_length, $apu, $apv);
67
    }
68
69
    /**
70
     * @param JWK $private_key
71
     * @param JWK $public_key
72
     *
73
     * @throws \InvalidArgumentException
74
     *
75
     * @return string
76
     */
77
    public function calculateAgreementKey(JWK $private_key, JWK $public_key): string
78
    {
79
        switch ($public_key->get('crv')) {
80
            case 'P-256':
81
            case 'P-384':
82
            case 'P-521':
83
                $p = $this->getGenerator($private_key);
84
85
                $rec_x = $this->convertBase64ToGmp($public_key->get('x'));
86
                $rec_y = $this->convertBase64ToGmp($public_key->get('y'));
87
                $sen_d = $this->convertBase64ToGmp($private_key->get('d'));
88
89
                $priv_key = $p->getPrivateKeyFrom($sen_d);
90
                $pub_key = $p->getPublicKeyFrom($rec_x, $rec_y);
91
92
                $ecdh = new EcDH(EccFactory::getAdapter());
0 ignored issues
show
Bug introduced by
It seems like \Mdanter\Ecc\EccFactory::getAdapter() can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
93
                $ecdh->setSenderKey($priv_key);
94
                $ecdh->setRecipientKey($pub_key);
95
96
                return $this->convertDecToBin($ecdh->calculateSharedKey());
97
            case 'X25519':
98
                $sKey = Base64Url::decode($private_key->get('d'));
99
                $recipientPublickey = Base64Url::decode($public_key->get('x'));
100
101
                return sodium_crypto_scalarmult($sKey, $recipientPublickey);
102
            default:
103
                throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported', $public_key->get('crv')));
104
        }
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function name(): string
111
    {
112
        return 'ECDH-ES';
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function getKeyManagementMode(): string
119
    {
120
        return self::MODE_AGREEMENT;
121
    }
122
123
    /**
124
     * @param array $complete_header
125
     *
126
     * @return JWK
127
     */
128
    private function getPublicKey(array $complete_header)
129
    {
130
        if (!array_key_exists('epk', $complete_header)) {
131
            throw new \InvalidArgumentException('The header parameter "epk" is missing');
132
        }
133
        if (!is_array($complete_header['epk'])) {
134
            throw new \InvalidArgumentException('The header parameter "epk" is not an array of parameter');
135
        }
136
137
        $public_key = JWK::create($complete_header['epk']);
138
        $this->checkKey($public_key, false);
139
140
        return $public_key;
141
    }
142
143
    /**
144
     * @param JWK  $key
145
     * @param bool $is_private
146
     */
147
    private function checkKey(JWK $key, $is_private)
148
    {
149
        foreach (['x', 'crv'] as $k) {
150
            if (!$key->has($k)) {
151
                throw new \InvalidArgumentException(sprintf('The key parameter "%s" is missing.', $k));
152
            }
153
        }
154
155
        switch ($key->get('crv')) {
156
            case 'P-256':
157
            case 'P-384':
158
            case 'P-521':
159
                if ('EC' !== $key->get('kty')) {
160
                    throw new \InvalidArgumentException('Wrong key type.');
161
                }
162
                if (!$key->has('y')) {
163
                    throw new \InvalidArgumentException('The key parameter "y" is missing.');
164
                }
165
                break;
166
            case 'X25519':
167
                if ('OKP' !== $key->get('kty')) {
168
                    throw new \InvalidArgumentException('Wrong key type.');
169
                }
170
                break;
171
            default:
172
                throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported', $key->get('crv')));
173
        }
174
        if (true === $is_private) {
175
            if (!$key->has('d')) {
176
                throw new \InvalidArgumentException('The key parameter "d" is missing.');
177
            }
178
        }
179
    }
180
181
    /**
182
     * @param JWK $key
183
     *
184
     * @throws \InvalidArgumentException
185
     *
186
     * @return \Mdanter\Ecc\Primitives\GeneratorPoint
187
     */
188
    private function getGenerator(JWK $key)
189
    {
190
        $crv = $key->get('crv');
191
192
        switch ($crv) {
193
            case 'P-256':
194
                return EccFactory::getNistCurves()->generator256();
195
            case 'P-384':
196
                return EccFactory::getNistCurves()->generator384();
197
            case 'P-521':
198
                return EccFactory::getNistCurves()->generator521();
199
            default:
200
                throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported', $crv));
201
        }
202
    }
203
204
    /**
205
     * @param string $value
206
     *
207
     * @return \GMP
208
     */
209
    private function convertBase64ToGmp($value)
210
    {
211
        $value = unpack('H*', Base64Url::decode($value));
212
213
        return gmp_init($value[1], 16);
214
    }
215
216
    /**
217
     * @param \GMP $value
218
     *
219
     * @return string
220
     */
221
    private function convertDecToBin($value)
222
    {
223
        $value = gmp_strval($value, 10);
224
        $adapter = EccFactory::getAdapter();
225
226
        return hex2bin($adapter->decHex($value));
227
    }
228
}
229