EncryptionEO::completeMultipartUploadSecurely()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 14
c 1
b 0
f 0
dl 0
loc 18
rs 9.7998
cc 4
nc 4
nop 1
1
<?php
2
require_once KS3_API_PATH.DIRECTORY_SEPARATOR."encryption".DIRECTORY_SEPARATOR."EncryptionUtil.php";
3
require_once KS3_API_PATH.DIRECTORY_SEPARATOR."encryption".DIRECTORY_SEPARATOR."EncryptionCallBack.php";
4
require_once KS3_API_PATH.DIRECTORY_SEPARATOR."exceptions".DIRECTORY_SEPARATOR."Exceptions.php";
5
interface EncryptionHandler{
6
	public function putObjectByContentSecurely($args=array());
7
	public function putObjectByFileSecurely($args=array());
8
	public function getObjectSecurely($args=array());
9
	public function initMultipartUploadSecurely($args=array());
10
	public function uploadPartSecurely($args=array());
11
	public function abortMultipartUploadSecurely($args=array());
12
	public function completeMultipartUploadSecurely($args=array());
13
}
14
class EncryptionEO implements EncryptionHandler{
15
	private $encryptionMaterials = NULL;
16
	private $ks3client = NULL;
17
	public function __construct($ks3client,$encryptionMaterials){
18
		$this->encryptionMaterials = $encryptionMaterials;
19
		$this->ks3client = $ks3client;
20
	}
21
	public function putObjectByContentSecurely($args=array()){
22
		$sek = EncryptionUtil::genereateOnceUsedKey();
23
		$encryptedSek = EncryptionUtil::encodeCek($this->encryptionMaterials,$sek);
24
		$content = $args["Content"];
25
		if(empty($content))
26
			throw new Ks3ClientException("please specifie Content in request args");
27
		$metaContentLength = EncryptionUtil::metaTextLength($args);
28
		$plainTextLength = strlen($content);
29
		if($metaContentLength > 0 && $metaContentLength < $plainTextLength){
30
			$plainTextLength = $metaContentLength;
31
		}
32
		if($plainTextLength > 0)
33
			$args["UserMeta"]["x-kss-meta-x-kss-unencrypted-content-length"] = $plainTextLength;
34
		else
35
			throw new Ks3ClientException("unexpected content length ".$plainTextLength);
36
37
		$content =  substr($content, 0,$plainTextLength);
38
39
40
		$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
41
		$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td),MCRYPT_RAND);
42
		mcrypt_generic_init($td,$sek,$iv);
43
		//对content进行pkcs5填充
44
		$content = EncryptionUtil::PKCS5Padding($content,mcrypt_get_block_size(MCRYPT_RIJNDAEL_128,MCRYPT_MODE_CBC));
45
		$encrypted = mcrypt_generic($td,$content);
46
		mcrypt_generic_deinit($td);
47
48
		$args["ObjectMeta"]["Content-Length"] = strlen($encrypted);
49
		$args["Content"] = $encrypted; 
50
51
		$args = EncryptionUtil::updateContentMD5Header($args);
52
53
		//TODO
54
		$matdesc = "{}";
55
		if(ENCRYPTPTION_STORAGE_MODE == "ObjectMetadata"){
0 ignored issues
show
introduced by
The condition ENCRYPTPTION_STORAGE_MODE == 'ObjectMetadata' is always false.
Loading history...
56
			$args["UserMeta"]["x-kss-meta-x-kss-key"] = base64_encode($encryptedSek);
57
			$args["UserMeta"]["x-kss-meta-x-kss-iv"] = base64_encode($iv);
58
			$args["UserMeta"]["x-kss-meta-x-kss-matdesc"] = $matdesc;
59
		}
60
61
		$result = $this->ks3client->putObjectByContent($args);
62
63
		if(ENCRYPTPTION_STORAGE_MODE == "InstructionFile"){
0 ignored issues
show
introduced by
The condition ENCRYPTPTION_STORAGE_MODE == 'InstructionFile' is always true.
Loading history...
64
			$req = EncryptionUtil::createInstructionFile($args["Bucket"],$args["Key"],
65
			base64_encode($encryptedSek),base64_encode($iv),$matdesc);
66
			$this->ks3client->putObjectByContent($req);
67
		}
68
69
		return $result;
70
	}
