Skip to content

Commit

Permalink
Implement openssl_sign/openssl_verify in light runtime (#1106)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkornaukhov03 authored Sep 20, 2024
1 parent 755b06b commit c092716
Show file tree
Hide file tree
Showing 14 changed files with 313 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
<?php

// ===== SUPPORTED =====

define('OPENSSL_ALGO_SHA1' , 0x215fb97d);
define('OPENSSL_ALGO_MD5' , 0x257ddf13);
define('OPENSSL_ALGO_MD4' , 0x317fe3d1);
define('OPENSSL_ALGO_MD2' , 0x5aca6998);
define('OPENSSL_ALGO_DSS1' , 0xf572d6b6);
define('OPENSSL_ALGO_SHA224', 0x8bce55e9);
define('OPENSSL_ALGO_SHA256', 0x6c977f8c);
define('OPENSSL_ALGO_SHA384', 0xf54c2608);
define('OPENSSL_ALGO_SHA512', 0x225df2b6);
define('OPENSSL_ALGO_RMD160', 0x1887e6b4);


/** @kphp-extern-func-info interruptible */
function openssl_sign ($data ::: string, &$signature ::: string, $priv_key_id ::: string, $signature_alg ::: int = 1) ::: bool;
/** @kphp-extern-func-info interruptible */
function openssl_verify ($data ::: string, $signature ::: string, $pub_key_id ::: string, $signature_alg ::: int = 1) ::: int;

// ===== UNSUPPORTED =====

define('X509_PURPOSE_SSL_CLIENT' , 1);
define('X509_PURPOSE_SSL_SERVER' , 2);
Expand All @@ -11,17 +31,6 @@ define('X509_PURPOSE_ANY' , 7);
define('X509_PURPOSE_OCSP_HELPER' , 8);
define('X509_PURPOSE_TIMESTAMP_SIGN', 9);

define('OPENSSL_ALGO_SHA1' , 1);
define('OPENSSL_ALGO_MD5' , 2);
define('OPENSSL_ALGO_MD4' , 3);
define('OPENSSL_ALGO_MD2' , 4);
define('OPENSSL_ALGO_DSS1' , 5);
define('OPENSSL_ALGO_SHA224', 6);
define('OPENSSL_ALGO_SHA256', 7);
define('OPENSSL_ALGO_SHA384', 8);
define('OPENSSL_ALGO_SHA512', 9);
define('OPENSSL_ALGO_RMD160', 10);

define('OPENSSL_RAW_DATA', 1);
define('OPENSSL_ZERO_PADDING', 2);
define('OPENSSL_DONT_ZERO_PAD_KEY', 4);
Expand All @@ -45,8 +54,5 @@ function openssl_pkey_get_private ($key ::: string, $passphrase ::: string = '')
/** @kphp-extern-func-info generate-stub */
function openssl_pkey_get_public ($key ::: string) ::: string | false;
/** @kphp-extern-func-info generate-stub */
function openssl_sign ($data ::: string, &$signature ::: string, $priv_key_id ::: string, $signature_alg ::: int = 1) ::: bool;
/** @kphp-extern-func-info generate-stub */
function openssl_verify ($data ::: string, $signature ::: string, $pub_key_id ::: string, $signature_alg ::: int = 1) ::: int;
/** @kphp-extern-func-info generate-stub */
function openssl_x509_verify ($x509cert ::: string, $public_key ::: string) ::: int;

2 changes: 2 additions & 0 deletions builtin-functions/kphp-light/functions.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php

require_once __DIR__ . '/array.txt';
require_once __DIR__ . '/crypto.txt';
require_once __DIR__ . '/hash.txt';
require_once __DIR__ . '/job-workers.txt';

define('TODO', -1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
<?php

// ===== SUPPORTED =====

/** @kphp-extern-func-info */
function base64_decode ($str ::: string, $strict ::: bool = false) ::: string | false;
/** @kphp-extern-func-info */
function base64_encode ($str ::: string) ::: string;

// ===== UNSUPPORTED =====

/** @kphp-extern-func-info generate-stub */
function gzencode ($str ::: string, $level ::: int = -1) ::: string;
/** @kphp-extern-func-info generate-stub */
Expand Down Expand Up @@ -58,7 +67,4 @@ final class DeflateContext {
function deflate_init(int $encoding, mixed $options = []) ::: ?DeflateContext;
/** @kphp-extern-func-info generate-stub */
function deflate_add(DeflateContext $context, string $data, int $flush_mode = ZLIB_SYNC_FLUSH) ::: string | false;
/** @kphp-extern-func-info generate-stub */
function base64_decode ($str ::: string, $strict ::: bool = false) ::: string | false;
/** @kphp-extern-func-info generate-stub */
function base64_encode ($str ::: string) ::: string;

2 changes: 0 additions & 2 deletions builtin-functions/kphp-light/unsupported-functions.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
<?php

require_once __DIR__ . '/unsupported/arrays.txt';
require_once __DIR__ . '/unsupported/crypto.txt';
require_once __DIR__ . '/unsupported/curl.txt';
require_once __DIR__ . '/unsupported/error.txt';
require_once __DIR__ . '/unsupported/file.txt';
require_once __DIR__ . '/unsupported/fork.txt';
require_once __DIR__ . '/unsupported/hash.txt';
require_once __DIR__ . '/unsupported/kml.txt';
require_once __DIR__ . '/unsupported/kphp-toggles.txt';
require_once __DIR__ . '/unsupported/kphp-tracing.txt';
Expand Down
55 changes: 53 additions & 2 deletions runtime-light/stdlib/crypto/crypto-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
#include "runtime-light/tl/tl-functions.h"
#include "runtime-light/tl/tl-types.h"

namespace {

const string CRYPTO_COMPONENT = string("crypto");

} // namespace

task_t<Optional<string>> f$openssl_random_pseudo_bytes(int64_t length) noexcept {
if (length <= 0 || length > string::max_size()) {
co_return false;
Expand All @@ -25,7 +31,7 @@ task_t<Optional<string>> f$openssl_random_pseudo_bytes(int64_t length) noexcept
string request_buf;
request_buf.append(buffer.data(), buffer.size());

auto query = f$component_client_send_request(string("crypto"), string{buffer.data(), static_cast<string::size_type>(buffer.size())});
auto query = f$component_client_send_request(CRYPTO_COMPONENT, string{buffer.data(), static_cast<string::size_type>(buffer.size())});
string resp = co_await f$component_client_fetch_response(co_await query);

buffer.clean();
Expand All @@ -49,7 +55,7 @@ task_t<Optional<array<mixed>>> f$openssl_x509_parse(const string &data, bool sho
string request_buf;
request_buf.append(buffer.data(), buffer.size());

auto query = f$component_client_send_request(string("crypto"), request_buf);
auto query = f$component_client_send_request(CRYPTO_COMPONENT, request_buf);
string resp_from_platform = co_await f$component_client_fetch_response(co_await query);

buffer.clean();
Expand All @@ -62,3 +68,48 @@ task_t<Optional<array<mixed>>> f$openssl_x509_parse(const string &data, bool sho

co_return response.data;
}

task_t<bool> f$openssl_sign(const string &data, string &signature, const string &private_key, int64_t algo) noexcept {
tl::DigestSign request{.data = data, .private_key = private_key, .algorithm = static_cast<tl::DigestAlgorithm>(algo)};

tl::TLBuffer buffer;
request.store(buffer);

auto query = f$component_client_send_request(CRYPTO_COMPONENT, string(buffer.data(), buffer.size()));
string resp_from_platform = co_await f$component_client_fetch_response(co_await query);

buffer.clean();
buffer.store_bytes(resp_from_platform.c_str(), resp_from_platform.size());

std::optional<uint32_t> magic = buffer.fetch_trivial<uint32_t>();
if (!magic.has_value() || *magic != TL_MAYBE_TRUE) {
co_return false;
}

std::string_view str_view = buffer.fetch_string();
signature = string(str_view.data(), str_view.size());

co_return true;
}

task_t<int64_t> f$openssl_verify(const string &data, const string &signature, const string &pub_key, int64_t algo) noexcept {
tl::DigestVerify request{.data = data, .public_key = pub_key, .algorithm = static_cast<tl::DigestAlgorithm>(algo), .signature = signature};

tl::TLBuffer buffer;
request.store(buffer);

auto query = f$component_client_send_request(CRYPTO_COMPONENT, string(buffer.data(), buffer.size()));
string resp_from_platform = co_await f$component_client_fetch_response(co_await query);

buffer.clean();
buffer.store_bytes(resp_from_platform.c_str(), resp_from_platform.size());

// For now returns only 1 or 0, -1 is never returned
// Because it's currently impossible to distiguish error from negative verification
std::optional<uint32_t> magic = buffer.fetch_trivial<uint32_t>();
if (!magic.has_value() || *magic != TL_BOOL_TRUE) {
co_return 0;
}

co_return 1;
}
3 changes: 3 additions & 0 deletions runtime-light/stdlib/crypto/crypto-functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

#include "runtime-core/runtime-core.h"
#include "runtime-light/coroutine/task.h"
#include "runtime-light/tl/tl-types.h"

task_t<Optional<string>> f$openssl_random_pseudo_bytes(int64_t length) noexcept;
task_t<Optional<array<mixed>>> f$openssl_x509_parse(const string &data, bool shortnames = true) noexcept;
task_t<bool> f$openssl_sign(const string &data, string &signature, const string &private_key, int64_t algo=tl::DigestAlgorithm::SHA1) noexcept;
task_t<int64_t> f$openssl_verify(const string &data, const string &signature, const string &pub_key_id, int64_t algo=tl::DigestAlgorithm::SHA1) noexcept;
24 changes: 24 additions & 0 deletions runtime-light/stdlib/crypto/crypto_schema.tl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ certInfoItemLong#533ff89f l:long = CertInfoItem;
certInfoItemStr#c427feef s:string = CertInfoItem;
certInfoItemDict#1ea8a774 d: %(Dictionary string) = CertInfoItem;

digestAlgorithmDSS1#f572d6b6 = DigestAlgorithm; // DSA digest + with SHA1 hash function
digestAlgorithmSHA1#215fb97d = DigestAlgorithm;
digestAlgorithmSHA224#8bce55e9 = DigestAlgorithm;
digestAlgorithmSHA256#6c977f8c = DigestAlgorithm;
digestAlgorithmSHA384#f54c2608 = DigestAlgorithm;
digestAlgorithmSHA512#225df2b6 = DigestAlgorithm;
digestAlgorithmRMD160#1887e6b4 = DigestAlgorithm;
digestAlgorithmMD5#257ddf13 = DigestAlgorithm;
digestAlgorithmMD4#317fe3d1 = DigestAlgorithm;
digestAlgorithmMD2#5aca6998 = DigestAlgorithm;

---functions---

getCryptosecurePseudorandomBytes#2491b81d
Expand All @@ -32,3 +43,16 @@ getPemCertInfo#a50cfd6c
short : Bool
bytes : string
= Maybe (Dictionary CertInfoItem);

digestSign#d345f658
data : string
private_key : string
algorithm : DigestAlgorithm
= Maybe string;

digestVerify#5760bd0e
data : string
private_key : string
algorithm : DigestAlgorithm
signature : string
= Bool;
139 changes: 139 additions & 0 deletions runtime-light/stdlib/hash/hash-functions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Compiler for PHP (aka KPHP)
// Copyright (c) 2024 LLC «V Kontakte»
// Distributed under the GPL v3 License, see LICENSE.notice.txt

#include "runtime-light/stdlib/hash/hash-functions.h"

static int base64_encode(const unsigned char *const input, int ilen, char *output, int olen) {
static const char *const symbols64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

auto next_input_uchar = [input, ilen](int &i) {
if (i >= ilen) {
return static_cast<unsigned char>(0);
}
return input[i++];
};

int j = 0;
char buf[4];
for (int i = 0; i < ilen;) {
int old_i = i;
int o = next_input_uchar(i);
o <<= 8;
o |= next_input_uchar(i);
o <<= 8;
o |= next_input_uchar(i);
int l = i - old_i;
assert(l > 0 && l <= 3);
for (int u = 3; u >= 0; u--) {
buf[u] = symbols64[o & 63];
o >>= 6;
}
if (l == 1) {
buf[2] = buf[3] = '=';
} else if (l == 2) {
buf[3] = '=';
}
if (j + 3 >= olen) {
return -1;
}
memcpy(&output[j], buf, 4);
j += 4;
}
if (j >= olen) {
return -1;
}
output[j++] = 0;
return 0;
}

string f$base64_encode(const string &s) {
int result_len = (s.size() + 2) / 3 * 4;
string res(result_len, false);
result_len = base64_encode(reinterpret_cast<const unsigned char *>(s.c_str()), static_cast<int>(s.size()), res.buffer(), result_len + 1);

if (result_len != 0) {
return {};
}

return res;
}

static constexpr short base64_reverse_table[256] = {-2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2, -2, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2};

/* Function f$base64_decode ported from https://github.com/php/php-src/blob/master/ext/standard/base64.c#L130
* "This product includes PHP software, freely available from <http://www.php.net/software/>".
*/
Optional<string> f$base64_decode(const string &s, bool strict) {
/* run through the whole string, converting as we go */
string::size_type result_len = (s.size() + 3) / 4 * 3;
string result(result_len, false);
int i = 0;
string::size_type j = 0;
int padding = 0;
for (string::size_type pos = 0; pos < s.size(); pos++) {
int ch = static_cast<unsigned char>(s[pos]);
if (ch == '=') {
padding++;
continue;
}

ch = base64_reverse_table[ch];
if (!strict) {
/* skip unknown characters and whitespace */
if (ch < 0) {
continue;
}
} else {
/* skip whitespace */
if (ch == -1) {
continue;
}
/* fail on bad characters or if any data follows padding */
if (ch == -2 || padding) {
return false;
}
}

switch (i % 4) {
case 0:
result[j] = static_cast<unsigned char>(ch << 2);
break;
case 1:
result[j] = static_cast<char>(result[j] | (ch >> 4));
result[++j] = static_cast<unsigned char>((ch & 0x0f) << 4);
break;
case 2:
result[j] = static_cast<char>(result[j] | (ch >> 2));
result[++j] = static_cast<unsigned char>((ch & 0x03) << 6);
break;
case 3:
result[j] = static_cast<char>(result[j] | ch);
++j;
break;
}
i++;
}
/* fail if the input is truncated (only one char in last group) */
if (strict && i % 4 == 1) {
return false;
}
/* fail if the padding length is wrong (not VV==, VVV=), but accept zero padding
* RFC 4648: "In some circumstances, the use of padding [--] is not required" */
if (strict && padding && (padding > 2 || (i + padding) % 4 != 0)) {
return false;
}

result.shrink(j);

return result;
}
10 changes: 10 additions & 0 deletions runtime-light/stdlib/hash/hash-functions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Compiler for PHP (aka KPHP)
// Copyright (c) 2024 LLC «V Kontakte»
// Distributed under the GPL v3 License, see LICENSE.notice.txt

#pragma once

#include "runtime-core/runtime-core.h"

string f$base64_encode(const string &s);
Optional<string> f$base64_decode(const string &s, bool strict);
1 change: 1 addition & 0 deletions runtime-light/stdlib/stdlib.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ prepend(
curl/curl-context.cpp
exit/exit-functions.cpp
fork/fork-context.cpp
hash/hash-functions.cpp
job-worker/job-worker-api.cpp
job-worker/job-worker-context.cpp
output/output-buffer.cpp
Expand Down
Loading

0 comments on commit c092716

Please sign in to comment.