ucode-mod-pkgen: add ucode module for generating crypto keys/certificates

This also includes a script to use the functionality from the
command line

Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Felix Fietkau 2024-03-22 16:21:18 +01:00
parent 44c877f197
commit 2c78e9d7bd
6 changed files with 1586 additions and 0 deletions

View File

@ -0,0 +1,44 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ucode-mod-pkgen
PKG_RELEASE:=1
PKG_LICENSE:=GPL-2.0-or-later
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/cmake.mk
CMAKE_INSTALL := 1
define Package/ucode-mod-pkgen
SECTION:=utils
CATEGORY:=Utilities
TITLE:=ucode module for generating public keys/certificates
DEPENDS:=+libucode +libmbedtls
endef
define Package/ucode-mod-pkgen/description
The pkgen module provides functionality for generating cryptographic keys and
(self-)signed certificates. It supports exporting PEM/DER format files, as
well as PKCS#12 bundle for client cert/key pairs with CA.
endef
define Package/pkgen
SECTION:=utils
CATEGORY:=Utilities
TITLE:=ucode script for generating public keys/certificates
DEPENDS:=+ucode +ucode-mod-pkgen +ucode-mod-fs
endef
define Package/ucode-mod-pkgen/install
$(INSTALL_DIR) $(1)/usr/lib/ucode
$(CP) $(PKG_INSTALL_DIR)/usr/lib/ucode/pkgen.so $(1)/usr/lib/ucode/
endef
define Package/pkgen/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) ./files/pkgen $(1)/usr/bin
endef
$(eval $(call BuildPackage,ucode-mod-pkgen))
$(eval $(call BuildPackage,pkgen))

View File

@ -0,0 +1,252 @@
#!/usr/bin/env ucode
'use strict';
import { basename, readfile, writefile, stdin } from "fs";
let pk = require("pkgen");
let valid_from = "20240101000000";
let valid_to = "21001231235959";
let subject, password, password_stdin;
let keytype = "ec";
let keylen = 2048;
let keyexp = 65537;
let keycurve = "secp256r1";
let no_ca;
let legacy;
const usage_message = `Usage: ${basename(sourcepath())} [<options>] <command> [<arguments>]
Commands:
ca <ca.pem>: Create a new CA.
(creates ca.pem, ca.key, ca.serial)
cert <ca.pem> <cert.pem>: Create a new certificate/key using the CA
from ca.pem. (creates cert.pem and ca.key)
cert_p12 <ca.pem> <cert.p12>: Create a new PKCS#12 certificate/key
using the CA from ca.pem. (creates ca.p12)
selfsigned <cert.pem>: Create a self-signed certificate
(creates cert.pem)
Options:
-C <curve> Set EC curve type (default: ${keycurve})
Possible values: secp521r1, secp384r1, secp256r1,
secp256k1, secp224r1, secp224k1, secp192r1,
secp192k1
-E <exponent> Set RSA key exponent (default: ${keyexp})
-L <len> Set RSA key length (default: ${keylen})
-N Omit CA certificate for PKCS#12 files
-p <password> Set PKCS#12 password to <password>
-P Read PKCS#12 password from stdin
(default: random password, printed to stdout)
-s <name> Set subject for generated certificate to <name>.
-t rsa|ec Set key type to rsa or ec (default: ec)
-V <from> <to> Set validity for generated certificates.
(default: ${valid_from} ${valid_to})
-W Use weaker PKCS#12 encryption for
compatibility with Windows and Apple systems
`;
function perror(msg) {
let err = pk.errno() == -1 ? "Invalid arguments" : pk.error();
warn(`${msg}: ${err}\n`);
exit(1);
}
function usage() {
warn(usage_message);
exit(1);
}
function check_pem_path(pem_file) {
if (substr(pem_file, -4) != ".pem") {
warn(`Path with .pem extension expected\n`);
exit(1);
}
return pem_file;
}
function gen_key() {
let key = pk.generate_key({
type: keytype,
curve: keycurve,
size: keylen,
exponent: keyexp,
});
if (!key)
perror("Failed to generate CA key");
return key;
}
function gen_cert(key, args) {
let cert = pk.generate_cert({
subject_name: subject,
subject_key: key,
validity: [ valid_from, valid_to ],
...args
});
if (!cert)
perror("Failed to generate certificate");
cert = cert.pem();
if (!cert)
perror("Failed to complete certificate");
return cert;
}
function gen_client_cert(ca_file, ca_data, key) {
let ca_base = substr(ca_file, 0, -4);
let ca_info = pk.cert_info(ca_data);
if (!length(ca_info))
perror("Failed to load CA certificate");
let ca_key = pk.load_key(readfile(ca_base + ".key"));
if (!ca_key)
perror("Failed to load CA key");
let ca_serial = +readfile(ca_base + ".serial");
if (!ca_serial)
perror("Failed to load CA serial");
let cert = gen_cert(key, {
serial: ++ca_serial,
issuer_name: ca_info[0].subject,
issuer_key: ca_key,
});
writefile(ca_base + ".serial", "" + ca_serial);
return cert;
}
let cmds = {
ca: function(args) {
let ca_file = check_pem_path(shift(args));
let ca_base = substr(ca_file, 0, -4);
let key = gen_key();
let ca_cert = gen_cert(key, {
ca: true,
serial: 1,
issuer_name: subject,
issuer_key: key,
key_usage: [ "key_cert_sign" ],
});
writefile(ca_file, ca_cert);
writefile(ca_base + ".key", key.pem());
writefile(ca_base + ".serial", "1");
},
cert: function (args) {
let ca_file = check_pem_path(shift(args));
let crt_file = check_pem_path(shift(args));
let crt_base = substr(crt_file, 0, -4);
let key = gen_key();
let ca_data = readfile(ca_file);
let cert = gen_client_cert(ca_file, ca_data, key);
writefile(crt_base + ".key", key.pem());
writefile(crt_file, cert);
},
cert_p12: function (args) {
let ca_file = check_pem_path(shift(args));
let p12_file = shift(args);
if (!p12_file)
usage();
let key = gen_key();
let ca_data = readfile(ca_file);
let cert = gen_client_cert(ca_file, ca_data, key);
if (password_stdin)
password = rtrim(stdin.read("line"));
else if (!password)
print((password = hexenc(readfile("/dev/urandom", 8))) + "\n");
let p12 = pk.generate_pkcs12({
password, cert, key, legacy,
extra: no_ca ? null : [ ca_data ],
});
writefile(p12_file, p12);
},
selfsigned: function(args) {
let crt_file = check_pem_path(shift(args));
let crt_base = substr(crt_file, 0, -4);
let key = gen_key();
let cert = gen_cert(key, {
serial: 1,
issuer_name: subject,
issuer_key: key,
});
writefile(crt_base + ".key", key.pem());
writefile(crt_file, cert);
},
};
while (substr(ARGV[0], 0, 1) == "-") {
let opt = substr(shift(ARGV), 1);
switch (opt) {
case 'C':
keycurve = shift(ARGV);
break;
case 'L':
keylen = +shift(ARGV);
break;
case 'N':
no_ca = true;
break;
case 'p':
password = shift(ARGV);
if (password_stdin)
usage();
break;
case 'P':
password_stdin = true;
if (password)
usage();
break;
case 's':
subject = shift(ARGV);
break;
case 't':
keytype = shift(ARGV);
if (keytype != "rsa" && keytype != "ec") {
warn(`Unsupported key type ${keytype}\n`);
exit(1);
}
break;
case 'V':
valid_from = shift(ARGV);
valid_to = shift(ARGV);
break;
case 'W':
legacy = true;
break;
default:
usage();
break;
}
}
let cmd = shift(ARGV);
if (!cmd || !cmds[cmd])
usage();
if (subject == null) {
warn(`Missing -s option\n`);
exit(1);
}
cmds[cmd](ARGV);

