PNServerHelper::retrievePosInt()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
nc 2
nop 1
dl 0
loc 7
rs 10
c 1
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace SKien\PNServer;
5
6
/**
7
 * Helper trait containing some methods used by multiple classes in package.
8
 *
9
 * @package PNServer
10
 * @author Stefanius <[email protected]>
11
 * @copyright MIT License - see the LICENSE file for details
12
 */
13
trait PNServerHelper
14
{
15
    /**
16
     * Get classname without namespace.
17
     * @param mixed $o
18
     * @return string
19
     */
20
    public static function className($o) : string
21
    {
22
        $strName = '';
23
        if (is_object($o)) {
24
            $path = explode('\\', get_class($o));
25
            $strName = array_pop($path);
26
        }
27
        return $strName;
28
    }
29
30
    /**
31
     * Encode data to Base64URL.
32
     * @param string $data
33
     * @return string   encoded string
34
     */
35
    public static function encodeBase64URL(string $data) : string
36
    {
37
        // Convert Base64 to Base64URL by replacing “+” with “-” and “/” with “_”
38
        $url = strtr(base64_encode($data), '+/', '-_');
39
40
        // Remove padding character from the end of line and return the Base64URL result
41
        return rtrim($url, '=');
42
    }
43
44
    /**
45
     * Decode data from Base64URL.
46
     * If the strict parameter is set to TRUE then the function will return false
47
     * if the input contains character from outside the base64 alphabet. Otherwise
48
     * invalid characters will be silently discarded.
49
     * @param string $data
50
     * @param boolean $strict
51
     * @return string
52
     */
53
    public static function decodeBase64URL(string $data, bool $strict = false) : string
54
    {
55
        // Convert Base64URL to Base64 by replacing “-” with “+” and “_” with “/”
56
        $b64 = strtr($data, '-_', '+/');
57
58
        // Decode Base64 string and return the original data
59
        $strDecoded = base64_decode($b64, $strict);
60
61
        return $strDecoded !== false ? $strDecoded : 'error';
62
    }
63
64
    public static function getP256PEM(string $strPublicKey, string $strPrivateKey) : string
65
    {
66
        $der  = self::p256PrivateKey($strPrivateKey);
67
        $der .= $strPublicKey;
68
69
        $pem = '-----BEGIN EC PRIVATE KEY-----' . PHP_EOL;
70
        $pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
71
        $pem .= '-----END EC PRIVATE KEY-----' . PHP_EOL;
72
73
        return $pem;
74
    }
75
76
    private static function p256PrivateKey(string $strPrivateKey) : string
77
    {
78
        $aUP = \unpack('H*', str_pad($strPrivateKey, 32, "\0", STR_PAD_LEFT));
79
        $key = '';
80
        if ($aUP !== false) {
81
            $key = $aUP[1];
82
        }
83
        return pack(
84
                'H*',
85
                '3077'                  // SEQUENCE, length 87+length($d)=32
86
                . '020101'              // INTEGER, 1
87
                . '0420'                // OCTET STRING, length($d) = 32
88
                . $key
89
                . 'a00a'                // TAGGED OBJECT #0, length 10
90
                . '0608'                // OID, length 8
91
                . '2a8648ce3d030107'    // 1.3.132.0.34 = P-256 Curve
92
                . 'a144'                //  TAGGED OBJECT #1, length 68
93
                . '0342'                // BIT STRING, length 66
94
                . '00'                  // prepend with NUL - pubkey will follow
95
            );
96
    }
97
98
    /**
99
     * @param string $der
100
     * @return string|false
101
     */
102
    public static function signatureFromDER(string $der)
103
    {
104
        $sig = false;
105
        $R = false;
106
        $S = false;
107
        $aUP = \unpack('H*', $der);
108
        $hex = '';
109
        if ($aUP !== false) {
110
            $hex = $aUP[1];
111
        }
112
        if ('30' === \mb_substr($hex, 0, 2, '8bit')) {
113
            // SEQUENCE
114
            if ('81' === \mb_substr($hex, 2, 2, '8bit')) {
115
                // LENGTH > 128
116
                $hex = \mb_substr($hex, 6, null, '8bit');
117
            } else {
118
                $hex = \mb_substr($hex, 4, null, '8bit');
119
            }
120
            if ('02' === \mb_substr($hex, 0, 2, '8bit')) {
121
                // INTEGER
122
                $Rl = (int) \hexdec(\mb_substr($hex, 2, 2, '8bit'));
123
                $R = self::retrievePosInt(\mb_substr($hex, 4, $Rl * 2, '8bit'));
124
                $R = \str_pad($R, 64, '0', STR_PAD_LEFT);
125
126
                $hex = \mb_substr($hex, 4 + $Rl * 2, null, '8bit');
127
                if ('02' === \mb_substr($hex, 0, 2, '8bit')) {
128
                    // INTEGER
129
                    $Sl = (int) \hexdec(\mb_substr($hex, 2, 2, '8bit'));
130
                    $S = self::retrievePosInt(\mb_substr($hex, 4, $Sl * 2, '8bit'));
131
                    $S = \str_pad($S, 64, '0', STR_PAD_LEFT);
132
                }
133
            }
134
        }
135
136
        if ($R !== false && $S !== false) {
137
            $sig = \pack('H*', $R . $S);
138
        }
139
140
        return $sig;
141
    }
142
143
    private static function retrievePosInt(string $data) : string
144
    {
145
        while ('00' === \mb_substr($data, 0, 2, '8bit') && \mb_substr($data, 2, 2, '8bit') > '7f') {
146
            $data = \mb_substr($data, 2, null, '8bit');
147
        }
148
149
        return $data;
150
    }
151
152
    public static function getXYFromPublicKey(string $strKey, string &$x, string &$y) : bool
153
    {
154
        $bSucceeded = false;
155
        $hexData = bin2hex($strKey);
156
        if (mb_substr($hexData, 0, 2, '8bit') === '04') {
157
            $hexData = mb_substr($hexData, 2, null, '8bit');
158
            $dataLength = mb_strlen($hexData, '8bit');
159
160
            $x = hex2bin(mb_substr($hexData, 0, $dataLength / 2, '8bit'));
161
            $y = hex2bin(mb_substr($hexData, $dataLength / 2, null, '8bit'));
162
        }
163
        return $bSucceeded;
164
    }
165
}
166