• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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 "keymaster/cppcose/cppcose.h"
18 #include <keymaster/logger.h>
19 #include <keymaster/remote_provisioning_utils.h>
20 #include <string_view>
21 
22 namespace keymaster {
23 
24 using cppcose::ALGORITHM;
25 using cppcose::COSE_KEY;
26 using cppcose::CoseKey;
27 using cppcose::CoseKeyCurve;
28 using cppcose::EC2;
29 using cppcose::ECDH_ES_HKDF_256;
30 using cppcose::ES256;
31 using cppcose::generateCoseMac0Mac;
32 using cppcose::HMAC_256;
33 using cppcose::kCoseMac0EntryCount;
34 using cppcose::kCoseMac0Payload;
35 using cppcose::kCoseMac0ProtectedParams;
36 using cppcose::kCoseMac0Tag;
37 using cppcose::kCoseMac0UnprotectedParams;
38 using cppcose::KEY_ID;
39 using cppcose::OCTET_KEY_PAIR;
40 using cppcose::P256;
41 using cppcose::verifyAndParseCoseSign1;
42 
43 using byte_view = std::basic_string_view<uint8_t>;
44 
45 struct KeyInfo {
46     CoseKeyCurve curve;
47     byte_view pubkey;
48     // Note: There's no need to include algorithm here, since it is assumed
49     //       that all root keys are EDDSA.
50 
operator ==keymaster::KeyInfo51     bool operator==(const KeyInfo& other) const {
52         return curve == other.curve && pubkey == other.pubkey;
53     }
54 };
55 
56 // The production root signing key for Google Endpoint Encryption Key cert chains.
57 inline constexpr uint8_t kGeekRoot[] = {
58     0x99, 0xB9, 0xEE, 0xDD, 0x5E, 0xE4, 0x52, 0xF6, 0x85, 0xC6, 0x4C, 0x62, 0xDC, 0x3E, 0x61, 0xAB,
59     0x57, 0x48, 0x7D, 0x75, 0x37, 0x29, 0xAD, 0x76, 0x80, 0x32, 0xD2, 0xB3, 0xCB, 0x63, 0x58, 0xD9};
60 
61 // Hard-coded set of acceptable public COSE_Keys that can act as roots of EEK chains.
62 inline constexpr KeyInfo kAuthorizedEekRoots[] = {
63     {CoseKeyCurve::ED25519, byte_view(kGeekRoot, sizeof(kGeekRoot))},
64 };
65 
66 StatusOr<std::pair<std::vector<uint8_t> /* EEK pub */, std::vector<uint8_t> /* EEK ID */>>
validateAndExtractEekPubAndId(bool testMode,const KeymasterBlob & endpointEncryptionCertChain)67 validateAndExtractEekPubAndId(bool testMode, const KeymasterBlob& endpointEncryptionCertChain) {
68     auto [item, newPos, errMsg] =
69         cppbor::parse(endpointEncryptionCertChain.begin(), endpointEncryptionCertChain.end());
70 
71     if (!item || !item->asArray()) {
72         LOG_E("Error parsing EEK chain: %s", errMsg.c_str());
73         return kStatusFailed;
74     }
75 
76     const cppbor::Array* certArr = item->asArray();
77     std::vector<uint8_t> lastPubKey;
78     for (size_t i = 0; i < certArr->size(); ++i) {
79         auto cosePubKey =
80             verifyAndParseCoseSign1(certArr->get(i)->asArray(), lastPubKey, {} /* AAD */);
81         if (!cosePubKey) {
82             LOG_E("Failed to validate EEK chain: %s", cosePubKey.moveMessage().c_str());
83             return kStatusInvalidEek;
84         }
85         lastPubKey = *std::move(cosePubKey);
86 
87         // In prod mode the first pubkey should match a well-known Google public key.
88         if (!testMode && i == 0) {
89             auto parsedPubKey = CoseKey::parse(lastPubKey);
90             if (!parsedPubKey) {
91                 LOG_E("%s", parsedPubKey.moveMessage().c_str());
92                 return kStatusFailed;
93             }
94 
95             auto curve = parsedPubKey->getIntValue(CoseKey::CURVE);
96             if (!curve) {
97                 LOG_E("Key is missing required label 'CURVE'", 0);
98                 return kStatusInvalidEek;
99             }
100 
101             auto rawPubKey = parsedPubKey->getBstrValue(CoseKey::PUBKEY_X);
102             if (!rawPubKey) {
103                 LOG_E("Key is missing required label 'PUBKEY_X'", 0);
104                 return kStatusInvalidEek;
105             }
106 
107             KeyInfo matcher = {static_cast<CoseKeyCurve>(*curve),
108                                byte_view(rawPubKey->data(), rawPubKey->size())};
109             if (std::find(std::begin(kAuthorizedEekRoots), std::end(kAuthorizedEekRoots),
110                           matcher) == std::end(kAuthorizedEekRoots)) {
111                 LOG_E("Unrecognized root of EEK chain", 0);
112                 return kStatusInvalidEek;
113             }
114         }
115     }
116 
117     auto eek = CoseKey::parseX25519(lastPubKey, true /* requireKid */);
118     if (!eek) {
119         LOG_E("Failed to get EEK: %s", eek.moveMessage().c_str());
120         return kStatusInvalidEek;
121     }
122 
123     return std::make_pair(eek->getBstrValue(CoseKey::PUBKEY_X).value(),
124                           eek->getBstrValue(CoseKey::KEY_ID).value());
125 }
126 
127 StatusOr<std::vector<uint8_t> /* pubkeys */>
validateAndExtractPubkeys(bool testMode,uint32_t numKeys,KeymasterBlob * keysToSign,cppcose::HmacSha256Function macFunction)128 validateAndExtractPubkeys(bool testMode, uint32_t numKeys, KeymasterBlob* keysToSign,
129                           cppcose::HmacSha256Function macFunction) {
130     auto pubKeysToMac = cppbor::Array();
131     for (size_t i = 0; i < numKeys; i++) {
132         auto [macedKeyItem, _, coseMacErrMsg] =
133             cppbor::parse(keysToSign[i].begin(), keysToSign[i].end());
134         if (!macedKeyItem || !macedKeyItem->asArray() ||
135             macedKeyItem->asArray()->size() != kCoseMac0EntryCount) {
136             LOG_E("Invalid COSE_Mac0 structure", 0);
137             return kStatusFailed;
138         }
139 
140         auto protectedParms = macedKeyItem->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
141         auto unprotectedParms = macedKeyItem->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
142         auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr();
143         auto tag = macedKeyItem->asArray()->get(kCoseMac0Tag)->asBstr();
144         if (!protectedParms || !unprotectedParms || !payload || !tag) {
145             LOG_E("Invalid COSE_Mac0 contents", 0);
146             return kStatusFailed;
147         }
148 
149         auto [protectedMap, __, errMsg] = cppbor::parse(protectedParms);
150         if (!protectedMap || !protectedMap->asMap()) {
151             LOG_E("Invalid Mac0 protected: %s", errMsg.c_str());
152             return kStatusFailed;
153         }
154         auto& algo = protectedMap->asMap()->get(ALGORITHM);
155         if (!algo || !algo->asInt() || algo->asInt()->value() != HMAC_256) {
156             LOG_E("Unsupported Mac0 algorithm", 0);
157             return kStatusFailed;
158         }
159 
160         auto pubKey = CoseKey::parse(payload->value(), EC2, ES256, P256);
161         if (!pubKey) {
162             LOG_E("%s", pubKey.moveMessage().c_str());
163             return kStatusFailed;
164         }
165 
166         bool testKey = static_cast<bool>(pubKey->getMap().get(CoseKey::TEST_KEY));
167         if (testMode && !testKey) {
168             LOG_E("Production key in test request", 0);
169             return kStatusProductionKeyInTestRequest;
170         } else if (!testMode && testKey) {
171             LOG_E("Test key in production request", 0);
172             return kStatusTestKeyInProductionRequest;
173         }
174 
175         auto macTag = generateCoseMac0Mac(macFunction, {} /* external_aad */, payload->value());
176         if (!macTag) {
177             LOG_E("%s", macTag.moveMessage().c_str());
178             return kStatusInvalidMac;
179         }
180         if (macTag->size() != tag->value().size() ||
181             CRYPTO_memcmp(macTag->data(), tag->value().data(), macTag->size()) != 0) {
182             LOG_E("MAC tag mismatch", 0);
183             return kStatusInvalidMac;
184         }
185 
186         pubKeysToMac.add(pubKey->moveMap());
187     }
188 
189     return pubKeysToMac.encode();
190 }
191 
buildCertReqRecipients(const std::vector<uint8_t> & pubkey,const std::vector<uint8_t> & kid)192 cppbor::Array buildCertReqRecipients(const std::vector<uint8_t>& pubkey,
193                                      const std::vector<uint8_t>& kid) {
194     return cppbor::Array()           // Array of recipients
195         .add(cppbor::Array()         // Recipient
196                  .add(cppbor::Map()  // Protected
197                           .add(ALGORITHM, ECDH_ES_HKDF_256)
198                           .canonicalize()
199                           .encode())
200                  .add(cppbor::Map()  // Unprotected
201                           .add(COSE_KEY, cppbor::Map()
202                                              .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
203                                              .add(CoseKey::CURVE, cppcose::X25519)
204                                              .add(CoseKey::PUBKEY_X, pubkey)
205                                              .canonicalize())
206                           .add(KEY_ID, kid)
207                           .canonicalize())
208                  .add(cppbor::Null()));  // No ciphertext
209 }
210 
211 }  // namespace keymaster
212