1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // This file provides methods to encode credential metadata for storage in the
6 // macOS keychain.
7 
8 #ifndef DEVICE_FIDO_MAC_CREDENTIAL_METADATA_H_
9 #define DEVICE_FIDO_MAC_CREDENTIAL_METADATA_H_
10 
11 #include <memory>
12 #include <string>
13 #include <vector>
14 
15 #include "base/component_export.h"
16 #include "base/containers/span.h"
17 #include "base/macros.h"
18 #include "base/optional.h"
19 #include "base/strings/string_piece_forward.h"
20 #include "crypto/aead.h"
21 #include "crypto/hmac.h"
22 #include "crypto/symmetric_key.h"
23 
24 namespace device {
25 
26 class PublicKeyCredentialUserEntity;
27 
28 namespace fido {
29 namespace mac {
30 
31 // CredentialMetadata is the metadata for a Touch ID credential stored, in an
32 // encrypted/authenticated format, in the macOS keychain.  Values of this type
33 // should be moved whenever possible.
COMPONENT_EXPORT(DEVICE_FIDO)34 struct COMPONENT_EXPORT(DEVICE_FIDO) CredentialMetadata {
35  public:
36   static CredentialMetadata FromPublicKeyCredentialUserEntity(
37       const PublicKeyCredentialUserEntity&,
38       bool is_resident);
39 
40   CredentialMetadata(std::vector<uint8_t> user_id_,
41                      std::string user_name_,
42                      std::string user_display_name_,
43                      bool is_resident_);
44   CredentialMetadata(const CredentialMetadata&);
45   CredentialMetadata(CredentialMetadata&&);
46   CredentialMetadata& operator=(CredentialMetadata&&);
47   ~CredentialMetadata();
48 
49   PublicKeyCredentialUserEntity ToPublicKeyCredentialUserEntity();
50 
51   // The following correspond to the fields of the same name in
52   // PublicKeyCredentialUserEntity
53   // (https://www.w3.org/TR/webauthn/#sctn-user-credential-params).
54   std::vector<uint8_t> user_id;
55   std::string user_name;
56   std::string user_display_name;
57 
58   // Whether this credential has the resident key (rk) bit and may be returned
59   // in response to GetAssertion requests with an empty allowList.
60   bool is_resident;
61 };
62 
63 // Generates a random secret for encrypting and authenticating credential
64 // metadata for storage in the macOS keychain.
65 //
66 // Chrome stores this secret in the Profile Prefs. This allows us to logically
67 // separate credentials per Chrome profile despite being stored in the same
68 // keychain. It also guarantees that account metadata in the OS keychain is
69 // rendered unusable after the Chrome profile and the associated encryption key
70 // have been deleted, in order to limit leakage of credential metadata into the
71 // keychain.
72 COMPONENT_EXPORT(DEVICE_FIDO)
73 std::string GenerateCredentialMetadataSecret();
74 
75 // SealCredentialId encrypts the given CredentialMetadata into a credential id.
76 //
77 // Credential IDs have following format:
78 //
79 //    | version  |    nonce   | AEAD(pt=CBOR(metadata),    |
80 //    | (1 byte) | (12 bytes) |      nonce=nonce,          |
81 //    |          |            |      ad=(version, rpID))   |
82 //
83 // with version as 0x00, a random 12-byte nonce, and using AES-256-GCM as the
84 // AEAD.
85 //
86 // The |user_name| and |user_display_name| fields may be truncated before
87 // encryption. The truncated values are guaranteed to be valid UTF-8.
88 COMPONENT_EXPORT(DEVICE_FIDO)
89 std::vector<uint8_t> SealCredentialId(const std::string& secret,
90                                       const std::string& rp_id,
91                                       const CredentialMetadata& metadata);
92 
93 // UnsealCredentialId attempts to decrypt a CredentialMetadata from a credential
94 // id.
95 COMPONENT_EXPORT(DEVICE_FIDO)
96 base::Optional<CredentialMetadata> UnsealCredentialId(
97     const std::string& secret,
98     const std::string& rp_id,
99     base::span<const uint8_t> credential_id);
100 
101 // EncodeRpIdAndUserId encodes the concatenation of RP ID and user ID for
102 // storage in the macOS keychain.
103 //
104 // This encoding allows lookup of credentials for a given RP and user but
105 // without the credential ID.
106 COMPONENT_EXPORT(DEVICE_FIDO)
107 std::string EncodeRpIdAndUserId(const std::string& secret,
108                                 const std::string& rp_id,
109                                 base::span<const uint8_t> user_id);
110 
111 // EncodeRpId encodes the given RP ID for storage in the macOS keychain.
112 COMPONENT_EXPORT(DEVICE_FIDO)
113 std::string EncodeRpId(const std::string& secret, const std::string& rp_id);
114 
115 // DecodeRpId attempts to decode a given RP ID from the keychain.
116 //
117 // This can be used to test whether a set of credential metadata was created
118 // under the given secret without knowing the RP ID (which would be required to
119 // unseal a credential ID).
120 COMPONENT_EXPORT(DEVICE_FIDO)
121 base::Optional<std::string> DecodeRpId(const std::string& secret,
122                                        const std::string& ciphertext);
123 
124 // Seals a legacy V0 credential ID.
125 COMPONENT_EXPORT(DEVICE_FIDO)
126 std::vector<uint8_t> SealLegacyV0CredentialIdForTestingOnly(
127     const std::string& secret,
128     const std::string& rp_id,
129     const std::vector<uint8_t>& user_id,
130     const std::string& user_name,
131     const std::string& user_display_name);
132 
133 }  // namespace mac
134 }  // namespace fido
135 }  // namespace device
136 
137 #endif  // DEVICE_FIDO_MAC_CREDENTIAL_METADATA_H_
138