View File

@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.13)
PROJECT(ucode-pkgen C)
ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -ffunction-sections -fwrapv -D_GNU_SOURCE)
IF(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6)
ADD_DEFINITIONS(-Wextra -Werror=implicit-function-declaration)
ADD_DEFINITIONS(-Wformat -Werror=format-security -Werror=format-nonliteral)
ENDIF()
ADD_DEFINITIONS(-Wmissing-declarations -Wno-error=unused-variable -Wno-unused-parameter)
IF(APPLE)
SET(UCODE_MODULE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup")
ELSE()
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-Wl,--gc-sections")
ENDIF()
IF(DEBUG)
ADD_DEFINITIONS(-DDEBUG -g3 -O0)
ELSE()
ADD_DEFINITIONS(-DNDEBUG)
ENDIF()
FIND_LIBRARY(mbedtls NAMES mbedtls)
FIND_LIBRARY(ucode NAMES ucode)
FIND_PATH(mbedtls_include_dir NAMES mbedtls/pk.h)
FIND_PATH(ucode_include_dir NAMES ucode/module.h)
INCLUDE_DIRECTORIES(${mbedtls_include_dir} ${ucode_include_dir})
ADD_LIBRARY(pkgen_lib MODULE ucode.c pkcs12.c)
SET_TARGET_PROPERTIES(pkgen_lib PROPERTIES OUTPUT_NAME pkgen PREFIX "")
TARGET_LINK_OPTIONS(pkgen_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS})
TARGET_LINK_LIBRARIES(pkgen_lib ${mbedtls})
INSTALL(TARGETS pkgen_lib LIBRARY DESTINATION lib/ucode)

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
*/
#ifndef __UCODE_PK_H
#define __UCODE_PK_H
#include <ucode/lib.h>
#include <ucode/vm.h>
#include <mbedtls/bignum.h>
#include <mbedtls/pk.h>
#include <mbedtls/oid.h>
#include <mbedtls/error.h>
#include <mbedtls/version.h>
#if MBEDTLS_VERSION_MAJOR < 3
#define MBEDTLS_LEGACY
#endif
int random_cb(void *ctx, unsigned char *out, size_t len);
uc_value_t *uc_generate_pkcs12(uc_vm_t *vm, size_t nargs);
int64_t get_int_arg(uc_value_t *obj, const char *key, int64_t defval);
extern int mbedtls_errno;
extern char buf[32 * 1024];
#define C(ret) \
({ \
int __ret = (ret); \
mbedtls_errno = __ret < 0 ? __ret : 0; \
__ret; \
})
#define CHECK(ret) do { \
if (C(ret) < 0) \
return NULL; \
} while (0)
#define CHECK_INT(ret) do { \
if (C(ret) < 0) \
return -1; \
} while (0)
#define INVALID_ARG() do { C(-1); return NULL; } while (0)
#endif

View File

