+ len = (username ? strlen(username) : 0) +
+ (host ? strlen(host) : 0) +
+ (realname ? strlen(realname) : 0) +
+ (email ? strlen(email) : 0) +
+ (org ? strlen(org) : 0) +
+ (country ? strlen(country) : 0);
+
+ if (len < 3)
+ return NULL;
+
+ len += 3 + 5 + 5 + 4 + 4 + 4;
+ buf = silc_buffer_alloc(len);
+ silc_buffer_pull_tail(buf, len);
+
+ if (username) {
+ silc_buffer_format(buf,
+ SILC_STR_UI32_STRING("UN="),
+ SILC_STR_UI32_STRING(username),
+ SILC_STR_END);
+ silc_buffer_pull(buf, 3 + strlen(username));
+ tlen = 3 + strlen(username);
+ }
+
+ if (host) {
+ silc_buffer_format(buf,
+ SILC_STR_UI32_STRING(", "),
+ SILC_STR_UI32_STRING("HN="),
+ SILC_STR_UI32_STRING(host),
+ SILC_STR_END);
+ silc_buffer_pull(buf, 5 + strlen(host));
+ tlen += 5 + strlen(host);
+ }
+
+ if (realname) {
+ silc_buffer_format(buf,
+ SILC_STR_UI32_STRING(", "),
+ SILC_STR_UI32_STRING("RN="),
+ SILC_STR_UI32_STRING(realname),
+ SILC_STR_END);
+ silc_buffer_pull(buf, 5 + strlen(realname));
+ tlen += 5 + strlen(realname);
+ }
+
+ if (email) {
+ silc_buffer_format(buf,
+ SILC_STR_UI32_STRING(", "),
+ SILC_STR_UI32_STRING("E="),
+ SILC_STR_UI32_STRING(email),
+ SILC_STR_END);
+ silc_buffer_pull(buf, 4 + strlen(email));
+ tlen += 4 + strlen(email);
+ }
+
+ if (org) {
+ silc_buffer_format(buf,
+ SILC_STR_UI32_STRING(", "),
+ SILC_STR_UI32_STRING("O="),
+ SILC_STR_UI32_STRING(org),
+ SILC_STR_END);
+ silc_buffer_pull(buf, 4 + strlen(org));
+ tlen += 4 + strlen(org);
+ }
+
+ if (country) {
+ silc_buffer_format(buf,
+ SILC_STR_UI32_STRING(", "),
+ SILC_STR_UI32_STRING("C="),
+ SILC_STR_UI32_STRING(country),
+ SILC_STR_END);
+ silc_buffer_pull(buf, 4 + strlen(country));
+ tlen += 4 + strlen(country);
+ }
+
+ silc_buffer_push(buf, buf->data - buf->head);
+ identifier = silc_calloc(tlen + 1, sizeof(*identifier));
+ memcpy(identifier, buf->data, tlen);
+ silc_buffer_free(buf);
+
+ return identifier;
+}
+
+/* Decodes the provided `identifier' and returns allocated context for
+ the identifier. */
+
+SilcPublicKeyIdentifier silc_pkcs_decode_identifier(char *identifier)
+{
+ SilcPublicKeyIdentifier ident;
+ char *cp, *item;
+ int len;
+
+ ident = silc_calloc(1, sizeof(*ident));
+
+ cp = identifier;
+ while (cp) {
+ len = strcspn(cp, ",");
+ if (len < 1) {
+ cp = NULL;
+ break;
+ }
+ if (len - 1 >= 0 && cp[len - 1] == '\\') {
+ while (cp) {
+ if (len + 1 > strlen(cp)) {
+ cp = NULL;
+ break;
+ }
+ cp += len + 1;
+ len = strcspn(cp, ",") + len;
+ if (len < 1) {
+ cp = NULL;
+ break;
+ }
+ if (len - 1 >= 0 && cp[len - 1] != '\\')
+ break;
+ }
+ }
+
+ if (!cp)
+ break;
+
+ item = silc_calloc(len + 1, sizeof(char));
+ if (len > strlen(cp))
+ break;
+ memcpy(item, cp, len);
+
+ if (strstr(item, "UN="))
+ ident->username = strdup(item + strcspn(cp, "=") + 1);
+ else if (strstr(item, "HN="))
+ ident->host = strdup(item + strcspn(cp, "=") + 1);
+ else if (strstr(item, "RN="))
+ ident->realname = strdup(item + strcspn(cp, "=") + 1);
+ else if (strstr(item, "E="))
+ ident->email = strdup(item + strcspn(cp, "=") + 1);
+ else if (strstr(item, "O="))
+ ident->org = strdup(item + strcspn(cp, "=") + 1);
+ else if (strstr(item, "C="))
+ ident->country = strdup(item + strcspn(cp, "=") + 1);
+
+ cp += len;
+ if (strlen(cp) < 1)
+ cp = NULL;
+ else
+ cp += 1;
+
+ if (item)
+ silc_free(item);
+ }
+
+ return ident;
+}
+
+/* Free's decoded public key identifier context. Call this to free the
+ context returned by the silc_pkcs_decode_identifier. */
+
+void silc_pkcs_free_identifier(SilcPublicKeyIdentifier identifier)
+{
+ silc_free(identifier->username);
+ silc_free(identifier->host);
+ silc_free(identifier->realname);
+ silc_free(identifier->email);
+ silc_free(identifier->org);
+ silc_free(identifier->country);
+ silc_free(identifier);
+}
+
+/* Allocates SILC style public key formed from sent arguments. All data
+ is duplicated. */
+
+SilcPublicKey silc_pkcs_public_key_alloc(const char *name,
+ const char *identifier,
+ const unsigned char *pk,
+ SilcUInt32 pk_len)
+{
+ SilcPublicKey public_key;
+ char *tmp = NULL;
+
+ public_key = silc_calloc(1, sizeof(*public_key));
+ public_key->name = strdup(name);
+ public_key->pk_len = pk_len;
+ public_key->pk = silc_calloc(pk_len, sizeof(*public_key->pk));
+ public_key->pk_type = SILC_SKE_PK_TYPE_SILC;
+ memcpy(public_key->pk, pk, pk_len);
+
+ if (!silc_utf8_valid(identifier, strlen(identifier))) {
+ int len = silc_utf8_encoded_len(identifier, strlen(identifier), 0);
+ tmp = silc_calloc(len + 1, sizeof(*tmp));
+ silc_utf8_encode(identifier, strlen(identifier), 0, tmp, len);
+ identifier = tmp;
+ }
+
+ public_key->identifier = strdup(identifier);
+ public_key->len = 2 + strlen(name) + 2 + strlen(identifier) + pk_len;
+ silc_free(tmp);
+
+ return public_key;
+}
+
+/* Free's public key */
+
+void silc_pkcs_public_key_free(SilcPublicKey public_key)
+{
+ if (public_key) {
+ silc_free(public_key->name);
+ silc_free(public_key->identifier);
+ silc_free(public_key->pk);
+ silc_free(public_key);
+ }
+}
+
+/* Allocates SILC private key formed from sent arguments. All data is
+ duplicated. */
+
+SilcPrivateKey silc_pkcs_private_key_alloc(const char *name,
+ const unsigned char *prv,
+ SilcUInt32 prv_len)
+{
+ SilcPrivateKey private_key;
+
+ private_key = silc_calloc(1, sizeof(*private_key));
+ private_key->name = strdup(name);
+ private_key->prv_len = prv_len;
+ private_key->prv = silc_calloc(prv_len, sizeof(*private_key->prv));
+ memcpy(private_key->prv, prv, prv_len);
+
+ return private_key;
+}
+
+/* Free's private key */
+
+void silc_pkcs_private_key_free(SilcPrivateKey private_key)
+{
+ if (private_key) {
+ silc_free(private_key->name);
+ silc_free(private_key->prv);
+ silc_free(private_key);
+ }
+}
+
+/* Encodes SILC style public key from SilcPublicKey. Returns the encoded
+ data. */
+
+unsigned char *
+silc_pkcs_public_key_encode(SilcPublicKey public_key, SilcUInt32 *len)
+{
+ SilcBuffer buf;
+ unsigned char *ret;
+
+ buf = silc_buffer_alloc_size(public_key->len + 4);
+ if (!buf)
+ return NULL;