Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: STM32F746_iothub_client_sample_mqtt f767zi_mqtt iothub_client_sample_amqp iothub_client_sample_http ... more
sastoken.c
00001 // Copyright (c) Microsoft. All rights reserved. 00002 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 00003 00004 #include <stdlib.h> 00005 #include <stdio.h> 00006 #include <string.h> 00007 #include "azure_c_shared_utility/gballoc.h" 00008 #include "azure_c_shared_utility/sastoken.h" 00009 #include "azure_c_shared_utility/urlencode.h" 00010 #include "azure_c_shared_utility/hmacsha256.h" 00011 #include "azure_c_shared_utility/base64.h" 00012 #include "azure_c_shared_utility/agenttime.h" 00013 #include "azure_c_shared_utility/strings.h" 00014 #include "azure_c_shared_utility/buffer_.h" 00015 #include "azure_c_shared_utility/xlogging.h" 00016 #include "azure_c_shared_utility/crt_abstractions.h" 00017 00018 static double getExpiryValue(const char* expiryASCII) 00019 { 00020 double value = 0; 00021 size_t i = 0; 00022 for (i = 0; expiryASCII[i] != '\0'; i++) 00023 { 00024 if (expiryASCII[i] >= '0' && expiryASCII[i] <= '9') 00025 { 00026 value = value * 10 + (expiryASCII[i] - '0'); 00027 } 00028 else 00029 { 00030 value = 0; 00031 break; 00032 } 00033 } 00034 return value; 00035 } 00036 00037 bool SASToken_Validate(STRING_HANDLE sasToken) 00038 { 00039 bool result; 00040 /*Codes_SRS_SASTOKEN_25_025: [**SASToken_Validate shall get the SASToken value by invoking STRING_c_str on the handle.**]***/ 00041 const char* sasTokenArray = STRING_c_str(sasToken); 00042 00043 /* Codes_SRS_SASTOKEN_25_024: [**If handle is NULL then SASToken_Validate shall return false.**] */ 00044 /* Codes_SRS_SASTOKEN_25_026: [**If STRING_c_str on handle return NULL then SASToken_Validate shall return false.**] */ 00045 if (sasToken == NULL || sasTokenArray == NULL) 00046 { 00047 result = false; 00048 } 00049 else 00050 { 00051 int seStart = -1, seStop = -1; 00052 int srStart = -1, srStop = -1; 00053 int sigStart = -1, sigStop = -1; 00054 int tokenLength = (int)STRING_length(sasToken); 00055 int i; 00056 for (i = 0; i < tokenLength; i++) 00057 { 00058 if (sasTokenArray[i] == 's' && sasTokenArray[i + 1] == 'e' && sasTokenArray[i + 2] == '=') // Look for se= 00059 { 00060 seStart = i + 3; 00061 if (srStart > 0 && srStop < 0) 00062 { 00063 if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') // look for either & or space 00064 srStop = i - 1; 00065 else if (sasTokenArray[i - 1] == '&') 00066 srStop = i - 2; 00067 else 00068 seStart = -1; // as the format is not either "&se=" or " se=" 00069 } 00070 else if (sigStart > 0 && sigStop < 0) 00071 { 00072 if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') 00073 sigStop = i - 1; 00074 else if (sasTokenArray[i - 1] == '&') 00075 sigStop = i - 2; 00076 else 00077 seStart = -1; 00078 } 00079 } 00080 else if (sasTokenArray[i] == 's' && sasTokenArray[i + 1] == 'r' && sasTokenArray[i + 2] == '=') // Look for sr= 00081 { 00082 srStart = i + 3; 00083 if (seStart > 0 && seStop < 0) 00084 { 00085 if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') 00086 seStop = i - 1; 00087 else if (sasTokenArray[i - 1] == '&') 00088 seStop = i - 2; 00089 else 00090 srStart = -1; 00091 } 00092 else if (sigStart > 0 && sigStop < 0) 00093 { 00094 if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') 00095 sigStop = i - 1; 00096 else if (sasTokenArray[i - 1] == '&') 00097 sigStop = i - 2; 00098 else 00099 srStart = -1; 00100 } 00101 } 00102 else if (sasTokenArray[i] == 's' && sasTokenArray[i + 1] == 'i' && sasTokenArray[i + 2] == 'g' && sasTokenArray[i + 3] == '=') // Look for sig= 00103 { 00104 sigStart = i + 4; 00105 if (srStart > 0 && srStop < 0) 00106 { 00107 if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') 00108 srStop = i - 1; 00109 else if (sasTokenArray[i - 1] == '&') 00110 srStop = i - 2; 00111 else 00112 sigStart = -1; 00113 } 00114 else if (seStart > 0 && seStop < 0) 00115 { 00116 if (sasTokenArray[i - 1] != '&' && sasTokenArray[i - 1] == ' ') 00117 seStop = i - 1; 00118 else if (sasTokenArray[i - 1] == '&') 00119 seStop = i - 2; 00120 else 00121 sigStart = -1; 00122 } 00123 } 00124 } 00125 00126 /*Codes_SRS_SASTOKEN_25_027: [**If SASTOKEN does not obey the SASToken format then SASToken_Validate shall return false.**]***/ 00127 /*Codes_SRS_SASTOKEN_25_028: [**SASToken_validate shall check for the presence of sr, se and sig from the token and return false if not found**]***/ 00128 if (seStart < 0 || srStart < 0 || sigStart < 0) 00129 { 00130 result = false; 00131 } 00132 else 00133 { 00134 if (seStop < 0) 00135 { 00136 seStop = tokenLength; 00137 } 00138 else if (srStop < 0) 00139 { 00140 srStop = tokenLength; 00141 } 00142 else if (sigStop < 0) 00143 { 00144 sigStop = tokenLength; 00145 } 00146 00147 if ((seStop <= seStart) || 00148 (srStop <= srStart) || 00149 (sigStop <= sigStart)) 00150 { 00151 result = false; 00152 } 00153 else 00154 { 00155 char* expiryASCII = (char*)malloc(seStop - seStart + 1); 00156 /*Codes_SRS_SASTOKEN_25_031: [**If malloc fails during validation then SASToken_Validate shall return false.**]***/ 00157 if (expiryASCII == NULL) 00158 { 00159 result = false; 00160 } 00161 else 00162 { 00163 double expiry; 00164 // Add the Null terminator here 00165 memset(expiryASCII, 0, seStop - seStart + 1); 00166 for (i = seStart; i < seStop; i++) 00167 { 00168 // The se contains the expiration values, if a & token is encountered then 00169 // the se field is complete. 00170 if (sasTokenArray[i] == '&') 00171 { 00172 break; 00173 } 00174 expiryASCII[i - seStart] = sasTokenArray[i]; 00175 } 00176 expiry = getExpiryValue(expiryASCII); 00177 /*Codes_SRS_SASTOKEN_25_029: [**SASToken_validate shall check for expiry time from token and if token has expired then would return false **]***/ 00178 if (expiry <= 0) 00179 { 00180 result = false; 00181 } 00182 else 00183 { 00184 double secSinceEpoch = get_difftime(get_time(NULL), (time_t)0); 00185 if (expiry < secSinceEpoch) 00186 { 00187 /*Codes_SRS_SASTOKEN_25_029: [**SASToken_validate shall check for expiry time from token and if token has expired then would return false **]***/ 00188 result = false; 00189 } 00190 else 00191 { 00192 /*Codes_SRS_SASTOKEN_25_030: [**SASToken_validate shall return true only if the format is obeyed and the token has not yet expired **]***/ 00193 result = true; 00194 } 00195 } 00196 free(expiryASCII); 00197 } 00198 } 00199 } 00200 } 00201 00202 return result; 00203 } 00204 00205 static STRING_HANDLE construct_sas_token(const char* key, const char* scope, const char* keyname, size_t expiry) 00206 { 00207 STRING_HANDLE result; 00208 00209 char tokenExpirationTime[32] = { 0 }; 00210 00211 BUFFER_HANDLE decodedKey; 00212 00213 /*Codes_SRS_SASTOKEN_06_029: [The key parameter is decoded from base64.]*/ 00214 if ((decodedKey = Base64_Decoder(key)) == NULL) 00215 { 00216 /*Codes_SRS_SASTOKEN_06_030: [If there is an error in the decoding then SASToken_Create shall return NULL.]*/ 00217 LogError("Unable to decode the key for generating the SAS."); 00218 result = NULL; 00219 } 00220 else 00221 { 00222 /*Codes_SRS_SASTOKEN_06_026: [If the conversion to string form fails for any reason then SASToken_Create shall return NULL.]*/ 00223 if (size_tToString(tokenExpirationTime, sizeof(tokenExpirationTime), expiry) != 0) 00224 { 00225 LogError("For some reason converting seconds to a string failed. No SAS can be generated."); 00226 result = NULL; 00227 } 00228 else 00229 { 00230 STRING_HANDLE toBeHashed = NULL; 00231 BUFFER_HANDLE hash = NULL; 00232 if (((hash = BUFFER_new()) == NULL) || 00233 ((toBeHashed = STRING_new()) == NULL) || 00234 ((result = STRING_new()) == NULL)) 00235 { 00236 LogError("Unable to allocate memory to prepare SAS token."); 00237 result = NULL; 00238 } 00239 else 00240 { 00241 /*Codes_SRS_SASTOKEN_06_009: [The scope is the basis for creating a STRING_HANDLE.]*/ 00242 /*Codes_SRS_SASTOKEN_06_010: [A "\n" is appended to that string.]*/ 00243 /*Codes_SRS_SASTOKEN_06_011: [tokenExpirationTime is appended to that string.]*/ 00244 if ((STRING_concat(toBeHashed, scope) != 0) || 00245 (STRING_concat(toBeHashed, "\n") != 0) || 00246 (STRING_concat(toBeHashed, tokenExpirationTime) != 0)) 00247 { 00248 LogError("Unable to build the input to the HMAC to prepare SAS token."); 00249 STRING_delete(result); 00250 result = NULL; 00251 } 00252 else 00253 { 00254 STRING_HANDLE base64Signature = NULL; 00255 STRING_HANDLE urlEncodedSignature = NULL; 00256 size_t inLen = STRING_length(toBeHashed); 00257 const unsigned char* inBuf = (const unsigned char*)STRING_c_str(toBeHashed); 00258 size_t outLen = BUFFER_length(decodedKey); 00259 unsigned char* outBuf = BUFFER_u_char(decodedKey); 00260 /*Codes_SRS_SASTOKEN_06_013: [If an error is returned from the HMAC256 function then NULL is returned from SASToken_Create.]*/ 00261 /*Codes_SRS_SASTOKEN_06_012: [An HMAC256 hash is calculated using the decodedKey, over toBeHashed.]*/ 00262 /*Codes_SRS_SASTOKEN_06_014: [If there are any errors from the following operations then NULL shall be returned.]*/ 00263 /*Codes_SRS_SASTOKEN_06_015: [The hash is base 64 encoded.]*/ 00264 /*Codes_SRS_SASTOKEN_06_028: [base64Signature shall be url encoded.]*/ 00265 /*Codes_SRS_SASTOKEN_06_016: [The string "SharedAccessSignature sr=" is the first part of the result of SASToken_Create.]*/ 00266 /*Codes_SRS_SASTOKEN_06_017: [The scope parameter is appended to result.]*/ 00267 /*Codes_SRS_SASTOKEN_06_018: [The string "&sig=" is appended to result.]*/ 00268 /*Codes_SRS_SASTOKEN_06_019: [The string urlEncodedSignature shall be appended to result.]*/ 00269 /*Codes_SRS_SASTOKEN_06_020: [The string "&se=" shall be appended to result.]*/ 00270 /*Codes_SRS_SASTOKEN_06_021: [tokenExpirationTime is appended to result.]*/ 00271 /*Codes_SRS_SASTOKEN_06_022: [If keyName is non-NULL, the string "&skn=" is appended to result.]*/ 00272 /*Codes_SRS_SASTOKEN_06_023: [If keyName is non-NULL, the argument keyName is appended to result.]*/ 00273 if ((HMACSHA256_ComputeHash(outBuf, outLen, inBuf, inLen, hash) != HMACSHA256_OK) || 00274 ((base64Signature = Base64_Encoder(hash)) == NULL) || 00275 ((urlEncodedSignature = URL_Encode(base64Signature)) == NULL) || 00276 (STRING_copy(result, "SharedAccessSignature sr=") != 0) || 00277 (STRING_concat(result, scope) != 0) || 00278 (STRING_concat(result, "&sig=") != 0) || 00279 (STRING_concat_with_STRING(result, urlEncodedSignature) != 0) || 00280 (STRING_concat(result, "&se=") != 0) || 00281 (STRING_concat(result, tokenExpirationTime) != 0) || 00282 ((keyname != NULL) && (STRING_concat(result, "&skn=") != 0)) || 00283 ((keyname != NULL) && (STRING_concat(result, keyname) != 0))) 00284 { 00285 LogError("Unable to build the SAS token."); 00286 STRING_delete(result); 00287 result = NULL; 00288 } 00289 else 00290 { 00291 /* everything OK */ 00292 } 00293 STRING_delete(base64Signature); 00294 STRING_delete(urlEncodedSignature); 00295 } 00296 } 00297 STRING_delete(toBeHashed); 00298 BUFFER_delete(hash); 00299 } 00300 BUFFER_delete(decodedKey); 00301 } 00302 return result; 00303 } 00304 00305 STRING_HANDLE SASToken_Create(STRING_HANDLE key, STRING_HANDLE scope, STRING_HANDLE keyName, size_t expiry) 00306 { 00307 STRING_HANDLE result; 00308 00309 /*Codes_SRS_SASTOKEN_06_001: [If key is NULL then SASToken_Create shall return NULL.]*/ 00310 /*Codes_SRS_SASTOKEN_06_003: [If scope is NULL then SASToken_Create shall return NULL.]*/ 00311 /*Codes_SRS_SASTOKEN_06_007: [keyName is optional and can be set to NULL.]*/ 00312 if ((key == NULL) || 00313 (scope == NULL)) 00314 { 00315 LogError("Invalid Parameter to SASToken_Create. handle key: %p, handle scope: %p, handle keyName: %p", key, scope, keyName); 00316 result = NULL; 00317 } 00318 else 00319 { 00320 const char* string_key = STRING_c_str(key); 00321 const char* string_scope = STRING_c_str(scope); 00322 const char* string_name = STRING_c_str(keyName); 00323 result = construct_sas_token(string_key, string_scope, string_name, expiry); 00324 } 00325 return result; 00326 } 00327 00328 STRING_HANDLE SASToken_CreateString(const char* key, const char* scope, const char* keyName, size_t expiry) 00329 { 00330 STRING_HANDLE result; 00331 00332 /*Codes_SRS_SASTOKEN_06_001: [If key is NULL then SASToken_Create shall return NULL.]*/ 00333 /*Codes_SRS_SASTOKEN_06_003: [If scope is NULL then SASToken_Create shall return NULL.]*/ 00334 /*Codes_SRS_SASTOKEN_06_007: [keyName is optional and can be set to NULL.]*/ 00335 if ((key == NULL) || 00336 (scope == NULL)) 00337 { 00338 LogError("Invalid Parameter to SASToken_Create. handle key: %p, handle scope: %p, handle keyName: %p", key, scope, keyName); 00339 result = NULL; 00340 } 00341 else 00342 { 00343 result = construct_sas_token(key, scope, keyName, expiry); 00344 } 00345 return result; 00346 }
Generated on Wed Jul 13 2022 23:38:02 by
1.7.2
