Failed Conditions
Push — v7 ( 514019...c6ee27 )
by Florent
02:57
created

ECSignature::toDER()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 21
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.3142
c 0
b 0
f 0
cc 3
eloc 15
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace Jose\Component\Core\Util;
15
16
/**
17
 * Class ECSignature.
18
 */
19
final class ECSignature
20
{
21
    /**
22
     * @param string $signature
23
     * @param int    $partLength
24
     *
25
     * @return string
26
     */
27
    public static function toDER(string $signature, int $partLength): string
28
    {
29
        $signature = unpack('H*', $signature)[1];
30
        if (mb_strlen($signature, '8bit') !== 2 * $partLength) {
31
            throw new \InvalidArgumentException('Invalid length.');
32
        }
33
        $R = mb_substr($signature, 0, $partLength, '8bit');
34
        $S = mb_substr($signature, $partLength, null, '8bit');
35
36
        $R = self::preparePositiveInteger($R);
37
        $Rl = mb_strlen($R, '8bit')/2;
38
        $S = self::preparePositiveInteger($S);
39
        $Sl = mb_strlen($S, '8bit')/2;
40
        $der = pack('H*',
41
            '30'. ($Rl+$Sl+4 > 128 ? '81' : '').dechex($Rl+$Sl+4)
42
            .'02'.dechex($Rl).$R
43
            .'02'.dechex($Sl).$S
44
        );
45
46
        return $der;
47
    }
48
49
    /**
50
     * @param string $der
51
     * @param int    $partLength
52
     *
53
     * @return string
54
     */
55
    public static function fromDER(string $der, int $partLength): string
56
    {
57
        $hex = unpack('H*', $der)[1];
58
        if ('30' !== mb_substr($hex, 0, 2, '8bit')) { // SEQUENCE
59
            throw new \RuntimeException();
60
        }
61
        if ('81' === mb_substr($hex, 2, 2, '8bit')) { // LENGTH > 128
62
            $hex = mb_substr($hex, 6, null, '8bit');
63
        } else {
64
            $hex = mb_substr($hex, 4, null, '8bit');
65
        }
66
        if ('02' !== mb_substr($hex, 0, 2, '8bit')) { // INTEGER
67
            throw new \RuntimeException();
68
        }
69
70
        $Rl = hexdec(mb_substr($hex, 2, 2, '8bit'));
71
        $R = self::retrievePositiveInteger(mb_substr($hex, 4, $Rl*2, '8bit'));
72
        $R = str_pad($R, $partLength, '0', STR_PAD_LEFT);
73
74
        $hex = mb_substr($hex, 4+$Rl*2, null, '8bit');
75
        if ('02' !== mb_substr($hex, 0, 2, '8bit')) { // INTEGER
76
            throw new \RuntimeException();
77
        }
78
        $Sl = hexdec(mb_substr($hex, 2, 2, '8bit'));
79
        $S = self::retrievePositiveInteger(mb_substr($hex, 4, $Sl*2, '8bit'));
80
        $S = str_pad($S, $partLength, '0', STR_PAD_LEFT);
81
82
        return pack('H*', $R.$S);
83
    }
84
85
    /**
86
     * @param string $data
87
     *
88
     * @return string
89
     */
90
    private static function preparePositiveInteger(string $data): string
91
    {
92
        if (mb_substr($data, 0, 2, '8bit') >= '7f') {
93
            return '00'.$data;
94
        }
95
        while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') < '7f') {
96
            $data = mb_substr($data, 2, null, '8bit');
97
        }
98
99
        return $data;
100
    }
101
102
    /**
103
     * @param string $data
104
     *
105
     * @return string
106
     */
107
    private static function retrievePositiveInteger(string $data): string
108
    {
109
        while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') > '7f') {
110
            $data = mb_substr($data, 2, null, '8bit');
111
        }
112
113
        return $data;
114
    }
115
}
116