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