// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "../../src/private/attestation_client_models_private.hpp" #include "../../src/private/attestation_client_private.hpp" #include "../../src/private/attestation_deserializers_private.hpp" #include "../../src/private/crypto/inc/crypto.hpp" #include "azure/attestation/attestation_client.hpp" #include #include #include #include #include // cspell:words jwk jwks namespace Azure { namespace Security { namespace Attestation { namespace Test { using namespace Azure::Core::Json::_internal; using namespace Azure::Core::Http; using namespace Azure::Security::Attestation; using namespace Azure::Security::Attestation::_detail; using namespace Azure::Security::Attestation::Models; using namespace Azure::Security::Attestation::Models::_detail; using namespace Azure::Core::_internal; TEST(SerializationTests, TestDeserializePrimitivesJsonObject) { // Present JSON field. { Azure::Nullable val; JsonHelpers::SetIfExistsJson( val, json::parse(R"({ "jsonObjectValue": {"stringField": "SF2"}})"), "jsonObjectValue"); EXPECT_TRUE(val); EXPECT_EQ(R"({"stringField":"SF2"})", *val); } // Not present field. { Azure::Nullable val; JsonHelpers::SetIfExistsJson( val, json::parse("{ \"objectValue\":{\"String Field\": 27}}"), "intValue"); EXPECT_FALSE(val); } } TEST(SerializationTests, TestDeserializePrimitivesBase64Url) { std::string testData("Test Data"); std::string encodedData = Azure::Core::_internal::Base64Url::Base64UrlEncode( std::vector(testData.begin(), testData.end())); // Present JSON field. { Azure::Nullable> val; JsonOptional::SetIfExists>( val, json::parse("{ \"base64Urlfield\": \"" + encodedData + "\"}"), "base64Urlfield", Base64Url::Base64UrlDecode); EXPECT_EQ(9ul, val->size()); EXPECT_EQ(testData, std::string(val->begin(), val->end())); } // Not present field. { Azure::Nullable> val; JsonOptional::SetIfExists>( val, json::parse("{ \"base64Urlfield\": \"" + encodedData + "\"}"), "intValue", Base64Url::Base64UrlDecode); EXPECT_FALSE(val); } } TEST(SerializationTests, TestHexString) { { auto bin(JsonHelpers::HexStringToBinary("010203AABBccddee")); EXPECT_EQ(bin[0], 0x01); EXPECT_EQ(bin[1], 0x02); EXPECT_EQ(bin[2], 0x03); EXPECT_EQ(bin[3], 0xaa); EXPECT_EQ(bin[4], 0xbb); EXPECT_EQ(bin[5], 0xcc); EXPECT_EQ(bin[6], 0xdd); EXPECT_EQ(bin[7], 0xee); } { // cspell: disable EXPECT_THROW(JsonHelpers::HexStringToBinary("ABCEQWERTY"), std::invalid_argument); // cspell: enable } { // cspell: disable EXPECT_THROW(JsonHelpers::HexStringToBinary("ABC"), std::invalid_argument); // cspell: enable } } TEST(SerializationTests, TestDeserializeJWK) { { EXPECT_THROW( JsonWebKeySerializer::Deserialize(json::parse(R"({"Alg": "none"})")), std::runtime_error); } // cspell:disable { auto val(JsonWebKeySerializer::Deserialize(json::parse(R"( {"kty":"EC", "crv":"P-256", "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", "use":"enc", "kid":"1"})"))); EXPECT_EQ("EC", *val.Kty); EXPECT_EQ("P-256", *val.Crv); EXPECT_EQ("MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", *val.X); EXPECT_EQ("4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", *val.Y); EXPECT_EQ("enc", *val.Use); EXPECT_EQ("1", *val.Kid); } { auto val(JsonWebKeySerializer::Deserialize(json::parse(R"({"kty":"RSA", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e":"AQAB", "alg":"RS256", "kid":"2011-04-29"})"))); EXPECT_EQ("RS256", *val.Alg); EXPECT_EQ( "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_" "BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_" "FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-" "bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", *val.N); EXPECT_EQ("AQAB", *val.E); EXPECT_EQ("2011-04-29", *val.Kid); // Now serialize the JWK back to JSON. auto serializedKey(JsonWebKeySerializer::Serialize(val)); } // cspell:enable } TEST(SerializationTests, TestPolicyCertificateManagementBody) { IsolatedModeCertificateBody body; // cspell: disable body.policyCertificate = JsonWebKeySerializer::Deserialize(json::parse(R"({"kty":"RSA", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e":"AQAB", "alg":"RS256", "kid":"2011-04-29"})")); // cspell: enable std::string serializedBody = IsolatedModeCertificateBodySerializer::Serialize(body); auto deserializedBody = IsolatedModeCertificateBodySerializer::Deserialize(json::parse(serializedBody)); EXPECT_EQ(body.policyCertificate.N.Value(), deserializedBody.policyCertificate.N.Value()); } TEST(SerializationTests, TestDeserializeJWKS) { { EXPECT_THROW( JsonWebKeySetSerializer::Deserialize(json::parse(R"({"keys": [{"Alg": "none"}]})")), std::runtime_error); } // cspell:disable { auto val(JsonWebKeySetSerializer::Deserialize(json::parse(R"( {"keys": [ {"kty":"EC", "crv":"P-256", "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", "use":"enc", "kid":"1"}, {"kty":"RSA", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e":"AQAB", "alg":"RS256", "kid":"2011-04-29"} ]})"))); EXPECT_EQ(2ul, val.Keys.size()); EXPECT_EQ("EC", *val.Keys[0].Kty); EXPECT_EQ("P-256", *val.Keys[0].Crv); EXPECT_EQ("MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", *val.Keys[0].X); EXPECT_EQ("4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", *val.Keys[0].Y); EXPECT_EQ("enc", *val.Keys[0].Use); EXPECT_EQ("1", *val.Keys[0].Kid); EXPECT_EQ("RS256", *val.Keys[1].Alg); EXPECT_EQ( "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_" "BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_" "FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-" "bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", *val.Keys[1].N); EXPECT_EQ("AQAB", *val.Keys[1].E); EXPECT_EQ("2011-04-29", *val.Keys[1].Kid); } { EXPECT_THROW( JsonWebKeySetSerializer::Deserialize(json::parse(R"({"xxx": [{"Alg": "none"}]})")), std::runtime_error); } { EXPECT_THROW( JsonWebKeySetSerializer::Deserialize(json::parse(R"({"keys": {"Alg": "none"}})")), std::runtime_error); } // cspell:enable } TEST(SerializationTests, TestSerializeAttestOpenEnclaveRequest) { { Models::_detail::AttestOpenEnclaveRequest request; request.Report = {1, 2, 3, 4}; request.RunTimeData = AttestationData{{4, 5, 7, 8}, AttestationDataType::Binary}; request.InitTimeData = AttestationData{{1, 2, 3, 4}, AttestationDataType::Json}; request.DraftPolicyForAttestation = "Draft"; request.Nonce = "My Nonce"; std::string val = AttestOpenEnclaveRequestSerializer::Serialize(request); json parsedRequest = json::parse(val); EXPECT_TRUE(parsedRequest.is_object()); EXPECT_TRUE(parsedRequest["report"].is_string()); EXPECT_TRUE(parsedRequest["inittimeData"].is_object()); EXPECT_TRUE(parsedRequest["runtimeData"].is_object()); EXPECT_TRUE(parsedRequest["draftPolicyForAttestation"].is_string()); EXPECT_TRUE(parsedRequest["nonce"].is_string()); } } TEST(SerializationTests, TestDeserializeTokenResponse) { { auto val(AttestationServiceTokenResponseSerializer::Deserialize( json::parse(R"({"token": "ABCDEFG.123.456"} )"))); EXPECT_EQ("ABCDEFG.123.456", val); } { EXPECT_THROW( AttestationServiceTokenResponseSerializer::Deserialize( json::parse(R"({"fred": "ABCDEFG.123.456"} )")), std::runtime_error); } } TEST(SerializationTests, TestDeserializeSignerToJson) { auto asymmetricKey = Cryptography::CreateRsaKey(2048); auto cert = Cryptography::CreateX509CertificateForPrivateKey(asymmetricKey, "CN=TestSubject, C=US"); AttestationSigner signer{ std::string{"ABCDEFG"}, std::vector{cert->ExportAsBase64()}}; std::string serializedSigner = AttestationSignerInternal::SerializeToJson(signer); auto jsonSigner(json::parse(serializedSigner)); EXPECT_TRUE(jsonSigner["Kid"].is_string()); auto kidJson(jsonSigner["Kid"]); auto Kid = kidJson.get(); EXPECT_EQ(signer.KeyId.Value(), Kid); EXPECT_TRUE(jsonSigner["X5c"].is_array()); auto X5c = jsonSigner["X5c"].get>(); EXPECT_TRUE(X5c[0].is_string()); auto x5c0(X5c[0]); std::string x5cval(x5c0.get()); EXPECT_EQ(x5cval, (*signer.CertificateChain)[0]); } template bool CompareNullable(Nullable const& me, Nullable const& them) { if (me.HasValue() != them.HasValue()) { return false; } if (me) { return (*me == *them); } return true; } template <> bool CompareNullable(Nullable const& me, Nullable const& them) { if (me.HasValue() != them.HasValue()) { return false; } if (me) { Azure::DateTime meTime(Azure::Core::_internal::PosixTimeConverter::PosixTimeToDateTime( PosixTimeConverter::DateTimeToPosixTime(*me))); Azure::DateTime themTime(Azure::Core::_internal::PosixTimeConverter::PosixTimeToDateTime( PosixTimeConverter::DateTimeToPosixTime(*them))); return (meTime == themTime); } return true; } struct TestObject { Azure::Nullable Algorithm; Azure::Nullable Integer; Azure::Nullable ExpiresAt; Azure::Nullable IssuedOn; Azure::Nullable NotBefore; Azure::Nullable> IntegerArray; Azure::Nullable Issuer; bool operator==(TestObject const& that) const { if (!CompareNullable(Algorithm, that.Algorithm)) { return false; } if (!CompareNullable(Integer, that.Integer)) { return false; } if (!CompareNullable(IntegerArray, that.IntegerArray)) { return false; } if (!CompareNullable(ExpiresAt, that.ExpiresAt)) { return false; } if (!CompareNullable(IssuedOn, that.IssuedOn)) { return false; } if (!CompareNullable(NotBefore, that.NotBefore)) { return false; } if (!CompareNullable(Issuer, that.Issuer)) { return false; } return true; } bool operator!=(TestObject const& that) { return !(*this == that); } }; struct TestObjectSerializer { static TestObject Deserialize(json const& serialized) { TestObject returnValue; JsonOptional::SetIfExists(returnValue.Algorithm, serialized, "Alg"); JsonOptional::SetIfExists( returnValue.ExpiresAt, serialized, "exp", Azure::Core::_internal::PosixTimeConverter::PosixTimeToDateTime); JsonOptional::SetIfExists( returnValue.IssuedOn, serialized, "iat", Azure::Core::_internal::PosixTimeConverter::PosixTimeToDateTime); JsonOptional::SetIfExists( returnValue.NotBefore, serialized, "nbf", Azure::Core::_internal::PosixTimeConverter::PosixTimeToDateTime); JsonOptional::SetIfExists(returnValue.IntegerArray, serialized, "intArray"); JsonOptional::SetIfExists(returnValue.Issuer, serialized, "iss"); JsonOptional::SetIfExists(returnValue.Integer, serialized, "int"); return returnValue; } static std::string Serialize(TestObject const& testObject) { json returnValue; JsonOptional::SetFromNullable(testObject.Algorithm, returnValue, "Alg"); JsonOptional::SetFromNullable(testObject.Integer, returnValue, "int"); JsonOptional::SetFromNullable(testObject.IntegerArray, returnValue, "intArray"); JsonOptional::SetFromNullable( testObject.ExpiresAt, returnValue, "exp", Azure::Core::_internal::PosixTimeConverter::DateTimeToPosixTime); JsonOptional::SetFromNullable(testObject.Issuer, returnValue, "iss"); JsonOptional::SetFromNullable( testObject.IssuedOn, returnValue, "iat", Azure::Core::_internal::PosixTimeConverter::DateTimeToPosixTime); JsonOptional::SetFromNullable( testObject.NotBefore, returnValue, "nbf", Azure::Core::_internal::PosixTimeConverter::DateTimeToPosixTime); return returnValue.dump(); } }; TEST(SerializationTests, SerializeDeserializeTestObject) { { TestObject testObject; testObject.Algorithm = "RSA"; testObject.Integer = 314; testObject.ExpiresAt = std::chrono::system_clock::now(); testObject.IssuedOn = std::chrono::system_clock::now(); testObject.NotBefore = std::chrono::system_clock::now(); testObject.IntegerArray = {1, 2, 99, 32}; testObject.Issuer = "George"; auto serializedObject = TestObjectSerializer::Serialize(testObject); auto targetObject = TestObjectSerializer::Deserialize(json::parse(serializedObject)); EXPECT_EQ(testObject, targetObject); } { TestObject testObject; testObject.Algorithm = "RSA"; testObject.ExpiresAt = std::chrono::system_clock::now(); testObject.IssuedOn = std::chrono::system_clock::now(); testObject.IntegerArray = {1, 2, 99, 32}; auto serializedObject = TestObjectSerializer::Serialize(testObject); auto targetObject = TestObjectSerializer::Deserialize(json::parse(serializedObject)); EXPECT_EQ(testObject, targetObject); } } TEST(AttestationTokenTests, CreateUnsecuredTokenFromObject) { { TestObject testObject; testObject.Algorithm = "RSA"; testObject.Integer = 314; testObject.ExpiresAt = std::chrono::system_clock::now() + std::chrono::seconds(30); testObject.IssuedOn = std::chrono::system_clock::now(); testObject.NotBefore = std::chrono::system_clock::now(); testObject.IntegerArray = {1, 2, 99, 32}; testObject.Issuer = "George"; auto testToken = AttestationTokenInternal::CreateToken(testObject); EXPECT_NO_THROW(testToken.ValidateToken({})); AttestationToken token = testToken; EXPECT_EQ(testObject, token.Body); EXPECT_TRUE(token.ExpiresOn); EXPECT_TRUE(token.IssuedOn); EXPECT_TRUE(token.NotBefore); EXPECT_TRUE(token.Issuer); EXPECT_EQ(*token.Issuer, "George"); EXPECT_TRUE(token.Header.Algorithm); EXPECT_EQ("none", *token.Header.Algorithm); } } TEST(AttestationTokenTests, TestUnsecuredTokenValidation) { // Test expired tokens. { // Capture the current time, needed for future validation. auto now = std::chrono::system_clock::now() - std::chrono::seconds(30); TestObject testObject; testObject.Algorithm = "RSA"; testObject.Integer = 314; testObject.IntegerArray = {1, 2, 99, 32}; testObject.Issuer = "George"; // This token was issued 40 seconds ago, and is valid for 15 seconds. testObject.ExpiresAt = now + std::chrono::seconds(15); testObject.IssuedOn = now; testObject.NotBefore = now; auto testToken = AttestationTokenInternal::CreateToken(testObject); // Simple valdation should throw an exception. EXPECT_THROW(testToken.ValidateToken({}), std::runtime_error); // Validate the token asking to ignore token validation. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateToken = false; EXPECT_NO_THROW(testToken.ValidateToken(tokenOptions)); } // Validate the token asking to ignore token expiration time. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateExpirationTime = false; EXPECT_NO_THROW(testToken.ValidateToken(tokenOptions)); } } // Test tokens which are not yet ready. { // Capture the current time, 30 seconds in the future. auto now = std::chrono::system_clock::now() + std::chrono::seconds(30); TestObject testObject; testObject.Algorithm = "RSA"; testObject.Integer = 314; testObject.IntegerArray = {1, 2, 99, 32}; testObject.Issuer = "George"; // This token will be issued 30 seconds from now, and is valid for 15 seconds. testObject.ExpiresAt = now + std::chrono::seconds(15); testObject.IssuedOn = now; testObject.NotBefore = now; auto testToken = AttestationTokenInternal::CreateToken(testObject); // Simple valdation should throw an exception. EXPECT_THROW(testToken.ValidateToken({}), std::runtime_error); // Validate the token asking to ignore token validation. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateToken = false; EXPECT_NO_THROW(testToken.ValidateToken(tokenOptions)); } // Validate the token asking to ignore token expiration time. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateNotBeforeTime = false; EXPECT_NO_THROW(testToken.ValidateToken(tokenOptions)); } } } void CreateSecuredToken( std::unique_ptr const& key, std::unique_ptr const& cert) { TestObject testObject; testObject.Algorithm = "UnknownAlgorithm"; testObject.Integer = 314; // Capture the current time, needed for future validation. auto now = std::chrono::system_clock::now(); // This token was issued now, and is valid for 30 seconds. testObject.ExpiresAt = now + std::chrono::seconds(30); testObject.IssuedOn = now; testObject.NotBefore = now; testObject.IntegerArray = {1, 2, 99, 32}; testObject.Issuer = "George"; AttestationSigningKey signingKey{key->ExportPrivateKey(), cert->ExportAsPEM()}; // Create a secured attestation token wrapped around the TestObject. auto testToken = AttestationTokenInternal::CreateToken( testObject, signingKey); // Validate this token - it should not throw. EXPECT_NO_THROW(testToken.ValidateToken({})); { AttestationTokenValidationOptions validationOptions; validationOptions.ValidateIssuer = true; validationOptions.ExpectedIssuer = "George"; EXPECT_NO_THROW(testToken.ValidateToken(validationOptions)); } AttestationToken token = testToken; EXPECT_EQ(testObject, token.Body); EXPECT_TRUE(token.ExpiresOn); EXPECT_TRUE(token.IssuedOn); EXPECT_TRUE(token.NotBefore); EXPECT_TRUE(token.Issuer); EXPECT_EQ(*token.Issuer, "George"); } TEST(AttestationTokenTests, CreateSecuredTokenFromObject) { { // Create an RSA public/private key pair. auto asymmetricKey = Cryptography::CreateRsaKey(2048); auto cert = Cryptography::CreateX509CertificateForPrivateKey(asymmetricKey, "CN=TestSubject, C=US"); CreateSecuredToken(asymmetricKey, cert); } { // Create an RSA public/private key pair. auto asymmetricKey = Cryptography::CreateEcdsaKey(); auto cert = Cryptography::CreateX509CertificateForPrivateKey(asymmetricKey, "CN=TestSubject, C=US"); CreateSecuredToken(asymmetricKey, cert); } } TEST(AttestationTokenTests, TestSecuredTokenValidation) { // Create an RSA public/private key pair. Use these for the subsequent tests. auto asymmetricKey = Cryptography::CreateRsaKey(2048); auto cert = Cryptography::CreateX509CertificateForPrivateKey(asymmetricKey, "CN=TestSubject, C=US"); AttestationSigningKey signingKey{asymmetricKey->ExportPrivateKey(), cert->ExportAsPEM()}; // Test expired tokens. { // Capture the current time, needed for future validation. auto now = std::chrono::system_clock::now() - std::chrono::seconds(30); TestObject testObject; testObject.Algorithm = "RSA"; testObject.Integer = 314; testObject.IntegerArray = {1, 2, 99, 32}; testObject.Issuer = "George"; // This token was issued 40 seconds ago, and is valid for 15 seconds. testObject.ExpiresAt = now + std::chrono::seconds(15); testObject.IssuedOn = now; testObject.NotBefore = now; auto testToken = AttestationTokenInternal::CreateToken( testObject, signingKey); // Simple valdation should throw an exception. EXPECT_THROW(testToken.ValidateToken({}), std::runtime_error); // Validate the token asking to ignore token validation. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateToken = false; EXPECT_NO_THROW(testToken.ValidateToken(tokenOptions)); } // Validate the token asking to ignore token expiration time. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateExpirationTime = false; EXPECT_NO_THROW(testToken.ValidateToken(tokenOptions)); } } // Test tokens which are not yet ready. { // Capture the current time, 30 seconds in the future. auto now = std::chrono::system_clock::now() + std::chrono::seconds(30); TestObject testObject; testObject.Algorithm = "RSA"; testObject.Integer = 314; testObject.IntegerArray = {1, 2, 99, 32}; testObject.Issuer = "George"; // This token will be issued 30 seconds from now, and is valid for 15 seconds. testObject.ExpiresAt = now + std::chrono::seconds(15); testObject.IssuedOn = now; testObject.NotBefore = now; auto testToken = AttestationTokenInternal::CreateToken( testObject, signingKey); // Simple valdation should throw an exception. EXPECT_THROW(testToken.ValidateToken({}), std::runtime_error); // Validate the token asking to ignore token validation. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateToken = false; EXPECT_NO_THROW(testToken.ValidateToken(tokenOptions)); } // Validate the token asking to ignore token expiration time. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateNotBeforeTime = false; EXPECT_NO_THROW(testToken.ValidateToken(tokenOptions)); } } // Test signature corruptions... { // Capture the current time. auto now = std::chrono::system_clock::now(); TestObject testObject; testObject.Algorithm = "RSA"; testObject.Integer = 314; testObject.IntegerArray = {1, 2, 99, 32}; testObject.Issuer = "George"; // This token will be issued 30 seconds from now, and is valid for 15 seconds. testObject.ExpiresAt = now + std::chrono::seconds(15); testObject.IssuedOn = now; testObject.NotBefore = now; auto goodToken = AttestationTokenInternal::CreateToken( testObject, signingKey); auto signedToken = static_cast>(goodToken).RawToken; signedToken += "ABCDEFGH"; // Corrupt the signature on the signedToken. auto badToken = AttestationTokenInternal(signedToken); // Simple valdation should throw an exception - the signature of the token is invalid. EXPECT_THROW(badToken.ValidateToken({}), std::runtime_error); // Validate the token asking to ignore token validation. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateToken = false; EXPECT_NO_THROW(badToken.ValidateToken(tokenOptions)); } // Validate the token asking to ignore token signature validation. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateSigner = false; EXPECT_NO_THROW(badToken.ValidateToken(tokenOptions)); } } // Test incorrect issuer... { // Capture the current time. auto now = std::chrono::system_clock::now(); TestObject testObject; testObject.Algorithm = "RSA"; testObject.Integer = 314; testObject.IntegerArray = {1, 2, 99, 32}; testObject.Issuer = "George"; // This token will be issued 30 seconds from now, and is valid for 15 seconds. testObject.ExpiresAt = now + std::chrono::seconds(15); testObject.IssuedOn = now; testObject.NotBefore = now; auto testToken = AttestationTokenInternal::CreateToken( testObject, signingKey); AttestationTokenValidationOptions validationOptions; validationOptions.ValidateIssuer = true; validationOptions.ExpectedIssuer = "Fred"; // Simple valdation should throw an exception - the signature of the token is invalid. EXPECT_THROW(testToken.ValidateToken(validationOptions), std::runtime_error); // Validate the token asking to ignore token signature validation. { AttestationTokenValidationOptions tokenOptions{}; tokenOptions.ValidateIssuer = false; tokenOptions.ExpectedIssuer = "Fred"; EXPECT_NO_THROW(testToken.ValidateToken(tokenOptions)); } } // Test no issuer but issuer validation requested. { // Capture the current time. auto now = std::chrono::system_clock::now(); TestObject testObject; testObject.Algorithm = "RSA"; testObject.Integer = 314; testObject.IntegerArray = {1, 2, 99, 32}; // This token will be issued 30 seconds from now, and is valid for 15 seconds. testObject.ExpiresAt = now + std::chrono::seconds(15); testObject.IssuedOn = now; testObject.NotBefore = now; auto testToken = AttestationTokenInternal::CreateToken( testObject, signingKey); AttestationTokenValidationOptions validationOptions; validationOptions.ValidateIssuer = true; validationOptions.ExpectedIssuer = "Fred"; // Simple valdation should throw an exception - the signature of the token is invalid. EXPECT_THROW(testToken.ValidateToken(validationOptions), std::runtime_error); } } }}}} // namespace Azure::Security::Attestation::Test