Completed
Push — master ( 668fd8...cdc0a6 )
by Florent
12:11
created

ECDSA   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 151
wmc 16
lcom 1
cbo 9
rs 10

12 Methods

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