71
	public function putObjectByFileSecurely($args=array()){
72
		$sek = EncryptionUtil::genereateOnceUsedKey();
73
		$encryptedSek = EncryptionUtil::encodeCek($this->encryptionMaterials,$sek);
74
		if(!isset($args["Content"])||!is_array($args["Content"])
75
			||!isset($args["Content"]["content"])
76
			||empty($args["Content"]["content"]))
77
			throw new Ks3ClientException("please specifie file content in request args");
78
		$content = $args["Content"];
0 ignored issues
show
Unused Code introduced by
The assignment to $content is dead and can be removed.
Loading history...
79
		$plainTextLength = EncryptionUtil::plainTextLength($args);
80
		if($plainTextLength <= 0){
81
			throw new Ks3ClientException("get content length failed ,unexpected content length ".$plainTextLength);
82
		}
83
		$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
84
		$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td),MCRYPT_RAND);
85
86
		$args = EncryptionUtil::updateContentMD5Header($args);
87
		$encryptedLength = EncryptionUtil::getPKCS5EncrypedLength($plainTextLength,mcrypt_get_block_size(MCRYPT_RIJNDAEL_128,MCRYPT_MODE_CBC));
88
89
		$args["ObjectMeta"]["Content-Length"] = $encryptedLength;
90
		$args["UserMeta"]["x-kss-meta-x-kss-unencrypted-content-length"] = $plainTextLength;
91
92
		$readCallBack = new AESCBCStreamReadCallBack();
93
		$readCallBack->iv = $iv;
0 ignored issues
show
Bug Best Practice introduced by
The property $iv is declared private in AESCBCStreamReadCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
94
		$readCallBack->cek = $sek;
0 ignored issues
show
Bug Best Practice introduced by
The property $cek is declared private in AESCBCStreamReadCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
95
		$readCallBack->contentLength = $plainTextLength;
0 ignored issues
show
Bug Best Practice introduced by
The property $contentLength is declared private in AESCBCStreamReadCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
96
		$args["readCallBack"] = $readCallBack;
97
98
		//TODO
99
		$matdesc = "{}";
100
		if(ENCRYPTPTION_STORAGE_MODE == "ObjectMetadata"){
0 ignored issues
show
introduced by
The condition ENCRYPTPTION_STORAGE_MODE == 'ObjectMetadata' is always false.
Loading history...
101
			$args["UserMeta"]["x-kss-meta-x-kss-key"] = base64_encode($encryptedSek);
102
			$args["UserMeta"]["x-kss-meta-x-kss-iv"] = base64_encode($iv);
103
			$args["UserMeta"]["x-kss-meta-x-kss-matdesc"] = $matdesc;
104
		}
105
106
		$result = $this->ks3client->putObjectByFile($args);
107
108
		if(ENCRYPTPTION_STORAGE_MODE == "InstructionFile"){
0 ignored issues
show
introduced by
The condition ENCRYPTPTION_STORAGE_MODE == 'InstructionFile' is always true.
Loading history...
109
			$req = EncryptionUtil::createInstructionFile($args["Bucket"],$args["Key"],
110
			base64_encode($encryptedSek),base64_encode($iv),$matdesc);
111
			$this->ks3client->putObjectByContent($req);
112
		}
113
114
		return $result;
115
	}
