GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/receiver/session_token.cc
Date: 2025-06-22 02:36:02
Exec Total Coverage
Lines: 64 83 77.1%
Branches: 77 152 50.7%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * This file contains a very simple implementation of session tokens.
5 */
6
7 #include "session_token.h"
8
9 #include <limits>
10
11 #include "crypto/encrypt.h"
12 #include "json.h"
13 #include "json_document.h"
14 #include "util/logging.h"
15 #include "util/platform.h"
16 #include "util/pointer.h"
17 #include "util/string.h"
18
19 namespace receiver {
20
21 /**
22 * Generate a session token from a public key_id, a path argument and a max
23 * lease time (given in seconds).
24 *
25 * The session token encodes a lease valid for "path" until now() +
26 * max_lease_time.
27 *
28 * Returns the session token, the (public) token_id and the token secret.
29 */
30 14 bool GenerateSessionToken(const std::string &key_id, const std::string &path,
31 uint64_t max_lease_time, std::string *session_token,
32 std::string *public_token_id,
33 std::string *token_secret) {
34
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
14 if (session_token == NULL || public_token_id == NULL
35
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 || token_secret == NULL) {
36 2 return false;
37 }
38
39
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 12 times.
12 if (key_id.empty() && path.empty()) {
40 return false;
41 }
42
43
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 const UniquePtr<cipher::Key> secret(cipher::Key::CreateRandomly(32));
44
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if (!secret.IsValid()) {
45 return false;
46 }
47
48 const UniquePtr<cipher::Cipher> cipher(
49
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 cipher::Cipher::Create(cipher::kAes256Cbc));
50
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if (!cipher.IsValid()) {
51 return false;
52 }
53
54
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 *public_token_id = key_id + path;
55
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 *token_secret = secret->ToBase64();
56
57 12 const uint64_t current_time = platform_monotonic_time();
58
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if (std::numeric_limits<uint64_t>::max() - max_lease_time < current_time) {
59 return false;
60 }
61
62
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 const std::string expiry(StringifyUint(current_time + max_lease_time));
63
64 12 std::string encrypted_body;
65
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
24 if (!cipher->Encrypt(
66
5/10
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 12 times.
✗ Branch 11 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 12 times.
24 "{\"path\" : \"" + path + "\", \"expiry\" : \"" + expiry + "\"}",
67 12 *secret, &encrypted_body)) {
68 return false;
69 }
70
71
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
36 *session_token = Base64("{\"token_id\" : \"" + *public_token_id
72
3/6
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
36 + "\", \"blob\" : \"" + Base64(encrypted_body)
73
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
36 + "\"}");
74
75 12 return true;
76 12 }
77
78 /**
79 * Obtain the public_id from a session token
80 */
81
82 4 bool GetTokenPublicId(const std::string &token, std::string *public_id) {
83
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (public_id == NULL) {
84 return false;
85 }
86
87 4 std::string debased64_token;
88
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 if (!Debase64(token, &debased64_token)) {
89 return false;
90 }
91
92 const UniquePtr<JsonDocument> token_json(
93
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 JsonDocument::Create(debased64_token));
94
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!token_json.IsValid()) {
95 return false;
96 }
97
98
2/4
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
4 const JSON *token_id = JsonDocument::SearchInObject(token_json->root(),
99 "token_id", JSON_STRING);
100
2/4
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
4 const JSON *blob = JsonDocument::SearchInObject(token_json->root(), "blob",
101 JSON_STRING);
102
103
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (token_id == NULL || blob == NULL) {
104 return false;
105 }
106
107
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 *public_id = token_id->string_value;
108
109 4 return true;
110 4 }
111
112 /*
113 * Check the validity of a session token using the associated secret
114 */
115 6 TokenCheckResult CheckToken(const std::string &token, const std::string &secret,
116 std::string *lease_path) {
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!lease_path) {
118 return kInvalid;
119 }
120
121 6 std::string debased64_token;
122
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
6 if (!Debase64(token, &debased64_token)) {
123 return kInvalid;
124 }
125
126 const UniquePtr<JsonDocument> token_json(
127
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 JsonDocument::Create(debased64_token));
128
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (!token_json.IsValid()) {
129 return kInvalid;
130 }
131
132
2/4
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
6 const JSON *token_id = JsonDocument::SearchInObject(token_json->root(),
133 "token_id", JSON_STRING);
134
2/4
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
6 const JSON *blob = JsonDocument::SearchInObject(token_json->root(), "blob",
135 JSON_STRING);
136
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (token_id == NULL || blob == NULL) {
137 return kInvalid;
138 }
139
140 6 std::string debased64_secret;
141
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
6 if (!Debase64(secret, &debased64_secret)) {
142 return kInvalid;
143 }
144 const UniquePtr<cipher::Key> key(
145
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 cipher::Key::CreateFromString(debased64_secret));
146
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (!key.IsValid()) {
147 return kInvalid;
148 }
149
150 6 std::string encrypted_body;
151
3/6
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 6 times.
6 if (!Debase64(blob->string_value, &encrypted_body)) {
152 return kInvalid;
153 }
154
155 6 std::string body;
156
2/4
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 if (!cipher::Cipher::Decrypt(encrypted_body, *key, &body)) {
157 return kInvalid;
158 }
159
160
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 const UniquePtr<JsonDocument> body_json(JsonDocument::Create(body));
161
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (!token_json.IsValid()) {
162 return kInvalid;
163 }
164
165
2/4
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
6 const JSON *path = JsonDocument::SearchInObject(body_json->root(), "path",
166 JSON_STRING);
167
2/4
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
6 const JSON *expiry = JsonDocument::SearchInObject(body_json->root(), "expiry",
168 JSON_STRING);
169
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (path == NULL || expiry == NULL) {
170 return kInvalid;
171 }
172
173 // TODO(radu): can we still use monotonic time if the process restarts?
174
2/4
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
6 const uint64_t expiry_time = String2Uint64(expiry->string_value);
175 6 const uint64_t current_time = platform_monotonic_time();
176
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (current_time > expiry_time) {
177 2 return kExpired;
178 }
179
180
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 *lease_path = path->string_value;
181
182 4 return kValid;
183 6 }
184
185 } // namespace receiver
186