| // |
| // Copyright (C) 2015 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #include "attestation/server/attestation_service.h" |
| |
| #include <string> |
| |
| #include <base/callback.h> |
| #include <base/sha1.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <brillo/bind_lambda.h> |
| #include <brillo/cryptohome.h> |
| #include <brillo/data_encoding.h> |
| #include <brillo/http/http_utils.h> |
| #include <brillo/mime_utils.h> |
| #include <crypto/sha2.h> |
| extern "C" { |
| #include <vboot/crossystem.h> |
| } |
| |
| #include "attestation/common/attestation_ca.pb.h" |
| #include "attestation/common/database.pb.h" |
| #include "attestation/common/tpm_utility_factory.h" |
| #include "attestation/server/database_impl.h" |
| |
| namespace { |
| |
| // Google Attestation Certificate Authority (ACA) production instance. |
| const char kDefaultACAWebOrigin[] = "https://chromeos-ca.gstatic.com"; |
| const char kDefaultACAPublicKey[] = |
| "A2976637E113CC457013F4334312A416395B08D4B2A9724FC9BAD65D0290F39C" |
| "866D1163C2CD6474A24A55403C968CF78FA153C338179407FE568C6E550949B1" |
| "B3A80731BA9311EC16F8F66060A2C550914D252DB90B44D19BC6C15E923FFCFB" |
| "E8A366038772803EE57C7D7E5B3D5E8090BF0960D4F6A6644CB9A456708508F0" |
| "6C19245486C3A49F807AB07C65D5E9954F4F8832BC9F882E9EE1AAA2621B1F43" |
| "4083FD98758745CBFFD6F55DA699B2EE983307C14C9990DDFB48897F26DF8FB2" |
| "CFFF03E631E62FAE59CBF89525EDACD1F7BBE0BA478B5418E756FF3E14AC9970" |
| "D334DB04A1DF267D2343C75E5D282A287060D345981ABDA0B2506AD882579FEF"; |
| const char kDefaultACAPublicKeyID[] = "\x00\xc7\x0e\x50\xb1"; |
| |
| // Google Attestation Certificate Authority (ACA) test instance. |
| const char kTestACAWebOrigin[] = "https://asbestos-qa.corp.google.com"; |
| const char kTestACAPublicKey[] = |
| "A1D50D088994000492B5F3ED8A9C5FC8772706219F4C063B2F6A8C6B74D3AD6B" |
| "212A53D01DABB34A6261288540D420D3BA59ED279D859DE6227A7AB6BD88FADD" |
| "FC3078D465F4DF97E03A52A587BD0165AE3B180FE7B255B7BEDC1BE81CB1383F" |
| "E9E46F9312B1EF28F4025E7D332E33F4416525FEB8F0FC7B815E8FBB79CDABE6" |
| "327B5A155FEF13F559A7086CB8A543D72AD6ECAEE2E704FF28824149D7F4E393" |
| "D3C74E721ACA97F7ADBE2CCF7B4BCC165F7380F48065F2C8370F25F066091259" |
| "D14EA362BAF236E3CD8771A94BDEDA3900577143A238AB92B6C55F11DEFAFB31" |
| "7D1DC5B6AE210C52B008D87F2A7BFF6EB5C4FB32D6ECEC6505796173951A3167"; |
| const char kTestACAPublicKeyID[] = "\x00\xc2\xb0\x56\x2d"; |
| |
| #ifdef USE_TEST_ACA |
| #error "Do not compile with USE_TEST_ACA" |
| " but provide the right aca_type in requests." |
| #endif |
| |
| const char kEnterpriseSigningPublicKey[] = |
| "bf7fefa3a661437b26aed0801db64d7ba8b58875c351d3bdc9f653847d4a67b3" |
| "b67479327724d56aa0f71a3f57c2290fdc1ff05df80589715e381dfbbda2c4ac" |
| "114c30d0a73c5b7b2e22178d26d8b65860aa8dd65e1b3d61a07c81de87c1e7e4" |
| "590145624936a011ece10434c1d5d41f917c3dc4b41dd8392479130c4fd6eafc" |
| "3bb4e0dedcc8f6a9c28428bf8fbba8bd6438a325a9d3eabee1e89e838138ad99" |
| "69c292c6d9f6f52522333b84ddf9471ffe00f01bf2de5faa1621f967f49e158b" |
| "f2b305360f886826cc6fdbef11a12b2d6002d70d8d1e8f40e0901ff94c203cb2" |
| "01a36a0bd6e83955f14b494f4f2f17c0c826657b85c25ffb8a73599721fa17ab"; |
| |
| const char kEnterpriseEncryptionPublicKey[] = |
| "edba5e723da811e41636f792c7a77aef633fbf39b542aa537c93c93eaba7a3b1" |
| "0bc3e484388c13d625ef5573358ec9e7fbeb6baaaa87ca87d93fb61bf5760e29" |
| "6813c435763ed2c81f631e26e3ff1a670261cdc3c39a4640b6bbf4ead3d6587b" |
| "e43ef7f1f08e7596b628ec0b44c9b7ad71c9ee3a1258852c7a986c7614f0c4ec" |
| "f0ce147650a53b6aa9ae107374a2d6d4e7922065f2f6eb537a994372e1936c87" |
| "eb08318611d44daf6044f8527687dc7ce5319b51eae6ab12bee6bd16e59c499e" |
| "fa53d80232ae886c7ee9ad8bc1cbd6e4ac55cb8fa515671f7e7ad66e98769f52" |
| "c3c309f98bf08a3b8fbb0166e97906151b46402217e65c5d01ddac8514340e8b"; |
| |
| // This value is opaque; it is proprietary to the system managing the private |
| // key. In this case the value has been supplied by the enterprise server |
| // maintainers. |
| const char kEnterpriseEncryptionPublicKeyID[] = |
| "\x00\x4a\xe2\xdc\xae"; |
| |
| const size_t kNonceSize = 20; // As per TPM_NONCE definition. |
| const int kNumTemporalValues = 5; |
| |
| const char kKnownBootModes[8][3] = { |
| {0, 0, 0}, {0, 0, 1}, |
| {0, 1, 0}, {0, 1, 1}, |
| {1, 0, 0}, {1, 0, 1}, |
| {1, 1, 0}, {1, 1, 1} |
| }; |
| const char kVerifiedBootMode[3] = {0, 0, 1}; |
| |
| struct CertificateAuthority { |
| const char* issuer; |
| const char* modulus; // In hex format. |
| }; |
| |
| const CertificateAuthority kKnownEndorsementCA[] = { |
| { "IFX TPM EK Intermediate CA 06", |
| "de9e58a353313d21d683c687d6aaaab240248717557c077161c5e515f41d8efa" |
| "48329f45658fb550f43f91d1ba0c2519429fb6ef964f89657098c90a9783ad6d" |
| "3baea625db044734c478768db53b6022c556d8174ed744bd6e4455665715cd5c" |
| "beb7c3fcb822ab3dfab1ecee1a628c3d53f6085983431598fb646f04347d5ae0" |
| "021d5757cc6e3027c1e13f10633ae48bbf98732c079c17684b0db58bd0291add" |
| "e277b037dd13fa3db910e81a4969622a79c85ac768d870f079b54c2b98c856e7" |
| "15ef0ba9c01ee1da1241838a1307fe94b1ddfa65cdf7eeaa7e5b4b8a94c3dcd0" |
| "29bb5ebcfc935e56641f4c8cb5e726c68f9dd6b41f8602ef6dc78d870a773571" }, |
| { "IFX TPM EK Intermediate CA 07", |
| "f04c9b5b9f3cbc2509179f5e0f31dceb302900f528458e002c3e914d6b29e5e0" |
| "924b0bcab2dd053f65d9d4a8eea8269c85c419dba640a88e14dc5f8c8c1a4269" |
| "7a5ac4594b36f923110f91d1803d385540c01a433140b06054c77a144ee3a6a6" |
| "5950c20f9215be3473b1002eb6b1756a22fbc18d21efacbbc8c270c66cf74982" |
| "e24f057825cab51c0dd840a4f2d059032239c33e3f52c6ca06fe49bf4f60cc28" |
| "a0fb1173d2ee05a141d30e8ffa32dbb86c1aeb5b309f76c2e462965612ec929a" |
| "0d3b04acfa4525912c76f765e948be71f505d619cc673a889f0ed9e1d75f237b" |
| "7af6a68550253cb4c3a8ff16c8091dbcbdea0ff8eee3d5bd92f49c53c5a15c93" }, |
| { "IFX TPM EK Intermediate CA 14", |
| "D5B2EB8F8F23DD0B5CA0C15D4376E27A0380FD8EB1E52C2C270D961E8C0F66FD" |
| "62E6ED6B3660FFBD8B0735179476F5E9C2EA4C762F5FEEDD3B5EB91785A724BC" |
| "4C0617B83966336DD9DC407640871BF99DF4E1701EB5A1F5647FC57879CBB973" |
| "B2A72BABA8536B2646A37AA5B73E32A4C8F03E35C8834B391AD363F1F7D1DF2B" |
| "EE39233F47384F3E2D2E8EF83C9539B4DFC360C8AEB88B6111E757AF646DC01A" |
| "68DAA908C7F8068894E9E991C59005068DD9B0F87113E6A80AB045DB4C1B23FF" |
| "38A106098C2E184E1CF42A43EA68753F2649999048E8A3C3406032BEB1457070" |
| "BCBE3A93E122638F6F18FF505C35FB827CE5D0C12F27F45C0F59C8A4A8697849" }, |
| { "IFX TPM EK Intermediate CA 16", |
| "B98D42D5284620036A6613ED05A1BE11431AE7DE435EC55F72814652B9265EC2" |
| "9035D401B538A9C84BB5B875450FAE8FBEDEF3430C4108D8516404F3DE4D4615" |
| "2F471013673A7C7F236304C7363B91C0E0FD9FC7A9EC751521A60A6042839CF7" |
| "7AEDE3243D0F51F47ACC39676D236BD5298E18B9A4783C60B2A1CD1B32124909" |
| "D5844649EE4539D6AA05A5902C147B4F062D5145708EAE224EC65A8B51D7A418" |
| "6327DA8F3B9E7C796F8B2DB3D2BDB39B829BDEBA8D2BF882CBADDB75D76FA8FA" |
| "313682688BCD2835533A3A68A4AFDF7E597D8B965402FF22A5A4A418FDB4B549" |
| "F218C3908E66BDCEAB3E2FE5EE0A4A1D9EB41A286ED07B6C112581FDAEA088D9" }, |
| { "IFX TPM EK Intermediate CA 17", |
| "B0F3CC6F02E8C0486501102731069644A815F631ED41676C05CE3F7E5E5E40DF" |
| "B3BF6D99787F2A9BE8F8B8035C03D5C2226072985230D4CE8407ACD6403F72E1" |
| "A4DBF069504E56FA8C0807A704526EAC1E379AE559EB4BBAD9DB4E652B3B14E5" |
| "38497A5E7768BCE0BFFAF800C61F1F2262775C526E1790A2BECF9A072A58F6A0" |
| "F3042B5279FE9957BCADC3C9725428B66B15D5263F00C528AC47716DE6938199" |
| "0FF23BC28F2C33B72D89B5F8EEEF9053B60D230431081D656EA8EC16C7CEFD9E" |
| "F5A9061A3C921394D453D9AC77397D59B4C3BAF258266F65559469C3007987D5" |
| "A8338E10FC54CD930303C37007D6E1E6C63F36BCFBA1E494AFB3ECD9A2407FF9" }, |
| { "IFX TPM EK Intermediate CA 21", |
| "8149397109974D6C0850C8A60304ED7D209B1B88F435B695394DAD9FB4E64180" |
| "02A3940966D2F04103C88659600EEA8E2A5C697C5F989F62D33A06DA10B50075" |
| "F37F3CE6AD070413A0E109E16FE652B393C4DAFC5579CCB9915E9A70F5C05BCE" |
| "0D341D6B887F43C4334BD8EC6A293FFAB737F77A45069CD0345D3D534E84D029" |
| "029C37A267C0CC2D8DCE3E2C76F21A40F5D8D463882A8CBB92D8235685266753" |
| "E8F051E78B681E87810A5B21EF719662A8208DFD94C55A126A112E39E0D732D7" |
| "3C599095FAFF52BBC0E8C5B3DCD904D05DE00D5C5112F3DF7B76602ABE5DC0F8" |
| "F89B55889A24C54EDBA1234AE498BE9B02CB5C8048D1DC90210705BAFC0E2837" }, |
| { "IFX TPM EK Intermediate CA 29", |
| "cd424370776890ace339c62d7faae843bb2c765d27685c0441d278361a929062" |
| "b4c95cc57213c864e91cbb92b1151f17a346a4e754c666f2a3e07ea9ffb9c80f" |
| "e54d9479f73458c64bf7b0ca4e38821dd318e82d6fe387903ca73ca3e59db48e" |
| "fe3b3c7c89599be87bb5e439a6f5843a412d4a321f154955448b71ca0b5fda47" |
| "5c86a1c999dde7a01aa16436e65f0b04874c0db3970546bd806157058c5576a5" |
| "c00b2bce7173c887f388dc4d5267c68fa5c47fcee3d8491071cd7742d43162cb" |
| "285f5ba5e0daa0e910fdce566c5bbf7b3701d51660090344195fd7278456bd98" |
| "48382fc5fceaebf93a2ec88c5722723519692e90d23f869c34d8b1af499d4127" }, |
| { "IFX TPM EK Intermediate CA 30", |
| "a01cc43c4b66076d483086d0713a336f435e33ed23d3cda05f3c60a6f707416a" |
| "9e53f0ef0de62c82a720e9ad94df29805b56b44279fd7389de4c60d498c81e3b" |
| "a27692a045d993e9aaae152768588e5c62213721154529c95b09b201bcb3e573" |
| "3d98e398d6e05215867d94e3d222e5b7df9f948c14533285821658b282be4bd7" |
| "fe7197baa642f556d4f18738adef26b2eebfc64045cf4c5dcbff661aa95429f4" |
| "e2c4921a8723bd8116f0efc038cd4530bb6e9299b7d70327e3fe8790d3d6db3a" |
| "ebd3ccd12aef3d43cf89463a28ad1306a9d430b08c3411bfeeda63b9fdcc9a23" |
| "1ff5cc203a7f5ee713d50e1930add1cd32ff64637fc740edb63380a5e6725381" }, |
| { "NTC TPM EK Root CA 01", |
| "e836ac61b43e3252d5e1a8a4061997a6a0a272ba3d519d6be6360cc8b4b79e8c" |
| "d53c07a7ce9e9310ca84b82bbdad32184544ada357d458cf224c4a3130c97d00" |
| "4933b5db232d8b6509412eb4777e9e1b093c58b82b1679c84e57a6b218b4d61f" |
| "6dd4c3a66b2dd33b52cb1ffdff543289fa36dd71b7c83b66c1aae37caf7fe88d" |
| "851a3523e3ea92b59a6b0ca095c5e1d191484c1bff8a33048c3976e826d4c12a" |
| "e198f7199d183e0e70c8b46e8106edec3914397e051ae2b9a7f0b4bb9cd7f2ed" |
| "f71064eb0eb473df27b7ccef9a018d715c5fe6ab012a8315f933c7f4fc35d34c" |
| "efc27de224b2e3de3b3ba316d5df8b90b2eb879e219d270141b78dbb671a3a05" }, |
| { "STM TPM EK Intermediate CA 03", |
| "a5152b4fbd2c70c0c9a0dd919f48ddcde2b5c0c9988cff3b04ecd844f6cc0035" |
| "6c4e01b52463deb5179f36acf0c06d4574327c37572292fcd0f272c2d45ea7f2" |
| "2e8d8d18aa62354c279e03be9220f0c3822d16de1ea1c130b59afc56e08f22f1" |
| "902a07f881ebea3703badaa594ecbdf8fd1709211ba16769f73e76f348e2755d" |
| "bba2f94c1869ef71e726f56f8ece987f345c622e8b5c2a5466d41093c0dc2982" |
| "e6203d96f539b542347a08e87fc6e248a346d61a505f52add7f768a5203d70b8" |
| "68b6ec92ef7a83a4e6d1e1d259018705755d812175489fae83c4ab2957f69a99" |
| "9394ac7a243a5c1cd85f92b8648a8e0d23165fdd86fad06990bfd16fb3293379" }, |
| /* TODO(ngm): remove by: Aug 30 08:44:33 2020 GMT */ |
| { "CROS TPM DEV EK ROOT CA", |
| "cdc108745dc50dd6a1098c31486fb31578607fd64f64b0d91b994244ca1a9a69" |
| "a74c6bccc7f24923e1513e132dc0d9dbcb1b22089299bb6cb669cbf4b704c992" |
| "27bb769fa1f91ab11f67fb464a065b34b1a0e824136af5e59d1ac04bda22c199" |
| "9f7a5b34bd6b50c81b4a88cc097d4dfeb4dc695096463d9529d69f116e2a26de" |
| "070ef3118287072bdbe94466b8737049809bb8e1276b245930051b2bbbad71dd" |
| "20d26349d1d83cdb2ff9c65251a17dae4f400ecc3e77f89e27a75fe0709dc81f" |
| "e172008a3e65de685d9df43e036c557e88f1a9aedf7a91644391523d9728f946" |
| "45c0e8adaf37e9a15777021ad43b675583302402912d66233c59ad05fa3b34ed" |
| }, |
| { "CROS TPM PRD EK ROOT CA", |
| "bd6f0198ffa7f7d20c15f81642096e335e2cd74734f73008265fc9957bbe018d" |
| "fbac0d2a0ea99f5fb7bbff6f0d367b81199e837c390527972aa5392c2ca0f2a3" |
| "506ee7d4a938f47158a7c56a390df2b781344a82b885a62f1de78f37ec105749" |
| "69d8abf3163f0cf5c67fa05dd4fb3eb07a7571888b7a87ed57735ce476156bf7" |
| "d6eff6cb8c8b303c21ebfe0e11b660edbdf903c70ac16927345d0b38c72f1e60" |
| "1460743584f5a3eaef303dbc5cfda48e4c7a1f338108c7f0c70a694f814b6691" |
| "ba9d058ab988152bb7097a010e400462187811c3e062001bce8aa808db485bd8" |
| "2f7f0e1e2a2ddb95c364dffea4c23e872fc3874c4756e85e6cf8eca6eb6a07bf" |
| }, |
| }; |
| |
| const CertificateAuthority kKnownCrosCoreEndorsementCA[] = { |
| { "IFX TPM EK Intermediate CA 24", |
| "9D3F39677EBDB7B95F383021EA6EF90AD2BEA4E38B10CA65DCD84D0B33D400FA" |
| "E7E56FC553975FDADD425227F055C029B6544331E3BA50ED33F6CC02D833EA4E" |
| "0EECFE9AD1ADD7095F3A804C560F031E8705A3AD5189CBD62678B5B8205C37ED" |
| "780A3EDE8DE64A08980C048872E789937A49FC4048EADCAC9B3FD0F0DD085E76" |
| "30DDF9C0C31EFF3B77C6C3601AA7C3DCD10F08616C01435697746A61F920335C" |
| "0C45A41149F5D22FCD23DBE35003A9AF7FD91C18715E3709F86A38AB149113C4" |
| "D5273C3C90599734FF627ACBF408B082C76E486091F27446E175C50D340DA0FE" |
| "5C3FE3D590B8729F4E364E5BF7D854D9AE28EFBCD0CE8F19E6462B3A593983DF" } |
| }; |
| |
| // Returns a human-readable description for a known 3-byte |mode|. |
| std::string GetDescriptionForMode(const char* mode) { |
| return base::StringPrintf( |
| "Developer Mode: %s, Recovery Mode: %s, Firmware Type: %s", |
| mode[0] ? "On" : "Off", |
| mode[1] ? "On" : "Off", |
| mode[2] ? "Verified" : "Developer"); |
| } |
| |
| std::string GetHardwareID() { |
| char buffer[VB_MAX_STRING_PROPERTY]; |
| const char* property = |
| VbGetSystemPropertyString("hwid", buffer, arraysize(buffer)); |
| if (property != nullptr) { |
| return std::string(property); |
| } |
| LOG(WARNING) << "Could not read hwid property."; |
| return std::string(); |
| } |
| |
| // Finds CA by |issuer_name| and |is_cros_core| flag. On success returns true |
| // and fills |public_key_hex| with CA public key hex modulus. |
| bool GetAuthorityPublicKey( |
| const std::string& issuer_name, |
| bool is_cros_core, |
| std::string* public_key_hex) { |
| const CertificateAuthority* const kKnownCA = |
| is_cros_core ? kKnownCrosCoreEndorsementCA : kKnownEndorsementCA; |
| const int kNumIssuers = |
| is_cros_core ? arraysize(kKnownCrosCoreEndorsementCA) : |
| arraysize(kKnownEndorsementCA); |
| for (int i = 0; i < kNumIssuers; ++i) { |
| if (issuer_name == kKnownCA[i].issuer) { |
| public_key_hex->assign(kKnownCA[i].modulus); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| namespace attestation { |
| |
| const size_t kChallengeSignatureNonceSize = 20; // For all TPMs. |
| |
| AttestationService::AttestationService() : weak_factory_(this) {} |
| |
| bool AttestationService::Initialize() { |
| if (!worker_thread_) { |
| worker_thread_.reset(new ServiceWorkerThread(this)); |
| worker_thread_->StartWithOptions( |
| base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); |
| LOG(INFO) << "Attestation service started."; |
| } |
| worker_thread_->task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&AttestationService::InitializeTask, base::Unretained(this))); |
| return true; |
| } |
| |
| void AttestationService::InitializeTask() { |
| if (!tpm_utility_) { |
| default_tpm_utility_.reset(TpmUtilityFactory::New()); |
| CHECK(default_tpm_utility_->Initialize()); |
| tpm_utility_ = default_tpm_utility_.get(); |
| } |
| if (!crypto_utility_) { |
| default_crypto_utility_.reset(new CryptoUtilityImpl(tpm_utility_)); |
| crypto_utility_ = default_crypto_utility_.get(); |
| } |
| if (!database_) { |
| default_database_.reset(new DatabaseImpl(crypto_utility_)); |
| default_database_->Initialize(); |
| database_ = default_database_.get(); |
| } |
| if (!key_store_) { |
| pkcs11_token_manager_.reset(new chaps::TokenManagerClient()); |
| default_key_store_.reset(new Pkcs11KeyStore(pkcs11_token_manager_.get())); |
| key_store_ = default_key_store_.get(); |
| } |
| if (hwid_.empty()) { |
| hwid_ = GetHardwareID(); |
| } |
| if (!IsPreparedForEnrollment()) { |
| worker_thread_->task_runner()->PostTask( |
| FROM_HERE, base::Bind(&AttestationService::PrepareForEnrollment, |
| base::Unretained(this))); |
| } else { |
| // Ignore errors. If failed this time, will be re-attempted on next boot. |
| tpm_utility_->RemoveOwnerDependency(); |
| } |
| } |
| |
| void AttestationService::ShutdownTask() { |
| tpm_utility_ = nullptr; |
| default_tpm_utility_.reset(nullptr); |
| } |
| |
| void AttestationService::CreateGoogleAttestedKey( |
| const CreateGoogleAttestedKeyRequest& request, |
| const CreateGoogleAttestedKeyCallback& callback) { |
| auto result = std::make_shared<CreateGoogleAttestedKeyReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::CreateGoogleAttestedKeyTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<CreateGoogleAttestedKeyReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::CreateGoogleAttestedKeyTask( |
| const CreateGoogleAttestedKeyRequest& request, |
| const std::shared_ptr<CreateGoogleAttestedKeyReply>& result) { |
| LOG(INFO) << "Creating attested key: " << request.key_label(); |
| if (!IsPreparedForEnrollment()) { |
| LOG(ERROR) << "Attestation: TPM is not ready."; |
| result->set_status(STATUS_NOT_READY); |
| return; |
| } |
| if (!IsEnrolled()) { |
| std::string enroll_request; |
| if (!CreateEnrollRequestInternal(request.aca_type(), &enroll_request)) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| std::string enroll_reply; |
| if (!SendACARequestAndBlock(request.aca_type(), kEnroll, enroll_request, |
| &enroll_reply)) { |
| result->set_status(STATUS_CA_NOT_AVAILABLE); |
| return; |
| } |
| std::string server_error; |
| if (!FinishEnrollInternal(enroll_reply, &server_error)) { |
| if (server_error.empty()) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| result->set_status(STATUS_REQUEST_DENIED_BY_CA); |
| result->set_server_error(server_error); |
| return; |
| } |
| } |
| CertifiedKey key; |
| if (!CreateKey(request.username(), request.key_label(), request.key_type(), |
| request.key_usage(), &key)) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| std::string certificate_request; |
| std::string message_id; |
| if (!CreateCertificateRequestInternal(request.username(), key, |
| request.certificate_profile(), request.origin(), |
| &certificate_request, &message_id)) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| std::string certificate_reply; |
| if (!SendACARequestAndBlock(request.aca_type(), kGetCertificate, |
| certificate_request, &certificate_reply)) { |
| result->set_status(STATUS_CA_NOT_AVAILABLE); |
| return; |
| } |
| std::string certificate_chain; |
| std::string server_error; |
| if (!FinishCertificateRequestInternal(certificate_reply, request.username(), |
| request.key_label(), message_id, &key, |
| &certificate_chain, &server_error)) { |
| if (server_error.empty()) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| result->set_status(STATUS_REQUEST_DENIED_BY_CA); |
| result->set_server_error(server_error); |
| return; |
| } |
| result->set_certificate_chain(certificate_chain); |
| } |
| |
| void AttestationService::GetKeyInfo(const GetKeyInfoRequest& request, |
| const GetKeyInfoCallback& callback) { |
| auto result = std::make_shared<GetKeyInfoReply>(); |
| base::Closure task = base::Bind(&AttestationService::GetKeyInfoTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = |
| base::Bind(&AttestationService::TaskRelayCallback<GetKeyInfoReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::GetKeyInfoTask( |
| const GetKeyInfoRequest& request, |
| const std::shared_ptr<GetKeyInfoReply>& result) { |
| CertifiedKey key; |
| if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| std::string public_key_info; |
| if (!GetSubjectPublicKeyInfo(key.key_type(), key.public_key(), |
| &public_key_info)) { |
| LOG(ERROR) << __func__ << ": Bad public key."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| result->set_key_type(key.key_type()); |
| result->set_key_usage(key.key_usage()); |
| result->set_public_key(public_key_info); |
| result->set_certify_info(key.certified_key_info()); |
| result->set_certify_info_signature(key.certified_key_proof()); |
| if (key.has_intermediate_ca_cert()) { |
| result->set_certificate(CreatePEMCertificateChain(key)); |
| } else { |
| result->set_certificate(key.certified_key_credential()); |
| } |
| result->set_payload(key.payload()); |
| } |
| |
| void AttestationService::GetEndorsementInfo( |
| const GetEndorsementInfoRequest& request, |
| const GetEndorsementInfoCallback& callback) { |
| auto result = std::make_shared<GetEndorsementInfoReply>(); |
| base::Closure task = base::Bind(&AttestationService::GetEndorsementInfoTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<GetEndorsementInfoReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::GetEndorsementInfoTask( |
| const GetEndorsementInfoRequest& request, |
| const std::shared_ptr<GetEndorsementInfoReply>& result) { |
| if (request.key_type() != KEY_TYPE_RSA) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| auto database_pb = database_->GetProtobuf(); |
| if (!database_pb.has_credentials() || |
| !database_pb.credentials().has_endorsement_public_key()) { |
| // Try to read the public key directly. |
| std::string public_key; |
| if (!tpm_utility_->GetEndorsementPublicKey(KEY_TYPE_RSA, &public_key)) { |
| result->set_status(STATUS_NOT_AVAILABLE); |
| return; |
| } |
| database_pb.mutable_credentials()->set_endorsement_public_key(public_key); |
| } |
| std::string public_key_info; |
| if (!GetSubjectPublicKeyInfo( |
| request.key_type(), |
| database_pb.credentials().endorsement_public_key(), |
| &public_key_info)) { |
| LOG(ERROR) << __func__ << ": Bad public key."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| result->set_ek_public_key(public_key_info); |
| if (database_pb.credentials().has_endorsement_credential()) { |
| result->set_ek_certificate( |
| database_pb.credentials().endorsement_credential()); |
| } |
| std::string ek_cert; |
| if (database_pb.credentials().has_endorsement_credential()) { |
| ek_cert = database_pb.credentials().endorsement_credential(); |
| } else { |
| if (!tpm_utility_->GetEndorsementCertificate(KEY_TYPE_RSA, &ek_cert)) { |
| LOG(ERROR) << __func__ << ": Endorsement cert not available."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| } |
| std::string hash = crypto::SHA256HashString(ek_cert); |
| result->set_ek_info(base::StringPrintf( |
| "EK Certificate:\n%s\nHash:\n%s\n", |
| CreatePEMCertificate(ek_cert).c_str(), |
| base::HexEncode(hash.data(), hash.size()).c_str())); |
| } |
| |
| void AttestationService::GetAttestationKeyInfo( |
| const GetAttestationKeyInfoRequest& request, |
| const GetAttestationKeyInfoCallback& callback) { |
| auto result = std::make_shared<GetAttestationKeyInfoReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::GetAttestationKeyInfoTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<GetAttestationKeyInfoReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::GetAttestationKeyInfoTask( |
| const GetAttestationKeyInfoRequest& request, |
| const std::shared_ptr<GetAttestationKeyInfoReply>& result) { |
| if (request.key_type() != KEY_TYPE_RSA) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| auto database_pb = database_->GetProtobuf(); |
| if (!IsPreparedForEnrollment() || !database_pb.has_identity_key()) { |
| result->set_status(STATUS_NOT_AVAILABLE); |
| return; |
| } |
| if (database_pb.identity_key().has_identity_public_key()) { |
| std::string public_key_info; |
| if (!GetSubjectPublicKeyInfo( |
| request.key_type(), |
| database_pb.identity_key().identity_public_key(), |
| &public_key_info)) { |
| LOG(ERROR) << __func__ << ": Bad public key."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| result->set_public_key(public_key_info); |
| } |
| if (database_pb.has_identity_binding() && |
| database_pb.identity_binding().has_identity_public_key()) { |
| result->set_public_key_tpm_format( |
| database_pb.identity_binding().identity_public_key()); |
| } |
| if (database_pb.identity_key().has_identity_credential()) { |
| result->set_certificate(database_pb.identity_key().identity_credential()); |
| } |
| if (database_pb.has_pcr0_quote()) { |
| *result->mutable_pcr0_quote() = database_pb.pcr0_quote(); |
| } |
| if (database_pb.has_pcr1_quote()) { |
| *result->mutable_pcr1_quote() = database_pb.pcr1_quote(); |
| } |
| } |
| |
| void AttestationService::ActivateAttestationKey( |
| const ActivateAttestationKeyRequest& request, |
| const ActivateAttestationKeyCallback& callback) { |
| auto result = std::make_shared<ActivateAttestationKeyReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::ActivateAttestationKeyTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<ActivateAttestationKeyReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::ActivateAttestationKeyTask( |
| const ActivateAttestationKeyRequest& request, |
| const std::shared_ptr<ActivateAttestationKeyReply>& result) { |
| if (request.key_type() != KEY_TYPE_RSA) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| LOG(ERROR) << __func__ << ": Only RSA currently supported."; |
| return; |
| } |
| if (request.encrypted_certificate().tpm_version() != |
| tpm_utility_->GetVersion()) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| LOG(ERROR) << __func__ << ": TPM version mismatch."; |
| return; |
| } |
| std::string certificate; |
| if (!ActivateAttestationKeyInternal(request.encrypted_certificate(), |
| request.save_certificate(), |
| &certificate)) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| result->set_certificate(certificate); |
| } |
| |
| void AttestationService::CreateCertifiableKey( |
| const CreateCertifiableKeyRequest& request, |
| const CreateCertifiableKeyCallback& callback) { |
| auto result = std::make_shared<CreateCertifiableKeyReply>(); |
| base::Closure task = base::Bind(&AttestationService::CreateCertifiableKeyTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<CreateCertifiableKeyReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::CreateCertifiableKeyTask( |
| const CreateCertifiableKeyRequest& request, |
| const std::shared_ptr<CreateCertifiableKeyReply>& result) { |
| CertifiedKey key; |
| if (!CreateKey(request.username(), request.key_label(), request.key_type(), |
| request.key_usage(), &key)) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| std::string public_key_info; |
| if (!GetSubjectPublicKeyInfo(key.key_type(), key.public_key(), |
| &public_key_info)) { |
| LOG(ERROR) << __func__ << ": Bad public key."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| result->set_public_key(public_key_info); |
| result->set_certify_info(key.certified_key_info()); |
| result->set_certify_info_signature(key.certified_key_proof()); |
| } |
| |
| void AttestationService::Decrypt(const DecryptRequest& request, |
| const DecryptCallback& callback) { |
| auto result = std::make_shared<DecryptReply>(); |
| base::Closure task = base::Bind(&AttestationService::DecryptTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = |
| base::Bind(&AttestationService::TaskRelayCallback<DecryptReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::DecryptTask( |
| const DecryptRequest& request, |
| const std::shared_ptr<DecryptReply>& result) { |
| CertifiedKey key; |
| if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| std::string data; |
| if (!tpm_utility_->Unbind(key.key_blob(), request.encrypted_data(), &data)) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| result->set_decrypted_data(data); |
| } |
| |
| void AttestationService::Sign(const SignRequest& request, |
| const SignCallback& callback) { |
| auto result = std::make_shared<SignReply>(); |
| base::Closure task = base::Bind(&AttestationService::SignTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = |
| base::Bind(&AttestationService::TaskRelayCallback<SignReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::SignTask(const SignRequest& request, |
| const std::shared_ptr<SignReply>& result) { |
| CertifiedKey key; |
| if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| std::string signature; |
| if (!tpm_utility_->Sign(key.key_blob(), request.data_to_sign(), &signature)) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| result->set_signature(signature); |
| } |
| |
| void AttestationService::RegisterKeyWithChapsToken( |
| const RegisterKeyWithChapsTokenRequest& request, |
| const RegisterKeyWithChapsTokenCallback& callback) { |
| auto result = std::make_shared<RegisterKeyWithChapsTokenReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::RegisterKeyWithChapsTokenTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<RegisterKeyWithChapsTokenReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::RegisterKeyWithChapsTokenTask( |
| const RegisterKeyWithChapsTokenRequest& request, |
| const std::shared_ptr<RegisterKeyWithChapsTokenReply>& result) { |
| CertifiedKey key; |
| if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| std::string certificate; |
| if (request.include_certificates()) { |
| certificate = key.certified_key_credential(); |
| } |
| if (!key_store_->Register(request.username(), request.key_label(), |
| key.key_type(), key.key_usage(), key.key_blob(), |
| key.public_key(), certificate)) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| if (request.include_certificates()) { |
| if (key.has_intermediate_ca_cert() && |
| !key_store_->RegisterCertificate(request.username(), |
| key.intermediate_ca_cert())) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| for (int i = 0; i < key.additional_intermediate_ca_cert_size(); ++i) { |
| if (!key_store_->RegisterCertificate( |
| request.username(), key.additional_intermediate_ca_cert(i))) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| } |
| } |
| DeleteKey(request.username(), request.key_label()); |
| } |
| |
| bool AttestationService::IsPreparedForEnrollment() { |
| if (!tpm_utility_->IsTpmReady()) { |
| return false; |
| } |
| auto database_pb = database_->GetProtobuf(); |
| if (!database_pb.has_credentials()) { |
| return false; |
| } |
| return ( |
| database_pb.credentials().has_endorsement_credential() || |
| database_pb.credentials().has_default_encrypted_endorsement_credential()); |
| } |
| |
| bool AttestationService::IsEnrolled() { |
| auto database_pb = database_->GetProtobuf(); |
| return database_pb.has_identity_key() && |
| database_pb.identity_key().has_identity_credential(); |
| } |
| |
| bool AttestationService::CreateEnrollRequestInternal(ACAType aca_type, |
| std::string* enroll_request) { |
| if (!IsPreparedForEnrollment()) { |
| LOG(ERROR) << __func__ << ": Enrollment is not possible, attestation data " |
| << "does not exist."; |
| return false; |
| } |
| auto database_pb = database_->GetProtobuf(); |
| AttestationEnrollmentRequest request_pb; |
| request_pb.set_tpm_version(tpm_utility_->GetVersion()); |
| *request_pb.mutable_encrypted_endorsement_credential() = |
| aca_type == DEFAULT_ACA ? |
| database_pb.credentials().default_encrypted_endorsement_credential() : |
| database_pb.credentials().test_encrypted_endorsement_credential(); |
| request_pb.set_identity_public_key( |
| database_pb.identity_binding().identity_public_key()); |
| *request_pb.mutable_pcr0_quote() = database_pb.pcr0_quote(); |
| *request_pb.mutable_pcr1_quote() = database_pb.pcr1_quote(); |
| if (!request_pb.SerializeToString(enroll_request)) { |
| LOG(ERROR) << __func__ << ": Failed to serialize protobuf."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool AttestationService::FinishEnrollInternal(const std::string& enroll_response, |
| std::string* server_error) { |
| if (!tpm_utility_->IsTpmReady()) { |
| return false; |
| } |
| AttestationEnrollmentResponse response_pb; |
| if (!response_pb.ParseFromString(enroll_response)) { |
| LOG(ERROR) << __func__ << ": Failed to parse response from CA."; |
| return false; |
| } |
| if (response_pb.status() != OK) { |
| *server_error = response_pb.detail(); |
| LOG(ERROR) << __func__ |
| << ": Error received from CA: " << response_pb.detail(); |
| return false; |
| } |
| if (response_pb.encrypted_identity_credential().tpm_version() != |
| tpm_utility_->GetVersion()) { |
| LOG(ERROR) << __func__ << ": TPM version mismatch."; |
| return false; |
| } |
| if (!ActivateAttestationKeyInternal( |
| response_pb.encrypted_identity_credential(), |
| true /* save_certificate */, nullptr /* certificate */)) { |
| return false; |
| } |
| LOG(INFO) << "Attestation: Enrollment complete."; |
| return true; |
| } |
| |
| bool AttestationService::CreateCertificateRequestInternal( |
| const std::string& username, |
| const CertifiedKey& key, |
| CertificateProfile profile, |
| const std::string& origin, |
| std::string* certificate_request, |
| std::string* message_id) { |
| if (!tpm_utility_->IsTpmReady()) { |
| return false; |
| } |
| if (!IsEnrolled()) { |
| LOG(ERROR) << __func__ << ": Device is not enrolled for attestation."; |
| return false; |
| } |
| AttestationCertificateRequest request_pb; |
| if (!crypto_utility_->GetRandom(kNonceSize, message_id)) { |
| LOG(ERROR) << __func__ << ": GetRandom(message_id) failed."; |
| return false; |
| } |
| request_pb.set_tpm_version(tpm_utility_->GetVersion()); |
| request_pb.set_message_id(*message_id); |
| auto database_pb = database_->GetProtobuf(); |
| request_pb.set_identity_credential( |
| database_pb.identity_key().identity_credential()); |
| request_pb.set_profile(profile); |
| if (!origin.empty() && |
| (profile == CONTENT_PROTECTION_CERTIFICATE_WITH_STABLE_ID)) { |
| request_pb.set_origin(origin); |
| request_pb.set_temporal_index(ChooseTemporalIndex(username, origin)); |
| } |
| request_pb.set_certified_public_key(key.public_key_tpm_format()); |
| request_pb.set_certified_key_info(key.certified_key_info()); |
| request_pb.set_certified_key_proof(key.certified_key_proof()); |
| if (!request_pb.SerializeToString(certificate_request)) { |
| LOG(ERROR) << __func__ << ": Failed to serialize protobuf."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool AttestationService::FinishCertificateRequestInternal( |
| const std::string& certificate_response, |
| const std::string& username, |
| const std::string& key_label, |
| const std::string& message_id, |
| CertifiedKey* key, |
| std::string* certificate_chain, |
| std::string* server_error) { |
| if (!tpm_utility_->IsTpmReady()) { |
| return false; |
| } |
| AttestationCertificateResponse response_pb; |
| if (!response_pb.ParseFromString(certificate_response)) { |
| LOG(ERROR) << __func__ << ": Failed to parse response from Attestation CA."; |
| return false; |
| } |
| if (response_pb.status() != OK) { |
| *server_error = response_pb.detail(); |
| LOG(ERROR) << __func__ << ": Error received from Attestation CA: " |
| << response_pb.detail(); |
| return false; |
| } |
| if (message_id != response_pb.message_id()) { |
| LOG(ERROR) << __func__ << ": Message ID mismatch."; |
| return false; |
| } |
| return PopulateAndStoreCertifiedKey(response_pb, username, key_label, |
| key, certificate_chain); |
| } |
| |
| bool AttestationService::PopulateAndStoreCertifiedKey( |
| const AttestationCertificateResponse& response_pb, |
| const std::string& username, |
| const std::string& key_label, |
| CertifiedKey* key, |
| std::string* certificate_chain) { |
| // Finish populating the CertifiedKey protobuf and store it. |
| key->set_key_name(key_label); |
| key->set_certified_key_credential(response_pb.certified_key_credential()); |
| key->set_intermediate_ca_cert(response_pb.intermediate_ca_cert()); |
| key->mutable_additional_intermediate_ca_cert()->MergeFrom( |
| response_pb.additional_intermediate_ca_cert()); |
| if (!SaveKey(username, key_label, *key)) { |
| return false; |
| } |
| LOG(INFO) << "Attestation: Certified key credential received and stored."; |
| *certificate_chain = CreatePEMCertificateChain(*key); |
| return true; |
| } |
| |
| bool AttestationService::SendACARequestAndBlock(ACAType aca_type, |
| ACARequestType request_type, |
| const std::string& request, |
| std::string* reply) { |
| std::shared_ptr<brillo::http::Transport> transport = http_transport_; |
| if (!transport) { |
| transport = brillo::http::Transport::CreateDefault(); |
| } |
| std::unique_ptr<brillo::http::Response> response = PostBinaryAndBlock( |
| GetACAURL(aca_type, request_type), request.data(), request.size(), |
| brillo::mime::application::kOctet_stream, {}, // headers |
| transport, |
| nullptr); // error |
| if (!response || !response->IsSuccessful()) { |
| LOG(ERROR) << "HTTP request to Attestation CA failed."; |
| return false; |
| } |
| *reply = response->ExtractDataAsString(); |
| return true; |
| } |
| |
| bool AttestationService::FindKeyByLabel(const std::string& username, |
| const std::string& key_label, |
| CertifiedKey* key) { |
| if (!username.empty()) { |
| std::string key_data; |
| if (!key_store_->Read(username, key_label, &key_data)) { |
| LOG(INFO) << "Key not found: " << key_label; |
| return false; |
| } |
| if (key && !key->ParseFromString(key_data)) { |
| LOG(ERROR) << "Failed to parse key: " << key_label; |
| return false; |
| } |
| return true; |
| } |
| auto database_pb = database_->GetProtobuf(); |
| for (int i = 0; i < database_pb.device_keys_size(); ++i) { |
| if (database_pb.device_keys(i).key_name() == key_label) { |
| *key = database_pb.device_keys(i); |
| return true; |
| } |
| } |
| LOG(INFO) << "Key not found: " << key_label; |
| return false; |
| } |
| |
| bool AttestationService::CreateKey(const std::string& username, |
| const std::string& key_label, |
| KeyType key_type, |
| KeyUsage key_usage, |
| CertifiedKey* key) { |
| std::string nonce; |
| if (!crypto_utility_->GetRandom(kNonceSize, &nonce)) { |
| LOG(ERROR) << __func__ << ": GetRandom(nonce) failed."; |
| return false; |
| } |
| std::string key_blob; |
| std::string public_key; |
| std::string public_key_tpm_format; |
| std::string key_info; |
| std::string proof; |
| auto database_pb = database_->GetProtobuf(); |
| if (!tpm_utility_->CreateCertifiedKey( |
| key_type, key_usage, database_pb.identity_key().identity_key_blob(), |
| nonce, &key_blob, &public_key, &public_key_tpm_format, &key_info, |
| &proof)) { |
| return false; |
| } |
| key->set_key_blob(key_blob); |
| key->set_public_key(public_key); |
| key->set_key_name(key_label); |
| key->set_public_key_tpm_format(public_key_tpm_format); |
| key->set_certified_key_info(key_info); |
| key->set_certified_key_proof(proof); |
| key->set_key_type(key_type); |
| key->set_key_usage(key_usage); |
| return SaveKey(username, key_label, *key); |
| } |
| |
| bool AttestationService::SaveKey(const std::string& username, |
| const std::string& key_label, |
| const CertifiedKey& key) { |
| if (!username.empty()) { |
| std::string key_data; |
| if (!key.SerializeToString(&key_data)) { |
| LOG(ERROR) << __func__ << ": Failed to serialize protobuf."; |
| return false; |
| } |
| if (!key_store_->Write(username, key_label, key_data)) { |
| LOG(ERROR) << __func__ << ": Failed to store certified key for user."; |
| return false; |
| } |
| } else { |
| if (!AddDeviceKey(key_label, key)) { |
| LOG(ERROR) << __func__ << ": Failed to store certified key for device."; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void AttestationService::DeleteKey(const std::string& username, |
| const std::string& key_label) { |
| if (!username.empty()) { |
| key_store_->Delete(username, key_label); |
| } else { |
| RemoveDeviceKey(key_label); |
| } |
| } |
| |
| bool AttestationService::DeleteKeysByPrefix( |
| const std::string& username, |
| const std::string& key_prefix) { |
| if (!username.empty()) { |
| return key_store_->DeleteByPrefix(username, key_prefix); |
| } |
| return RemoveDeviceKeysByPrefix(key_prefix); |
| } |
| |
| bool AttestationService::AddDeviceKey(const std::string& key_label, |
| const CertifiedKey& key) { |
| // If a key by this name already exists, reuse the field. |
| auto* database_pb = database_->GetMutableProtobuf(); |
| bool found = false; |
| for (int i = 0; i < database_pb->device_keys_size(); ++i) { |
| if (database_pb->device_keys(i).key_name() == key_label) { |
| found = true; |
| *database_pb->mutable_device_keys(i) = key; |
| break; |
| } |
| } |
| if (!found) |
| *database_pb->add_device_keys() = key; |
| return database_->SaveChanges(); |
| } |
| |
| void AttestationService::RemoveDeviceKey(const std::string& key_label) { |
| auto* database_pb = database_->GetMutableProtobuf(); |
| bool found = false; |
| for (int i = 0; i < database_pb->device_keys_size(); ++i) { |
| if (database_pb->device_keys(i).key_name() == key_label) { |
| found = true; |
| int last = database_pb->device_keys_size() - 1; |
| if (i < last) { |
| database_pb->mutable_device_keys()->SwapElements(i, last); |
| } |
| database_pb->mutable_device_keys()->RemoveLast(); |
| break; |
| } |
| } |
| if (found) { |
| if (!database_->SaveChanges()) { |
| LOG(WARNING) << __func__ << ": Failed to persist key deletion."; |
| } |
| } |
| } |
| |
| bool AttestationService::RemoveDeviceKeysByPrefix( |
| const std::string& key_prefix) { |
| // Manipulate the device keys protobuf field. Linear time strategy is to swap |
| // all elements we want to keep to the front and then truncate. |
| auto device_keys = database_->GetMutableProtobuf()->mutable_device_keys(); |
| int next_keep_index = 0; |
| for (int i = 0; i < device_keys->size(); ++i) { |
| if (device_keys->Get(i).key_name().find(key_prefix) != 0) { |
| // Prefix doesn't match -> keep. |
| if (i != next_keep_index) |
| device_keys->SwapElements(next_keep_index, i); |
| ++next_keep_index; |
| } |
| } |
| // If no matching keys, do nothing and return success. |
| if (next_keep_index == device_keys->size()) { |
| return true; |
| } |
| while (next_keep_index < device_keys->size()) { |
| device_keys->RemoveLast(); |
| } |
| return database_->SaveChanges(); |
| } |
| |
| std::string AttestationService::CreatePEMCertificateChain( |
| const CertifiedKey& key) { |
| if (key.certified_key_credential().empty()) { |
| LOG(WARNING) << "Certificate is empty."; |
| return std::string(); |
| } |
| std::string pem = CreatePEMCertificate(key.certified_key_credential()); |
| if (!key.intermediate_ca_cert().empty()) { |
| pem += "\n"; |
| pem += CreatePEMCertificate(key.intermediate_ca_cert()); |
| } |
| for (int i = 0; i < key.additional_intermediate_ca_cert_size(); ++i) { |
| pem += "\n"; |
| pem += CreatePEMCertificate(key.additional_intermediate_ca_cert(i)); |
| } |
| return pem; |
| } |
| |
| std::string AttestationService::CreatePEMCertificate( |
| const std::string& certificate) { |
| const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----\n"; |
| const char kEndCertificate[] = "-----END CERTIFICATE-----"; |
| |
| std::string pem = kBeginCertificate; |
| pem += brillo::data_encoding::Base64EncodeWrapLines(certificate); |
| pem += kEndCertificate; |
| return pem; |
| } |
| |
| int AttestationService::ChooseTemporalIndex(const std::string& user, |
| const std::string& origin) { |
| std::string user_hash = crypto::SHA256HashString(user); |
| std::string origin_hash = crypto::SHA256HashString(origin); |
| int histogram[kNumTemporalValues] = {}; |
| auto database_pb = database_->GetProtobuf(); |
| for (int i = 0; i < database_pb.temporal_index_record_size(); ++i) { |
| const AttestationDatabase::TemporalIndexRecord& record = |
| database_pb.temporal_index_record(i); |
| // Ignore out-of-range index values. |
| if (record.temporal_index() < 0 || |
| record.temporal_index() >= kNumTemporalValues) |
| continue; |
| if (record.origin_hash() == origin_hash) { |
| if (record.user_hash() == user_hash) { |
| // We've previously chosen this index for this user, reuse it. |
| return record.temporal_index(); |
| } else { |
| // We've previously chosen this index for another user. |
| ++histogram[record.temporal_index()]; |
| } |
| } |
| } |
| int least_used_index = 0; |
| for (int i = 1; i < kNumTemporalValues; ++i) { |
| if (histogram[i] < histogram[least_used_index]) |
| least_used_index = i; |
| } |
| if (histogram[least_used_index] > 0) { |
| LOG(WARNING) << "Unique origin-specific identifiers have been exhausted."; |
| } |
| // Record our choice for later reference. |
| AttestationDatabase::TemporalIndexRecord* new_record = |
| database_pb.add_temporal_index_record(); |
| new_record->set_origin_hash(origin_hash); |
| new_record->set_user_hash(user_hash); |
| new_record->set_temporal_index(least_used_index); |
| database_->SaveChanges(); |
| return least_used_index; |
| } |
| |
| std::string AttestationService::GetACAWebOrigin(ACAType aca_type) const { |
| if (aca_type == TEST_ACA) { |
| return kTestACAWebOrigin; |
| } |
| return kDefaultACAWebOrigin; |
| } |
| |
| std::string AttestationService::GetACAURL(ACAType aca_type, |
| ACARequestType request_type) const { |
| std::string url = GetACAWebOrigin(aca_type); |
| switch (request_type) { |
| case kEnroll: |
| url += "/enroll"; |
| break; |
| case kGetCertificate: |
| url += "/sign"; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| return url; |
| } |
| |
| bool AttestationService::GetSubjectPublicKeyInfo( |
| KeyType key_type, |
| const std::string& public_key, |
| std::string* public_key_info) const { |
| // Only RSA is supported currently. |
| if (key_type != KEY_TYPE_RSA) { |
| return false; |
| } |
| return crypto_utility_->GetRSASubjectPublicKeyInfo(public_key, |
| public_key_info); |
| } |
| |
| void AttestationService::PrepareForEnrollment() { |
| if (IsPreparedForEnrollment()) { |
| return; |
| } |
| if (!tpm_utility_->IsTpmReady()) { |
| // Try again later. |
| worker_thread_->task_runner()->PostDelayedTask( |
| FROM_HERE, base::Bind(&AttestationService::PrepareForEnrollment, |
| base::Unretained(this)), |
| base::TimeDelta::FromSeconds(3)); |
| return; |
| } |
| base::TimeTicks start = base::TimeTicks::Now(); |
| LOG(INFO) << "Attestation: Preparing for enrollment..."; |
| // Gather information about the endorsement key. |
| std::string rsa_ek_public_key; |
| if (!tpm_utility_->GetEndorsementPublicKey(KEY_TYPE_RSA, |
| &rsa_ek_public_key)) { |
| LOG(ERROR) << "Attestation: Failed to get RSA EK public key."; |
| return; |
| } |
| std::string ecc_ek_public_key; |
| if (!tpm_utility_->GetEndorsementPublicKey(KEY_TYPE_ECC, |
| &ecc_ek_public_key)) { |
| LOG(WARNING) << "Attestation: Failed to get ECC EK public key."; |
| } |
| std::string rsa_ek_certificate; |
| if (!tpm_utility_->GetEndorsementCertificate(KEY_TYPE_RSA, |
| &rsa_ek_certificate)) { |
| LOG(ERROR) << "Attestation: Failed to get RSA EK certificate."; |
| return; |
| } |
| std::string ecc_ek_certificate; |
| if (!tpm_utility_->GetEndorsementCertificate(KEY_TYPE_ECC, |
| &ecc_ek_certificate)) { |
| LOG(WARNING) << "Attestation: Failed to get ECC EK certificate."; |
| } |
| // Create an identity key. |
| std::string rsa_identity_public_key; |
| std::string rsa_identity_key_blob; |
| if (!tpm_utility_->CreateRestrictedKey(KEY_TYPE_RSA, KEY_USAGE_SIGN, |
| &rsa_identity_public_key, |
| &rsa_identity_key_blob)) { |
| LOG(ERROR) << "Attestation: Failed to create RSA AIK."; |
| return; |
| } |
| std::string rsa_identity_public_key_der; |
| if (!tpm_utility_->GetRSAPublicKeyFromTpmPublicKey( |
| rsa_identity_public_key, &rsa_identity_public_key_der)) { |
| LOG(ERROR) << "Attestation: Failed to parse AIK public key."; |
| return; |
| } |
| // Quote PCRs. These quotes are intended to be valid for the lifetime of the |
| // identity key so they do not need external data. This only works when |
| // firmware ensures that these PCRs will not change unless the TPM owner is |
| // cleared. |
| std::string quoted_pcr_value0; |
| std::string quoted_data0; |
| std::string quote0; |
| if (!tpm_utility_->QuotePCR(0, rsa_identity_key_blob, "ed_pcr_value0, |
| "ed_data0, "e0)) { |
| LOG(ERROR) << "Attestation: Failed to generate quote for PCR_0."; |
| return; |
| } |
| std::string quoted_pcr_value1; |
| std::string quoted_data1; |
| std::string quote1; |
| if (!tpm_utility_->QuotePCR(1, rsa_identity_key_blob, "ed_pcr_value1, |
| "ed_data1, "e1)) { |
| LOG(ERROR) << "Attestation: Failed to generate quote for PCR_1."; |
| return; |
| } |
| // Store all this in the attestation database. |
| AttestationDatabase* database_pb = database_->GetMutableProtobuf(); |
| TPMCredentials* credentials_pb = database_pb->mutable_credentials(); |
| credentials_pb->set_endorsement_public_key(rsa_ek_public_key); |
| credentials_pb->set_endorsement_credential(rsa_ek_certificate); |
| credentials_pb->set_ecc_endorsement_public_key(ecc_ek_public_key); |
| credentials_pb->set_ecc_endorsement_credential(ecc_ek_certificate); |
| |
| if (!crypto_utility_->EncryptDataForGoogle( |
| rsa_ek_certificate, kDefaultACAPublicKey, |
| std::string(kDefaultACAPublicKeyID, |
| arraysize(kDefaultACAPublicKeyID) - 1), |
| credentials_pb->mutable_default_encrypted_endorsement_credential())) { |
| LOG(ERROR) << "Attestation: Failed to encrypt EK certificate."; |
| return; |
| } |
| if (!crypto_utility_->EncryptDataForGoogle( |
| rsa_ek_certificate, kTestACAPublicKey, |
| std::string(kTestACAPublicKeyID, arraysize(kTestACAPublicKeyID) - 1), |
| credentials_pb->mutable_test_encrypted_endorsement_credential())) { |
| LOG(ERROR) << "Attestation: Failed to encrypt EK certificate (test)."; |
| return; |
| } |
| IdentityKey* key_pb = database_pb->mutable_identity_key(); |
| key_pb->set_identity_public_key(rsa_identity_public_key_der); |
| key_pb->set_identity_key_blob(rsa_identity_key_blob); |
| IdentityBinding* binding_pb = database_pb->mutable_identity_binding(); |
| binding_pb->set_identity_public_key_der(rsa_identity_public_key_der); |
| binding_pb->set_identity_public_key(rsa_identity_public_key); |
| Quote* quote_pb0 = database_pb->mutable_pcr0_quote(); |
| quote_pb0->set_quote(quote0); |
| quote_pb0->set_quoted_data(quoted_data0); |
| quote_pb0->set_quoted_pcr_value(quoted_pcr_value0); |
| Quote* quote_pb1 = database_pb->mutable_pcr1_quote(); |
| quote_pb1->set_quote(quote1); |
| quote_pb1->set_quoted_data(quoted_data1); |
| quote_pb1->set_quoted_pcr_value(quoted_pcr_value1); |
| quote_pb1->set_pcr_source_hint(hwid_); |
| if (!database_->SaveChanges()) { |
| LOG(ERROR) << "Attestation: Failed to write database."; |
| return; |
| } |
| // Ignore errors when removing dependency. If failed this time, will be |
| // re-attempted on next boot. |
| tpm_utility_->RemoveOwnerDependency(); |
| base::TimeDelta delta = (base::TimeTicks::Now() - start); |
| LOG(INFO) << "Attestation: Prepared successfully (" << delta.InMilliseconds() |
| << "ms)."; |
| } |
| |
| bool AttestationService::ActivateAttestationKeyInternal( |
| const EncryptedIdentityCredential& encrypted_certificate, |
| bool save_certificate, |
| std::string* certificate) { |
| std::string certificate_local; |
| auto database_pb = database_->GetProtobuf(); |
| if (encrypted_certificate.tpm_version() == TPM_1_2) { |
| // TPM 1.2 style activate. |
| if (!tpm_utility_->ActivateIdentity( |
| database_pb.delegate().blob(), database_pb.delegate().secret(), |
| database_pb.identity_key().identity_key_blob(), |
| encrypted_certificate.asym_ca_contents(), |
| encrypted_certificate.sym_ca_attestation(), |
| &certificate_local)) { |
| LOG(ERROR) << __func__ << ": Failed to activate identity."; |
| return false; |
| } |
| } else { |
| // TPM 2.0 style activate. |
| std::string credential; |
| if (!tpm_utility_->ActivateIdentityForTpm2( |
| KEY_TYPE_RSA, database_pb.identity_key().identity_key_blob(), |
| encrypted_certificate.encrypted_seed(), |
| encrypted_certificate.credential_mac(), |
| encrypted_certificate.wrapped_certificate().wrapped_key(), |
| &credential)) { |
| LOG(ERROR) << __func__ << ": Failed to activate identity."; |
| return false; |
| } |
| if (!crypto_utility_->DecryptIdentityCertificateForTpm2( |
| credential, encrypted_certificate.wrapped_certificate(), |
| &certificate_local)) { |
| LOG(ERROR) << __func__ << ": Failed to decrypt identity certificate."; |
| return false; |
| } |
| } |
| if (save_certificate) { |
| database_->GetMutableProtobuf() |
| ->mutable_identity_key() |
| ->set_identity_credential(certificate_local); |
| if (!database_->SaveChanges()) { |
| LOG(ERROR) << __func__ << ": Failed to persist database changes."; |
| return false; |
| } |
| } |
| if (certificate) { |
| *certificate = certificate_local; |
| } |
| return true; |
| } |
| |
| void AttestationService::GetStatus( |
| const GetStatusRequest& request, |
| const GetStatusCallback& callback) { |
| auto result = std::make_shared<GetStatusReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::GetStatusTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<GetStatusReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| bool AttestationService::IsVerifiedMode() const { |
| if (!tpm_utility_->IsTpmReady()) { |
| VLOG(2) << __func__ << ": Tpm is not ready."; |
| return false; |
| } |
| std::string pcr_value; |
| if (!tpm_utility_->ReadPCR(0, &pcr_value)) { |
| LOG(WARNING) << __func__ << ": Failed to read PCR0."; |
| return false; |
| } |
| return (pcr_value == GetPCRValueForMode(kVerifiedBootMode)); |
| } |
| |
| void AttestationService::GetStatusTask( |
| const GetStatusRequest& request, |
| const std::shared_ptr<GetStatusReply>& result) { |
| result->set_prepared_for_enrollment(IsPreparedForEnrollment()); |
| result->set_enrolled(IsEnrolled()); |
| if (request.extended_status()) { |
| result->set_verified_boot(IsVerifiedMode()); |
| } |
| } |
| |
| void AttestationService::Verify( |
| const VerifyRequest& request, |
| const VerifyCallback& callback) { |
| auto result = std::make_shared<VerifyReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::VerifyTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<VerifyReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| bool AttestationService::VerifyIdentityBinding( |
| const IdentityBinding& binding) { |
| if (tpm_utility_->GetVersion() == TPM_1_2) { |
| // Reconstruct and hash a serialized TPM_IDENTITY_CONTENTS structure. |
| const std::string header("\x01\x01\x00\x00\x00\x00\x00\x79", 8); |
| std::string digest = |
| base::SHA1HashString(binding.identity_label() + |
| binding.pca_public_key()); |
| std::string identity_public_key_info; |
| if (!GetSubjectPublicKeyInfo(KEY_TYPE_RSA, |
| binding.identity_public_key_der(), |
| &identity_public_key_info)) { |
| LOG(ERROR) << __func__ << ": Failed to get identity public key info."; |
| return false; |
| } |
| if (!crypto_utility_->VerifySignature( |
| identity_public_key_info, |
| header + digest + binding.identity_public_key(), |
| binding.identity_binding())) { |
| LOG(ERROR) << __func__ << ": Failed to verify identity binding signature."; |
| return false; |
| } |
| } else if (tpm_utility_->GetVersion() == TPM_2_0) { |
| VLOG(1) << __func__ << ": Nothing to do for TPM 2.0."; |
| } else { |
| LOG(ERROR) << __func__ << ": Unsupported TPM version."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool AttestationService::VerifyQuoteSignature( |
| const std::string& aik_public_key_info, |
| const Quote& quote, |
| int pcr_index) { |
| if (!crypto_utility_->VerifySignature(aik_public_key_info, |
| quote.quoted_data(), |
| quote.quote())) { |
| LOG(ERROR) << __func__ << ": Signature mismatch."; |
| return false; |
| } |
| if (!tpm_utility_->IsQuoteForPCR(quote.quoted_data(), pcr_index)) { |
| LOG(ERROR) << __func__ << ": Invalid quote."; |
| return false; |
| } |
| return true; |
| } |
| |
| std::string AttestationService::GetPCRValueForMode(const char* mode) const { |
| std::string mode_str(mode, 3); |
| std::string mode_digest = base::SHA1HashString(mode_str); |
| std::string pcr_value; |
| if (tpm_utility_->GetVersion() == TPM_1_2) { |
| // Use SHA-1 digests for TPM 1.2. |
| std::string initial(base::kSHA1Length, 0); |
| pcr_value = base::SHA1HashString(initial + mode_digest); |
| } else if (tpm_utility_->GetVersion() == TPM_2_0) { |
| // Use SHA-256 digests for TPM 2.0. |
| std::string initial(crypto::kSHA256Length, 0); |
| mode_digest.resize(crypto::kSHA256Length); |
| pcr_value = crypto::SHA256HashString(initial + mode_digest); |
| } else { |
| LOG(ERROR) << __func__ << ": Unsupported TPM version."; |
| } |
| return pcr_value; |
| } |
| |
| bool AttestationService::VerifyPCR0Quote( |
| const std::string& aik_public_key_info, |
| const Quote& pcr0_quote) { |
| if (!VerifyQuoteSignature(aik_public_key_info, pcr0_quote, 0)) { |
| return false; |
| } |
| |
| // Check if the PCR0 value represents a known mode. |
| for (size_t i = 0; i < arraysize(kKnownBootModes); ++i) { |
| std::string pcr_value = GetPCRValueForMode(kKnownBootModes[i]); |
| if (pcr0_quote.quoted_pcr_value() == pcr_value) { |
| LOG(INFO) << "PCR0: " << GetDescriptionForMode(kKnownBootModes[i]); |
| return true; |
| } |
| } |
| LOG(WARNING) << "PCR0 value not recognized."; |
| return true; |
| } |
| |
| bool AttestationService::VerifyPCR1Quote( |
| const std::string& aik_public_key_info, |
| const Quote& pcr1_quote) { |
| if (!VerifyQuoteSignature(aik_public_key_info, pcr1_quote, 1)) { |
| return false; |
| } |
| |
| // Check that the source hint is correctly populated. |
| if (hwid_ != pcr1_quote.pcr_source_hint()) { |
| LOG(ERROR) << "PCR1 source hint does not match HWID: " << hwid_; |
| return false; |
| } |
| |
| LOG(INFO) << "PCR1 verified as " << hwid_; |
| return true; |
| } |
| |
| bool AttestationService::GetCertifiedKeyDigest( |
| const std::string& public_key_info, |
| const std::string& public_key_tpm_format, |
| std::string* key_digest) { |
| if (tpm_utility_->GetVersion() == TPM_1_2) { |
| return crypto_utility_->GetKeyDigest(public_key_info, key_digest); |
| } else if (tpm_utility_->GetVersion() == TPM_2_0) { |
| // TPM_ALG_SHA256 = 0x000B, here in big-endian order. |
| std::string prefix("\x00\x0B", 2); |
| key_digest->assign(prefix + crypto::SHA256HashString(public_key_tpm_format)); |
| return true; |
| } |
| LOG(ERROR) << __func__ << ": Unsupported TPM version."; |
| return false; |
| } |
| |
| bool AttestationService::VerifyCertifiedKey( |
| const std::string& aik_public_key_info, |
| const std::string& public_key_info, |
| const std::string& public_key_tpm_format, |
| const std::string& key_info, |
| const std::string& proof) { |
| if (!crypto_utility_->VerifySignature(aik_public_key_info, key_info, proof)) { |
| LOG(ERROR) << __func__ << ": Bad key signature."; |
| return false; |
| } |
| std::string key_digest; |
| if (!GetCertifiedKeyDigest(public_key_info, public_key_tpm_format, |
| &key_digest)) { |
| LOG(ERROR) << __func__ << ": Failed to get key digest."; |
| return false; |
| } |
| if (key_info.find(key_digest) == std::string::npos) { |
| LOG(ERROR) << __func__ << ": Public key mismatch."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool AttestationService::VerifyCertifiedKeyGeneration( |
| const std::string& aik_key_blob, |
| const std::string& aik_public_key_info) { |
| std::string key_blob; |
| std::string public_key; |
| std::string public_key_tpm_format; |
| std::string public_key_der; |
| std::string key_info; |
| std::string proof; |
| std::string nonce; |
| if (!crypto_utility_->GetRandom(kNonceSize, &nonce)) { |
| LOG(ERROR) << __func__ << ": GetRandom(nonce) failed."; |
| return false; |
| } |
| if (!tpm_utility_->CreateCertifiedKey( |
| KEY_TYPE_RSA, KEY_USAGE_SIGN, aik_key_blob, nonce, &key_blob, |
| &public_key, &public_key_tpm_format, &key_info, &proof)) { |
| LOG(ERROR) << __func__ << ": Failed to create certified key."; |
| return false; |
| } |
| if (!tpm_utility_->GetRSAPublicKeyFromTpmPublicKey(public_key_tpm_format, |
| &public_key_der)) { |
| LOG(ERROR) << __func__ << ": Failed to convert key to DER format."; |
| return false; |
| } |
| std::string public_key_info; |
| if (!GetSubjectPublicKeyInfo(KEY_TYPE_RSA, public_key_der, &public_key_info)) { |
| LOG(ERROR) << __func__ << ": Failed to get public key info."; |
| return false; |
| } |
| if (!VerifyCertifiedKey(aik_public_key_info, public_key_info, |
| public_key_tpm_format, key_info, proof)) { |
| LOG(ERROR) << __func__ << ": Bad certified key."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool AttestationService::VerifyActivateIdentity( |
| const std::string& ek_public_key_info, |
| const std::string& aik_public_key_tpm_format) { |
| std::string test_credential = "test credential"; |
| EncryptedIdentityCredential encrypted_credential; |
| if (!crypto_utility_->EncryptIdentityCredential(tpm_utility_->GetVersion(), |
| test_credential, |
| ek_public_key_info, |
| aik_public_key_tpm_format, |
| &encrypted_credential)) { |
| LOG(ERROR) << __func__ << ": Failed to encrypt identity credential"; |
| return false; |
| } |
| if (!ActivateAttestationKeyInternal(encrypted_credential, false, nullptr)) { |
| LOG(ERROR) << __func__ << ": Failed to activate identity"; |
| return false; |
| } |
| return true; |
| } |
| |
| void AttestationService::VerifyTask( |
| const VerifyRequest& request, |
| const std::shared_ptr<VerifyReply>& result) { |
| auto database_pb = database_->GetProtobuf(); |
| const TPMCredentials& credentials = database_pb.credentials(); |
| std::string ek_public_key; |
| std::string ek_cert; |
| result->set_verified(false); |
| if (credentials.has_endorsement_credential()) { |
| ek_cert = credentials.endorsement_credential(); |
| } else { |
| if (!tpm_utility_->GetEndorsementCertificate(KEY_TYPE_RSA, &ek_cert)) { |
| LOG(ERROR) << __func__ << ": Endorsement cert not available."; |
| return; |
| } |
| } |
| if (credentials.has_endorsement_public_key()) { |
| ek_public_key = credentials.endorsement_public_key(); |
| } else { |
| if (!tpm_utility_->GetEndorsementPublicKey(KEY_TYPE_RSA, &ek_public_key)) { |
| LOG(ERROR) << __func__ << ": Endorsement key not available."; |
| return; |
| } |
| } |
| std::string issuer; |
| if (!crypto_utility_->GetCertificateIssuerName(ek_cert, &issuer)) { |
| LOG(ERROR) << __func__ << ": Failed to get certificate issuer."; |
| return; |
| } |
| std::string ca_public_key; |
| if (!GetAuthorityPublicKey(issuer, request.cros_core(), &ca_public_key)) { |
| LOG(ERROR) << __func__ << ": Failed to get CA public key."; |
| return; |
| } |
| if (!crypto_utility_->VerifyCertificate(ek_cert, ca_public_key)) { |
| LOG(WARNING) << __func__ << ": Bad endorsement credential."; |
| return; |
| } |
| // Verify that the given public key matches the public key in the credential. |
| // Note: Do not use any openssl functions that attempt to decode the public |
| // key. These will fail because openssl does not recognize the OAEP key type. |
| std::string cert_public_key; |
| if (!crypto_utility_->GetCertificatePublicKey(ek_cert, &cert_public_key)) { |
| LOG(ERROR) << __func__ << ": Failed to get certificate public key."; |
| return; |
| } |
| if (cert_public_key != ek_public_key) { |
| LOG(ERROR) << __func__ << ": Bad certificate public key."; |
| return; |
| } |
| // All done if we only needed to verify EK. Otherwise, continue with full |
| // verification. |
| if (request.ek_only()) { |
| result->set_verified(true); |
| return; |
| } |
| std::string identity_public_key_info; |
| if (!GetSubjectPublicKeyInfo(KEY_TYPE_RSA, |
| database_pb.identity_binding().identity_public_key_der(), |
| &identity_public_key_info)) { |
| LOG(ERROR) << __func__ << ": Failed to get identity public key info."; |
| return; |
| } |
| if (!VerifyIdentityBinding(database_pb.identity_binding())) { |
| LOG(ERROR) << __func__ << ": Bad identity binding."; |
| return; |
| } |
| if (!VerifyPCR0Quote(identity_public_key_info, database_pb.pcr0_quote())) { |
| LOG(ERROR) << __func__ << ": Bad PCR0 quote."; |
| return; |
| } |
| if (!VerifyPCR1Quote(identity_public_key_info, database_pb.pcr1_quote())) { |
| // Don't fail because many devices don't use PCR1. |
| LOG(WARNING) << __func__ << ": Bad PCR1 quote."; |
| } |
| if (!VerifyCertifiedKeyGeneration( |
| database_pb.identity_key().identity_key_blob(), |
| identity_public_key_info)) { |
| LOG(ERROR) << __func__ << ": Failed to verify certified key generation."; |
| return; |
| } |
| std::string ek_public_key_info; |
| if (!GetSubjectPublicKeyInfo(KEY_TYPE_RSA, ek_public_key, |
| &ek_public_key_info)) { |
| LOG(ERROR) << __func__ << ": Failed to get EK public key info."; |
| return; |
| } |
| if (!VerifyActivateIdentity( |
| ek_public_key_info, |
| database_pb.identity_binding().identity_public_key())) { |
| LOG(ERROR) << __func__ << ": Failed to verify identity activation."; |
| return; |
| } |
| LOG(INFO) << "Attestation: Verified OK."; |
| result->set_verified(true); |
| } |
| |
| void AttestationService::CreateEnrollRequest( |
| const CreateEnrollRequestRequest& request, |
| const CreateEnrollRequestCallback& callback) { |
| auto result = std::make_shared<CreateEnrollRequestReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::CreateEnrollRequestTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<CreateEnrollRequestReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::CreateEnrollRequestTask( |
| const CreateEnrollRequestRequest& request, |
| const std::shared_ptr<CreateEnrollRequestReply>& result) { |
| if (!CreateEnrollRequestInternal(request.aca_type(), |
| result->mutable_pca_request())) { |
| result->clear_pca_request(); |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| } |
| } |
| |
| void AttestationService::FinishEnroll( |
| const FinishEnrollRequest& request, |
| const FinishEnrollCallback& callback) { |
| auto result = std::make_shared<FinishEnrollReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::FinishEnrollTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<FinishEnrollReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::FinishEnrollTask( |
| const FinishEnrollRequest& request, |
| const std::shared_ptr<FinishEnrollReply>& result) { |
| std::string server_error; |
| if (!FinishEnrollInternal(request.pca_response(), &server_error)) { |
| if (server_error.empty()) { |
| LOG(ERROR) << __func__ << ": Server error"; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| } else { |
| LOG(ERROR) << __func__ << ": Server error details: " << server_error; |
| result->set_status(STATUS_REQUEST_DENIED_BY_CA); |
| } |
| } |
| } |
| |
| void AttestationService::CreateCertificateRequest( |
| const CreateCertificateRequestRequest& request, |
| const CreateCertificateRequestCallback& callback) { |
| auto result = std::make_shared<CreateCertificateRequestReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::CreateCertificateRequestTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<CreateCertificateRequestReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::CreateCertificateRequestTask( |
| const CreateCertificateRequestRequest& request, |
| const std::shared_ptr<CreateCertificateRequestReply>& result) { |
| std::string key_label; |
| if (!crypto_utility_->GetRandom(kNonceSize, &key_label)) { |
| LOG(ERROR) << __func__ << ": GetRandom(message_id) failed."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| std::string nonce; |
| if (!crypto_utility_->GetRandom(kNonceSize, &nonce)) { |
| LOG(ERROR) << __func__ << ": GetRandom(nonce) failed."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| std::string key_blob; |
| std::string public_key; |
| std::string public_key_tpm_format; |
| std::string key_info; |
| std::string proof; |
| auto database_pb = database_->GetProtobuf(); |
| CertifiedKey key; |
| if (!tpm_utility_->CreateCertifiedKey( |
| KEY_TYPE_RSA, KEY_USAGE_SIGN, |
| database_pb.identity_key().identity_key_blob(), nonce, &key_blob, |
| &public_key, &public_key_tpm_format, &key_info, &proof)) { |
| LOG(ERROR) << __func__ << ": Failed to create a key."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| key.set_key_blob(key_blob); |
| key.set_public_key(public_key); |
| key.set_key_name(key_label); |
| key.set_public_key_tpm_format(public_key_tpm_format); |
| key.set_certified_key_info(key_info); |
| key.set_certified_key_proof(proof); |
| key.set_key_type(KEY_TYPE_RSA); |
| key.set_key_usage(KEY_USAGE_SIGN); |
| std::string message_id; |
| if (!CreateCertificateRequestInternal(request.username(), key, |
| request.certificate_profile(), |
| request.request_origin(), |
| result->mutable_pca_request(), |
| &message_id)) { |
| result->clear_pca_request(); |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| std::string serialized_key; |
| if (!key.SerializeToString(&serialized_key)) { |
| LOG(ERROR) << __func__ << ": Failed to serialize key protobuf."; |
| result->clear_pca_request(); |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| pending_cert_requests_[message_id] = serialized_key; |
| } |
| |
| void AttestationService::FinishCertificateRequest( |
| const FinishCertificateRequestRequest& request, |
| const FinishCertificateRequestCallback& callback) { |
| auto result = std::make_shared<FinishCertificateRequestReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::FinishCertificateRequestTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<FinishCertificateRequestReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::FinishCertificateRequestTask( |
| const FinishCertificateRequestRequest& request, |
| const std::shared_ptr<FinishCertificateRequestReply>& result) { |
| AttestationCertificateResponse response_pb; |
| if (!response_pb.ParseFromString(request.pca_response())) { |
| LOG(ERROR) << __func__ << ": Failed to parse response from Attestation CA."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| CertRequestMap::iterator iter = pending_cert_requests_.find( |
| response_pb.message_id()); |
| if (iter == pending_cert_requests_.end()) { |
| LOG(ERROR) << __func__ << ": Pending request not found."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| if (response_pb.status() != OK) { |
| LOG(ERROR) << __func__ << ": Error received from Attestation CA: " |
| << response_pb.detail(); |
| pending_cert_requests_.erase(iter); |
| result->set_status(STATUS_REQUEST_DENIED_BY_CA); |
| return; |
| } |
| CertifiedKey key; |
| if (!key.ParseFromArray(iter->second.data(), |
| iter->second.size())) { |
| LOG(ERROR) << __func__ << ": Failed to parse pending request key."; |
| pending_cert_requests_.erase(iter); |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| pending_cert_requests_.erase(iter); |
| if (!PopulateAndStoreCertifiedKey(response_pb, request.username(), |
| request.key_label(), &key, |
| result->mutable_certificate())) { |
| result->clear_certificate(); |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| } |
| |
| bool AttestationService::ValidateEnterpriseChallenge( |
| const SignedData& signed_challenge) { |
| const char kExpectedChallengePrefix[] = "EnterpriseKeyChallenge"; |
| if (!crypto_utility_->VerifySignatureUsingHexKey( |
| kEnterpriseSigningPublicKey, |
| signed_challenge.data(), |
| signed_challenge.signature())) { |
| LOG(ERROR) << __func__ << ": Failed to verify challenge signature."; |
| return false; |
| } |
| Challenge challenge; |
| if (!challenge.ParseFromString(signed_challenge.data())) { |
| LOG(ERROR) << __func__ << ": Failed to parse challenge protobuf."; |
| return false; |
| } |
| if (challenge.prefix() != kExpectedChallengePrefix) { |
| LOG(ERROR) << __func__ << ": Unexpected challenge prefix."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool AttestationService::EncryptEnterpriseKeyInfo( |
| const KeyInfo& key_info, |
| EncryptedData* encrypted_data) { |
| std::string serialized; |
| if (!key_info.SerializeToString(&serialized)) { |
| LOG(ERROR) << "Failed to serialize key info."; |
| return false; |
| } |
| std::string enterprise_key_id( |
| kEnterpriseEncryptionPublicKeyID, |
| arraysize(kEnterpriseEncryptionPublicKeyID) - 1); |
| return crypto_utility_->EncryptDataForGoogle( |
| serialized, kEnterpriseEncryptionPublicKey, |
| enterprise_key_id, encrypted_data); |
| } |
| |
| void AttestationService::SignEnterpriseChallenge( |
| const SignEnterpriseChallengeRequest& request, |
| const SignEnterpriseChallengeCallback& callback) { |
| auto result = std::make_shared<SignEnterpriseChallengeReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::SignEnterpriseChallengeTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<SignEnterpriseChallengeReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::SignEnterpriseChallengeTask( |
| const SignEnterpriseChallengeRequest& request, |
| const std::shared_ptr<SignEnterpriseChallengeReply>& result) { |
| CertifiedKey key; |
| if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| |
| // Validate that the challenge is coming from the expected source. |
| SignedData signed_challenge; |
| if (!signed_challenge.ParseFromString(request.challenge())) { |
| LOG(ERROR) << __func__ << ": Failed to parse signed challenge."; |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| if (!ValidateEnterpriseChallenge(signed_challenge)) { |
| LOG(ERROR) << __func__ << ": Invalid challenge."; |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| // Add a nonce to ensure this service cannot be used to sign arbitrary data. |
| std::string nonce; |
| if (!crypto_utility_->GetRandom(kChallengeSignatureNonceSize, &nonce)) { |
| LOG(ERROR) << __func__ << ": Failed to generate nonce."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| |
| bool is_user_specific = request.has_username(); |
| KeyInfo key_info; |
| // EUK -> Enterprise User Key |
| // EMK -> Enterprise Machine Key |
| key_info.set_key_type(is_user_specific ? EUK : EMK); |
| key_info.set_domain(request.domain()); |
| key_info.set_device_id(request.device_id()); |
| // Only include the certificate if this is a user key. |
| if (is_user_specific) { |
| key_info.set_certificate(CreatePEMCertificateChain(key)); |
| } |
| if (is_user_specific && request.include_signed_public_key()) { |
| std::string spkac; |
| if (!crypto_utility_->CreateSPKAC(key.key_blob(), key.public_key(), |
| &spkac)) { |
| LOG(ERROR) << __func__ << ": Failed to create signed public key."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| key_info.set_signed_public_key_and_challenge(spkac); |
| } |
| ChallengeResponse response_pb; |
| *response_pb.mutable_challenge() = signed_challenge; |
| response_pb.set_nonce(nonce); |
| if (!EncryptEnterpriseKeyInfo(key_info, |
| response_pb.mutable_encrypted_key_info())) { |
| LOG(ERROR) << __func__ << ": Failed to encrypt KeyInfo."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| |
| // Serialize and sign the response protobuf. |
| std::string serialized; |
| if (!response_pb.SerializeToString(&serialized)) { |
| LOG(ERROR) << __func__ << ": Failed to serialize response protobuf."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| if (!SignChallengeData(key, serialized, |
| result->mutable_challenge_response())) { |
| result->clear_challenge_response(); |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| } |
| |
| void AttestationService::SignSimpleChallenge( |
| const SignSimpleChallengeRequest& request, |
| const SignSimpleChallengeCallback& callback) { |
| auto result = std::make_shared<SignSimpleChallengeReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::SignSimpleChallengeTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<SignSimpleChallengeReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::SignSimpleChallengeTask( |
| const SignSimpleChallengeRequest& request, |
| const std::shared_ptr<SignSimpleChallengeReply>& result) { |
| CertifiedKey key; |
| if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| // Add a nonce to ensure this service cannot be used to sign arbitrary data. |
| std::string nonce; |
| if (!crypto_utility_->GetRandom(kChallengeSignatureNonceSize, &nonce)) { |
| LOG(ERROR) << __func__ << ": Failed to generate nonce."; |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| if (!SignChallengeData(key, request.challenge() + nonce, |
| result->mutable_challenge_response())) { |
| result->clear_challenge_response(); |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| } |
| |
| bool AttestationService::SignChallengeData(const CertifiedKey& key, |
| const std::string& data_to_sign, |
| std::string* response) { |
| std::string signature; |
| if (!tpm_utility_->Sign(key.key_blob(), data_to_sign, &signature)) { |
| LOG(ERROR) << __func__ << ": Failed to sign data."; |
| return false; |
| } |
| SignedData signed_data; |
| signed_data.set_data(data_to_sign); |
| signed_data.set_signature(signature); |
| if (!signed_data.SerializeToString(response)) { |
| LOG(ERROR) << __func__ << ": Failed to serialize signed data."; |
| return false; |
| } |
| return true; |
| } |
| |
| void AttestationService::SetKeyPayload( |
| const SetKeyPayloadRequest& request, |
| const SetKeyPayloadCallback& callback) { |
| auto result = std::make_shared<SetKeyPayloadReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::SetKeyPayloadTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<SetKeyPayloadReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::SetKeyPayloadTask( |
| const SetKeyPayloadRequest& request, |
| const std::shared_ptr<SetKeyPayloadReply>& result) { |
| CertifiedKey key; |
| if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { |
| result->set_status(STATUS_INVALID_PARAMETER); |
| return; |
| } |
| key.set_payload(request.payload()); |
| if (!SaveKey(request.username(), request.key_label(), key)) { |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| } |
| |
| void AttestationService::DeleteKeys( |
| const DeleteKeysRequest& request, |
| const DeleteKeysCallback& callback) { |
| auto result = std::make_shared<DeleteKeysReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::DeleteKeysTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<DeleteKeysReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::DeleteKeysTask( |
| const DeleteKeysRequest& request, |
| const std::shared_ptr<DeleteKeysReply>& result) { |
| if (!DeleteKeysByPrefix(request.username(), request.key_prefix())) { |
| LOG(ERROR) << __func__ << ": Failed to delete keys with prefix: " |
| << request.key_prefix(); |
| result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); |
| return; |
| } |
| } |
| |
| void AttestationService::ResetIdentity( |
| const ResetIdentityRequest& request, |
| const ResetIdentityCallback& callback) { |
| auto result = std::make_shared<ResetIdentityReply>(); |
| base::Closure task = |
| base::Bind(&AttestationService::ResetIdentityTask, |
| base::Unretained(this), request, result); |
| base::Closure reply = base::Bind( |
| &AttestationService::TaskRelayCallback<ResetIdentityReply>, |
| GetWeakPtr(), callback, result); |
| worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); |
| } |
| |
| void AttestationService::ResetIdentityTask( |
| const ResetIdentityRequest& request, |
| const std::shared_ptr<ResetIdentityReply>& result) { |
| LOG(ERROR) << __func__ << ": Not implemented."; |
| result->set_status(STATUS_NOT_SUPPORTED); |
| } |
| |
| void AttestationService::SetSystemSalt( |
| const SetSystemSaltRequest& request, |
| const SetSystemSaltCallback& callback) { |
| system_salt_.assign(request.system_salt()); |
| brillo::cryptohome::home::SetSystemSalt(&system_salt_); |
| SetSystemSaltReply result; |
| callback.Run(result); |
| } |
| |
| base::WeakPtr<AttestationService> AttestationService::GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| } // namespace attestation |