116
	public function getObjectSecurely($args=array()){
117
		$meta = $this->ks3client->getObjectMeta($args);
118
		if(isset($meta["UserMeta"]["x-kss-meta-x-kss-key"])&&isset($meta["UserMeta"]["x-kss-meta-x-kss-iv"])){
119
			$encryptedInMeta = TRUE;
120
		}else{
121
			$encryptedInMeta = FALSE;
122
		}
123
		$encrypted = TRUE;
124
		$encryptionInfo = array();
125
		if($encryptedInMeta){
126
			$encryptionInfo["iv"] = base64_decode($meta["UserMeta"]["x-kss-meta-x-kss-iv"]);
127
			$matdesc =$meta["UserMeta"]["x-kss-meta-x-kss-matdesc"];
128
			$encryptionInfo["matdesc"] = $matdesc;
129
			$cekEncrypted = base64_decode($meta["UserMeta"]["x-kss-meta-x-kss-key"]);
130
			$encryptionInfo["cek"] = $cek = EncryptionUtil::decodeCek($this->encryptionMaterials,$cekEncrypted);
0 ignored issues
show
Unused Code introduced by
The assignment to $cek is dead and can be removed.
Loading history...
131
		}else{
132
			if($this->ks3client->objectExists(array(
133
				"Bucket"=>$args["Bucket"],
134
				"Key"=>$args["Key"].EncryptionUtil::$INSTRUCTION_SUFFIX)
135
				)
136
			){
137
				$insKey = $args["Key"].EncryptionUtil::$INSTRUCTION_SUFFIX;
138
				$getIns = array(
139
					"Bucket"=>$args["Bucket"],
140
					"Key"=>$insKey,
141
				);
142
				$s3Object = $this->ks3client->getObject($getIns);
143
				if(!EncryptionUtil::isInstructionFile($s3Object))
144
					throw new Ks3ClientException($insKey." is not an InstructionFile");
145
146
				$content = $s3Object["Content"];
147
				$content = json_decode($content,TRUE);
148
				$encryptionInfo["iv"] = base64_decode($content["x-kss-iv"]);
149
				$matdesc =$content["x-kss-matdesc"];
150
				$encryptionInfo["matdesc"] = $matdesc;
151
				$cekEncrypted = base64_decode($content["x-kss-key"]);
152
				$encryptionInfo["cek"] = $cek = EncryptionUtil::decodeCek($this->encryptionMaterials,$cekEncrypted);
153
			}else{
154
				$encrypted =FALSE;
155
			}
156
		}
157
		//是否为下载到文件中
158
		$isWriteToFile=FALSE;
0 ignored issues
show
Unused Code introduced by
The assignment to $isWriteToFile is dead and can be removed.
Loading history...
159
		if($encrypted)
160
		{
161
			$iv = $encryptionInfo["iv"];
162
			$cek = $encryptionInfo["cek"];
163
164
			if(empty($iv))
165
				throw new Ks3ClientException("can not find iv in UserMeta or InstructionFile");
166
			if(empty($cek))
167
				throw new Ks3ClientException("can not find cek in UserMeta or InstructionFile");
168
169
			if(isset($args["Range"])){
170
				$range = $args["Range"];
171
				if(!is_array($range)){
172
					if(preg_match('/^bytes=[0-9]*-[0-9]*$/', $range)){
173
						$ranges = explode("-",substr($range,strlen("bytes=")));
174
						$a = $ranges[0];
175
						$b = $ranges[1];
176
						if($a > $b){
177
							throw new Ks3ClientException("Invalid range ".$range);
178
						}
179
						$range = array("start"=>$a,"end"=>$b);
180
					}else{
181
						throw new Ks3ClientException("Invalid range ".$range);
182
					}
183
				}else{
184
					if(!isset($range["start"])||!isset($range["end"])){
185
						throw new Ks3ClientException("Invalid range ".serialize($range));
186
					}
187
					if($range["start"] > $range["end"]){
188
						throw new Ks3ClientException("Invalid range ".serialize($range));
189
					}
190
				}
191
			}
192
193
			$isWriteToFile = isset($args["WriteTo"]);
194
			$contentLength = $meta["ObjectMeta"]["Content-Length"];
195
			if($isWriteToFile){
196
				$writeCallBack = new AESCBCStreamWriteCallBack();
197
				$writeCallBack->iv=$iv;
0 ignored issues
show
Bug Best Practice introduced by
The property $iv is declared private in AESCBCStreamWriteCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
198
				$writeCallBack->cek=$cek;
0 ignored issues
show
Bug Best Practice introduced by
The property $cek is declared private in AESCBCStreamWriteCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
199
				$writeCallBack->contentLength = $contentLength;
0 ignored issues
show
Bug Best Practice introduced by
The property $contentLength is declared private in AESCBCStreamWriteCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
200
				if(isset($range)){
201
					$blocksize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128,MCRYPT_MODE_CBC);
202
					$adjustedRange = EncryptionUtil::getAdjustedRange($range,$blocksize);
203
					$writeCallBack->expectedRange = $range;
0 ignored issues
show
Bug Best Practice introduced by
The property $expectedRange is declared private in AESCBCStreamWriteCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
204
					$writeCallBack->adjustedRange = $adjustedRange;
0 ignored issues
show
Bug Best Practice introduced by
The property $adjustedRange is declared private in AESCBCStreamWriteCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
205
					$args["Range"]=$adjustedRange;
206
				}
207
				$args["writeCallBack"] = $writeCallBack;
208
				return $this->ks3client->getObject($args);
209
			}else{
210
				$offset = 0;
211
				$blocksize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128,MCRYPT_MODE_CBC);
212
				if(isset($range)){
213
					$adjustedRange = EncryptionUtil::getAdjustedRange($range,$blocksize);
214
					$args["Range"]=$adjustedRange;
215
				}
216
				$s3Object = $this->ks3client->getObject($args);
217
				$content = $s3Object["Content"];
218
219
				if(isset($range)){
220
					if($adjustedRange["start"] > 0){
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $adjustedRange does not seem to be defined for all execution paths leading up to this point.
Loading history...
221
						$iv = substr($content,0,$blocksize);
222
						$content = substr($content,$blocksize);
223
						$offset = $blocksize+$adjustedRange["start"];
224
					}
225
				}
226
				if(!empty($content)){
227
					$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
228
					mcrypt_generic_init($td,$cek,$iv);
229
					$decoded = mdecrypt_generic($td,$content);
230
					mcrypt_generic_deinit($td);
231
					mcrypt_module_close($td);
232
				}else{
233
					$decoded = "";
234
				}
235
236
				//判断是否需要删除最后填充的字符,以及获取填充的字符
237
				$needRemovePad = FALSE;
238
				$pad = NULL;
239
				if($offset+strlen($decoded) >=$contentLength){
240
					$needRemovePad = TRUE;
241
					$pad = ord(substr($decoded,strlen($decoded)-1,1));
242
					if($pad<=0||$pad>$blocksize)
243
					{
244
						//invalid pad
245
						$needRemovePad = FALSE;
246
					}
247
				}
248
				$endOffset = 0;
249
				if(isset($range)){
250
					if($offset+strlen($decoded)>$range["end"]){
251
						$preLength = strlen($decoded);
252
						$decoded = substr($decoded, 0,$range["end"]-$offset+1);
253
						$endOffset = $preLength-strlen($decoded);
254
					}
255
					if($offset<$range["start"]){
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $range does not seem to be defined for all execution paths leading up to this point.
Loading history...
256
						$decoded = substr($decoded,$range["start"] - $offset);
257
					}
258
				}
259
				//再次根据截取的长度判断是否需要删除最后填充的字符
260
				if($needRemovePad&&$endOffset > $pad){
261
					$needRemovePad = FALSE;
262
				}
263
				if($needRemovePad){
264
					$padOffset = $pad-$endOffset;
265
					$actualWriteCount = strlen($decoded)-$padOffset;
266
					if($actualWriteCount <= 0)//负数的情况就是用户期望的range里全是填充的
267
						$decoded = "";
268
					else
269
						$decoded = substr($decoded,0,strlen($decoded)-$padOffset);
270
				}
271
				$s3Object["Content"] = $decoded;
272
				return $s3Object;
273
			}
