]> git.mxchange.org Git - friendica-addons.git/blob - securemail/php-gpg/libs/GPG/Public_Key.php
Merge pull request #323 from fabrixxm/master
[friendica-addons.git] / securemail / php-gpg / libs / GPG / Public_Key.php
1 <?php\r
2 /** @package    php-gpg::GPG */\r
3 \r
4 /** require supporting files */\r
5 require_once("Expanded_Key.php");\r
6 \r
7 define("PK_TYPE_ELGAMAL", 1);\r
8 define("PK_TYPE_RSA", 0);\r
9 define("PK_TYPE_UNKNOWN", -1);\r
10 \r
11 /**\r
12  * Pure PHP implementation of PHP/GPG public key\r
13  *\r
14  * @package php-gpg::GPG\r
15  * @link http://www.verysimple.com/\r
16  * @copyright  1997-2011 VerySimple, Inc.\r
17  * @license    http://www.gnu.org/licenses/lgpl.html  LGPL\r
18  * @todo implement decryption\r
19  * @version 1.0\r
20  */\r
21 class GPG_Public_Key {\r
22     var $version;\r
23         var $fp;\r
24         var $key_id;\r
25         var $user;\r
26         var $public_key;\r
27         var $type;\r
28         \r
29         function IsValid()\r
30         {\r
31                 return $this->version != -1 && $this->GetKeyType() != PK_TYPE_UNKNOWN;\r
32         }\r
33         \r
34         function GetKeyType()\r
35         {\r
36                 if (!strcmp($this->type, "ELGAMAL")) return PK_TYPE_ELGAMAL;\r
37                 if (!strcmp($this->type, "RSA")) return PK_TYPE_RSA;\r
38                 return PK_TYPE_UNKNOWN;\r
39         }\r
40 \r
41         function GetFingerprint()\r
42         {\r
43                 return strtoupper( trim(chunk_split($this->fp, 4, ' ')) );\r
44         }\r
45         \r
46         function GetKeyId()\r
47         {\r
48                 return (strlen($this->key_id) == 16) ? strtoupper($this->key_id) : '0000000000000000';\r
49         }\r
50         \r
51         function GetPublicKey()\r
52         {\r
53                 return str_replace("\n", "", $this->public_key);\r
54         }\r
55         \r
56         function GPG_Public_Key($asc) {\r
57                 $found = 0;\r
58                 \r
59                 // normalize line breaks\r
60                 $asc = str_replace("\r\n", "\n", $asc);\r
61                 \r
62                 if (strpos($asc, "-----BEGIN PGP PUBLIC KEY BLOCK-----\n") === false)\r
63                         throw new Exception("Missing header block in Public Key");\r
64 \r
65                 if (strpos($asc, "\n\n") === false)\r
66                         throw new Exception("Missing body delimiter in Public Key");\r
67                 \r
68                 if (strpos($asc, "\n-----END PGP PUBLIC KEY BLOCK-----") === false)\r
69                         throw new Exception("Missing footer block in Public Key");\r
70                 \r
71                 // get rid of everything except the base64 encoded key\r
72                 $headerbody = explode("\n\n", str_replace("\n-----END PGP PUBLIC KEY BLOCK-----", "", $asc), 2);\r
73                 $asc = trim($headerbody[1]);\r
74                 \r
75                 \r
76                 $len = 0;\r
77                 $s =  base64_decode($asc);\r
78                 $sa = str_split($s);\r
79                 \r
80                 for($i = 0; $i < strlen($s);) {\r
81                         $tag = ord($sa[$i++]);\r
82                         \r
83                         // echo 'TAG=' . $tag . '/';\r
84                         \r
85                         if(($tag & 128) == 0) break;\r
86                         \r
87                         if($tag & 64) {\r
88                                 $tag &= 63;\r
89                                 $len = ord($sa[$i++]);\r
90                                 if ($len > 191 && $len < 224) $len = (($len - 192) << 8) + ord($sa[$i++]);\r
91                                 else if ($len == 255) $len = (ord($sa[$i++]) << 24) + (ord($sa[$i++]) << 16) + (ord($sa[$i++]) << 8) + ord($sa[$i++]);\r
92                                         else if ($len > 223 && $len < 255) $len = (1 << ($len & 0x1f));\r
93                         } else {\r
94                                 $len = $tag & 3;\r
95                                 $tag = ($tag >> 2) & 15;\r
96                                 if ($len == 0) $len = ord($sa[$i++]);\r
97                                 else if($len == 1) $len = (ord($sa[$i++]) << 8) + ord($sa[$i++]);\r
98                                         else if($len == 2) $len = (ord($sa[$i++]) << 24) + (ord($sa[$i++]) << 16) + (ord($sa[$i++]) << 8) + ord($sa[$i++]);\r
99                                                 else $len = strlen($s) - 1;\r
100                         }\r
101                         \r
102                         // echo $tag . ' ';\r
103                         \r
104                         if ($tag == 6 || $tag == 14) {\r
105                                 $k = $i;\r
106                                 $version = ord($sa[$i++]);\r
107                                 $found = 1;\r
108                                 $this->version = $version;\r
109                                 \r
110                                 $time = (ord($sa[$i++]) << 24) + (ord($sa[$i++]) << 16) + (ord($sa[$i++]) << 8) + ord($sa[$i++]);\r
111                                 \r
112                                 if($version == 2 || $version == 3) $valid = ord($sa[$i++]) << 8 + ord($sa[$i++]);\r
113                                 \r
114                                 $algo = ord($sa[$i++]);\r
115                                 \r
116                                 if($algo == 1 || $algo == 2) {\r
117                                         $m = $i;\r
118                                         $lm = floor((ord($sa[$i]) * 256 + ord($sa[$i + 1]) + 7) / 8);\r
119                                         $i += $lm + 2;\r
120                                         \r
121                                         $mod = substr($s, $m, $lm + 2);\r
122                                         $le = floor((ord($sa[$i]) * 256 + ord($sa[$i+1]) + 7) / 8);\r
123                                         $i += $le + 2;\r
124                                         \r
125                                         $this->public_key = base64_encode(substr($s, $m, $lm + $le + 4));\r
126                                         $this->type = "RSA";\r
127                                         \r
128                                         if ($version == 3) {\r
129                                                 $this->fp = '';\r
130                                                 $this->key_id = bin2hex(substr($mod, strlen($mod) - 8, 8));\r
131                                         } else if($version == 4) {\r
132                                                 \r
133                                                 // https://tools.ietf.org/html/rfc4880#section-12\r
134                                                 $headerPos = strpos($s, chr(0x04));  // TODO: is this always the correct starting point for the pulic key packet 'version' field?\r
135                                                 $delim = chr(0x01) . chr(0x00);  // TODO: is this the correct delimiter for the end of the public key packet? \r
136                                                 $delimPos = strpos($s, $delim) + (3-$headerPos);\r
137                                                 \r
138                                                 // echo "POSITION: $delimPos\n";\r
139                                                 \r
140                                                 // this does not work, tried it with RSA 1024 and RSA 4096 keys generated by GnuPG v2 (2.0.29) on Windows running Apache and PHP 5.6.3\r
141                                                 // $pkt = chr(0x99) . chr($delimPos >> 8) . chr($delimPos & 255) . substr($s, $headerPos, $delimPos);\r
142                                                 \r
143                                                 // this is the original signing string which seems to have only worked for key lengths of 1024 or less\r
144                                                 $pkt = chr(0x99) . chr($len >> 8) . chr($len & 255) . substr($s, $k, $len); // use this for now\r
145                                                 \r
146                                                 $fp = sha1($pkt);\r
147                                                 $this->fp = $fp;\r
148                                                 $this->key_id = substr($fp, strlen($fp) - 16, 16);\r
149                                                 \r
150                                                 // uncomment to debug the start point for the signing string\r
151 //                                              for ($ii = 5; $ii > -1; $ii--) {\r
152 //                                                      $pkt = chr(0x99) . chr($ii >> 8) . chr($ii & 255) . substr($s, $headerPos, $ii);\r
153 //                                                      $fp = sha1($pkt);\r
154 //                                                      echo "LENGTH=" . $headerPos . '->' . $ii . " CHR(" . ord(substr($s,$ii, 1)) . ") = " . substr($fp, strlen($fp) - 16, 16) . "\n";\r
155 //                                              }\r
156 //                                              echo "\n";\r
157                                                 \r
158                                                 // uncomment to debug the end point for the signing string\r
159 //                                              for ($ii = strlen($s); $ii > 1; $ii--) {\r
160 //                                                      $pkt = chr(0x99) . chr($ii >> 8) . chr($ii & 255) . substr($s, $headerPos, $ii);\r
161 //                                                      $fp = sha1($pkt);\r
162 //                                                      echo "LENGTH=" . $headerPos . '->' . $ii . " CHR(" . ord(substr($s,$ii, 1)) . ") = " . substr($fp, strlen($fp) - 16, 16) . "\n";\r
163 //                                              }\r
164                                         } else {\r
165                                                 throw new Exception('GPG Key Version ' . $version . ' is not supported');\r
166                                         }\r
167                                         $found = 2;\r
168                                 } else if(($algo == 16 || $algo == 20) && $version == 4) {\r
169                                                 $m = $i;\r
170                                                 \r
171                                                 $lp = floor((ord($sa[$i]) * 256 + ord($sa[$i +1]) + 7) / 8);\r
172                                                 $i += $lp + 2;\r
173                                                 \r
174                                                 $lg = floor((ord($sa[$i]) * 256 + ord($sa[$i + 1]) + 7) / 8);\r
175                                                 $i += $lg + 2;\r
176                                                 \r
177                                                 $ly = floor((ord($sa[$i]) * 256 + ord($sa[$i + 1]) + 7)/8);\r
178                                                 $i += $ly + 2;\r
179                                                 \r
180                                                 $this->public_key = base64_encode(substr($s, $m, $lp + $lg + $ly + 6));\r
181                                                 \r
182                                                 // TODO: should this be adjusted as it was for RSA (above)..?\r
183                                                 \r
184                                                 $pkt = chr(0x99) . chr($len >> 8) . chr($len & 255) . substr($s, $k, $len);\r
185                                                 $fp = sha1($pkt);\r
186                                                 $this->fp = $fp;\r
187                                                 $this->key_id = substr($fp, strlen($fp) - 16, 16);\r
188                                                 $this->type = "ELGAMAL";\r
189                                                 $found = 3;\r
190                                         } else {\r
191                                                 $i = $k + $len;\r
192                                         }\r
193                         } else if ($tag == 13) {\r
194                                         $this->user = substr($s, $i, $len);\r
195                                         $i += $len;\r
196                                 } else {\r
197                                         $i += $len;\r
198                                 }\r
199                 }\r
200                 \r
201                 if($found < 2) {  \r
202                         \r
203                         throw new Exception("Unable to parse Public Key");\r
204 //                      $this->version = "";\r
205 //                      $this->fp = "";\r
206 //                      $this->key_id = "";\r
207 //                      $this->user = ""; \r
208 //                      $this->public_key = "";\r
209                 }\r
210         }\r
211         \r
212         function GetExpandedKey()\r
213         {\r
214                 $ek = new Expanded_Key($this->public_key);\r
215         }\r
216 }\r
217 \r
218 ?>\r