@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
*/
#include "pk.h"
#include <mbedtls/x509_crt.h>
#include <mbedtls/ecp.h>
#include <mbedtls/rsa.h>
#include <mbedtls/asn1write.h>
#include <mbedtls/pkcs5.h>
#include <mbedtls/pkcs12.h>
#include <mbedtls/base64.h>
#include <mbedtls/sha1.h>
#define OID_TAG(n) MBEDTLS_OID_##n, MBEDTLS_OID_SIZE(MBEDTLS_OID_##n)
#ifndef MBEDTLS_OID_AES_256_CBC
#define MBEDTLS_OID_AES_256_CBC MBEDTLS_OID_AES "\x2a"
#endif
#define MBEDTLS_OID_PKCS9_LOCAL_KEY_ID MBEDTLS_OID_PKCS9 "\x15"
#define MBEDTLS_OID_PKCS9_CERT_TYPE MBEDTLS_OID_PKCS9 "\x16"
#define MBEDTLS_OID_PKCS9_CERT_TYPE_X509 MBEDTLS_OID_PKCS9_CERT_TYPE "\x01"
#define MBEDTLS_OID_PKCS12_KEY_BAG MBEDTLS_OID_PKCS12 "\x0a\x01\x01"
#define MBEDTLS_OID_PKCS12_SHROUDED_KEY_BAG MBEDTLS_OID_PKCS12 "\x0a\x01\x02"
#define MBEDTLS_OID_PKCS12_CERT_BAG MBEDTLS_OID_PKCS12 "\x0a\x01\x03"
#ifndef MBEDTLS_OID_PKCS7
#define MBEDTLS_OID_PKCS7 MBEDTLS_OID_PKCS "\x07"
#define MBEDTLS_OID_PKCS7_DATA MBEDTLS_OID_PKCS7 "\x01"
#define MBEDTLS_OID_PKCS7_ENCRYPTED_DATA MBEDTLS_OID_PKCS7 "\x06"
#endif
#define NUM_ITER 2048
#define SALT_LEN 16
#define IV_LEN 16
#define IV_LEN_LEGACY 8
#define KEY_LEN 32
#define CERT_HASH_LEN 20
#define CONTEXT_TAG(n) (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | (n))
#define SEQUENCE_TAG (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)
#define SET_TAG (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET)
#define CTX_ADD(ctx, n) \
do { \
int __n = (n); \
if ((ctx)->p - (ctx)->start <= __n) \
return -1; \
(ctx)->p -= __n; \
} while (0)
struct pkcs12_ctx {
uint8_t *p, *start;
uint8_t cert_hash[20];
uint8_t *key_cert_hash;
uint8_t salt[SALT_LEN];
uint8_t iv[IV_LEN];
const char *password;
uint8_t *pwd;
size_t pwd_len;
bool legacy;
};
#ifdef MBEDTLS_LEGACY
static inline int
mbedtls_pkcs5_pbkdf2_hmac_ext(mbedtls_md_type_t md_alg, const unsigned char *password,
size_t plen, const unsigned char *salt, size_t slen,
unsigned int iteration_count,
uint32_t key_length, unsigned char *output)
{
mbedtls_md_context_t md_ctx;
const mbedtls_md_info_t *md_info;
int ret;
md_info = mbedtls_md_info_from_type(md_alg);
if (!md_info)
return MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE;
mbedtls_md_init(&md_ctx);
ret = mbedtls_md_setup(&md_ctx, md_info, 1);
if (ret)
goto out;
ret = mbedtls_pkcs5_pbkdf2_hmac(&md_ctx, password, plen, salt, slen,
iteration_count, key_length, output);
out:
mbedtls_md_free(&md_ctx);
return ret;
}
#endif
static void
uc_p12_add_tag(struct pkcs12_ctx *ctx, uint8_t *end, uint8_t tag)
{
mbedtls_asn1_write_len(&ctx->p, ctx->start, end - ctx->p);
mbedtls_asn1_write_tag(&ctx->p, ctx->start, tag);
}
static void
uc_p12_add_sequence(struct pkcs12_ctx *ctx, uint8_t *end)
{
uc_p12_add_tag(ctx, end, SEQUENCE_TAG);
}
static void
uc_p12_add_algo(struct pkcs12_ctx *ctx, const char *oid, size_t oid_len, size_t par_len)
{
mbedtls_asn1_write_algorithm_identifier(&ctx->p, ctx->start, oid, oid_len, par_len);
}
static void
uc_p12_add_attribute(struct pkcs12_ctx *ctx, uint8_t *end, const char *oid, size_t oid_len)
{
uc_p12_add_tag(ctx, end, SET_TAG);
mbedtls_asn1_write_oid(&ctx->p, ctx->start, oid, oid_len);
uc_p12_add_sequence(ctx, end);
}
static void
uc_p12_add_localkeyid(struct pkcs12_ctx *ctx, bool key)
{
uint8_t *end = ctx->p;
ctx->p -= CERT_HASH_LEN;
if (key)
ctx->key_cert_hash = ctx->p;
else if (ctx->key_cert_hash)
memcpy(ctx->p, ctx->key_cert_hash, CERT_HASH_LEN);
uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
uc_p12_add_attribute(ctx, end, OID_TAG(PKCS9_LOCAL_KEY_ID));
}
static void
uc_p12_add_bag(struct pkcs12_ctx *ctx, uint8_t *data_end, uint8_t *end, const char *oid, size_t oid_len)
{
uc_p12_add_tag(ctx, data_end, CONTEXT_TAG(0));
mbedtls_asn1_write_oid(&ctx->p, ctx->start, oid, oid_len);
uc_p12_add_sequence(ctx, end);
}
static int
uc_p12_enc_setup(struct pkcs12_ctx *ctx, mbedtls_cipher_context_t *cipher)
{
const mbedtls_cipher_info_t *cipher_info;
uint8_t key[KEY_LEN];
int ret;
random_cb(NULL, ctx->iv, IV_LEN);
ret = mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA256,
(void *)ctx->password, strlen(ctx->password),
ctx->salt, SALT_LEN, NUM_ITER,
KEY_LEN, key);
if (ret < 0)
return ret;
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_256_CBC);
ret = mbedtls_cipher_setup(cipher, cipher_info);
if (ret < 0)
return ret;
return mbedtls_cipher_setkey(cipher, key, 8 * KEY_LEN, MBEDTLS_ENCRYPT);
}
static int
uc_p12_enc_legacy(struct pkcs12_ctx *ctx, mbedtls_cipher_context_t *cipher)
{
const mbedtls_cipher_info_t *cipher_info;
uint8_t key[24];
int ret;
ret = mbedtls_pkcs12_derivation(key, sizeof(key), ctx->pwd, ctx->pwd_len,
ctx->salt, SALT_LEN, MBEDTLS_MD_SHA1,
MBEDTLS_PKCS12_DERIVE_KEY, NUM_ITER);
if (ret < 0)
return ret;
ret = mbedtls_pkcs12_derivation(ctx->iv, IV_LEN_LEGACY, ctx->pwd, ctx->pwd_len,
ctx->salt, SALT_LEN, MBEDTLS_MD_SHA1,
MBEDTLS_PKCS12_DERIVE_IV, NUM_ITER);
if (ret < 0)
return ret;
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_EDE3_CBC);
ret = mbedtls_cipher_setup(cipher, cipher_info);
if (ret < 0)
return ret;
return mbedtls_cipher_setkey(cipher, key, 8 * sizeof(key), MBEDTLS_ENCRYPT);
}
static int
uc_p12_encrypt(struct pkcs12_ctx *ctx, uint8_t *end)
{
mbedtls_cipher_context_t cipher;
size_t iv_len = ctx->legacy ? IV_LEN_LEGACY : IV_LEN;
size_t out_len = 0;
int ret;
random_cb(NULL, ctx->salt, SALT_LEN);
if (ctx->legacy)
ret = uc_p12_enc_legacy(ctx, &cipher);
else
ret = uc_p12_enc_setup(ctx, &cipher);
if (ret < 0)
goto out;
ret = mbedtls_cipher_set_padding_mode(&cipher, MBEDTLS_PADDING_PKCS7);
if (ret < 0)
goto out;
ret = mbedtls_cipher_crypt(&cipher, ctx->iv, iv_len, ctx->p, end - ctx->p,
(void *)buf, &out_len);
if (ret < 0)
goto out;
CTX_ADD(ctx, out_len - (end - ctx->p));
memcpy(ctx->p, buf, out_len);
uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
out:
mbedtls_cipher_free(&cipher);
return ret;
}
static int
uc_p12_add_enc_params(struct pkcs12_ctx *ctx)
{
uint8_t *par_end = ctx->p;
uint8_t *kdf_end;
if (ctx->legacy) {
mbedtls_asn1_write_int(&ctx->p, ctx->start, NUM_ITER);
CTX_ADD(ctx, SALT_LEN);
memcpy(ctx->p, ctx->salt, SALT_LEN);
uc_p12_add_tag(ctx, ctx->p + SALT_LEN, MBEDTLS_ASN1_OCTET_STRING);
uc_p12_add_sequence(ctx, par_end);
uc_p12_add_algo(ctx, OID_TAG(PKCS12_PBE_SHA1_DES3_EDE_CBC), par_end - ctx->p);
} else {
CTX_ADD(ctx, IV_LEN);
memcpy(ctx->p, ctx->iv, IV_LEN);
uc_p12_add_tag(ctx, par_end, MBEDTLS_ASN1_OCTET_STRING);
uc_p12_add_algo(ctx, OID_TAG(AES_256_CBC), par_end - ctx->p);
kdf_end = ctx->p;
uc_p12_add_algo(ctx, OID_TAG(HMAC_SHA256), 0);
mbedtls_asn1_write_int(&ctx->p, ctx->start, NUM_ITER);
CTX_ADD(ctx, SALT_LEN);
memcpy(ctx->p, ctx->salt, SALT_LEN);
uc_p12_add_tag(ctx, ctx->p + SALT_LEN, MBEDTLS_ASN1_OCTET_STRING);
uc_p12_add_sequence(ctx, kdf_end);
uc_p12_add_algo(ctx, OID_TAG(PKCS5_PBKDF2), kdf_end - ctx->p);
uc_p12_add_sequence(ctx, par_end);
uc_p12_add_algo(ctx, OID_TAG(PKCS5_PBES2), par_end - ctx->p);
}
return 0;
}
static int
uc_p12_add_cert(struct pkcs12_ctx *ctx, uc_value_t *arg, bool ca)
{
const char *str = ucv_string_get(arg), *str_end;
uint8_t *bag_end, *end;
size_t len;
int ret;
#define START_TAG "-----BEGIN CERTIFICATE-----"
#define END_TAG "-----END CERTIFICATE-----"
if (!str)
return -1;
str = strstr(str, START_TAG);
if (!str)
return -1;
str += sizeof(START_TAG);
str_end = strstr(str, END_TAG);
if (!str_end)
return -1;
if ((size_t)(str_end - str) > sizeof(buf) / 2)
return -1;
ret = mbedtls_base64_decode((void *)buf, sizeof(buf) / 2, &len,
(const void *)str, str_end - str);
if (ret)
return ret;
bag_end = ctx->p;
if (!ca && ctx->key_cert_hash) {
mbedtls_sha1((const void *)buf, len, ctx->key_cert_hash);
uc_p12_add_localkeyid(ctx, false);
uc_p12_add_tag(ctx, bag_end, SET_TAG);
}
end = ctx->p;
CTX_ADD(ctx, len);
memcpy(ctx->p, buf, len);
uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
/* CertBag */
uc_p12_add_tag(ctx, end, CONTEXT_TAG(0));
mbedtls_asn1_write_oid(&ctx->p, ctx->start, OID_TAG(PKCS9_CERT_TYPE_X509));
uc_p12_add_sequence(ctx, end);
uc_p12_add_bag(ctx, end, bag_end, OID_TAG(PKCS12_CERT_BAG));
return 0;
}
static int
uc_p12_add_key(struct pkcs12_ctx *ctx, uc_value_t *arg)
{
mbedtls_pk_context *pk = ucv_resource_data(arg, "mbedtls.pk");
uint8_t *bag_end, *end;
uint8_t *param = NULL;
size_t param_len = 0;
const char *oid;
size_t oid_len = 0;
int ret, len;
if (!pk)
return -1;
bag_end = ctx->p;
uc_p12_add_localkeyid(ctx, true);
uc_p12_add_tag(ctx, bag_end, SET_TAG);
end = ctx->p;
len = mbedtls_pk_write_key_der(pk, (void *)ctx->start, end - ctx->start);
if (len < 0)
return len;
ctx->p -= len;
/* Convert EC key contents to PKCS#8 style */
if (mbedtls_pk_get_type(pk) == MBEDTLS_PK_ECKEY) {
mbedtls_ecp_group_id grp_id;
mbedtls_asn1_buf tag_buf;
uint8_t *pkey_start, *pkey_end;
size_t seq_len, pkey_len, param_tag_len;
uint8_t *p = ctx->p;
uint8_t *_end = end;
uint8_t *_start;
int version;
ret = mbedtls_asn1_get_tag(&p, end, &seq_len, SEQUENCE_TAG);
if (ret < 0)
return ret;
_start = p;
_end = p + seq_len;
ret = mbedtls_asn1_get_int(&p, _end, &version);
if (ret < 0)
return ret;
/* private key */
ret = mbedtls_asn1_get_tag(&p, _end, &pkey_len, MBEDTLS_ASN1_OCTET_STRING);
if (ret < 0)
return ret;
pkey_start = p;
p += pkey_len;
pkey_end = p;
/* parameters */
ret = mbedtls_asn1_get_tag(&p, _end, &param_len, CONTEXT_TAG(0));
if (ret < 0)
return ret;
param = memcpy(buf, p, param_len);
p += param_len;
/* overwrite parameters */
param_tag_len = p - pkey_end;
ctx->p += param_tag_len;
_start += param_tag_len;
memmove(ctx->p, ctx->p - param_tag_len, p - ctx->p);
/* replace sequence tag */
ctx->p = _start;
uc_p12_add_sequence(ctx, end);
/* check for Curve25519 or Curve448 */
tag_buf = (mbedtls_asn1_buf){
.p = (uint8_t *)buf,
.len = param_len,
};
tag_buf.tag = *tag_buf.p;
ret = mbedtls_asn1_get_tag(&tag_buf.p, tag_buf.p + param_len, &tag_buf.len, tag_buf.tag);
if (ret < 0)
return ret;
oid = MBEDTLS_OID_EC_ALG_UNRESTRICTED;
oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_ALG_UNRESTRICTED);
#ifdef MBEDTLS_LEGACY
(void)pkey_start;
(void)grp_id;
#else
ret = mbedtls_oid_get_ec_grp_algid(&tag_buf, &grp_id);
if (!ret && (grp_id == MBEDTLS_ECP_DP_CURVE25519 ||
grp_id == MBEDTLS_ECP_DP_CURVE448)) {
ctx->p = end - pkey_len;
memmove(ctx->p, pkey_start, pkey_len);
uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
}
#endif
} else {
mbedtls_oid_get_oid_by_pk_alg(mbedtls_pk_get_type(pk), &oid, &oid_len);
}
uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
/* KeyBag */
if (param_len) {
CTX_ADD(ctx, param_len);
memcpy(ctx->p, param, param_len);
}
uc_p12_add_algo(ctx, oid, oid_len, param_len);
mbedtls_asn1_write_int(&ctx->p, ctx->start, 0);
uc_p12_add_sequence(ctx, end);
ret = uc_p12_encrypt(ctx, end);
if (ret < 0)
return ret;
ret = uc_p12_add_enc_params(ctx);
if (ret < 0)
return ret;
uc_p12_add_sequence(ctx, end);
uc_p12_add_bag(ctx, end, bag_end, OID_TAG(PKCS12_SHROUDED_KEY_BAG));
return 0;
}
static int
uc_p12_add_authsafe(struct pkcs12_ctx *ctx, uc_value_t *arg)
{
uc_value_t *extra;
uint8_t *end = ctx->p;
size_t len;
int ret;
/* authSafe contents */
extra = ucv_object_get(arg, "extra", NULL);
if (extra) {
size_t len;
if (ucv_type(extra) != UC_ARRAY)
return -1;
len = ucv_array_length(extra);
for (size_t i = 0; i < len; i++) {
ret = uc_p12_add_cert(ctx, ucv_array_get(extra, i), true);
if (ret < 0)
return ret;
}
}
ret = uc_p12_add_key(ctx, ucv_object_get(arg, "key", NULL));
if (ret < 0)
return ret;
ret = uc_p12_add_cert(ctx, ucv_object_get(arg, "cert", NULL), false);
if (ret < 0)
return ret;
/* encrypted */
uc_p12_add_sequence(ctx, end);
ret = uc_p12_encrypt(ctx, end);
if (ret < 0)
return ret;
uc_p12_add_tag(ctx, end, CONTEXT_TAG(0));
ret = uc_p12_add_enc_params(ctx);
if (ret < 0)
return ret;
mbedtls_asn1_write_oid(&ctx->p, ctx->start, OID_TAG(PKCS7_DATA));
uc_p12_add_sequence(ctx, end);
mbedtls_asn1_write_int(&ctx->p, ctx->start, 0);
uc_p12_add_sequence(ctx, end);
uc_p12_add_tag(ctx, end, CONTEXT_TAG(0));
mbedtls_asn1_write_oid(&ctx->p, ctx->start, OID_TAG(PKCS7_ENCRYPTED_DATA));
uc_p12_add_sequence(ctx, end);
/* authSafe contents */
uc_p12_add_sequence(ctx, end);
/* authSafe header */
len = end - ctx->p;
uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
uc_p12_add_tag(ctx, end, CONTEXT_TAG(0));
mbedtls_asn1_write_oid(&ctx->p, ctx->start, OID_TAG(PKCS7_DATA));
uc_p12_add_sequence(ctx, end);
return len;
}
static void *
uc_p12_add_mac_digest_info(struct pkcs12_ctx *ctx)
{
uint8_t *end = ctx->p;
size_t len = 20;
ctx->p -= len;
uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
uc_p12_add_algo(ctx, OID_TAG(DIGEST_ALG_SHA1), 0);
uc_p12_add_sequence(ctx, end);
return end - len;
}
uc_value_t *uc_generate_pkcs12(uc_vm_t *vm, size_t nargs)
{
uc_value_t *arg = uc_fn_arg(0), *pwd_arg;
mbedtls_md_context_t hmac;
uint8_t *end = (uint8_t *)&buf[sizeof(buf)];
struct pkcs12_ctx ctx = {
.start = (uint8_t *)&buf[sizeof(buf) / 2],
.p = end,
};
uint8_t *hash, *data;
uint8_t key[20], *salt;
size_t salt_len = 8;
int data_len;
uc_value_t *ret = NULL;
const char *passwd;
if (ucv_type(arg) != UC_OBJECT)
INVALID_ARG();
ctx.legacy = ucv_is_truish(ucv_object_get(arg, "legacy", NULL));
mbedtls_md_init(&hmac);
pwd_arg = ucv_object_get(arg, "password", NULL);
passwd = ucv_string_get(pwd_arg);
if (!passwd)
INVALID_ARG();
/* password is expected to be a UTF-16 string */
ctx.password = passwd;
ctx.pwd_len = 2 * strlen(passwd) + 2;
ctx.pwd = malloc(ctx.pwd_len);
for (size_t i = 0; i < ctx.pwd_len; i += 2) {
ctx.pwd[i] = 0;
ctx.pwd[i + 1] = passwd[i / 2];
}
/* MacData */
mbedtls_asn1_write_int(&ctx.p, ctx.start, NUM_ITER);
ctx.p -= salt_len;
salt = ctx.p;
random_cb(NULL, salt, salt_len);
uc_p12_add_tag(&ctx, salt + salt_len, MBEDTLS_ASN1_OCTET_STRING);
hash = uc_p12_add_mac_digest_info(&ctx);
uc_p12_add_sequence(&ctx, end);
data = ctx.p;
data_len = uc_p12_add_authsafe(&ctx, arg);
if (C(data_len) < 0)
goto out;
data -= data_len;
mbedtls_asn1_write_int(&ctx.p, ctx.start, 3);
uc_p12_add_sequence(&ctx, end);
if (C(mbedtls_pkcs12_derivation(key, sizeof(key), ctx.pwd, ctx.pwd_len,
salt, salt_len, MBEDTLS_MD_SHA1,
MBEDTLS_PKCS12_DERIVE_MAC_KEY, NUM_ITER)))
goto out;
if (C(mbedtls_md_setup(&hmac, mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), 1)))
goto out;
if (C(mbedtls_md_hmac_starts(&hmac, key, sizeof(key))))
goto out;
if (C(mbedtls_md_hmac_update(&hmac, data, data_len)))
goto out;
if (C(mbedtls_md_hmac_finish(&hmac, hash)))
goto out;
ret = ucv_string_new_length((char *)ctx.p, end - ctx.p);
out:
free(ctx.pwd);
mbedtls_md_free(&hmac);
return ret;
}