274
		}else{
275
			return $this->ks3client->getObject($args);
276
		}
277
	}
278
	public function initMultipartUploadSecurely($args=array()){
279
		$sek = EncryptionUtil::genereateOnceUsedKey();
280
		$encryptedSek = EncryptionUtil::encodeCek($this->encryptionMaterials,$sek);
281
		$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
282
		$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td),MCRYPT_RAND);
283
		
284
		$matdesc = "{}";
285
		if(ENCRYPTPTION_STORAGE_MODE == "ObjectMetadata"){
0 ignored issues
show
introduced by
The condition ENCRYPTPTION_STORAGE_MODE == 'ObjectMetadata' is always false.
Loading history...
286
			$args["UserMeta"]["x-kss-meta-x-kss-key"] = base64_encode($encryptedSek);
287
			$args["UserMeta"]["x-kss-meta-x-kss-iv"] = base64_encode($iv);
288
			$args["UserMeta"]["x-kss-meta-x-kss-matdesc"] = $matdesc;
289
		}
290
291
		$initResult = $this->ks3client->initMultipartUpload($args);
292
293
		EncryptionUtil::initMultipartUploadContext($initResult,$iv,$sek,$encryptedSek,$matdesc);
294
295
		return $initResult;
296
	}
297
	public function uploadPartSecurely($args=array()){
298
		$uploadId = $args["Options"]["uploadId"];
299
		$isLastPart = FALSE;
300
		$blocksize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128,MCRYPT_MODE_CBC);
301
		if(isset($args["LastPart"]))
