1<?php
2/**
3 * Helper Component for Securelogin Dokuwiki Plugin
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Mikhail I. Izmestev
7 * @maintainer Matt Bagley
8 *
9 * @see also   https://www.dokuwiki.org/plugin:securelogin
10 */
11// must be run within Dokuwiki
12if(!defined('DOKU_INC')) die();
13
14/**
15 * This is the base class for all syntax classes, providing some general stuff
16 */
17class helper_plugin_securelogin extends DokuWiki_Plugin {
18    protected $_keyFile;
19    protected $_keyIFile;
20    protected $_key = null;
21    protected $_keyInfo = null;
22    protected $_workCorrect = false;
23    protected $_canWork = false;
24
25    /**
26     * constructor
27     */
28    function __construct() {
29        global $conf;
30
31        $this->_keyIFile = $conf['cachedir'].'/securelogin.ini';
32        $this->_keyFile = $conf['cachedir'].'/securelogin.key';
33
34        if( true
35            && function_exists("openssl_pkey_export_to_file")
36            && function_exists("openssl_pkey_get_private")
37            && function_exists("openssl_pkey_new")
38            && function_exists("openssl_private_decrypt")
39            )
40            $this->_canWork = true;
41    }
42
43    function canWork() {
44        return $this->_canWork;
45    }
46
47    function haveKey($onlyPublic = false) {
48        if($onlyPublic) {
49            if($this->_keyInfo) return true;
50
51            if(file_exists($this->_keyIFile)) {
52                $this->_keyInfo = parse_ini_file($this->_keyIFile);
53                return true;
54            }
55        }
56
57        if(!$this->_key && file_exists($this->_keyFile)) {
58            $this->_key = openssl_pkey_get_private(file_get_contents($this->_keyFile));
59            if($this->_key) {
60                if(file_exists($this->_keyIFile))
61                    $this->_keyInfo = parse_ini_file($this->_keyIFile);
62                else
63                    $this->savePublicInfo($this->getPublicKeyInfo(file_get_contents($this->_keyFile)));
64            }
65        }
66        return null != $this->_key;
67    }
68
69    function getKeyLengths() {
70        return array('default' => 'default', '512' => '512', '1024' => '1024', '2048' => '2048');
71    }
72
73    function generateKey($length) {
74        if(!array_key_exists($length, $this->getKeyLengths())) {
75            msg("Error key length $length not supported", -1);
76            return;
77        }
78
79        $newkey = @openssl_pkey_new(('default' == $length)?array():array('private_key_bits' => intval($length)));
80
81        if(!$newkey) {
82            msg('Error generating new key', -1);
83            return;
84        }
85        if(!openssl_pkey_export_to_file($newkey, $this->_keyFile))
86            msg('Error export new key', -1);
87        else {
88            $this->_key = openssl_pkey_get_private(file_get_contents($this->_keyFile));
89            $this->savePublicInfo($this->getPublicKeyInfo(file_get_contents($this->_keyFile)));
90        }
91    }
92
93    function getKeyLength() {
94        return (strlen($this->getModulus())-2)*4;
95    }
96
97    function getModulus() {
98        return ($this->haveKey(true))?$this->_keyInfo['modulus']:null;
99    }
100
101    function getExponent() {
102        return ($this->haveKey(true))?$this->_keyInfo['exponent']:null;
103    }
104
105    function savePublicInfo($info) {
106        $fpinfo = fopen($this->_keyIFile, "w");
107        foreach($info as $key => $val) {
108            fprintf($fpinfo, "%s=\"%s\"\n", $key, $val);
109        }
110        fclose($fpinfo);
111        $this->_keyInfo = parse_ini_file($this->_keyIFile);
112    }
113
114    function decrypt($text) {
115        if($this->haveKey())
116            openssl_private_decrypt(base64_decode($text), $decoded, $this->_key);
117        return $decoded;
118    }
119
120    function decodeBER($bin) {
121        function my_unpack($format, &$bin, $length) {
122            $res = unpack($format, $bin);
123            $bin = substr($bin, $length);
124            return $res;
125        }
126
127        function readBER(&$bin) {
128            if(!strlen($bin)) return FALSE;
129
130            $data = my_unpack("C1type/c1length", $bin, 2);
131
132            if($data['length'] < 0) {
133                $count = $data['length'] & 0x7F;
134                $data['length'] = 0;
135                while($count) {
136                    $data['length'] <<= 8;
137                    $tmp = my_unpack("C1length", $bin, 1);
138                    $data['length'] += $tmp['length'];
139                    $count--;
140                }
141            }
142
143            switch($data['type']) {
144                case 0x30:
145                    $data['value'] = array();
146                    do {
147                        $tmp = readBER($bin);
148                        if($tmp)
149                            $data['value'][] = $tmp;
150                    } while($tmp);
151                    break;
152                case 0x03:
153                    $null = my_unpack("C1", $bin, 1);
154                    $data['value'] = readBER($bin);
155                    break;
156                case 0x04:
157                    $data['value'] = readBER($bin);
158                    break;
159                default:
160                    $count = $data['length'];
161                    while($count) {
162                        $tmp = my_unpack("C1data", $bin, 1);
163                        $data['value'] .= sprintf("%02X", $tmp['data']);
164                        $count--;
165                    }
166            }
167            return $data;
168        }
169        return readBER($bin);
170    }
171
172    function getPublicKeyInfo($pubkey) {
173        function findKeyInfo($data, &$pubkeyinfo) {
174            if($data['type'] == 48) {
175                if(count($data['value']) != 9) {
176                    foreach($data['value'] as $subdata) {
177                        if(findKeyInfo($subdata, $pubkeyinfo))
178                            return true;
179                    }
180                } else {
181                    $pubkeyinfo = array(
182                        "modulus" => $data['value'][1]['value'],
183                        "exponent" => $data['value'][2]['value'],
184                    );
185                    return true;
186                }
187            }
188            elseif($data['type'] == 4) {
189                return findKeyInfo($data['value'], $pubkeyinfo);
190            }
191            return false;
192        }
193
194        $pubkey = preg_split("(-\n|\n-)", $pubkey);
195        $binary = base64_decode($pubkey[1]);
196
197        $data = $this->decodeBER($binary);
198
199        findKeyInfo($data, $pubkeyinfo);
200/*
201        $pubkeyinfo = array(
202            "modulus" => $data['value'][1]['value'][2]['value']['value'][1]['value'],
203            "exponent" => $data['value'][1]['value'][2]['value']['value'][2]['value'],
204        );
205*/
206        return $pubkeyinfo;
207    }
208
209    function workCorrect($yes = false) {
210        if($yes)
211            $this->_workCorrect = true;
212        return $this->_workCorrect;
213    }
214}
215