Kurlyk
Loading...
Searching...
No Matches
pkce.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef KURLYK_HEADER_KURLYK_UTILS_PKCE_HPP_INCLUDED
3#define KURLYK_HEADER_KURLYK_UTILS_PKCE_HPP_INCLUDED
4
7
8#include "base64_url.hpp"
9#include <openssl/evp.h>
10#include <openssl/rand.h>
11#include <limits>
12#include <stdexcept>
13#include <string>
14#include <vector>
15#include <cstdint>
16
17namespace kurlyk {
18namespace utils {
19
20namespace detail {
21
25 inline void random_bytes(uint8_t* data, std::size_t size) {
26 if (size > static_cast<std::size_t>((std::numeric_limits<int>::max)()) ||
27 RAND_bytes(
28 reinterpret_cast<unsigned char*>(data),
29 static_cast<int>(size)) != 1) {
30 throw std::runtime_error("OpenSSL RAND_bytes failed");
31 }
32 }
33
38 inline std::vector<uint8_t> sha256(const void* data, std::size_t size) {
39 std::vector<uint8_t> digest(EVP_MAX_MD_SIZE);
40 unsigned int digest_size = 0;
41
42 if (EVP_Digest(
43 data,
44 size,
45 reinterpret_cast<unsigned char*>(digest.data()),
46 &digest_size,
47 EVP_sha256(),
48 nullptr) != 1) {
49 throw std::runtime_error("OpenSSL SHA-256 digest failed");
50 }
51
52 digest.resize(digest_size);
53 return digest;
54 }
55
56} // namespace detail
57
60 struct PkcePair {
61 std::string code_verifier;
62 std::string code_challenge;
63 std::string code_challenge_method = "S256";
64 };
65
69 inline std::string generate_code_verifier(std::size_t length = 64) {
70 static const char allowed[] =
71 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~";
72 if (length < 43) length = 43;
73 if (length > 128) length = 128;
74
75 std::string verifier;
76 verifier.reserve(length);
77
78 const std::size_t allowed_count = sizeof(allowed) - 1; // exclude null terminator
79 const std::size_t accepted_limit = 256 - (256 % allowed_count);
80
81 while (verifier.size() < length) {
82 const std::size_t remaining = length - verifier.size();
83 std::vector<uint8_t> random_data(remaining);
84 detail::random_bytes(random_data.data(), random_data.size());
85
86 for (std::size_t i = 0; i < random_data.size() && verifier.size() < length; ++i) {
87 if (random_data[i] >= accepted_limit) {
88 continue;
89 }
90 verifier.push_back(allowed[random_data[i] % allowed_count]);
91 }
92 }
93 return verifier;
94 }
95
99 inline std::string make_s256_code_challenge(const std::string& verifier) {
100 std::vector<uint8_t> digest = detail::sha256(verifier.data(), verifier.size());
101 return base64url_encode(digest.data(), digest.size());
102 }
103
107 PkcePair pair;
110 return pair;
111 }
112
113} // namespace utils
114} // namespace kurlyk
115
116#endif // KURLYK_HEADER_KURLYK_UTILS_PKCE_HPP_INCLUDED
Provides Base64url encoding and decoding (RFC 4648, no padding).
void random_bytes(uint8_t *data, std::size_t size)
Fills a byte buffer with cryptographically secure random data.
Definition pkce.hpp:25
std::vector< uint8_t > sha256(const void *data, std::size_t size)
Calculates a SHA-256 digest.
Definition pkce.hpp:38
std::string base64url_encode(const uint8_t *data, std::size_t length)
Encodes a byte buffer using Base64url (RFC 4648) without padding.
std::string make_s256_code_challenge(const std::string &verifier)
Creates an S256 code challenge from a verifier.
Definition pkce.hpp:99
PkcePair make_pkce_pair()
Creates a PKCE pair with a freshly generated verifier.
Definition pkce.hpp:106
std::string generate_code_verifier(std::size_t length=64)
Generates a cryptographically strong PKCE code verifier.
Definition pkce.hpp:69
Primary namespace for the Kurlyk library, encompassing initialization, request management,...
Stores PKCE verifier and challenge values.
Definition pkce.hpp:60
std::string code_challenge_method
Challenge method, always "S256".
Definition pkce.hpp:63
std::string code_verifier
Randomly generated code verifier.
Definition pkce.hpp:61
std::string code_challenge
Derived S256 code challenge.
Definition pkce.hpp:62