Passed
Pull Request — develop (#22)
by Pieter van der
06:56
created

OCRA   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 263
Duplicated Lines 0 %

Test Coverage

Coverage 64.6%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 44
eloc 109
c 2
b 0
f 0
dl 0
loc 263
ccs 73
cts 113
cp 0.646
rs 8.8798

5 Methods

Rating   Name   Duplication   Size   Complexity  
A _hexStr2Bytes() 0 2 1
A _hmac_sha1() 0 6 1
A __construct() 0 1 1
F generateOCRA() 0 174 39
A _oath_truncate() 0 23 2

How to fix   Complexity   

Complex Class

Complex classes like OCRA often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use OCRA, and based on these observations, apply Extract Interface, too.

1
<?php 
2
/**
3
 * This file is part of the ocra-implementations package.
4
 *
5
 * More information: https://github.com/SURFnet/ocra-implementations/
6
 *
7
 * @author Ivo Jansch <[email protected]>
8
 * 
9
 * @license See the LICENSE file in the source distribution
10
 */
11
12
/**
13
 * This a PHP port of the example implementation of the 
14
 * OATH OCRA algorithm.
15
 * Visit www.openauthentication.org for more information.
16
 *
17
 * @author Johan Rydell, PortWise (original Java)
18
 * @author Ivo Jansch, Egeniq (PHP port)
19
 */
20
class OCRA {
21
22
    private function __construct() {
23
        
24
    }
25
26
    /**
27
     * This method uses the hmac_hash function to provide the crypto
28
     * algorithm.
29
     * HMAC computes a Hashed Message Authentication Code with the
30
     * crypto hash algorithm as a parameter.
31
     *
32
     * @param String crypto     the crypto algorithm (sha1, sha256 or sha512)
33
     * @param String keyBytes   the bytes to use for the HMAC key
34
     * @param String text       the message or text to be authenticated.
35
     */
36 6
    private static function _hmac_sha1($crypto,
37
            $keyBytes,
38
            $text)
39
    {
40 6
         $hash = hash_hmac ($crypto, $text, $keyBytes);
41 6
         return $hash;
42
    }
43
44
    /**
45
     * This method converts HEX string to Byte[]
46
     *
47
     * @param String hex   the HEX string
0 ignored issues
show
Bug introduced by
The type hex was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
48
     *
49
     * @return String a string with raw bytes
50
     */
51 6
    private static function _hexStr2Bytes($hex){
52 6
        return pack("H*", $hex);
53
    }
54
55
56
    /**
57
     * This method generates an OCRA HOTP value for the given
58
     * set of parameters.
59
     *
60
     * @param ocraSuite    the OCRA Suite
61
     * @param key          the shared secret, HEX encoded
62
     * @param counter      the counter that changes
63
     *                     on a per use basis,
64
     *                     HEX encoded
65
     * @param question     the challenge question, HEX encoded
66
     * @param password     a password that can be used,
67
     *                     HEX encoded
68
     * @param sessionInformation
69
     *                     Static information that identifies the
70
     *                     current session, Hex encoded
71
     * @param timeStamp    a value that reflects a time
72
     *
73
     * @return A numeric String in base 10 that includes
74
     * {@link truncationDigits} digits
75
     */
76 6
    static function generateOCRA($ocraSuite,
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
77
                                 $key,
78
                                 $counter,
79
                                 $question,
80
                                 $password,
81
                                 $sessionInformation,
82
                                 $timeStamp)
83
    {
84 6
        $codeDigits = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $codeDigits is dead and can be removed.
Loading history...
85 6
        $crypto = "";
86 6
        $result = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
87 6
        $ocraSuiteLength = strlen($ocraSuite);
88 6
        $counterLength = 0;
89 6
        $questionLength = 0;
90 6
        $passwordLength = 0;
91
92 6
        $sessionInformationLength = 0;
93 6
        $timeStampLength = 0;
94
95
        // How many digits should we return
96 6
        $components = explode(":", $ocraSuite);
97 6
        $cryptoFunction = $components[1];
98 6
        $dataInput = strtolower($components[2]); // lower here so we can do case insensitive comparisons
99
        
100 6
        if(stripos($cryptoFunction, "sha1")!==false)
101 6
            $crypto = "sha1";
102 6
        if(stripos($cryptoFunction, "sha256")!==false)
103
            $crypto = "sha256";
104 6
        if(stripos($cryptoFunction, "sha512")!==false)
105
            $crypto = "sha512";
106
        
107 6
        $codeDigits = substr($cryptoFunction, strrpos($cryptoFunction, "-")+1);
108
                
109
        // The size of the byte array message to be encrypted
110
        // Counter
111 6
        if($dataInput[0] == "c" ) {
112
            // Fix the length of the HEX string
113
            while(strlen($counter) < 16)
114
                $counter = "0" . $counter;
115
            $counterLength=8;
116
        }
117
        // Question
118 6
        if($dataInput[0] == "q" ||
119 6
                stripos($dataInput, "-q")!==false) {
120 6
            while(strlen($question) < 256)
121 6
                $question = $question . "0";
122 6
            $questionLength=128;
123
        }
124
125
        // Password
126 6
        if(stripos($dataInput, "psha1")!==false) {
127
            while(strlen($password) < 40)
128
                $password = "0" . $password;
129
            $passwordLength=20;
130
        }
131
    
132 6
        if(stripos($dataInput, "psha256")!==false) {
133
            while(strlen($password) < 64)
134
                $password = "0" . $password;
135
            $passwordLength=32;
136
        }
137
        
138 6
        if(stripos($dataInput, "psha512")!==false) {
139
            while(strlen($password) < 128)
140
                $password = "0" . $password;
141
            $passwordLength=64;
142
        }
143
        
144
        // sessionInformation
145 6
        if(stripos($dataInput, "s064") !==false) {
146
            while(strlen($sessionInformation) < 128)
147
                $sessionInformation = "0" . $sessionInformation;
148
149
            $sessionInformationLength=64;
150 6
        } else if(stripos($dataInput, "s128") !==false) {
151
            while(strlen($sessionInformation) < 256)
152
                $sessionInformation = "0" . $sessionInformation;
153
        
154
            $sessionInformationLength=128;
155 6
        } else if(stripos($dataInput, "s256") !==false) {
156
            while(strlen($sessionInformation) < 512)
157
                $sessionInformation = "0" . $sessionInformation;
158
        
159
            $sessionInformationLength=256;
160 6
        } else if(stripos($dataInput, "s512") !==false) {
161
            while(strlen($sessionInformation) < 128)
162
                $sessionInformation = "0" . $sessionInformation;
163
        
164
            $sessionInformationLength=64;
165 6
        } else if (stripos($dataInput, "s") !== false ) {
166
            // deviation from spec. Officially 's' without a length indicator is not in the reference implementation.
167
            // RFC is ambigious. However we have supported this in Tiqr since day 1, so we continue to support it.
168 5
            while(strlen($sessionInformation) < 128)
169 5
                $sessionInformation = "0" . $sessionInformation;
170
            
171 5
            $sessionInformationLength=64;
172
        }
173
        
174
        
175
             
176
        // TimeStamp
177 6
        if($dataInput[0] == "t" ||
178 6
                stripos($dataInput, "-t") !== false) {
179
            while(strlen($timeStamp) < 16)
180
                $timeStamp = "0" . $timeStamp;
181
            $timeStampLength=8;
182
        }
183
184
        // Put the bytes of "ocraSuite" parameters into the message
185
        
186 6
        $msg = array_fill(0,$ocraSuiteLength+$counterLength+$questionLength+$passwordLength+$sessionInformationLength+$timeStampLength+1, 0);
187
                
188 6
        for($i=0;$i<strlen($ocraSuite);$i++) {
189 6
            $msg[$i] = $ocraSuite[$i];
190
        }
191
        
192
        // Delimiter
193 6
        $msg[strlen($ocraSuite)] = self::_hexStr2Bytes("0");
194
195
        // Put the bytes of "Counter" to the message
196
        // Input is HEX encoded
197 6
        if($counterLength > 0 ) {
198
            $bArray = self::_hexStr2Bytes($counter);
199
            for ($i=0;$i<strlen($bArray);$i++) {
200
                $msg [$i + $ocraSuiteLength + 1] = $bArray[$i];
201
            }
202
        }
203
204
205
        // Put the bytes of "question" to the message
206
        // Input is text encoded
207 6
        if($questionLength > 0 ) {
208 6
            $bArray = self::_hexStr2Bytes($question);
209 6
            for ($i=0;$i<strlen($bArray);$i++) {
210 6
                $msg [$i + $ocraSuiteLength + 1 + $counterLength] = $bArray[$i];
211
            }
212
        }
213
214
        // Put the bytes of "password" to the message
215
        // Input is HEX encoded
216 6
        if($passwordLength > 0){
217
            $bArray = self::_hexStr2Bytes($password);
218
            for ($i=0;$i<strlen($bArray);$i++) {
219
                $msg [$i + $ocraSuiteLength + 1 + $counterLength + $questionLength] = $bArray[$i];
220
            }
221
        }
222
223
        // Put the bytes of "sessionInformation" to the message
224
        // Input is text encoded
225 6
        if($sessionInformationLength > 0 ){
226 5
            $bArray = self::_hexStr2Bytes($sessionInformation);
227 5
            for ($i=0;$i<strlen($bArray);$i++) {
228 5
                $msg [$i + $ocraSuiteLength + 1 + $counterLength + $questionLength + $passwordLength] = $bArray[$i];
229
            }
230
        }
231
232
        // Put the bytes of "time" to the message
233
        // Input is text value of minutes
234 6
        if($timeStampLength > 0){
235
            $bArray = self::_hexStr2Bytes($timeStamp);
236
            for ($i=0;$i<strlen($bArray);$i++) {
237
                $msg [$i + $ocraSuiteLength + 1 + $counterLength + $questionLength + $passwordLength + $sessionInformationLength] = $bArray[$i];
238
            }
239
        }
240
        
241 6
        $byteKey = self::_hexStr2Bytes($key);
242
              
243 6
        $msg = implode("", $msg);
244
        
245 6
        $hash = self::_hmac_sha1($crypto, $byteKey, $msg);
246
        
247 6
        $result = self::_oath_truncate($hash, $codeDigits);
0 ignored issues
show
Bug introduced by
$codeDigits of type string is incompatible with the type integer expected by parameter $length of OCRA::_oath_truncate(). ( Ignorable by Annotation )

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

247
        $result = self::_oath_truncate($hash, /** @scrutinizer ignore-type */ $codeDigits);
Loading history...
248
             
249 6
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result returns the type string which is incompatible with the documented return type A.
Loading history...
250
    }
251
252
    /**
253
     * Implementation of the Truncate function from RFC4226
254
     * Truncate a hex encoded (hash) result to a string of digital digits of $length
255
     *
256
     * @param string $hash hex encoded (hash) value to truncate. Minimum length is 20 bytes (i.e. a string of 40 hex digits)
257
     * @param int $length number of decimal digits to truncate to
258
     * @return string of $length digits
259
     */    
260 7
    static function _oath_truncate($hash, $length = 6)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
261
    {
262
        // Convert to dec
263 7
        foreach(str_split($hash,2) as $hex)
264
        {
265 7
            $hmac_result[]=hexdec($hex);
266
        }
267
    
268
        // Find offset
269 7
        $offset = $hmac_result[count($hmac_result) - 1] & 0xf;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $hmac_result seems to be defined by a foreach iteration on line 263. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
270
    
271
        $v = strval(
272 7
            (($hmac_result[$offset+0] & 0x7f) << 24 ) |
273 7
            (($hmac_result[$offset+1] & 0xff) << 16 ) |
274 7
            (($hmac_result[$offset+2] & 0xff) << 8 ) |
275 7
            ($hmac_result[$offset+3] & 0xff)
276
        );
277
278
        // Prefix truncated string with 0's to ensure it always has the required length
279 7
        $v=str_pad($v, $length, "0", STR_PAD_LEFT);
280
281 7
        $v = substr($v, strlen($v) - $length);
282 7
        return $v;
283
    }
284
    
285
}
286