/* Generate new SILC key pair. */
SilcBool silc_pkcs_silc_generate_key(const char *algorithm,
- const char *scheme,
SilcUInt32 bits_key_len,
const char *identifier,
SilcRng rng,
SilcSILCPrivateKey privkey;
const SilcPKCSAlgorithm *alg;
const SilcPKCSObject *pkcs;
+ SilcUInt32 version;
SILC_LOG_DEBUG(("Generating SILC %s key pair with key length %d bits",
algorithm, bits_key_len));
if (!pkcs)
return FALSE;
- alg = silc_pkcs_find_algorithm(algorithm, scheme);
- if (!alg)
- return FALSE;
-
/* Allocate SILC public key */
pubkey = silc_calloc(1, sizeof(*pubkey));
if (!pubkey)
return FALSE;
- pubkey->pkcs = alg;
/* Decode identifier */
- if (!silc_pkcs_silc_decode_identifier(identifier, &pubkey->identifier)) {
+ if (!silc_pkcs_silc_decode_identifier(identifier, &pubkey->identifier))
+ return FALSE;
+
+ if (pubkey->identifier.version && atoi(pubkey->identifier.version) >= 2)
+ version = 2;
+ else
+ version = 1;
+
+ /* Allocate algorithm */
+ alg = silc_pkcs_find_algorithm(algorithm, (version == 1 ? "pkcs1-no-oid" :
+ "pkcs1"));
+ if (!alg) {
silc_free(pubkey);
return FALSE;
}
+ pubkey->pkcs = alg;
/* Allocate SILC private key */
privkey = silc_calloc(1, sizeof(*privkey));
int len;
/* Protocol says that at least UN and HN must be provided as identifier */
- if (!strstr(identifier, "UN=") && !strstr(identifier, "HN=")) {
+ if (!strstr(identifier, "UN=") || !strstr(identifier, "HN=")) {
SILC_LOG_DEBUG(("The public does not have the required UN= and HN= "
"identifiers"));
return FALSE;
ident->org = strdup(item + strcspn(cp, "=") + 1);
else if (strstr(item, "C="))
ident->country = strdup(item + strcspn(cp, "=") + 1);
+ else if (strstr(item, "V="))
+ ident->version = strdup(item + strcspn(cp, "=") + 1);
cp += len;
if (strlen(cp) < 1)
char *silc_pkcs_silc_encode_identifier(char *username, char *host,
char *realname, char *email,
- char *org, char *country)
+ char *org, char *country,
+ char *version)
{
- SilcBuffer buf;
+ SilcBufferStruct buf;
char *identifier;
- SilcUInt32 len, tlen = 0;
if (!username || !host)
return NULL;
-
- 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)
+ if (strlen(username) < 1 || strlen(host) < 1)
return NULL;
- len += 3 + 5 + 5 + 4 + 4 + 4;
- buf = silc_buffer_alloc(len);
- if (!buf)
- return NULL;
- silc_buffer_pull_tail(buf, len);
+ memset(&buf, 0, sizeof(buf));
- if (username) {
- silc_buffer_format(buf,
+ if (username)
+ silc_buffer_format(&buf,
+ SILC_STR_ADVANCE,
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,
+ if (host)
+ silc_buffer_format(&buf,
+ SILC_STR_ADVANCE,
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,
+ if (realname)
+ silc_buffer_format(&buf,
+ SILC_STR_ADVANCE,
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,
+ if (email)
+ silc_buffer_format(&buf,
+ SILC_STR_ADVANCE,
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,
+ if (org)
+ silc_buffer_format(&buf,
+ SILC_STR_ADVANCE,
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,
+ if (country)
+ silc_buffer_format(&buf,
+ SILC_STR_ADVANCE,
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);
+
+ if (version) {
+ if (strlen(version) > 1 || !isdigit(version[0])) {
+ silc_buffer_purge(&buf);
+ return NULL;
+ }
+ silc_buffer_format(&buf,
+ SILC_STR_ADVANCE,
+ SILC_STR_UI32_STRING(", "),
+ SILC_STR_UI32_STRING("V="),
+ SILC_STR_UI32_STRING(version),
+ SILC_STR_END);
}
- silc_buffer_push(buf, buf->data - buf->head);
- identifier = silc_calloc(tlen + 1, sizeof(*identifier));
- if (!identifier)
- return NULL;
- memcpy(identifier, buf->data, tlen);
- silc_buffer_free(buf);
+ silc_buffer_format(&buf, SILC_STR_UI_CHAR(0), SILC_STR_END);
+ identifier = silc_buffer_steal(&buf, NULL);
return identifier;
}
+/* Return SILC public key version */
+
+int silc_pkcs_silc_public_key_version(SilcPublicKey public_key)
+{
+ SilcSILCPublicKey silc_pubkey;
+
+ if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC)
+ return -1;
+
+ silc_pubkey = public_key->public_key;
+
+ /* If version identifire is not present it is version 1. */
+ if (!silc_pubkey->identifier.version)
+ return 1;
+
+ return atoi(silc_pubkey->identifier.version);
+}
/*************************** Public key routines *****************************/
/* Check start of file and remove header from the data. */
len = strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN);
- if (filedata_len < len + strlen(SILC_PKCS_PUBLIC_KEYFILE_END))
+ if (filedata_len < len + strlen(SILC_PKCS_PUBLIC_KEYFILE_END)) {
+ SILC_LOG_ERROR(("Malformed SILC public key header"));
return FALSE;
+ }
for (i = 0; i < len; i++) {
- if (*filedata != SILC_PKCS_PUBLIC_KEYFILE_BEGIN[i])
+ if (*filedata != SILC_PKCS_PUBLIC_KEYFILE_BEGIN[i]) {
+ SILC_LOG_ERROR(("Malformed SILC public key header"));
return FALSE;
+ }
filedata++;
}
filedata_len -= (strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) +
break;
case SILC_PKCS_FILE_BASE64:
- data = silc_pem_decode(filedata, filedata_len, &filedata_len);
+ data = silc_base64_decode(filedata, filedata_len, &filedata_len);
if (!data)
return FALSE;
filedata = data;
/* Get length */
ret = silc_buffer_unformat(&buf,
+ SILC_STR_ADVANCE,
SILC_STR_UI_INT(&totlen),
SILC_STR_END);
if (ret == -1)
/* Get algorithm name and identifier */
ret =
silc_buffer_unformat(&buf,
- SILC_STR_OFFSET(4),
+ SILC_STR_ADVANCE,
SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
SILC_STR_UI16_NSTRING_ALLOC(&ident, &identifier_len),
SILC_STR_END);
goto err;
/* Get key data */
- silc_buffer_pull(&buf, 4 + 2 + pkcs_len + 2 + identifier_len);
keydata_len = silc_buffer_len(&buf);
ret = silc_buffer_unformat(&buf,
- SILC_STR_UI_XNSTRING(&key_data,
- keydata_len),
+ SILC_STR_DATA(&key_data, keydata_len),
SILC_STR_END);
if (ret == -1)
goto err;
if (!asn1)
goto err;
+ SILC_LOG_DEBUG(("Public key version %s",
+ (!silc_pubkey->identifier.version ? "1" :
+ silc_pubkey->identifier.version)));
+
if (!strcmp(pkcs_name, "rsa")) {
/* Parse the SILC RSA public key */
SilcUInt32 e_len, n_len;
SilcMPInt n, e;
- /* Get PKCS object */
- pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid");
+ /* Get PKCS object. Different PKCS #1 scheme is used with different
+ versions. */
+ if (!silc_pubkey->identifier.version ||
+ atoi(silc_pubkey->identifier.version) <= 1) {
+ /* Version 1 */
+ pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid");
+ } else {
+ /* Version 2 and newer */
+ pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1");
+ }
if (!pkcs) {
- SILC_LOG_DEBUG(("Unsupported PKCS algorithm"));
+ SILC_LOG_DEBUG(("Unsupported PKCS algorithm: rsa"));
goto err;
}
silc_pubkey->pkcs = pkcs;
break;
case SILC_PKCS_FILE_BASE64:
- data = silc_pem_encode_file(key, key_len);
+ data = silc_base64_encode_file(key, key_len);
if (!data)
return NULL;
silc_free(key);
/* Export PKCS algorithm public key */
if (pkcs->export_public_key)
pk = pkcs->export_public_key(silc_pubkey->public_key, &pk_len);
- if (!pk)
+ if (!pk) {
+ SILC_LOG_ERROR(("Error exporting PKCS algorithm key"));
return NULL;
+ }
silc_buffer_set(&alg_key, pk, pk_len);
/* Encode identifier */
silc_pubkey->identifier.realname,
silc_pubkey->identifier.email,
silc_pubkey->identifier.org,
- silc_pubkey->identifier.country);
- if (!identifier)
+ silc_pubkey->identifier.country,
+ silc_pubkey->identifier.version);
+ if (!identifier) {
+ SILC_LOG_ERROR(("Error encoding SILC public key identifier"));
goto err;
+ }
asn1 = silc_asn1_alloc();
if (!asn1)
goto err;
} else {
- SILC_LOG_DEBUG(("Unsupported PKCS algorithm"));
+ SILC_LOG_ERROR(("Unsupported PKCS algorithm: %s", pkcs->name));
goto err;
}
silc_buffer_free(buf);
silc_free(key);
silc_free(identifier);
+ silc_buffer_purge(&alg_key);
silc_asn1_free(asn1);
return ret;
void *silc_pkcs_silc_public_key_copy(void *public_key)
{
SilcSILCPublicKey silc_pubkey = public_key, new_pubkey;
+ SilcPublicKeyIdentifier ident = &silc_pubkey->identifier;
new_pubkey = silc_calloc(1, sizeof(*new_pubkey));
if (!new_pubkey)
return NULL;
}
+ if (ident->username)
+ new_pubkey->identifier.username =
+ silc_memdup(ident->username, strlen(ident->username));
+ if (ident->host)
+ new_pubkey->identifier.host =
+ silc_memdup(ident->host, strlen(ident->host));
+ if (ident->realname)
+ new_pubkey->identifier.realname =
+ silc_memdup(ident->realname, strlen(ident->realname));
+ if (ident->email)
+ new_pubkey->identifier.email =
+ silc_memdup(ident->email, strlen(ident->email));
+ if (ident->org)
+ new_pubkey->identifier.org =
+ silc_memdup(ident->org, strlen(ident->org));
+ if (ident->country)
+ new_pubkey->identifier.country =
+ silc_memdup(ident->country, strlen(ident->country));
+ if (ident->version)
+ new_pubkey->identifier.version =
+ silc_memdup(ident->version, strlen(ident->version));
+
return new_pubkey;
}
strcmp(k1->identifier.country, k2->identifier.country)))
return FALSE;
+ if ((k1->identifier.version && !k2->identifier.version) ||
+ (!k1->identifier.version && k2->identifier.version) ||
+ (k1->identifier.version && k2->identifier.version &&
+ strcmp(k1->identifier.version, k2->identifier.version)))
+ return FALSE;
+
return k1->pkcs->public_key_compare(k1->public_key, k2->public_key);
}
silc_free(silc_pubkey->identifier.email);
silc_free(silc_pubkey->identifier.org);
silc_free(silc_pubkey->identifier.country);
+ silc_free(silc_pubkey->identifier.version);
silc_free(silc_pubkey);
}
/* Check start of file and remove header from the data. */
len = strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN);
- if (filedata_len < len + strlen(SILC_PKCS_PRIVATE_KEYFILE_END))
+ if (filedata_len < len + strlen(SILC_PKCS_PRIVATE_KEYFILE_END)) {
+ SILC_LOG_ERROR(("Malformed SILC private key header"));
return FALSE;
+ }
for (i = 0; i < len; i++) {
- if (*filedata != SILC_PKCS_PRIVATE_KEYFILE_BEGIN[i])
+ if (*filedata != SILC_PKCS_PRIVATE_KEYFILE_BEGIN[i]) {
+ SILC_LOG_ERROR(("Malformed SILC private key header"));
return FALSE;
+ }
filedata++;
}
break;
case SILC_PKCS_FILE_BASE64:
- data = silc_pem_decode(filedata, filedata_len, &len);
+ data = silc_base64_decode(filedata, filedata_len, &len);
if (!data)
return FALSE;
filedata = data;
/* Private key version */
#define SILC_PRIVATE_KEY_VERSION_1 0x82171273
+#define SILC_PRIVATE_KEY_VERSION_2 0xf911a3d1
/* Imports SILC implementation style private key */
unsigned char *tmp;
SilcUInt32 len, ver;
- /* Get PKCS object */
- pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid");
- if (!pkcs) {
- SILC_LOG_DEBUG(("Unsupported PKCS algorithm"));
- goto err;
- }
- silc_privkey->pkcs = pkcs;
-
if (keydata_len < 4)
goto err;
-
silc_buffer_set(&k, key_data, keydata_len);
/* Get version. Key without the version is old style private key
goto err;
silc_buffer_pull(&k, 4);
- if (ver != SILC_PRIVATE_KEY_VERSION_1) {
+ if (ver != SILC_PRIVATE_KEY_VERSION_1 &&
+ ver != SILC_PRIVATE_KEY_VERSION_2) {
len = ver;
+ ver = 0;
} else {
if (silc_buffer_unformat(&k,
SILC_STR_UI_INT(&len),
silc_buffer_pull(&k, 4);
}
+ /* Get PKCS object. Different PKCS #1 scheme is used with different
+ versions. */
+ if (ver == 0 || ver == SILC_PRIVATE_KEY_VERSION_1) {
+ /* Version 0 and 1 */
+ pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid");
+ } else {
+ /* Version 2 and newer */
+ pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1");
+ }
+ if (!pkcs) {
+ SILC_LOG_DEBUG(("Unsupported PKCS algorithm"));
+ goto err;
+ }
+ silc_privkey->pkcs = pkcs;
+
+ SILC_LOG_DEBUG(("Private key version %s",
+ (ver == SILC_PRIVATE_KEY_VERSION_1 ? "1" :
+ ver == SILC_PRIVATE_KEY_VERSION_2 ? "2" : "0")));
+
/* Get e */
if (silc_buffer_unformat(&k,
- SILC_STR_UI_XNSTRING(&tmp, len),
+ SILC_STR_DATA(&tmp, len),
SILC_STR_END) < 0)
goto err;
silc_mp_init(&e);
goto err;
silc_buffer_pull(&k, 4);
if (silc_buffer_unformat(&k,
- SILC_STR_UI_XNSTRING(&tmp, len),
+ SILC_STR_DATA(&tmp, len),
SILC_STR_END) < 0)
goto err;
silc_mp_init(&n);
goto err;
silc_buffer_pull(&k, 4);
if (silc_buffer_unformat(&k,
- SILC_STR_UI_XNSTRING(&tmp, len),
+ SILC_STR_DATA(&tmp, len),
SILC_STR_END) < 0)
goto err;
silc_mp_init(&d);
goto err;
silc_buffer_pull(&k, 4);
if (silc_buffer_unformat(&k,
- SILC_STR_UI_XNSTRING(&tmp, len),
+ SILC_STR_DATA(&tmp, len),
SILC_STR_END) < 0)
goto err;
silc_mp_init(&dp);
goto err;
silc_buffer_pull(&k, 4);
if (silc_buffer_unformat(&k,
- SILC_STR_UI_XNSTRING(&tmp, len),
+ SILC_STR_DATA(&tmp, len),
SILC_STR_END) < 0)
goto err;
silc_mp_init(&dq);
silc_mp_bin2mp(tmp, len, &dq);
silc_buffer_pull(&k, len);
- if (ver != SILC_PRIVATE_KEY_VERSION_1) {
+ if (ver == 0) {
/* Old version */
/* Get pQ len */
goto err;
silc_buffer_pull(&k, 4);
if (silc_buffer_unformat(&k,
- SILC_STR_UI_XNSTRING(&tmp, len),
+ SILC_STR_DATA(&tmp, len),
SILC_STR_END) < 0)
goto err;
silc_mp_init(&qp);
goto err;
silc_buffer_pull(&k, 4);
if (silc_buffer_unformat(&k,
- SILC_STR_UI_XNSTRING(&tmp, len),
+ SILC_STR_DATA(&tmp, len),
SILC_STR_END) < 0)
goto err;
silc_mp_init(&p);
goto err;
silc_buffer_pull(&k, 4);
if (silc_buffer_unformat(&k,
- SILC_STR_UI_XNSTRING(&tmp, len),
+ SILC_STR_DATA(&tmp, len),
SILC_STR_END) < 0)
goto err;
silc_mp_init(&q);
silc_mp_bin2mp(tmp, len, &q);
silc_buffer_pull(&k, len);
- if (ver != SILC_PRIVATE_KEY_VERSION_1) {
+ if (ver == 0) {
/* Old version. Compute to new version */
SILC_LOG_DEBUG(("Old version private key"));
silc_mp_init(&qp);
silc_free(silc_privkey);
if (asn1)
silc_asn1_free(asn1);
+ SILC_LOG_ERROR(("Malformed SILC private key "));
return 0;
}
break;
case SILC_PKCS_FILE_BASE64:
- data = silc_pem_encode_file(enc->data, silc_buffer_len(enc));
+ data = silc_base64_encode_file(enc->data, silc_buffer_len(enc));
if (!data) {
silc_buffer_clear(enc);
silc_buffer_free(enc);
unsigned char *signature,
SilcUInt32 signature_size,
SilcUInt32 *ret_signature_len,
+ SilcBool compute_hash,
SilcHash hash)
{
SilcSILCPrivateKey silc_privkey = private_key;
return silc_privkey->pkcs->sign(silc_privkey->private_key,
src, src_len,
signature, signature_size,
- ret_signature_len, hash);
+ ret_signature_len, compute_hash, hash);
}
/* Verifies as specified in SILC protocol specification */