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