GCC Code Coverage Report


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