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