Passed
Push — master ( 1c7137...b5ef5e )
by Pieter van der
03:26 queued 14s
created

OCRA::_oath_truncate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 10
c 2
b 0
f 0
dl 0
loc 20
rs 9.9332
cc 2
nc 2
nop 2
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.
0 ignored issues
show
Bug introduced by
The type text 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...
35
     */
36
    private static function _hmac_sha1($crypto,
37
            $keyBytes,
38
            $text)
39
    {
40
         $hash = hash_hmac ($crypto, $text, $keyBytes);
41
         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
    private static function _hexStr2Bytes($hex){
52
        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
0 ignored issues
show
Bug introduced by
The type the 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...
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
    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
        $codeDigits = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $codeDigits is dead and can be removed.
Loading history...
85
        $crypto = "";
86
        $result = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
87
        $ocraSuiteLength = strlen($ocraSuite);
88
        $counterLength = 0;
89
        $questionLength = 0;
90
        $passwordLength = 0;
91
92
        $sessionInformationLength = 0;
93
        $timeStampLength = 0;
94
95
        // How many digits should we return
96
        $components = explode(":", $ocraSuite);
97
        $cryptoFunction = $components[1];
98
        $dataInput = strtolower($components[2]); // lower here so we can do case insensitive comparisons
99
        
100
        if(stripos($cryptoFunction, "sha1")!==false)
101
            $crypto = "sha1";
102
        if(stripos($cryptoFunction, "sha256")!==false)
103
            $crypto = "sha256";
104
        if(stripos($cryptoFunction, "sha512")!==false)
105
            $crypto = "sha512";
106
        
107
        $codeDigits = substr($cryptoFunction, strrpos($cryptoFunction, "-")+1);
108
                
109
        // The size of the byte array message to be encrypted
110
        // Counter
111
        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
        if($dataInput[0] == "q" ||
119
                stripos($dataInput, "-q")!==false) {
120
            while(strlen($question) < 256)
121
                $question = $question . "0";
122
            $questionLength=128;
123
        }
124
125
        // Password
126
        if(stripos($dataInput, "psha1")!==false) {
127
            while(strlen($password) < 40)
128
                $password = "0" . $password;
129
            $passwordLength=20;
130
        }
131
    
132
        if(stripos($dataInput, "psha256")!==false) {
133
            while(strlen($password) < 64)
134
                $password = "0" . $password;
135
            $passwordLength=32;
136
        }
137
        
138
        if(stripos($dataInput, "psha512")!==false) {
139
            while(strlen($password) < 128)
140
                $password = "0" . $password;
141
            $passwordLength=64;
142
        }
143
        
144
        // sessionInformation
145
        if(stripos($dataInput, "s064") !==false) {
146
            while(strlen($sessionInformation) < 128)
147
                $sessionInformation = "0" . $sessionInformation;
148
149
            $sessionInformationLength=64;
150
        } else if(stripos($dataInput, "s128") !==false) {
151
            while(strlen($sessionInformation) < 256)
152
                $sessionInformation = "0" . $sessionInformation;
153
        
154
            $sessionInformationLength=128;
155
        } else if(stripos($dataInput, "s256") !==false) {
156
            while(strlen($sessionInformation) < 512)
157
                $sessionInformation = "0" . $sessionInformation;
158
        
159
            $sessionInformationLength=256;
160
        } else if(stripos($dataInput, "s512") !==false) {
161
            while(strlen($sessionInformation) < 128)
162
                $sessionInformation = "0" . $sessionInformation;
163
        
164
            $sessionInformationLength=64;
165
        } 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
            while(strlen($sessionInformation) < 128)
169
                $sessionInformation = "0" . $sessionInformation;
170
            
171
            $sessionInformationLength=64;
172
        }
173
        
174
        
175
             
176
        // TimeStamp
177
        if($dataInput[0] == "t" ||
178
                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
        $msg = array_fill(0,$ocraSuiteLength+$counterLength+$questionLength+$passwordLength+$sessionInformationLength+$timeStampLength+1, 0);
187
                
188
        for($i=0;$i<strlen($ocraSuite);$i++) {
189
            $msg[$i] = $ocraSuite[$i];
190
        }
191
        
192
        // Delimiter
193
        $msg[strlen($ocraSuite)] = self::_hexStr2Bytes("0");
194
195
        // Put the bytes of "Counter" to the message
196
        // Input is HEX encoded
197
        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
        if($questionLength > 0 ) {
208
            $bArray = self::_hexStr2Bytes($question);
209
            for ($i=0;$i<strlen($bArray);$i++) {
210
                $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
        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
        if($sessionInformationLength > 0 ){
226
            $bArray = self::_hexStr2Bytes($sessionInformation);
227
            for ($i=0;$i<strlen($bArray);$i++) {
228
                $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
        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
        $byteKey = self::_hexStr2Bytes($key);
242
              
243
        $msg = implode("", $msg);
244
        
245
        $hash = self::_hmac_sha1($crypto, $byteKey, $msg);
246
        
247
        $result = self::_oath_truncate($hash, $codeDigits);
248
             
249
        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
     * Truncate a result to a certain length
254
     */    
255
    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...
256
    {
257
        // Convert to dec
258
        foreach(str_split($hash,2) as $hex)
259
        {
260
            $hmac_result[]=hexdec($hex);
261
        }
262
    
263
        // Find offset
264
        $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 258. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
265
    
266
        $v = strval(
267
            (($hmac_result[$offset+0] & 0x7f) << 24 ) |
268
            (($hmac_result[$offset+1] & 0xff) << 16 ) |
269
            (($hmac_result[$offset+2] & 0xff) << 8 ) |
270
            ($hmac_result[$offset+3] & 0xff)
271
        );	
272
        
273
        $v = substr($v, strlen($v) - $length);
274
        return $v;
275
    }
276
    
277
}
278