Skip to content

Commit

Permalink
FEAT: Addresses #113 where GetHighEntropyValues JavaScript property v…
Browse files Browse the repository at this point in the history
…alues must not return JavaScript when the required evidence is already present. Needed to improve performance.

Only executed when the JavascriptGetHighEntropyValues property is a required property that can be returned.
The Accept-CH properties in the DataSet are used to identify the headers that are needed for UACH/GHEV functionality. These are parsed during initialisation and stored in the device detection data set.
After evidence based device detection a check is performed to determine if the evidence contains all the UACH/GHEV fields. If it does then the value returned for a subsequent call JavascriptGetHighEntropyValues will be an empty string.
GettingStarted for C has been updated to better show the unpacking of evidence values to form other evidence values such as pseudo headers and UACH headers from base64.
  • Loading branch information
jwrosewell authored and justadreamer committed Oct 24, 2024
1 parent 71e80f4 commit 8ae20e7
Showing 7 changed files with 597 additions and 45 deletions.
155 changes: 113 additions & 42 deletions examples/C/Hash/GettingStarted.c
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
/* *********************************************************************
* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House,
* Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
*
* This Original Work is licensed under the European Union Public Licence
* (EUPL) v.1.2 and is subject to its terms as set out below.
*
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
*
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
*
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
/* *********************************************************************
* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House,
* Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
*
* This Original Work is licensed under the European Union Public Licence
* (EUPL) v.1.2 and is subject to its terms as set out below.
*
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
*
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
*
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
* ********************************************************************* */

/**
@@ -43,7 +43,7 @@ This example is available in full on [GitHub](https://github.com/51Degrees/devic
// which requires to be included before 'malloc.h'.
#include "ExampleBase.h"

#define MAX_EVIDENCE 7
#define MAX_EVIDENCE 8

static const char *dataDir = "device-detection-data";

@@ -58,7 +58,8 @@ static const char *dataDir = "device-detection-data";
static const char *dataFileName = "51Degrees-LiteV4.1.hash";

static char valueBuffer[1024] = "";
static const size_t valueBufferLength = sizeof(valueBuffer) / sizeof(valueBuffer[0]);
static const size_t valueBufferLength =
sizeof(valueBuffer) / sizeof(valueBuffer[0]);

typedef struct {
uint32_t count;
@@ -128,13 +129,22 @@ static evidence userAgentWithMobileID = {

static evidence headersWithWebView = {
7,
{ {FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "user-agent", "Mozilla/5.0 (Linux; Android 13; RMX3762 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/122.0.6261.106 Mobile Safari/537.36 TwitterAndroid"},
{ {FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "user-agent",
"Mozilla/5.0 (Linux; Android 13; RMX3762 Build/TP1A.220624.014; wv) "
"AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/122.0.6261.106 "
"Mobile Safari/537.36 TwitterAndroid"},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-mobile", "?1"},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua", "\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", \"Android WebView\";v=\"122\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-platform", "\"Android\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-platform-version", "\"13.0.0\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-model", "\"RMX3762\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-full-version", "\"122.0.6261.106\""} }
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua",
"\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", "
"\"Android WebView\";v=\"122\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-platform",
"\"Android\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-platform-version",
"\"13.0.0\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-model",
"\"RMX3762\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-full-version",
"\"122.0.6261.106\""} }
};

// Base 64 string for the JSON returned from a call to getHighEntropyValues
@@ -143,10 +153,16 @@ static evidence headersWithWebView = {
// 64 being returned to a second server request for device detection. Providing
// the value direct from user code would not be an expected use of the feature.
// The example is included to help those starting to work with the project
// understand that evidence is not always HTTP headers.
// understand that evidence is not always HTTP headers. A normal user-agent is
// included to show how this is ignored when better evidence is available.
static evidence getHighEntropyValues = {
1,
{ {FIFTYONE_DEGREES_EVIDENCE_QUERY, FIFTYONE_DEGREES_EVIDENCE_HIGH_ENTROPY_VALUES,
2,
{ {FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "user-agent",
"Mozilla/5.0 (Linux; Android 13; RMX3762 Build/TP1A.220624.014; wv) "
"AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 "
"Chrome/122.0.6261.106 Mobile Safari/537.36 TwitterAndroid"},
{FIFTYONE_DEGREES_EVIDENCE_QUERY,
FIFTYONE_DEGREES_EVIDENCE_HIGH_ENTROPY_VALUES,
"eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh"
"bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t"
"ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv"
@@ -159,11 +175,11 @@ static evidence getHighEntropyValues = {
// This collection contains the various input values that will
// be passed to the device detection algorithm.
static evidence* evidenceValues[] = {
&mobileDevice,
&desktopDevice,
&userAgentClientHints,
&userAgentWithMobileID,
&headersWithWebView,
// &mobileDevice,
// &desktopDevice,
// &userAgentClientHints,
// &userAgentWithMobileID,
// &headersWithWebView,
&getHighEntropyValues
};

@@ -220,6 +236,23 @@ static void reportStatus(StatusCode status,
Free((void*)message);
}

/**
* Report the header value from the evidence iteration. May not be the same as
* the original input values.
*/
static bool reportHeader(
void* state,
fiftyoneDegreesHeader* header,
const char* value,
size_t length) {
char* buffer = (char*)Malloc(length + 1);
strncpy(buffer, value, length);
buffer[length] = '\0';
fprintf((FILE*)state, "\n\t%s: %s", header->name, buffer);
Free(buffer);
return true;
}

