Skip to content

Commit

Permalink
Syw UID2-3679 cpp sdk base64 v4 token (#26)
Browse files Browse the repository at this point in the history
* added new CLion<=>Docker<=>Mac OS dev instructions
* able to decrypt Base64 (non url friendly) encoded v4 ad tokens
* changed to release version 3.1.0
  • Loading branch information
sunnywu authored Aug 21, 2024
1 parent 70e0606 commit 46cba28
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 13 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.12)

project("uid2-client" VERSION 3.0.1)
project("uid2-client" VERSION 3.1.0)

configure_file(VERSION.in VERSION @ONLY)

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ sudo ln -s $(brew --prefix llvm@14)/bin/clang-format /usr/local/bin/clang-format
sudo ln -s $(brew --prefix llvm@14)/bin/clang-tidy /usr/local/bin/clang-tidy-14
```

## Build and Test in CLion Using Docker on Mac OS
Run
```
docker build -t clion/ubuntu/cpp-env:1.0 -f tools/Dockerfile.cpp-env-ubuntu .
```
And [setup a Docker Toolchain in CLion](https://www.jetbrains.com/help/clion/clion-toolchains-in-docker.html)

And you would be able to develop and test within CLion easily.

## Build, Test, Install

To build, run unit tests, and install under the default prefix (`/usr/local`):
Expand Down
14 changes: 10 additions & 4 deletions lib/uid2encryption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ DecryptionResult DecryptToken(const std::string& token, const KeyContainer& keys
return DecryptionResult::MakeError(DecryptionStatus::INVALID_PAYLOAD);
}

const std::string headerStr = token.substr(0, 4);
const bool isBase64UrlEncoding = std::any_of(headerStr.begin(), headerStr.end(), [](char c) { return c == '-' || c == '_'; });
// check the whole ad token string instead of the headerStr to make sure
const bool isBase64UrlEncoding = std::any_of(token.begin(), token.end(), [](char c) { return c == '-' || c == '_'; });
try {
std::vector<std::uint8_t> encryptedId;
std::vector<std::uint8_t> headerBytes;
const std::string headerStr = token.substr(0, 4);

if (isBase64UrlEncoding) {
uid2::UID2Base64UrlCoder::Decode(headerStr, headerBytes);
Expand All @@ -69,8 +70,13 @@ DecryptionResult DecryptToken(const std::string& token, const KeyContainer& keys
return DecryptTokenV3(encryptedId, keys, now, identityScope, checkValidity);
}
if (headerBytes[1] == static_cast<std::uint8_t>(AdvertisingTokenVersion::V4)) {
// same as V3 but use Base64URL encoding
uid2::UID2Base64UrlCoder::Decode(token, encryptedId);
if (isBase64UrlEncoding) {
// same as V3 but use Base64URL encoding
uid2::UID2Base64UrlCoder::Decode(token, encryptedId);
} else {
// handling the rare situation where participant changed the encoding from Base64URL to Base64
macaron::Base64::Decode(token, encryptedId);
}
return DecryptTokenV3(encryptedId, keys, now, identityScope, checkValidity);
}
return DecryptionResult::MakeError(DecryptionStatus::INVALID_PAYLOAD);
Expand Down
42 changes: 34 additions & 8 deletions test/encryption_tests_v4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <gtest/gtest.h>

#include <algorithm>
#include <sstream>

using namespace uid2;
Expand Down Expand Up @@ -116,15 +117,43 @@ std::string GenerateUid2TokenV4AndValidate(
return advertisingToken;
}

void DecryptAndAssertSuccess(UID2Client& client, const std::string& advertisingTokenString, Timestamp timestamp = Timestamp::Now())
{
const auto res = client.Decrypt(advertisingTokenString, timestamp);
EXPECT_TRUE(res.IsSuccess());
EXPECT_EQ(DecryptionStatus::SUCCESS, res.GetStatus());
EXPECT_EQ(EXAMPLE_UID, res.GetUid());
}

TEST(EncryptionTestsV4, CanDecryptV4TokenEncodedAsBase64)
{
UID2Client client("ep", "ak", CLIENT_SECRET, IdentityScope::UID2);
client.RefreshJson(KeySetToJson({MASTER_KEY, SITE_KEY}));
std::string advertisingToken;

// for testing purposes, the token must have some Base64URL encoding characters
do {
advertisingToken = GenerateUid2TokenV4AndValidate(EXAMPLE_UID, MASTER_KEY, SITE_ID, SITE_KEY, EncryptTokenParams());
} while (!std::any_of(advertisingToken.begin(), advertisingToken.end(), [](char c) { return c == '-' || c == '_'; }));

std::vector<std::uint8_t> adTokenBytes;
uid2::UID2Base64UrlCoder::Decode(advertisingToken, adTokenBytes);

// explicitly encode into Base64 (non-URL friendly) encoding again
const auto base64NonURLAdTokenV4 = macaron::Base64::Encode(adTokenBytes);
const bool isBase64NonUrlEncoding =
std::any_of(base64NonURLAdTokenV4.begin(), base64NonURLAdTokenV4.end(), [](char c) { return c == '=' || c == '+' || c == '/'; });
EXPECT_TRUE(isBase64NonUrlEncoding);

DecryptAndAssertSuccess(client, base64NonURLAdTokenV4);
}

TEST(EncryptionTestsV4, SmokeTest)
{
UID2Client client("ep", "ak", CLIENT_SECRET, IdentityScope::UID2);
client.RefreshJson(KeySetToJson({MASTER_KEY, SITE_KEY}));
const auto advertisingToken = GenerateUid2TokenV4AndValidate(EXAMPLE_UID, MASTER_KEY, SITE_ID, SITE_KEY, EncryptTokenParams());
const auto res = client.Decrypt(advertisingToken, Timestamp::Now());
EXPECT_TRUE(res.IsSuccess());
EXPECT_EQ(DecryptionStatus::SUCCESS, res.GetStatus());
EXPECT_EQ(EXAMPLE_UID, res.GetUid());
DecryptAndAssertSuccess(client, advertisingToken);
}

TEST(EncryptionTestsV4, EmptyKeyContainer)
Expand Down Expand Up @@ -188,10 +217,7 @@ TEST(EncryptionTestsV4, TokenExpiryAndCustomNow)
EXPECT_FALSE(res.IsSuccess());
EXPECT_EQ(DecryptionStatus::EXPIRED_TOKEN, res.GetStatus());

res = client.Decrypt(advertisingToken, expiry.AddSeconds(-1));
EXPECT_TRUE(res.IsSuccess());
EXPECT_EQ(DecryptionStatus::SUCCESS, res.GetStatus());
EXPECT_EQ(EXAMPLE_UID, res.GetUid());
DecryptAndAssertSuccess(client, advertisingToken, expiry.AddSeconds(-1));
}

TEST(EncryptDataTestsV4, SiteIdFromToken)
Expand Down
28 changes: 28 additions & 0 deletions tools/Dockerfile.cpp-env-ubuntu
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Based on https://github.com/JetBrains/clion-remote/blob/master/Dockerfile.cpp-env-ubuntu
# Build and run:
# docker build -t clion/ubuntu/cpp-env:1.0 -f Dockerfile.cpp-env-ubuntu .

FROM ubuntu:22.04

RUN DEBIAN_FRONTEND="noninteractive" apt-get update && apt-get -y install tzdata

RUN apt-get update \
&& apt-get install -y build-essential \
gcc \
g++ \
gdb \
clang \
make \
ninja-build \
cmake \
autoconf \
automake \
libtool \
valgrind \
locales-all \
dos2unix \
rsync \
tar \
libssl-dev \
libgtest-dev \
&& apt-get clean

0 comments on commit 46cba28

Please sign in to comment.