Completed
Branch develop (8ddc4a)
by Florent
02:46
created

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