static void analyse(
ResultsHash* results,
EvidenceKeyValuePairArray* evidence,
@@ -237,7 +270,7 @@ static void analyse(
"\n\t%s%s: %s",
EvidencePrefixString(e.prefix), e.field, (char *)e.originalValue);
}
fprintf(output, "\n");
fprintf(output, "\n\n");

EXCEPTION_CREATE
ResultsHashFromEvidence(results, evidence, exception);
@@ -250,6 +283,10 @@ static void analyse(
outputValue(results, "Browser Name", "BrowserName", output);
outputValue(results, "Browser Version", "BrowserVersion", output);

// Shows the Device Id that can be used to look up the properties via
// reference tables.
// Note: Reference tables not shown and are available for on premise
// subscribers using the CSV data format.
HashGetDeviceIdFromResults(
results,
valueBuffer,
@@ -258,13 +295,39 @@ static void analyse(
EXCEPTION_THROW;
fprintf(output, "\n\tDevice ID: %s\n", valueBuffer);

// Shows how to get all the required properties as a single JSON stirng.
ResultsHashGetValuesJson(results,
valueBuffer,
sizeof(valueBuffer),
exception);
fprintf(output, "\n\tJSON: %s\n", valueBuffer);

// Shows the JavaScript that can be run in a User Agent Client Hint
// compatable web browsers to return evidence needed for device detection
// as a base64 string. See
// https://51degrees.com/documentation/4.4/_device_detection__features__u_a_c_h__overview.html
outputValue(
results,
"GetHighEntropyValues JS",
"JavascriptGetHighEntropyValues",
output);

fprintf(output, "\n\n");

// iterate the evidence to show pseudo headers and GHEV results which is
// only going to be exposed after the call to ResultsHashFromEvidence.
if (((DataSetHash*)results->b.b.dataSet)->b.ghevHeaders != NULL) {
fprintf(output, "UACH evidence:");
EvidenceIterateForHeaders(
evidence,
INT_MAX,
((DataSetHash*)results->b.b.dataSet)->b.ghevHeaders,
NULL,
0,
output,
reportHeader);
fprintf(output, "\n\n");
}
}

void fiftyoneDegreesHashGettingStarted(
@@ -275,12 +338,17 @@ void fiftyoneDegreesHashGettingStarted(
EXCEPTION_CREATE;

// Set the properties to be returned for each User-Agent. Specifying the
// properties that will later be retrieved at initialisation time improves
// performance.
// properties that will later be retrieved or used during device detection
// at initialisation time improves performance.
// Note: The Accept-CH properties are used to prevent the
// JavascriptGetHighEntropyValues value being returned when all the data is
// already present in the evidence.
PropertiesRequired properties = {
NULL,
0,
"IsMobile,PlatformName,PlatformVersion,BrowserName,BrowserVersion,HardwareImages",
"IsMobile,PlatformName,PlatformVersion,BrowserName,BrowserVersion,"
"HardwareImages,SetHeaderBrowserAccept-CH,SetHeaderHardwareAccept-CH,"
"SetHeaderPlatformAccept-CH,JavascriptGetHighEntropyValues",
NULL };

// Initialise the manager for device detection.
@@ -299,8 +367,11 @@ void fiftyoneDegreesHashGettingStarted(

// Create a results instance to store and process evidence.
// The capacity of the results should be the same as the maximum potential
// evidence that can be provided.
ResultsHash *results = ResultsHashCreate(&manager, 0);
// evidence that can be provided.
// Note: Capacity of overriding values dynamically is required to ensure
// that the JavascriptGetHighEntropyValues code can be blocked from being
// returned when all the required evidence is already present.
ResultsHash *results = ResultsHashCreate(&manager, 1);

for (int i = 0; i < (int)(sizeof(evidenceValues)/sizeof(evidence *)); i++) {
// Create an evidence collection and add the evidence to the collection
9 changes: 9 additions & 0 deletions src/dataset-dd.c
Original file line number Diff line number Diff line change
@@ -75,6 +75,10 @@ fiftyoneDegreesDataSetDeviceDetectionInitPropertiesAndHeaders(
state,
overridesFilter);

// The ghevHeaders member is initialised in
// fiftyoneDegreesGhevDeviceDetectionInit if required.
dataSet->ghevHeaders = NULL;

return status;
}

@@ -85,6 +89,10 @@ void fiftyoneDegreesDataSetDeviceDetectionRelease(

void fiftyoneDegreesDataSetDeviceDetectionFree(
fiftyoneDegreesDataSetDeviceDetection *dataSet) {
if (dataSet->ghevHeaders != NULL) {
Free(dataSet->ghevHeaders);
dataSet->ghevHeaders = NULL;
}
DataSetFree(&dataSet->b);
}

@@ -98,4 +106,5 @@ void fiftyoneDegreesDataSetDeviceDetectionReset(
fiftyoneDegreesDataSetDeviceDetection *dataSet) {
DataSetReset(&dataSet->b);
dataSet->uniqueUserAgentHeaderIndex = 0;
dataSet->ghevHeaders = NULL;
}
7 changes: 7 additions & 0 deletions src/dataset-dd.h
Original file line number Diff line number Diff line change
@@ -53,6 +53,13 @@ typedef struct fiftyone_degrees_dataset_device_detection_t {
fiftyoneDegreesDataSetBase b; /**< Base structure members */
uint32_t uniqueUserAgentHeaderIndex; /**< The unique HTTP header for the
field name "User-Agent" */
fiftyoneDegreesHeaderPtrArray *ghevHeaders; /**< Array of get high entropy
values headers that must all be
present to prevent the
gethighentropyvalues javascript
from being returned */
int ghevRequiredPropertyIndex; /**< Required property index for
JavascriptGetHighEntropyValues */
} fiftyoneDegreesDataSetDeviceDetection;

/**
5 changes: 5 additions & 0 deletions src/fiftyone.h
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@
#include "config-dd.h"
#include "results-dd.h"
#include "transform.h"
#include "gethighentropyvalues.h"

MAP_TYPE(ConfigDeviceDetection)
MAP_TYPE(ResultsDeviceDetection)
@@ -76,6 +77,10 @@ MAP_TYPE(TransformIterateResult)
#define TransformIterateGhevFromJson fiftyoneDegreesTransformIterateGhevFromJson /**< Synonym for fiftyoneDegreesTransformIterateGhevFromJson */
#define TransformCallback fiftyoneDegreesTransformCallback /**< Synonym for fiftyoneDegreesTransformCallback */

#define GhevDeviceDetectionInit fiftyoneDegreesGhevDeviceDetectionInit /**< Synonym for fiftyoneDegreesGhevDeviceDetectionInit */
#define GhevDeviceDetectionAllPresent fiftyoneDegreesGhevDeviceDetectionAllPresent /**< Synonym for fiftyoneDegreesGhevDeviceDetectionAllPresent */
#define GhevDeviceDetectionOverride fiftyoneDegreesGhevDeviceDetectionOverride /**< Synonym for fiftyoneDegreesGhevDeviceDetectionOverride */

/**
* @}
*/
Loading

0 comments on commit 8ae20e7

Please sign in to comment.