mirror of
https://github.com/openwrt/openwrt.git
synced 2025-03-22 03:55:22 +00:00
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:
parent
44c877f197
commit
2c78e9d7bd
44
package/utils/ucode-mod-pkgen/Makefile
Normal file
44
package/utils/ucode-mod-pkgen/Makefile
Normal 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))
|
252
package/utils/ucode-mod-pkgen/files/pkgen
Executable file
252
package/utils/ucode-mod-pkgen/files/pkgen
Executable 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);
|
35
package/utils/ucode-mod-pkgen/src/CMakeLists.txt
Normal file
35
package/utils/ucode-mod-pkgen/src/CMakeLists.txt
Normal 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)
|
45
package/utils/ucode-mod-pkgen/src/pk.h
Normal file
45
package/utils/ucode-mod-pkgen/src/pk.h
Normal 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
|
612
package/utils/ucode-mod-pkgen/src/pkcs12.c
Normal file
612
package/utils/ucode-mod-pkgen/src/pkcs12.c
Normal 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, ¶m_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;
|
||||
}
|
598
package/utils/ucode-mod-pkgen/src/ucode.c
Normal file
598
package/utils/ucode-mod-pkgen/src/ucode.c
Normal 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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user