Passed
Branch master (7f24b0)
by mahdi
25:20 queued 21:10
created

RSA::number_to_binary()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
eloc 8
c 2
b 1
f 1
dl 0
loc 13
rs 10
cc 2
nc 2
nop 2
1
<?php
2
3
namespace Shetabit\Payment\Drivers\Pasargad\Utils;
4
5
define("BCCOMP_LARGER", 1);
6
7
class RSA
8
{
9
    public function encrypt($message, $public_key, $modulus, $keylength)
10
    {
11
        $padded = $this->add_PKCS1_padding($message, true, $keylength / 8);
12
        $number = $this->binary_to_number($padded);
13
        $encrypted = $this->pow_mod($number, $public_key, $modulus);
14
        $result = $this->number_to_binary($encrypted, $keylength / 8);
15
16
        return $result;
17
    }
18
19
    public function decrypt($message, $private_key, $modulus, $keylength)
20
    {
21
        $number = $this->binary_to_number($message);
22
        $decrypted = $this->pow_mod($number, $private_key, $modulus);
23
        $result = $this->number_to_binary($decrypted, $keylength / 8);
24
25
        return $this->remove_PKCS1_padding($result, $keylength / 8);
26
    }
27
28
    public function sign($message, $private_key, $modulus, $keylength)
29
    {
30
        $padded = $this->add_PKCS1_padding($message, false, $keylength / 8);
31
        $number = $this->binary_to_number($padded);
32
        $signed = $this->pow_mod($number, $private_key, $modulus);
33
        $result = $this->number_to_binary($signed, $keylength / 8);
34
35
        return $result;
36
    }
37
38
    public function rsa_verify($message, $public_key, $modulus, $keylength)
39
    {
40
        return $this->decrypt($message, $public_key, $modulus, $keylength);
41
    }
42
43
    public function kyp_verify($message, $public_key, $modulus, $keylength)
44
    {
45
        $number = $this->binary_to_number($message);
46
        $decrypted = $this->pow_mod($number, $public_key, $modulus);
47
        $result = $this->number_to_binary($decrypted, $keylength / 8);
48
49
        return $this->remove_KYP_padding($result, $keylength / 8);
50
    }
51
52
    private function pow_mod($p, $q, $r)
53
    {
54
        $factors = array();
55
        $div = $q;
56
        $power_of_two = 0;
57
58
        while (bccomp($div, "0") == BCCOMP_LARGER) {
59
            $rem = bcmod($div, 2);
60
            $div = bcdiv($div, 2);
61
    
62
            if ($rem) {
63
                array_push($factors, $power_of_two);
64
            }
65
66
            $power_of_two++;
67
        }
68
    
69
        $partial_results = array();
70
        $part_res = $p;
71
        $idx = 0;
72
73
        foreach ($factors as $factor) {
74
            while ($idx < $factor) {
75
                $part_res = bcpow($part_res, "2");
76
                $part_res = bcmod($part_res, $r);
77
                $idx++;
78
            }
79
80
            array_push($partial_results, $part_res);
81
        }
82
83
        $result = "1";
84
85
        foreach ($partial_results as $part_res) {
86
            $result = bcmul($result, $part_res);
87
            $result = bcmod($result, $r);
88
        }
89
90
        return $result;
91
    }
92
93
    private function add_PKCS1_padding($data, $isPublicKey, $blocksize)
94
    {
95
        $pad_length = $blocksize - 3 - strlen($data);
96
97
        if ($isPublicKey) {
98
            $block_type = "\x02";
99
            $padding = "";
100
101
            for ($i = 0; $i < $pad_length; $i++) {
102
                $rnd = mt_rand(1, 255);
103
                $padding .= chr($rnd);
104
            }
105
        } else {
106
            $block_type = "\x01";
107
            $padding = str_repeat("\xFF", $pad_length);
108
        }
109
110
        return "\x00" . $block_type . $padding . "\x00" . $data;
111
    }
112
113
    private function remove_PKCS1_padding($data, $blocksize)
114
    {
115
        assert(strlen($data) == $blocksize);
116
        $data = substr($data, 1);
117
118
        if ($data{0} == '\0') {
119
            die("Block type 0 not implemented.");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
120
        }
121
122
        assert(($data{0} == "\x01") || ($data{0} == "\x02"));
123
124
        $offset = strpos($data, "\0", 1);
125
126
        return substr($data, $offset + 1);
127
    }
128
129
    private function remove_KYP_padding($data, $blocksize)
130
    {
131
        assert(strlen($data) == $blocksize);
132
        $offset = strpos($data, "\0");
133
134
        return substr($data, 0, $offset);
135
    }
136
137
    private function binary_to_number($data)
138
    {
139
        $base = "256";
140
        $radix = "1";
141
        $result = "0";
142
143
        for ($i = strlen($data) - 1; $i >= 0; $i--) {
144
            $digit = ord($data{$i});
145
            $part_res = bcmul($digit, $radix);
146
            $result = bcadd($result, $part_res);
147
            $radix = bcmul($radix, $base);
148
        }
149
150
        return $result;
151
    }
152
153
    private function number_to_binary($number, $blocksize)
154
    {
155
        $base = "256";
156
        $result = "";
157
        $div = $number;
158
159
        while ($div > 0) {
160
            $mod = bcmod($div, $base);
161
            $div = bcdiv($div, $base);
162
            $result = chr($mod) . $result;
0 ignored issues
show
Bug introduced by
It seems like $mod can also be of type string; however, parameter $ascii of chr() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

162
            $result = chr(/** @scrutinizer ignore-type */ $mod) . $result;
Loading history...
163
        }
164
165
        return str_pad($result, $blocksize, "\x00", STR_PAD_LEFT);
166
    }
167
}
168