18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* PKCS#8 Private Key parser [RFC 5208]. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "PKCS8: "fmt 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/export.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/oid_registry.h> 158c2ecf20Sopenharmony_ci#include <keys/asymmetric-subtype.h> 168c2ecf20Sopenharmony_ci#include <keys/asymmetric-parser.h> 178c2ecf20Sopenharmony_ci#include <crypto/public_key.h> 188c2ecf20Sopenharmony_ci#include "pkcs8.asn1.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct pkcs8_parse_context { 218c2ecf20Sopenharmony_ci struct public_key *pub; 228c2ecf20Sopenharmony_ci unsigned long data; /* Start of data */ 238c2ecf20Sopenharmony_ci enum OID last_oid; /* Last OID encountered */ 248c2ecf20Sopenharmony_ci enum OID algo_oid; /* Algorithm OID */ 258c2ecf20Sopenharmony_ci u32 key_size; 268c2ecf20Sopenharmony_ci const void *key; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * Note an OID when we find one for later processing when we know how to 318c2ecf20Sopenharmony_ci * interpret it. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ciint pkcs8_note_OID(void *context, size_t hdrlen, 348c2ecf20Sopenharmony_ci unsigned char tag, 358c2ecf20Sopenharmony_ci const void *value, size_t vlen) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct pkcs8_parse_context *ctx = context; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci ctx->last_oid = look_up_OID(value, vlen); 408c2ecf20Sopenharmony_ci if (ctx->last_oid == OID__NR) { 418c2ecf20Sopenharmony_ci char buffer[50]; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci sprint_oid(value, vlen, buffer, sizeof(buffer)); 448c2ecf20Sopenharmony_ci pr_info("Unknown OID: [%lu] %s\n", 458c2ecf20Sopenharmony_ci (unsigned long)value - ctx->data, buffer); 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * Note the version number of the ASN.1 blob. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ciint pkcs8_note_version(void *context, size_t hdrlen, 548c2ecf20Sopenharmony_ci unsigned char tag, 558c2ecf20Sopenharmony_ci const void *value, size_t vlen) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci if (vlen != 1 || ((const u8 *)value)[0] != 0) { 588c2ecf20Sopenharmony_ci pr_warn("Unsupported PKCS#8 version\n"); 598c2ecf20Sopenharmony_ci return -EBADMSG; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci return 0; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * Note the public algorithm. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ciint pkcs8_note_algo(void *context, size_t hdrlen, 688c2ecf20Sopenharmony_ci unsigned char tag, 698c2ecf20Sopenharmony_ci const void *value, size_t vlen) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct pkcs8_parse_context *ctx = context; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (ctx->last_oid != OID_rsaEncryption) 748c2ecf20Sopenharmony_ci return -ENOPKG; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci ctx->pub->pkey_algo = "rsa"; 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 818c2ecf20Sopenharmony_ci * Note the key data of the ASN.1 blob. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ciint pkcs8_note_key(void *context, size_t hdrlen, 848c2ecf20Sopenharmony_ci unsigned char tag, 858c2ecf20Sopenharmony_ci const void *value, size_t vlen) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct pkcs8_parse_context *ctx = context; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ctx->key = value; 908c2ecf20Sopenharmony_ci ctx->key_size = vlen; 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* 958c2ecf20Sopenharmony_ci * Parse a PKCS#8 private key blob. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic struct public_key *pkcs8_parse(const void *data, size_t datalen) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct pkcs8_parse_context ctx; 1008c2ecf20Sopenharmony_ci struct public_key *pub; 1018c2ecf20Sopenharmony_ci long ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci memset(&ctx, 0, sizeof(ctx)); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci ret = -ENOMEM; 1068c2ecf20Sopenharmony_ci ctx.pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); 1078c2ecf20Sopenharmony_ci if (!ctx.pub) 1088c2ecf20Sopenharmony_ci goto error; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ctx.data = (unsigned long)data; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Attempt to decode the private key */ 1138c2ecf20Sopenharmony_ci ret = asn1_ber_decoder(&pkcs8_decoder, &ctx, data, datalen); 1148c2ecf20Sopenharmony_ci if (ret < 0) 1158c2ecf20Sopenharmony_ci goto error_decode; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ret = -ENOMEM; 1188c2ecf20Sopenharmony_ci pub = ctx.pub; 1198c2ecf20Sopenharmony_ci pub->key = kmemdup(ctx.key, ctx.key_size, GFP_KERNEL); 1208c2ecf20Sopenharmony_ci if (!pub->key) 1218c2ecf20Sopenharmony_ci goto error_decode; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci pub->keylen = ctx.key_size; 1248c2ecf20Sopenharmony_ci pub->key_is_private = true; 1258c2ecf20Sopenharmony_ci return pub; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cierror_decode: 1288c2ecf20Sopenharmony_ci kfree(ctx.pub); 1298c2ecf20Sopenharmony_cierror: 1308c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * Attempt to parse a data blob for a key as a PKCS#8 private key. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cistatic int pkcs8_key_preparse(struct key_preparsed_payload *prep) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct public_key *pub; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pub = pkcs8_parse(prep->data, prep->datalen); 1418c2ecf20Sopenharmony_ci if (IS_ERR(pub)) 1428c2ecf20Sopenharmony_ci return PTR_ERR(pub); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci pr_devel("Cert Key Algo: %s\n", pub->pkey_algo); 1458c2ecf20Sopenharmony_ci pub->id_type = "PKCS8"; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* We're pinning the module by being linked against it */ 1488c2ecf20Sopenharmony_ci __module_get(public_key_subtype.owner); 1498c2ecf20Sopenharmony_ci prep->payload.data[asym_subtype] = &public_key_subtype; 1508c2ecf20Sopenharmony_ci prep->payload.data[asym_key_ids] = NULL; 1518c2ecf20Sopenharmony_ci prep->payload.data[asym_crypto] = pub; 1528c2ecf20Sopenharmony_ci prep->payload.data[asym_auth] = NULL; 1538c2ecf20Sopenharmony_ci prep->quotalen = 100; 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic struct asymmetric_key_parser pkcs8_key_parser = { 1588c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1598c2ecf20Sopenharmony_ci .name = "pkcs8", 1608c2ecf20Sopenharmony_ci .parse = pkcs8_key_preparse, 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* 1648c2ecf20Sopenharmony_ci * Module stuff 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_cistatic int __init pkcs8_key_init(void) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci return register_asymmetric_key_parser(&pkcs8_key_parser); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void __exit pkcs8_key_exit(void) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci unregister_asymmetric_key_parser(&pkcs8_key_parser); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cimodule_init(pkcs8_key_init); 1778c2ecf20Sopenharmony_cimodule_exit(pkcs8_key_exit); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PKCS#8 certificate parser"); 1808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 181