1 /*
2 * Copyright 2009-2017 Alibaba Cloud All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <alibabacloud/oss/Const.h>
18 #include "CryptoModule.h"
19 #include "CryptoStreamBuf.h"
20 #include "utils/Utils.h"
21
22 using namespace AlibabaCloud::OSS;
23
24 #define CHECK_FUNC_RET(outcome, func, ret) \
25 do { \
26 if (ret != 0) { \
27 std::string errMsg(#func" fail, return value:"); errMsg.append(std::to_string(ret));\
28 return outcome(OssError("EncryptionClientError", errMsg)); \
29 } \
30 } while (0)
31
32
33
getUserAgent(const std::string & prefix)34 static std::string getUserAgent(const std::string& prefix)
35 {
36 std::stringstream ss;
37 ss << prefix << "/OssEncryptionClient";
38 return ss.str();
39 }
40
CreateCryptoModule(const std::shared_ptr<EncryptionMaterials> & encryptionMaterials,const CryptoConfiguration & cryptoConfig)41 std::shared_ptr<CryptoModule> CryptoModule::CreateCryptoModule(const std::shared_ptr<EncryptionMaterials>& encryptionMaterials,
42 const CryptoConfiguration& cryptoConfig)
43 {
44 switch(cryptoConfig.cryptoMode) {
45 case CryptoMode::ENCRYPTION_AESCTR:
46 default:
47 return std::make_shared<CryptoModuleAESCTR>(encryptionMaterials, cryptoConfig);
48 };
49 }
50
CryptoModule(const std::shared_ptr<EncryptionMaterials> & encryptionMaterials,const CryptoConfiguration & cryptoConfig)51 CryptoModule::CryptoModule(const std::shared_ptr<EncryptionMaterials>& encryptionMaterials, const CryptoConfiguration& cryptoConfig):
52 encryptionMaterials_(encryptionMaterials),
53 cryptoConfig_(cryptoConfig)
54 {
55 }
56
~CryptoModule()57 CryptoModule::~CryptoModule()
58 {
59 }
60
PutObjectSecurely(const std::shared_ptr<OssClientImpl> & client,const PutObjectRequest & request)61 PutObjectOutcome CryptoModule::PutObjectSecurely(const std::shared_ptr<OssClientImpl>& client, const PutObjectRequest& request)
62 {
63 PutObjectRequest putRequest(request);
64 ContentCryptoMaterial material;
65
66 initEncryptionCipher(material);
67 generateKeyIV(material);
68 auto ret = encryptionMaterials_->EncryptCEK(material);
69 CHECK_FUNC_RET(PutObjectOutcome, EncryptCEK, ret);
70 addMetaData(material, putRequest.MetaData());
71 addUserAgent(putRequest.MetaData(), client->configuration().userAgent);
72 CryptoStreamBuf cryptoStream(*putRequest.Body(), cipher_, material.ContentKey(), material.ContentIV());
73
74 return client->PutObject(putRequest);
75 }
76
GetObjectSecurely(const std::shared_ptr<OssClientImpl> & client,const GetObjectRequest & request,const ObjectMetaData & meta)77 GetObjectOutcome CryptoModule::GetObjectSecurely(const std::shared_ptr<OssClientImpl>& client, const GetObjectRequest& request, const ObjectMetaData& meta)
78 {
79 GetObjectRequest getRequest(request);
80 ContentCryptoMaterial material;
81
82 readMetaData(material, meta);
83 initDecryptionCipher(material);
84
85 if (material.CipherName() != cipher_->Name()) {
86 std::stringstream ss;
87 ss << "Cipher name is not support, " <<
88 "expect " << cipher_->Name() << ", "
89 "got " << material.CipherName() << ".";
90 return GetObjectOutcome(OssError("EncryptionClientError", ss.str()));
91 }
92
93 auto ret = encryptionMaterials_->DecryptCEK(material);
94 CHECK_FUNC_RET(GetObjectOutcome, DecryptCEK, ret);
95
96 //range
97 auto iv = material.ContentIV();
98 auto range = request.Range();
99 int64_t skipCnt = 0;
100 int64_t blkSize = static_cast<int64_t>(cipher_->BlockSize());
101 if (range.first > 0) {
102 auto blockOfNum = range.first / blkSize;
103 auto newBegin = blockOfNum * blkSize;
104 skipCnt = range.first % blkSize;
105 getRequest.setRange(newBegin, range.second);
106 iv = cipher_->IncCTRCounter(iv, static_cast<uint64_t>(blockOfNum));
107 }
108
109 //ua
110 getRequest.setUserAgent(getUserAgent(client->configuration().userAgent));
111
112 std::shared_ptr<CryptoStreamBuf> cryptoStream = nullptr;
113 std::shared_ptr<std::iostream> userContent = nullptr;
114 getRequest.setResponseStreamFactory([&]() {
115 auto content = request.ResponseStreamFactory()();
116 cryptoStream = std::make_shared<CryptoStreamBuf>(*content, cipher_, material.ContentKey(), iv, static_cast<int>(skipCnt));
117 userContent = content;
118 return content;
119 }
120 );
121
122 auto outcome = client->GetObject(getRequest);
123 if (skipCnt > 0 && outcome.isSuccess()) {
124 ObjectMetaData ometa = outcome.result().Metadata();
125 auto len = ometa.ContentLength();
126 ometa.setContentLength(len - skipCnt);
127 outcome.result().setMetaData(ometa);
128 }
129
130 cryptoStream = nullptr;
131 userContent = nullptr;
132 return outcome;
133 }
134
InitiateMultipartUploadSecurely(const std::shared_ptr<OssClientImpl> & client,const InitiateMultipartUploadRequest & request,MultipartUploadCryptoContext & ctx)135 InitiateMultipartUploadOutcome CryptoModule::InitiateMultipartUploadSecurely(const std::shared_ptr<OssClientImpl>& client,
136 const InitiateMultipartUploadRequest& request, MultipartUploadCryptoContext& ctx)
137 {
138 std::string errMsg1;
139 if (!checkUserParameter(ctx, errMsg1)) {
140 return InitiateMultipartUploadOutcome(OssError("EncryptionClientError", errMsg1));
141 }
142
143 InitiateMultipartUploadRequest initRequest(request);
144 ContentCryptoMaterial material;
145
146 initEncryptionCipher(material);
147 generateKeyIV(material);
148 auto ret = encryptionMaterials_->EncryptCEK(material);
149 CHECK_FUNC_RET(InitiateMultipartUploadOutcome, EncryptCEK, ret);
150 addMetaData(material, initRequest.MetaData());
151 addMetaDataMultipart(ctx, initRequest.MetaData());
152 addUserAgent(initRequest.MetaData(), client->configuration().userAgent);
153
154 auto outcome = client->InitiateMultipartUpload(initRequest);
155 if (outcome.isSuccess()) {
156 ctx.setContentMaterial(material);
157 ctx.setUploadId(outcome.result().UploadId());
158 }
159 return outcome;
160 }
161
UploadPartSecurely(const std::shared_ptr<OssClientImpl> & client,const UploadPartRequest & request,const MultipartUploadCryptoContext & ctx)162 PutObjectOutcome CryptoModule::UploadPartSecurely(const std::shared_ptr<OssClientImpl>& client, const UploadPartRequest& request,
163 const MultipartUploadCryptoContext& ctx)
164 {
165 std::string errMsg;
166 if (!checkUserParameter(ctx, errMsg)) {
167 return PutObjectOutcome(OssError("EncryptionClientError", errMsg));
168 }
169
170 //check material
171 if (ctx.ContentMaterial().ContentKey().empty() ||
172 ctx.ContentMaterial().ContentIV().empty()) {
173 return PutObjectOutcome(OssError("EncryptionClientError", "ContentKey/IV in CryptoContext is empty."));
174 }
175
176 UploadPartRequest uRequest(request);
177 ContentCryptoMaterial material;
178 initEncryptionCipher(material);
179 #if 0
180 ObjectMetaData meta;
181 addMetaData(ctx.ContentMaterial(), meta);
182 addMetaDataMultipart(ctx, meta);
183 uRequest.setMetaData(meta);
184 #endif
185 //ua
186 uRequest.setUserAgent(getUserAgent(client->configuration().userAgent));
187
188 auto fileOffset = ctx.PartSize() * static_cast<int64_t>(uRequest.PartNumber() - 1);
189 auto blockNum = fileOffset / static_cast<int64_t>(cipher_->BlockSize());
190 auto iv = cipher_->IncCTRCounter(ctx.ContentMaterial().ContentIV(), static_cast<uint64_t>(blockNum));
191 CryptoStreamBuf cryptoStream(*uRequest.Body(), cipher_, ctx.ContentMaterial().ContentKey(), iv);
192 return client->UploadPart(uRequest);
193 }
194
addMetaData(const ContentCryptoMaterial & content,ObjectMetaData & meta)195 void CryptoModule::addMetaData(const ContentCryptoMaterial& content, ObjectMetaData& meta)
196 {
197 //x-oss-meta-client-side-encryption-key
198 meta.addUserHeader("client-side-encryption-key",
199 Base64Encode((const char*)content.EncryptedContentKey().data(), content.EncryptedContentKey().size()));
200
201 //x-oss-meta-client-side-encryption-start //iv
202 meta.addUserHeader("client-side-encryption-start",
203 Base64Encode((const char*)content.EncryptedContentIV().data(), content.EncryptedContentIV().size()));
204
205 //x-oss-meta-client-side-encryption-cek-alg
206 meta.addUserHeader("client-side-encryption-cek-alg", content.CipherName());
207
208 //x-oss-meta-client-side-encryption-wrap-alg
209 meta.addUserHeader("client-side-encryption-wrap-alg", content.KeyWrapAlgorithm());
210
211 //x-oss-meta-client-side-encryption-matdesc,json format
212 if (!content.Description().empty()) {
213 std::string jsonStr = MapToJsonString(content.Description());
214 if (!jsonStr.empty()) {
215 meta.addUserHeader("client-side-encryption-matdesc", jsonStr);
216 }
217 }
218
219 //x-oss-meta-client-side-encryption-magic-number-hmac
220 if (!content.MagicNumber().empty()) {
221 meta.addUserHeader("client-side-encryption-magic-number-hmac", content.MagicNumber());
222 }
223
224 //x-oss-meta-client-side-encryption-unencrypted-content-md5
225 if (meta.hasHeader(Http::CONTENT_MD5)) {
226 meta.addUserHeader("client-side-encryption-unencrypted-content-md5", meta.ContentMd5());
227 meta.removeHeader(Http::CONTENT_MD5);
228 }
229
230 //x-oss-meta-client-side-encryption-unencrypted-content-length
231 //ToDo
232 }
233
addMetaDataMultipart(const MultipartUploadCryptoContext & ctx,ObjectMetaData & meta)234 void CryptoModule::addMetaDataMultipart(const MultipartUploadCryptoContext& ctx, ObjectMetaData& meta)
235 {
236 if (ctx.DataSize() > 0) {
237 meta.addUserHeader("client-side-encryption-data-size", std::to_string(ctx.DataSize()));
238 }
239 meta.addUserHeader("client-side-encryption-part-size", std::to_string(ctx.PartSize()));
240 }
241
readMetaData(ContentCryptoMaterial & content,const ObjectMetaData & meta)242 void CryptoModule::readMetaData(ContentCryptoMaterial& content, const ObjectMetaData& meta)
243 {
244 //x-oss-meta-client-side-encryption-key
245 if (meta.hasUserHeader("client-side-encryption-key")) {
246 content.setEncryptedContentKey(Base64Decode(meta.UserMetaData().at("client-side-encryption-key")));
247 }
248
249 //x-oss-meta-client-side-encryption-start //iv
250 if (meta.hasUserHeader("client-side-encryption-start")) {
251 content.setEncryptedContentIV(Base64Decode(meta.UserMetaData().at("client-side-encryption-start")));
252 }
253
254 //x-oss-meta-client-side-encryption-cek-alg
255 if (meta.hasUserHeader("client-side-encryption-cek-alg")) {
256 content.setCipherName(meta.UserMetaData().at("client-side-encryption-cek-alg"));
257 }
258
259 //x-oss-meta-client-side-encryption-wrap-alg
260 if (meta.hasUserHeader("client-side-encryption-wrap-alg")) {
261 content.setKeyWrapAlgorithm(meta.UserMetaData().at("client-side-encryption-wrap-alg"));
262 }
263
264 //x-oss-meta-client-side-encryption-matdesc
265 if (meta.hasUserHeader("client-side-encryption-matdesc")) {
266 content.setDescription(JsonStringToMap(meta.UserMetaData().at("client-side-encryption-matdesc")));
267 }
268 }
269
addUserAgent(ObjectMetaData & meta,const std::string & prefix)270 void CryptoModule::addUserAgent(ObjectMetaData& meta, const std::string& prefix)
271 {
272 if (!meta.hasHeader(Http::USER_AGENT)) {
273 meta.addHeader(Http::USER_AGENT, getUserAgent(prefix));
274 }
275 }
276
277 //////////////////////////////////////////////////////////////////////////////////////////////////////////
278 //CryptoModuleAES265_CTR
279
CryptoModuleAESCTR(const std::shared_ptr<EncryptionMaterials> & encryptionMaterials,const CryptoConfiguration & cryptoConfig)280 CryptoModuleAESCTR::CryptoModuleAESCTR(const std::shared_ptr<EncryptionMaterials>& encryptionMaterials,
281 const CryptoConfiguration& cryptoConfig):
282 CryptoModule(encryptionMaterials, cryptoConfig)
283 {
284 }
285
~CryptoModuleAESCTR()286 CryptoModuleAESCTR::~CryptoModuleAESCTR()
287 {
288 }
289
initEncryptionCipher(ContentCryptoMaterial & content)290 void CryptoModuleAESCTR::initEncryptionCipher(ContentCryptoMaterial& content)
291 {
292 cipher_ = SymmetricCipher::CreateAES256_CTRImpl();
293 content.setCipherName(cipher_->Name());
294 }
295
generateKeyIV(ContentCryptoMaterial & content)296 void CryptoModuleAESCTR::generateKeyIV(ContentCryptoMaterial& content)
297 {
298 content.setContentKey(SymmetricCipher::GenerateKey(32));
299 auto iv = SymmetricCipher::GenerateIV(16);
300 iv[8] = iv[9] = iv[10] = iv[11] = 0;
301 content.setContentIV(iv);
302 }
303
initDecryptionCipher(ContentCryptoMaterial & content)304 void CryptoModuleAESCTR::initDecryptionCipher(ContentCryptoMaterial& content)
305 {
306 UNUSED_PARAM(content);
307 cipher_ = SymmetricCipher::CreateAES256_CTRImpl();
308 }
309
checkUserParameter(const MultipartUploadCryptoContext & ctx,std::string & errMsg)310 bool CryptoModuleAESCTR::checkUserParameter(const MultipartUploadCryptoContext& ctx, std::string& errMsg)
311 {
312 if (ctx.PartSize() < PartSizeLowerLimit) {
313 errMsg = "PartSize should not be less than 102400.";
314 return false;
315 }
316
317 if (ctx.PartSize() % 16) {
318 errMsg = "PartSize is not 16 bytes alignment.";
319 return false;
320 }
321 return true;
322 }
323