Completed
Push — master ( 1a59c2...c73113 )
by Florent
02:55
created

ECDSA::convertHexToDec()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 2
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 Base64Url\Base64Url;
16
use Jose\Algorithm\SignatureAlgorithmInterface;
17
use Jose\Object\JWKInterface;
18
use Mdanter\Ecc\Crypto\Signature\Signature;
19
use Mdanter\Ecc\EccFactory;
20
use Mdanter\Ecc\Random\RandomGeneratorFactory;
21
22
/**
23
 * Class ECDSA.
24
 */
25
abstract class ECDSA implements SignatureAlgorithmInterface
26
{
27
    /**
28
     * @var \Mdanter\Ecc\Math\MathAdapterInterface
29
     */
30
    private $adapter;
31
32
    public function __construct()
33
    {
34
        $this->adapter = EccFactory::getAdapter();
35
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40
    public function sign(JWKInterface $key, $data)
41
    {
42
        $this->checkKey($key);
43
        Assertion::true($key->has('d'), 'The EC key is not private');
44
45
        $p = $this->getGenerator();
46
        $d = $this->convertBase64ToDec($key->get('d'));
47
        $hash = $this->convertHexToDec(hash($this->getHashAlgorithm(), $data));
48
49
        $k = RandomGeneratorFactory::getRandomGenerator()->generate($p->getOrder());
50
51
        $signer = EccFactory::getSigner();
52
53
        $private_key = $p->getPrivateKeyFrom($d);
54
        $signature = $signer->sign($private_key, $hash, $k);
55
56
        $part_length = $this->getSignaturePartLength();
57
58
        $R = str_pad($this->convertDecToHex($signature->getR()), $part_length, '0', STR_PAD_LEFT);
59
        $S = str_pad($this->convertDecToHex($signature->getS()), $part_length, '0', STR_PAD_LEFT);
60
61
        return $this->convertHexToBin($R.$S);
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    public function verify(JWKInterface $key, $data, $signature)
68
    {
69
        $this->checkKey($key);
70
71
        $signature = $this->convertBinToHex($signature);
72
        $part_length = $this->getSignaturePartLength();
73
        if (mb_strlen($signature, '8bit') !== 2 * $part_length) {
74
            return false;
75
        }
76
77
        $p = $this->getGenerator();
78
        $x = $this->convertBase64ToDec($key->get('x'));
79
        $y = $this->convertBase64ToDec($key->get('y'));
80
        $R = $this->convertHexToDec(mb_substr($signature, 0, $part_length, '8bit'));
81
        $S = $this->convertHexToDec(mb_substr($signature, $part_length, null, '8bit'));
82
        $hash = $this->convertHexToDec(hash($this->getHashAlgorithm(), $data));
83
84
        $public_key = $p->getPublicKeyFrom($x, $y);
85
86
        $signer = EccFactory::getSigner();
87
88
        return $signer->verify($public_key, new Signature($R, $S), $hash);
89
    }
90
91
    /**
92
     * @return \Mdanter\Ecc\Primitives\GeneratorPoint
93
     */
94
    abstract protected function getGenerator();
95
96
    /**
97
     * @return string
98
     */
99
    abstract protected function getHashAlgorithm();
100
101
    /**
102
     * @return int
103
     */
104
    abstract protected function getSignaturePartLength();
105
106
    /**
107
     * @param string $value
108
     *
109
     * @return string
110
     */
111
    private function convertHexToBin($value)
112
    {
113
        return pack('H*', $value);
114
    }
115
116
    /**
117
     * @param string $value
118
     */
119
    private function convertBinToHex($value)
120
    {
121
        $value = unpack('H*', $value);
122
123
        return $value[1];
124
    }
125
126
    /**
127
     * @param $value
128
     *
129
     * @return string
130
     */
131
    private function convertDecToHex($value)
132
    {
133
        return $this->adapter->decHex($value);
134
    }
135
136
    /**
137
     * @param $value
138
     *
139
     * @return int|string
140
     */
141
    private function convertHexToDec($value)
142
    {
143
        return $this->adapter->hexDec($value);
144
    }
145
146
    /**
147
     * @param $value
148
     *
149
     * @return int|string
150
     */
151
    private function convertBase64ToDec($value)
152
    {
153
        $value = unpack('H*', Base64Url::decode($value));
154
155
        return $this->convertHexToDec($value[1]);
156
    }
157
158
    /**
159
     * @param JWKInterface $key
160
     */
161
    private function checkKey(JWKInterface $key)
162
    {
163
        Assertion::eq($key->get('kty'), 'EC', 'Wrong key type.');
164
        Assertion::true($key->has('x'), 'The key parameter "x" is missing.');
165
        Assertion::true($key->has('y'), 'The key parameter "y" is missing.');
166
        Assertion::true($key->has('crv'), 'The key parameter "crv" is missing.');
167
    }
168
}
169