1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "securegcm/d2d_crypto_ops.h"
16 
17 #include <sstream>
18 
19 #include "securemessage/secure_message_builder.h"
20 #include "securemessage/secure_message_parser.h"
21 #include "securemessage/util.h"
22 
23 namespace securegcm {
24 
25 using securemessage::CryptoOps;
26 using securemessage::HeaderAndBody;
27 using securemessage::SecureMessage;
28 using securemessage::SecureMessageBuilder;
29 using securemessage::SecureMessageParser;
30 using securemessage::Util;
31 
32 namespace {
33 
34 // The current protocol version.
35 const int kSecureGcmProtocolVersion = 1;
36 
37 // The number of bytes in an expected AES256 key.
38 const int kAes256KeyLength = 32;
39 }
40 
41 // static.
42 const uint8_t D2DCryptoOps::kSalt[] = {
43     0x82, 0xAA, 0x55, 0xA0, 0xD3, 0x97, 0xF8, 0x83, 0x46, 0xCA, 0x1C,
44     0xEE, 0x8D, 0x39, 0x09, 0xB9, 0x5F, 0x13, 0xFA, 0x7D, 0xEB, 0x1D,
45     0x4A, 0xB3, 0x83, 0x76, 0xB8, 0x25, 0x6D, 0xA8, 0x55, 0x10};
46 
47 // static.
48 const size_t D2DCryptoOps::kSaltLength = sizeof(D2DCryptoOps::kSalt);
49 
Payload(Type type,const string & message)50 D2DCryptoOps::Payload::Payload(Type type, const string& message)
51     : type_(type), message_(message) {}
52 
D2DCryptoOps()53 D2DCryptoOps::D2DCryptoOps() {}
54 
55 // static.
SigncryptPayload(const Payload & payload,const CryptoOps::SecretKey & secret_key)56 std::unique_ptr<string> D2DCryptoOps::SigncryptPayload(
57     const Payload& payload, const CryptoOps::SecretKey& secret_key) {
58   GcmMetadata gcm_metadata;
59   gcm_metadata.set_type(payload.type());
60   gcm_metadata.set_version(kSecureGcmProtocolVersion);
61 
62   SecureMessageBuilder builder;
63   builder.SetPublicMetadata(gcm_metadata.SerializeAsString());
64 
65   std::unique_ptr<SecureMessage> secure_message =
66       builder.BuildSignCryptedMessage(secret_key, CryptoOps::HMAC_SHA256,
67                                       secret_key, CryptoOps::AES_256_CBC,
68                                       payload.message());
69   if (!secure_message) {
70     Util::LogError("Unable to encrypt payload.");
71     return nullptr;
72   }
73 
74   return std::unique_ptr<string>(
75       new string(secure_message->SerializeAsString()));
76 }
77 
78 // static.
VerifyDecryptPayload(const string & signcrypted_message,const CryptoOps::SecretKey & secret_key)79 std::unique_ptr<D2DCryptoOps::Payload> D2DCryptoOps::VerifyDecryptPayload(
80     const string& signcrypted_message, const CryptoOps::SecretKey& secret_key) {
81   SecureMessage secure_message;
82   if (!secure_message.ParseFromString(signcrypted_message)) {
83     Util::LogError("VerifyDecryptPayload: error parsing SecureMessage.");
84     return nullptr;
85   }
86 
87   std::unique_ptr<HeaderAndBody> header_and_body =
88       SecureMessageParser::ParseSignCryptedMessage(
89           secure_message, secret_key, CryptoOps::HMAC_SHA256, secret_key,
90           CryptoOps::AES_256_CBC, string() /* associated_data */);
91   if (!header_and_body) {
92     Util::LogError("VerifyDecryptPayload: error verifying SecureMessage.");
93     return nullptr;
94   }
95 
96   if (!header_and_body->header().has_public_metadata()) {
97     Util::LogError("VerifyDecryptPayload: no public metadata in header.");
98     return nullptr;
99   }
100 
101   GcmMetadata metadata;
102   if (!metadata.ParseFromString(header_and_body->header().public_metadata())) {
103     Util::LogError("VerifyDecryptPayload: Failed to parse GcmMetadata.");
104     return nullptr;
105   }
106 
107   if (metadata.version() != kSecureGcmProtocolVersion) {
108     std::ostringstream stream;
109     stream << "VerifyDecryptPayload: Unsupported protocol version "
110            << metadata.version();
111     Util::LogError(stream.str());
112     return nullptr;
113   }
114 
115   return std::unique_ptr<Payload>(
116       new Payload(metadata.type(), header_and_body->body()));
117 }
118 
119 // static.
DeriveNewKeyForPurpose(const securemessage::CryptoOps::SecretKey & master_key,const string & purpose)120 std::unique_ptr<CryptoOps::SecretKey> D2DCryptoOps::DeriveNewKeyForPurpose(
121     const securemessage::CryptoOps::SecretKey& master_key,
122     const string& purpose) {
123   if (master_key.data().size() != kAes256KeyLength) {
124     Util::LogError("DeriveNewKeyForPurpose: Invalid master_key length.");
125     return nullptr;
126   }
127 
128   if (purpose.empty()) {
129     Util::LogError("DeriveNewKeyForPurpose: purpose is empty.");
130     return nullptr;
131   }
132 
133   std::unique_ptr<string> raw_derived_key = CryptoOps::Hkdf(
134       master_key.data().String(),
135       string(reinterpret_cast<const char *>(kSalt), kSaltLength),
136       purpose);
137   if (!raw_derived_key) {
138     Util::LogError("DeriveNewKeyForPurpose: hkdf failed.");
139     return nullptr;
140   }
141 
142   if (raw_derived_key->size() != kAes256KeyLength) {
143     Util::LogError("DeriveNewKeyForPurpose: Unexpected size of derived key.");
144     return nullptr;
145   }
146 
147   return std::unique_ptr<CryptoOps::SecretKey>(
148       new CryptoOps::SecretKey(*raw_derived_key, CryptoOps::AES_256_KEY));
149 }
150 
151 }  // namespace securegcm
152