302
			$isLastPart = $args["LastPart"];
303
		$exists = EncryptionUtil::multipartUploadContextExists($uploadId);
304
		if(!$exists){
305
			throw new Ks3ClientException("no such upload in cache/encryption/");
306
		}
307
		$context = EncryptionUtil::getMultipartUploadContext($uploadId);
308
		if($context["lastPart"]){
309
			throw new Ks3ClientException("this upload with uploadId ".$uploadId," has been upload last part");
0 ignored issues
show
Bug introduced by
' has been upload last part' of type string is incompatible with the type integer expected by parameter $code of Ks3ClientException::__construct(). ( Ignorable by Annotation )

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

309
			throw new Ks3ClientException("this upload with uploadId ".$uploadId,/** @scrutinizer ignore-type */ " has been upload last part");
Loading history...
310
		}
311
		$plainTextLength = EncryptionUtil::plainTextLength($args);
312
		if($plainTextLength <= 0){
313
			throw new Ks3ClientException("get content length failed ,unexpected content length ".$plainTextLength);
314
		}
315
		if(!$isLastPart){
316
			if($plainTextLength % $blocksize != 0)
317
				throw new Ks3ClientException("Invalid part size,part size (".$plainTextLength.") must be multiples of the block size ".$blocksize);
318
		}else{
319
			$args["ObjectMeta"]["Content-Length"] = $plainTextLength + ($blocksize - $plainTextLength%$blocksize);
320
		}
321
		$readCallBack = new AESCBCStreamReadCallBack();
322
		$readCallBack->iv = base64_decode($context["nextIv"]);
0 ignored issues
show
Bug Best Practice introduced by
The property $iv is declared private in AESCBCStreamReadCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
323
		$readCallBack->cek = base64_decode($context["cek"]);
0 ignored issues
show
Bug Best Practice introduced by
The property $cek is declared private in AESCBCStreamReadCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
324
		$readCallBack->contentLength = $plainTextLength;
0 ignored issues
show
Bug Best Practice introduced by
The property $contentLength is declared private in AESCBCStreamReadCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
325
		$readCallBack->mutipartUpload = TRUE;
0 ignored issues
show
Bug Best Practice introduced by
The property $mutipartUpload is declared private in AESCBCStreamReadCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
326
		$readCallBack->isLastPart = $isLastPart;
0 ignored issues
show
Bug Best Practice introduced by
The property $isLastPart is declared private in AESCBCStreamReadCallBack. Since you implement __set, consider adding a @property or @property-write.
Loading history...
327
		$args["readCallBack"] = $readCallBack;
328
329
		$upResult = $this->ks3client->uploadPart($args);
330
		EncryptionUtil::updateMultipartUploadContext($uploadId,$readCallBack->iv,$isLastPart);
331
		return $upResult;
332
	}
333
	public function abortMultipartUploadSecurely($args=array()){
334
		$uploadId = $args["Options"]["uploadId"];
335
		EncryptionUtil::deleteMultipartUploadContext($uploadId);
336
		return $this->ks3client->abortMultipartUpload($args);
337
	}
338
	public function completeMultipartUploadSecurely($args=array()){
339
		$uploadId = $args["Options"]["uploadId"];
340
		$exists = EncryptionUtil::multipartUploadContextExists($uploadId);
341
		if(!$exists){
342
			throw new Ks3ClientException("no such upload in cache/encryption/");
343
		}
344
		$context = EncryptionUtil::getMultipartUploadContext($uploadId);
345
		if(!$context["lastPart"]){
346
			throw new Ks3ClientException("Unable to complete an encrypted multipart upload without being told which part was the last. when upload part you can add item in args like args[\"LastPart\"]=TRUE");
347
		}
348
		$result = $this->ks3client->completeMultipartUpload($args);
349
		if(ENCRYPTPTION_STORAGE_MODE=="InstructionFile"){
0 ignored issues
show
introduced by
The condition ENCRYPTPTION_STORAGE_MODE == 'InstructionFile' is always true.
Loading history...
350
			$req = EncryptionUtil::createInstructionFile($args["Bucket"],$args["Key"],
351
			$context["encryptedCek"],$context["firstIv"],$context["matdesc"]);
352
			$this->ks3client->putObjectByContent($req);
353
		}
354
		EncryptionUtil::deleteMultipartUploadContext($uploadId);
355
		return $result;
356
	}
357
}
358
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...