Completed
Push — master ( a80b49...b6cabc )
by Florent
02:33
created

ECDSA::sign()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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