1 /*
2  * Copyright (c) 2019, The Android Open Source Project
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 <iterator>
18 #include <tuple>
19 
20 #include <android-base/properties.h>
21 #include <cppbor.h>
22 #include <json/json.h>
23 #include <openssl/base64.h>
24 #include <openssl/rand.h>
25 #include <remote_prov/remote_prov_utils.h>
26 
27 namespace aidl::android::hardware::security::keymint::remote_prov {
28 
29 bytevec kTestMacKey(32 /* count */, 0 /* byte value */);
30 
randomBytes(size_t numBytes)31 bytevec randomBytes(size_t numBytes) {
32     bytevec retval(numBytes);
33     RAND_bytes(retval.data(), numBytes);
34     return retval;
35 }
36 
generateEekChain(size_t length,const bytevec & eekId)37 ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId) {
38     if (length < 2) {
39         return "EEK chain must contain at least 2 certs.";
40     }
41 
42     auto eekChain = cppbor::Array();
43 
44     bytevec prev_priv_key;
45     for (size_t i = 0; i < length - 1; ++i) {
46         bytevec pub_key(ED25519_PUBLIC_KEY_LEN);
47         bytevec priv_key(ED25519_PRIVATE_KEY_LEN);
48 
49         ED25519_keypair(pub_key.data(), priv_key.data());
50 
51         // The first signing key is self-signed.
52         if (prev_priv_key.empty()) prev_priv_key = priv_key;
53 
54         auto coseSign1 = constructCoseSign1(prev_priv_key,
55                                             cppbor::Map() /* payload CoseKey */
56                                                     .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
57                                                     .add(CoseKey::ALGORITHM, EDDSA)
58                                                     .add(CoseKey::CURVE, ED25519)
59                                                     .add(CoseKey::PUBKEY_X, pub_key)
60                                                     .canonicalize()
61                                                     .encode(),
62                                             {} /* AAD */);
63         if (!coseSign1) return coseSign1.moveMessage();
64         eekChain.add(coseSign1.moveValue());
65 
66         prev_priv_key = priv_key;
67     }
68 
69     bytevec pub_key(X25519_PUBLIC_VALUE_LEN);
70     bytevec priv_key(X25519_PRIVATE_KEY_LEN);
71     X25519_keypair(pub_key.data(), priv_key.data());
72 
73     auto coseSign1 = constructCoseSign1(prev_priv_key,
74                                         cppbor::Map() /* payload CoseKey */
75                                                 .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
76                                                 .add(CoseKey::KEY_ID, eekId)
77                                                 .add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256)
78                                                 .add(CoseKey::CURVE, cppcose::X25519)
79                                                 .add(CoseKey::PUBKEY_X, pub_key)
80                                                 .canonicalize()
81                                                 .encode(),
82                                         {} /* AAD */);
83     if (!coseSign1) return coseSign1.moveMessage();
84     eekChain.add(coseSign1.moveValue());
85 
86     return EekChain{eekChain.encode(), pub_key, priv_key};
87 }
88 
getProdEekChain()89 bytevec getProdEekChain() {
90     bytevec prodEek;
91     prodEek.reserve(1 + sizeof(kCoseEncodedRootCert) + sizeof(kCoseEncodedGeekCert));
92 
93     // In CBOR encoding, 0x82 indicates an array of two items
94     prodEek.push_back(0x82);
95     prodEek.insert(prodEek.end(), std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert));
96     prodEek.insert(prodEek.end(), std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert));
97 
98     return prodEek;
99 }
100 
verifyAndParseCoseSign1Cwt(const cppbor::Array * coseSign1,const bytevec & signingCoseKey,const bytevec & aad)101 ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1,
102                                              const bytevec& signingCoseKey, const bytevec& aad) {
103     if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) {
104         return "Invalid COSE_Sign1";
105     }
106 
107     const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr();
108     const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap();
109     const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr();
110     const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr();
111 
112     if (!protectedParams || !unprotectedParams || !payload || !signature) {
113         return "Invalid COSE_Sign1";
114     }
115 
116     auto [parsedProtParams, _, errMsg] = cppbor::parse(protectedParams);
117     if (!parsedProtParams) {
118         return errMsg + " when parsing protected params.";
119     }
120     if (!parsedProtParams->asMap()) {
121         return "Protected params must be a map";
122     }
123 
124     auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM);
125     if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) {
126         return "Unsupported signature algorithm";
127     }
128 
129     // TODO(jbires): Handle CWTs as the CoseSign1 payload in a less hacky way. Since the CWT payload
130     //               is extremely remote provisioning specific, probably just make a separate
131     //               function there.
132     auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(payload);
133     if (!parsedPayload) return payloadErrMsg + " when parsing key";
134     if (!parsedPayload->asMap()) return "CWT must be a map";
135     auto serializedKey = parsedPayload->asMap()->get(-4670552)->clone();
136     if (!serializedKey || !serializedKey->asBstr()) return "Could not find key entry";
137 
138     bool selfSigned = signingCoseKey.empty();
139     auto key =
140             CoseKey::parseEd25519(selfSigned ? serializedKey->asBstr()->value() : signingCoseKey);
141     if (!key) return "Bad signing key: " + key.moveMessage();
142 
143     bytevec signatureInput =
144             cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode();
145 
146     if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(),
147                         key->getBstrValue(CoseKey::PUBKEY_X)->data())) {
148         return "Signature verification failed";
149     }
150 
151     return serializedKey->asBstr()->value();
152 }
153 
validateBcc(const cppbor::Array * bcc)154 ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc) {
155     if (!bcc || bcc->size() == 0) return "Invalid BCC";
156 
157     std::vector<BccEntryData> result;
158 
159     bytevec prevKey;
160     // TODO(jbires): Actually process the pubKey at the start of the new bcc entry
161     for (size_t i = 1; i < bcc->size(); ++i) {
162         const cppbor::Array* entry = bcc->get(i)->asArray();
163         if (!entry || entry->size() != kCoseSign1EntryCount) {
164             return "Invalid BCC entry " + std::to_string(i) + ": " + prettyPrint(entry);
165         }
166         auto payload = verifyAndParseCoseSign1Cwt(entry, std::move(prevKey), bytevec{} /* AAD */);
167         if (!payload) {
168             return "Failed to verify entry " + std::to_string(i) + ": " + payload.moveMessage();
169         }
170 
171         auto& certProtParms = entry->get(kCoseSign1ProtectedParams);
172         if (!certProtParms || !certProtParms->asBstr()) return "Invalid prot params";
173         auto [parsedProtParms, _, errMsg] = cppbor::parse(certProtParms->asBstr()->value());
174         if (!parsedProtParms || !parsedProtParms->asMap()) return "Invalid prot params";
175 
176         result.push_back(BccEntryData{*payload});
177 
178         // This entry's public key is the signing key for the next entry.
179         prevKey = payload.moveValue();
180     }
181 
182     return result;
183 }
184 
jsonEncodeCsrWithBuild(const cppbor::Array & csr)185 JsonOutput jsonEncodeCsrWithBuild(const cppbor::Array& csr) {
186     const std::string kFingerprintProp = "ro.build.fingerprint";
187 
188     if (!::android::base::WaitForPropertyCreation(kFingerprintProp)) {
189         return JsonOutput::Error("Unable to read build fingerprint");
190     }
191 
192     bytevec csrCbor = csr.encode();
193     size_t base64Length;
194     int rc = EVP_EncodedLength(&base64Length, csrCbor.size());
195     if (!rc) {
196         return JsonOutput::Error("Error getting base64 length. Size overflow?");
197     }
198 
199     std::vector<char> base64(base64Length);
200     rc = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(base64.data()), csrCbor.data(), csrCbor.size());
201     ++rc;  // Account for NUL, which BoringSSL does not for some reason.
202     if (rc != base64Length) {
203         return JsonOutput::Error("Error writing base64. Expected " + std::to_string(base64Length) +
204                                  " bytes to be written, but " + std::to_string(rc) +
205                                  " bytes were actually written.");
206     }
207 
208     Json::Value json(Json::objectValue);
209     json["build_fingerprint"] = ::android::base::GetProperty(kFingerprintProp, /*default=*/"");
210     json["csr"] = base64.data();  // Boring writes a NUL-terminated c-string
211 
212     Json::StreamWriterBuilder factory;
213     factory["indentation"] = "";  // disable pretty formatting
214     return JsonOutput::Ok(Json::writeString(factory, json));
215 }
216 
217 }  // namespace aidl::android::hardware::security::keymint::remote_prov
218