From 3e0c80d8a841fab6df6f8a15a5f2961507e6db61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Obiltschnig?= Date: Sat, 6 Feb 2021 11:32:04 +0100 Subject: [PATCH] #3202: JWT: ESxxx signature must include padding for ECDSA R and S values --- JWT/src/Signer.cpp | 15 +++- JWT/testsuite/src/SignerTest.cpp | 132 +++++++++++++++++++++++++++++++ JWT/testsuite/src/SignerTest.h | 4 + 3 files changed, 149 insertions(+), 2 deletions(-) diff --git a/JWT/src/Signer.cpp b/JWT/src/Signer.cpp index 03d138d13..817d6524c 100644 --- a/JWT/src/Signer.cpp +++ b/JWT/src/Signer.cpp @@ -222,6 +222,11 @@ public: class ECDSAAlgorithm: public Algorithm { public: + enum + { + RS_PADDING = 32 + }; + ECDSAAlgorithm(const std::string& digestType): _digestType(digestType) { @@ -237,11 +242,16 @@ public: ecdsa.update(payload); Poco::Crypto::ECDSASignature ecdsaSig(ecdsa.signature()); - Poco::DigestEngine::Digest jwtSig(ecdsaSig.rawR()); + Poco::DigestEngine::Digest rawR(ecdsaSig.rawR()); Poco::DigestEngine::Digest rawS(ecdsaSig.rawS()); + Poco::DigestEngine::Digest jwtSig; + jwtSig.reserve(RS_PADDING*2); + for (std::size_t i = rawR.size(); i < RS_PADDING; i++) jwtSig.push_back(0); + jwtSig.insert(jwtSig.end(), rawR.begin(), rawR.end()); + for (std::size_t i = rawS.size(); i < RS_PADDING; i++) jwtSig.push_back(0); jwtSig.insert(jwtSig.end(), rawS.begin(), rawS.end()); - return jwtSig; + return jwtSig; } bool verify(const Signer& signer, const std::string& header, const std::string& payload, const Poco::DigestEngine::Digest& signature) @@ -498,6 +508,7 @@ Poco::DigestEngine::Digest Signer::decode(const std::string& signature) digest.push_back(static_cast(static_cast(ch))); ch = decoder.get(); } + return digest; } diff --git a/JWT/testsuite/src/SignerTest.cpp b/JWT/testsuite/src/SignerTest.cpp index d8bac3148..29f163cde 100644 --- a/JWT/testsuite/src/SignerTest.cpp +++ b/JWT/testsuite/src/SignerTest.cpp @@ -441,6 +441,134 @@ void SignerTest::testVerifyES256() } +void SignerTest::testSignVerifyES384() +{ + // Note: ECDSA is a strange beast and does not return a "known" signature. + // That's why we do the signing and verification in a single test. + + Token token; + token.setType("JWT"); + token.setSubject("1234567890"); + token.payload().set("name", std::string("John Doe")); + token.setIssuedAt(Poco::Timestamp::fromEpochTime(1516239022)); + + std::istringstream privateKeyStream(ECDSA_PRIVATE_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::ECKey(0, &privateKeyStream); + + Signer signer(pKey); + std::string jwt = signer.sign(token, Signer::ALGO_ES384); + + std::istringstream publicKeyStream(ECDSA_PUBLIC_KEY); + pKey = new Poco::Crypto::ECKey(&publicKeyStream); + + Signer verifier(pKey); + verifier.addAlgorithm(Signer::ALGO_ES384); + try + { + Token token2 = verifier.verify(jwt); + assert (token2.getAlgorithm() == "ES384"); + assert (token2.getType() == "JWT"); + assert (token2.getSubject() == "1234567890"); + assert (token2.getIssuedAt().epochTime() == 1516239022); + assert (token2.payload().getValue("name") == "John Doe"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +void SignerTest::testVerifyES384() +{ + std::string jwt("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.ROGmzbopY2GfjSUKih4MmgZ5_1jLQdEc2db3ITYCDOZSHzeGM_14KtY-61qvx4BXxmeUiXfoInPZWlA75VL6dA"); + + std::istringstream publicKeyStream(ECDSA_PUBLIC_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::ECKey(&publicKeyStream); + + Signer signer(pKey); + signer.addAlgorithm(Signer::ALGO_ES384); + try + { + Token token = signer.verify(jwt); + assert (token.getAlgorithm() == "ES384"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == "kLfRdCmR-qewMgzhCtqJrXVoagoh7es0yWsn3VunuS51FMBBcxLTKRDfdgHih0os4gvBdLMYkJu61_IQqoIYZw"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +void SignerTest::testSignVerifyES512() +{ + // Note: ECDSA is a strange beast and does not return a "known" signature. + // That's why we do the signing and verification in a single test. + + Token token; + token.setType("JWT"); + token.setSubject("1234567890"); + token.payload().set("name", std::string("John Doe")); + token.setIssuedAt(Poco::Timestamp::fromEpochTime(1516239022)); + + std::istringstream privateKeyStream(ECDSA_PRIVATE_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::ECKey(0, &privateKeyStream); + + Signer signer(pKey); + std::string jwt = signer.sign(token, Signer::ALGO_ES512); + + std::istringstream publicKeyStream(ECDSA_PUBLIC_KEY); + pKey = new Poco::Crypto::ECKey(&publicKeyStream); + + Signer verifier(pKey); + verifier.addAlgorithm(Signer::ALGO_ES512); + try + { + Token token2 = verifier.verify(jwt); + assert (token2.getAlgorithm() == "ES512"); + assert (token2.getType() == "JWT"); + assert (token2.getSubject() == "1234567890"); + assert (token2.getIssuedAt().epochTime() == 1516239022); + assert (token2.payload().getValue("name") == "John Doe"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + +void SignerTest::testVerifyES512() +{ + std::string jwt("eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.8AYb9WDk3x2U-69Hi2DHou06L8VavXJlMqyH8dF-uiekm926CNM7D3pkgnWD6e_OfV_p2XIkdfIV018PjZtfuA"); + + std::istringstream publicKeyStream(ECDSA_PUBLIC_KEY); + Poco::SharedPtr pKey = new Poco::Crypto::ECKey(&publicKeyStream); + + Signer signer(pKey); + signer.addAlgorithm(Signer::ALGO_ES512); + try + { + Token token = signer.verify(jwt); + assert (token.getAlgorithm() == "ES512"); + assert (token.getType() == "JWT"); + assert (token.getSubject() == "1234567890"); + assert (token.getIssuedAt().epochTime() == 1516239022); + assert (token.payload().getValue("name") == "John Doe"); + assert (token.signature() == "kLfRdCmR-qewMgzhCtqJrXVoagoh7es0yWsn3VunuS51FMBBcxLTKRDfdgHih0os4gvBdLMYkJu61_IQqoIYZw"); + } + catch (JWTException&) + { + fail("Verification must succeed"); + } +} + + CppUnit::Test* SignerTest::suite() { CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("SignerTest"); @@ -460,7 +588,11 @@ CppUnit::Test* SignerTest::suite() CppUnit_addTest(pSuite, SignerTest, testVerifyRS384); CppUnit_addTest(pSuite, SignerTest, testVerifyRS512); CppUnit_addTest(pSuite, SignerTest, testSignVerifyES256); + CppUnit_addTest(pSuite, SignerTest, testSignVerifyES384); + CppUnit_addTest(pSuite, SignerTest, testSignVerifyES512); CppUnit_addTest(pSuite, SignerTest, testVerifyES256); + CppUnit_addTest(pSuite, SignerTest, testVerifyES384); + CppUnit_addTest(pSuite, SignerTest, testVerifyES512); return pSuite; } diff --git a/JWT/testsuite/src/SignerTest.h b/JWT/testsuite/src/SignerTest.h index 87dfc6d6f..875e3218e 100644 --- a/JWT/testsuite/src/SignerTest.h +++ b/JWT/testsuite/src/SignerTest.h @@ -43,6 +43,10 @@ public: void testVerifyRS512(); void testSignVerifyES256(); void testVerifyES256(); + void testSignVerifyES384(); + void testVerifyES384(); + void testSignVerifyES512(); + void testVerifyES512(); static CppUnit::Test* suite();