View File

@ -0,0 +1,598 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
*/
#include <sys/types.h>
#include <sys/random.h>
#include <stdint.h>
#include <limits.h>
#include <errno.h>
#include <mbedtls/entropy.h>
#include <mbedtls/x509_crt.h>
#include <mbedtls/ecp.h>
#include <mbedtls/rsa.h>
#include <ucode/module.h>
#include "pk.h"
/* mbedtls < 3.x compat */
#ifdef MBEDTLS_LEGACY
#define mbedtls_pk_parse_key(pk, key, keylen, passwd, passwdlen, random, random_ctx) \
mbedtls_pk_parse_key(pk, key, keylen, passwd, passwdlen)
#endif
static uc_resource_type_t *uc_pk_type, *uc_crt_type;
static uc_value_t *registry;
char buf[32 * 1024];
int mbedtls_errno;
struct uc_cert_wr {
mbedtls_x509write_cert crt; /* must be first */
mbedtls_mpi mpi;
unsigned int reg;
};
static unsigned int uc_reg_add(uc_value_t *val)
{
size_t i = 0;
while (ucv_array_get(registry, i))
i++;
ucv_array_set(registry, i, ucv_get(val));
return i;
}
int random_cb(void *ctx, unsigned char *out, size_t len)
{
#ifdef linux
if (getrandom(out, len, 0) != (ssize_t) len)
return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
#else
static FILE *f;
if (!f)
f = fopen("/dev/urandom", "r");
if (fread(out, len, 1, f) != 1)
return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
#endif
return 0;
}
int64_t get_int_arg(uc_value_t *obj, const char *key, int64_t defval)
{
uc_value_t *uval = ucv_object_get(obj, key, NULL);
int64_t val;
if (!uval)
return defval;
val = ucv_int64_get(uval);
if (errno || val < 0 || val > INT_MAX)
return INT_MIN;
return val ? val : defval;
}
static int
gen_rsa_key(mbedtls_pk_context *pk, uc_value_t *arg)
{
int64_t key_size, exp;
key_size = get_int_arg(arg, "size", 2048);
exp = get_int_arg(arg, "exponent", 65537);
if (key_size < 0 || exp < 0)
return -1;
return mbedtls_rsa_gen_key(mbedtls_pk_rsa(*pk), random_cb, NULL, key_size, exp);
}
static int
gen_ec_key(mbedtls_pk_context *pk, uc_value_t *arg)
{
mbedtls_ecp_group_id curve;
const char *c_name;
uc_value_t *c_arg;
c_arg = ucv_object_get(arg, "curve", NULL);
if (c_arg && ucv_type(c_arg) != UC_STRING)
return -1;
c_name = ucv_string_get(c_arg);
if (!c_name)
curve = MBEDTLS_ECP_DP_SECP256R1;
else {
const mbedtls_ecp_curve_info *curve_info;
curve_info = mbedtls_ecp_curve_info_from_name(c_name);
if (!curve_info)
return MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE;
curve = curve_info->grp_id;
}
return mbedtls_ecp_gen_key(curve, mbedtls_pk_ec(*pk), random_cb, NULL);
}
static void free_pk(void *pk)
{
if (!pk)
return;
mbedtls_pk_free(pk);
free(pk);
}
static void free_crt(void *ptr)
{
struct uc_cert_wr *crt = ptr;
if (!crt)
return;
mbedtls_x509write_crt_free(&crt->crt);
mbedtls_mpi_free(&crt->mpi);
ucv_array_set(registry, crt->reg, NULL);
free(crt);
}
static uc_value_t *
uc_generate_key(uc_vm_t *vm, size_t nargs)
{
uc_value_t *cur, *arg = uc_fn_arg(0);
mbedtls_pk_type_t pk_type;
mbedtls_pk_context *pk;
const char *type;
int ret;
if (ucv_type(arg) != UC_OBJECT)
INVALID_ARG();
cur = ucv_object_get(arg, "type", NULL);
type = ucv_string_get(cur);
if (!type)
INVALID_ARG();
if (!strcmp(type, "rsa"))
pk_type = MBEDTLS_PK_RSA;
else if (!strcmp(type, "ec"))
pk_type = MBEDTLS_PK_ECKEY;
else
INVALID_ARG();
pk = calloc(1, sizeof(*pk));
mbedtls_pk_init(pk);
mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(pk_type));
switch (pk_type) {
case MBEDTLS_PK_RSA:
ret = C(gen_rsa_key(pk, arg));
break;
case MBEDTLS_PK_ECKEY:
ret = C(gen_ec_key(pk, arg));
break;
default:
ret = -1;
}
if (ret) {
free_pk(pk);
return NULL;
}
return uc_resource_new(uc_pk_type, pk);
}
static uc_value_t *
uc_load_key(uc_vm_t *vm, size_t nargs)
{
uc_value_t *keystr = uc_fn_arg(0);
uc_value_t *pub = uc_fn_arg(1);
uc_value_t *passwd = uc_fn_arg(2);
mbedtls_pk_context *pk;
int ret;
if (ucv_type(keystr) != UC_STRING ||
(pub && ucv_type(pub) != UC_BOOLEAN) ||
(passwd && ucv_type(passwd) != UC_STRING))
INVALID_ARG();
pk = calloc(1, sizeof(*pk));
mbedtls_pk_init(pk);
if (ucv_is_truish(pub))
ret = C(mbedtls_pk_parse_public_key(pk, (const uint8_t *)ucv_string_get(keystr),
ucv_string_length(keystr) + 1));
else
ret = C(mbedtls_pk_parse_key(pk, (const uint8_t *)ucv_string_get(keystr),
ucv_string_length(keystr) + 1,
(const uint8_t *)ucv_string_get(passwd),
ucv_string_length(passwd) + 1,
random_cb, NULL));
if (ret) {
free_pk(pk);
return NULL;
}
return uc_resource_new(uc_pk_type, pk);
}
static void
uc_cert_info_add(uc_value_t *info, const char *name, int len)
{
uc_value_t *val;
if (len < 0)
return;
val = ucv_string_new_length(buf, len);
ucv_object_add(info, name, ucv_get(val));
}
static void
uc_cert_info_add_name(uc_value_t *info, const char *name, mbedtls_x509_name *dn)
{
int len;
len = mbedtls_x509_dn_gets(buf, sizeof(buf), dn);
uc_cert_info_add(info, name, len);
}
static void
uc_cert_info_add_time(uc_value_t *info, const char *name, mbedtls_x509_time *t)
{
int len;
len = snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02d",
t->year, t->mon, t->day, t->hour, t->min, t->sec);
uc_cert_info_add(info, name, len);
}
static uc_value_t *
uc_cert_info(uc_vm_t *vm, size_t nargs)
{
uc_value_t *arg = uc_fn_arg(0);
mbedtls_x509_crt crt, *cur;
uc_value_t *ret = NULL;
if (ucv_type(arg) != UC_STRING)
return NULL;
mbedtls_x509_crt_init(&crt);
if (C(mbedtls_x509_crt_parse(&crt, (const void *)ucv_string_get(arg), ucv_string_length(arg) + 1)) < 0)
goto out;
ret = ucv_array_new(vm);
for (cur = &crt; cur && cur->version != 0; cur = cur->next) {
uc_value_t *info = ucv_object_new(vm);
int len;
ucv_array_push(ret, ucv_get(info));
ucv_object_add(info, "version", ucv_int64_new(cur->version));
uc_cert_info_add_name(info, "issuer", &cur->issuer);
uc_cert_info_add_name(info, "subject", &cur->issuer);
uc_cert_info_add_time(info, "valid_from", &cur->valid_from);
uc_cert_info_add_time(info, "valid_to", &cur->valid_to);
len = mbedtls_x509_serial_gets(buf, sizeof(buf), &cur->serial);
uc_cert_info_add(info, "serial", len);
}
out:
mbedtls_x509_crt_free(&crt);
return ret;
}
static uc_value_t *
uc_pk_pem(uc_vm_t *vm, size_t nargs)
{
mbedtls_pk_context *pk = uc_fn_thisval("mbedtls.pk");
uc_value_t *pub = uc_fn_arg(0);
if (!pk)
return NULL;
if (ucv_is_truish(pub))
CHECK(mbedtls_pk_write_pubkey_pem(pk, (void *)buf, sizeof(buf)));
else
CHECK(mbedtls_pk_write_key_pem(pk, (void *)buf, sizeof(buf)));
return ucv_string_new(buf);
}
static uc_value_t *
uc_pk_der(uc_vm_t *vm, size_t nargs)
{
mbedtls_pk_context *pk = uc_fn_thisval("mbedtls.pk");
uc_value_t *pub = uc_fn_arg(0);
int len;
if (!pk)
return NULL;
if (ucv_is_truish(pub))
len = mbedtls_pk_write_pubkey_der(pk, (void *)buf, sizeof(buf));
else
len = mbedtls_pk_write_key_der(pk, (void *)buf, sizeof(buf));
if (len < 0)
CHECK(len);
return ucv_string_new_length(buf + sizeof(buf) - len, len);
}
static uc_value_t *
uc_crt_pem(uc_vm_t *vm, size_t nargs)
{
mbedtls_x509write_cert *crt = uc_fn_thisval("mbedtls.crt");
if (!crt)
return NULL;
CHECK(mbedtls_x509write_crt_pem(crt, (void *)buf, sizeof(buf), random_cb, NULL));
return ucv_string_new(buf);
}
static uc_value_t *
uc_crt_der(uc_vm_t *vm, size_t nargs)
{
mbedtls_x509write_cert *crt = uc_fn_thisval("mbedtls.crt");
int len;
if (!crt)
return NULL;
len = mbedtls_x509write_crt_der(crt, (void *)buf, sizeof(buf), random_cb, NULL);
if (len < 0)
CHECK(len);
return ucv_string_new_length(buf, len);
}
static int
uc_cert_set_validity(mbedtls_x509write_cert *crt, uc_value_t *arg)
{
uc_value_t *from = ucv_array_get(arg, 0);
uc_value_t *to = ucv_array_get(arg, 1);
if (ucv_type(from) != UC_STRING || ucv_type(to) != UC_STRING)
return -1;
return mbedtls_x509write_crt_set_validity(crt, ucv_string_get(from), ucv_string_get(to));
}
static int
uc_cert_init(mbedtls_x509write_cert *crt, mbedtls_mpi *mpi, uc_value_t *reg, uc_value_t *arg)
{
uc_value_t *cur;
int64_t serial;
int path_len;
int version;
bool ca;
mbedtls_mpi_init(mpi);
mbedtls_x509write_crt_init(crt);
mbedtls_x509write_crt_set_md_alg(crt, MBEDTLS_MD_SHA256);
ca = ucv_is_truish(ucv_object_get(arg, "ca", NULL));
path_len = get_int_arg(arg, "max_pathlen", ca ? -1 : 0);
if (path_len < -1)
return -1;
version = get_int_arg(arg, "version", 3);
if (version < 0 || version > 3)
return -1;
serial = get_int_arg(arg, "serial", 1);
if (serial < 0)
return -1;
mbedtls_mpi_lset(mpi, serial);
mbedtls_x509write_crt_set_serial(crt, mpi);
mbedtls_x509write_crt_set_version(crt, version - 1);
CHECK_INT(mbedtls_x509write_crt_set_basic_constraints(crt, ca, path_len));
cur = ucv_object_get(arg, "subject_name", NULL);
if (ucv_type(cur) == UC_STRING)
CHECK_INT(mbedtls_x509write_crt_set_subject_name(crt, ucv_string_get(cur)));
else
return -1;
cur = ucv_object_get(arg, "subject_key", NULL);
if (cur) {
mbedtls_pk_context *key = ucv_resource_data(cur, "mbedtls.pk");
if (!key)
return -1;
ucv_array_set(reg, 0, ucv_get(cur));
mbedtls_x509write_crt_set_subject_key(crt, key);
mbedtls_x509write_crt_set_subject_key_identifier(crt);
} else
return -1;
cur = ucv_object_get(arg, "issuer_name", NULL);
if (ucv_type(cur) == UC_STRING)
CHECK_INT(mbedtls_x509write_crt_set_issuer_name(crt, ucv_string_get(cur)));
else
return -1;
cur = ucv_object_get(arg, "issuer_key", NULL);
if (cur) {
mbedtls_pk_context *key = ucv_resource_data(cur, "mbedtls.pk");
if (!key)
return -1;
ucv_array_set(reg, 1, ucv_get(cur));
mbedtls_x509write_crt_set_issuer_key(crt, key);
mbedtls_x509write_crt_set_authority_key_identifier(crt);
} else
return -1;
cur = ucv_object_get(arg, "validity", NULL);
if (ucv_type(cur) != UC_ARRAY || ucv_array_length(cur) != 2)
return -1;
if (uc_cert_set_validity(crt, cur))
return -1;
cur = ucv_object_get(arg, "key_usage", NULL);
if (ucv_type(cur) == UC_ARRAY) {
static const struct {
const char *name;
uint8_t val;
} key_flags[] = {
{ "digital_signature", MBEDTLS_X509_KU_DIGITAL_SIGNATURE },
{ "non_repudiation", MBEDTLS_X509_KU_NON_REPUDIATION },
{ "key_encipherment", MBEDTLS_X509_KU_KEY_ENCIPHERMENT },
{ "data_encipherment", MBEDTLS_X509_KU_DATA_ENCIPHERMENT },
{ "key_agreement", MBEDTLS_X509_KU_KEY_AGREEMENT },
{ "key_cert_sign", MBEDTLS_X509_KU_KEY_CERT_SIGN },
{ "crl_sign", MBEDTLS_X509_KU_CRL_SIGN },
};
uint8_t key_usage = 0;
size_t len = ucv_array_length(cur);
for (size_t i = 0; i < len; i++) {
uc_value_t *val = ucv_array_get(cur, i);
const char *str;
size_t k;
str = ucv_string_get(val);
if (!str)
return -1;
for (k = 0; k < ARRAY_SIZE(key_flags); k++)
if (!strcmp(str, key_flags[k].name))
break;
if (k == ARRAY_SIZE(key_flags))
return -1;
key_usage |= key_flags[k].val;
}
CHECK_INT(mbedtls_x509write_crt_set_key_usage(crt, key_usage));
} else if (cur)
return -1;
#ifndef MBEDTLS_LEGACY
cur = ucv_object_get(arg, "ext_key_usage", NULL);
if (ucv_type(cur) == UC_ARRAY && ucv_array_length(cur)) {
static const struct {
const char *name;
struct mbedtls_asn1_buf val;
} key_flags[] = {
#define __oid(name, val) \
{ \
name, \
{ \
.tag = MBEDTLS_ASN1_OID, \
.len = sizeof(MBEDTLS_OID_##val), \
.p = (uint8_t *)MBEDTLS_OID_##val, \
} \
}
__oid("server_auth", SERVER_AUTH),
__oid("client_auth", CLIENT_AUTH),
__oid("code_signing", CODE_SIGNING),
__oid("email_protection", EMAIL_PROTECTION),
__oid("time_stamping", TIME_STAMPING),
__oid("ocsp_signing", OCSP_SIGNING),
__oid("any", ANY_EXTENDED_KEY_USAGE)
};
struct mbedtls_asn1_sequence *elem;
size_t len = ucv_array_length(cur);
elem = calloc(len, sizeof(*elem));
for (size_t i = 0; i < len; i++) {
uc_value_t *val = ucv_array_get(cur, i);
const char *str;
size_t k;
str = ucv_string_get(val);
if (!str)
return -1;
for (k = 0; k < ARRAY_SIZE(key_flags); k++)
if (!strcmp(str, key_flags[k].name))
break;
if (k == ARRAY_SIZE(key_flags)) {
free(elem);
return -1;
}
elem[i].buf = key_flags[k].val;
if (i + 1 < len)
elem[i].next = &elem[i + 1];
}
CHECK_INT(mbedtls_x509write_crt_set_ext_key_usage(crt, elem));
} else if (cur)
return -1;
#endif
return 0;
}
static uc_value_t *
uc_generate_cert(uc_vm_t *vm, size_t nargs)
{
struct uc_cert_wr *crt;
uc_value_t *arg = uc_fn_arg(0);
uc_value_t *reg;
if (ucv_type(arg) != UC_OBJECT)
return NULL;
reg = ucv_array_new(vm);
crt = calloc(1, sizeof(*crt));
if (C(uc_cert_init(&crt->crt, &crt->mpi, reg, arg))) {
free(crt);
return NULL;
}
crt->reg = uc_reg_add(reg);
return uc_resource_new(uc_crt_type, crt);
}
static uc_value_t *
uc_mbedtls_error(uc_vm_t *vm, size_t nargs)
{
mbedtls_strerror(mbedtls_errno, buf, sizeof(buf));
return ucv_string_new(buf);
}
static uc_value_t *
uc_mbedtls_errno(uc_vm_t *vm, size_t nargs)
{
return ucv_int64_new(mbedtls_errno);
}
static const uc_function_list_t pk_fns[] = {
{ "pem", uc_pk_pem },
{ "der", uc_pk_der },
};
static const uc_function_list_t crt_fns[] = {
{ "pem", uc_crt_pem },
{ "der", uc_crt_der },
};
static const uc_function_list_t global_fns[] = {
{ "load_key", uc_load_key },
{ "cert_info", uc_cert_info },
{ "generate_key", uc_generate_key },
{ "generate_cert", uc_generate_cert },
{ "generate_pkcs12", uc_generate_pkcs12 },
{ "errno", uc_mbedtls_errno },
{ "error", uc_mbedtls_error },
};
void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
{
uc_pk_type = uc_type_declare(vm, "mbedtls.pk", pk_fns, free_pk);
uc_crt_type = uc_type_declare(vm, "mbedtls.crt", crt_fns, free_crt);
uc_function_list_register(scope, global_fns);
registry = ucv_array_new(vm);
uc_vm_registry_set(vm, "pkgen.registry", registry);
}