Add DER-P1363 sig conversion to EC token sign/verify

OpenSSL generates ASN1.DER, JWS generates P1363
This commit is contained in:
Junhyun Shim 2022-08-18 14:26:38 +02:00
parent 62f8f01ed8
commit 37ff1c2eb6
1 changed files with 60 additions and 0 deletions

View File

@ -22,6 +22,7 @@
#include "flow/network.h"
#include "flow/serialize.h"
#include "flow/Arena.h"
#include "flow/AutoCPointer.h"
#include "flow/Error.h"
#include "flow/IRandom.h"
#include "flow/MkCert.h"
@ -87,6 +88,51 @@ bool checkSignAlgorithm(PKeyAlgorithm algo, PrivateKey key) {
}
}
Optional<StringRef> convertEs256P1363ToDer(Arena& arena, StringRef p1363) {
const int SIGLEN = p1363.size();
const int HALF_SIGLEN = SIGLEN / 2;
auto r = AutoCPointer(BN_bin2bn(p1363.begin(), HALF_SIGLEN, nullptr), &::BN_free);
auto s = AutoCPointer(BN_bin2bn(p1363.begin() + HALF_SIGLEN, HALF_SIGLEN, nullptr), &::BN_free);
if (!r || !s)
return {};
auto sig = AutoCPointer(::ECDSA_SIG_new(), &ECDSA_SIG_free);
if (!sig)
return {};
::ECDSA_SIG_set0(sig, r.release(), s.release());
auto const derLen = ::i2d_ECDSA_SIG(sig, nullptr);
if (derLen < 0)
return {};
auto buf = new (arena) uint8_t[derLen];
auto bufPtr = buf;
::i2d_ECDSA_SIG(sig, &bufPtr);
return StringRef(buf, derLen);
}
Optional<StringRef> convertEs256DerToP1363(Arena& arena, StringRef der) {
uint8_t const* derPtr = der.begin();
auto sig = AutoCPointer(::d2i_ECDSA_SIG(nullptr, &derPtr, der.size()), &::ECDSA_SIG_free);
if (!sig) {
return {};
}
// ES256-specific constant. Adapt as needed
constexpr const int SIGLEN = 64;
constexpr const int HALF_SIGLEN = SIGLEN / 2;
auto buf = new (arena) uint8_t[SIGLEN];
::memset(buf, 0, SIGLEN);
auto bufr = buf;
auto bufs = bufr + HALF_SIGLEN;
auto r = std::add_pointer_t<BIGNUM const>();
auto s = std::add_pointer_t<BIGNUM const>();
ECDSA_SIG_get0(sig, &r, &s);
auto const lenr = BN_num_bytes(r);
auto const lens = BN_num_bytes(s);
if (lenr > HALF_SIGLEN || lens > HALF_SIGLEN)
return {};
BN_bn2bin(r, bufr + (HALF_SIGLEN - lenr));
BN_bn2bin(s, bufs + (HALF_SIGLEN - lens));
return StringRef(buf, SIGLEN);
}
} // namespace
namespace authz {
@ -268,6 +314,13 @@ StringRef signToken(Arena& arena, TokenRef tokenSpec, PrivateKey privateKey) {
throw digital_signature_ops_error();
}
auto plainSig = privateKey.sign(tmpArena, tokenPart, *digest);
if (tokenSpec.algorithm == Algorithm::ES256) {
// Need to convert ASN.1/DER signature to IEEE-P1363
auto convertedSig = convertEs256DerToP1363(tmpArena, plainSig);
if (!convertedSig.present())
throw digital_signature_ops_error();
plainSig = convertedSig.get();
}
auto const sigPartLen = base64url::encodedLength(plainSig.size());
auto const totalLen = tokenPart.size() + 1 + sigPartLen;
auto out = new (arena) uint8_t[totalLen];
@ -442,6 +495,13 @@ bool verifyToken(StringRef signedToken, PublicKey publicKey) {
auto [verifyAlgo, digest] = getMethod(parsedToken.algorithm);
if (!checkVerifyAlgorithm(verifyAlgo, publicKey))
return false;
if (parsedToken.algorithm == Algorithm::ES256) {
// Need to convert IEEE-P1363 signature to ASN.1/DER
auto convertedSig = convertEs256P1363ToDer(arena, sig);
if (!convertedSig.present())
return false;
sig = convertedSig.get();
}
return publicKey.verify(b64urlTokenPart, sig, *digest);
}