Failed Conditions
Push — v7 ( 318c5f...2b71c7 )
by Florent
03:43
created

ECDSA::convertBase64ToGmp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

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 3
nc 1
nop 1
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2016 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Algorithm\Signature;
13
14
use Assert\Assertion;
15
use FG\ASN1\Object;
16
use FG\ASN1\Universal\Integer;
17
use FG\ASN1\Universal\Sequence;
18
use Jose\Algorithm\SignatureAlgorithmInterface;
19
use Jose\KeyConverter\ECKey;
20
use Jose\Object\JWKInterface;
21
22
/**
23
 * Class ECDSA.
24
 */
25
abstract class ECDSA implements SignatureAlgorithmInterface
26
{
27
    /**
28
     * {@inheritdoc}
29
     */
30
    public function sign(JWKInterface $key, string $data): string
31
    {
32
        $this->checkKey($key);
33
        Assertion::true($key->has('d'), 'The EC key is not private');
34
35
        return $this->getOpenSSLSignature($key, $data);
36
    }
37
38
    /**
39
     * @param \Jose\Object\JWKInterface $key
40
     * @param string                    $data
41
     *
42
     * @return string
43
     */
44
    private function getOpenSSLSignature(JWKInterface $key, string $data): string
45
    {
46
        $pem = (new ECKey($key))->toPEM();
47
        $result = openssl_sign($data, $signature, $pem, $this->getHashAlgorithm());
48
49
        Assertion::true($result, 'Signature failed');
50
51
        $asn = Object::fromBinary($signature);
52
        Assertion::isInstanceOf($asn, Sequence::class, 'Invalid signature');
53
54
        $res = '';
55
        foreach ($asn->getChildren() as $child) {
0 ignored issues
show
Bug introduced by
The method getChildren does only exist in FG\ASN1\Construct, but not in FG\ASN1\AbstractString a...d FG\ASN1\UnknownObject.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
56
            Assertion::isInstanceOf($child, Integer::class, 'Invalid signature');
57
            $res .= str_pad($this->convertDecToHex($child->getContent()), $this->getSignaturePartLength(), '0', STR_PAD_LEFT);
58
        }
59
60
        return $this->convertHexToBin($res);
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    public function verify(JWKInterface $key, string $data, string $signature): bool
67
    {
68
        $this->checkKey($key);
69
70
        $signature = $this->convertBinToHex($signature);
71
        $part_length = $this->getSignaturePartLength();
72
        if (mb_strlen($signature, '8bit') !== 2 * $part_length) {
73
            return false;
74
        }
75
        $R = mb_substr($signature, 0, $part_length, '8bit');
76
        $S = mb_substr($signature, $part_length, null, '8bit');
77
78
        return $this->verifyOpenSSLSignature($key, $data, $R, $S);
79
    }
80
81
    /**
82
     * @param \Jose\Object\JWKInterface $key
83
     * @param string                    $data
84
     * @param string                    $R
85
     * @param string                    $S
86
     *
87
     * @return bool
88
     */
89
    private function verifyOpenSSLSignature(JWKInterface $key, string $data, string $R, string $S): bool
90
    {
91
        $pem = ECKey::toPublic(new ECKey($key))->toPEM();
92
93
        $oid_sequence = new Sequence();
94
        $oid_sequence->addChildren([
95
            new Integer(gmp_strval($this->convertHexToGmp($R), 10)),
96
            new Integer(gmp_strval($this->convertHexToGmp($S), 10)),
97
        ]);
98
99
        return 1 === openssl_verify($data, $oid_sequence->getBinary(), $pem, $this->getHashAlgorithm());
100
    }
101
102
    /**
103
     * @return string
104
     */
105
    abstract protected function getHashAlgorithm(): string;
106
107
    /**
108
     * @return int
109
     */
110
    abstract protected function getSignaturePartLength(): int;
111
112
    /**
113
     * @param string $value
114
     *
115
     * @return string
116
     */
117
    private function convertHexToBin(string $value): string
118
    {
119
        return pack('H*', $value);
120
    }
121
122
    /**
123
     * @param string $value
124
     *
125
     * @return string
126
     */
127
    private function convertBinToHex(string $value): string
128
    {
129
        $value = unpack('H*', $value);
130
131
        return $value[1];
132
    }
133
134
    /**
135
     * @param string $value
136
     *
137
     * @return string
138
     */
139
    private function convertDecToHex(string $value): string
140
    {
141
        $value = gmp_strval($value, 10);
142
143
        return gmp_strval($value, 16);
144
    }
145
146
    /**
147
     * @param string $value
148
     *
149
     * @return \GMP
150
     */
151
    private function convertHexToGmp(string $value): \GMP
152
    {
153
        return gmp_init($value, 16);
154
    }
155
156
    /**
157
     * @param JWKInterface $key
158
     */
159
    private function checkKey(JWKInterface $key)
160
    {
161
        Assertion::eq($key->get('kty'), 'EC', 'Wrong key type.');
162
        Assertion::true($key->has('x'), 'The key parameter "x" is missing.');
163
        Assertion::true($key->has('y'), 'The key parameter "y" is missing.');
164
        Assertion::true($key->has('crv'), 'The key parameter "crv" is missing.');
165
    }
166
}
167