///Save a public key away to disk so it's completely usable... WINEXPORT gboolean cryptcurve25519_save_public_key(const char * key_id, ///< key id to save key under gpointer public_key, ///< pointer to public key data int keysize) ///< size of key { CryptFramePublicKey* pub; gpointer* newpub; if (keysize != crypto_box_PUBLICKEYBYTES) { g_warning("%s.%d: Attempt to save a public key of %d bytes (instead of %d)" , __FUNCTION__, __LINE__, keysize, crypto_box_PUBLICKEYBYTES); return FALSE; } if ((pub = cryptframe_public_key_by_id(key_id)) != NULL) { if (memcmp(public_key, pub->public_key, keysize) == 0) { return TRUE; } g_critical("%s.%d: Attempt to modify public key with id [%s]" , __FUNCTION__, __LINE__, key_id); return FALSE; } newpub = malloc(keysize); memcpy(newpub, public_key, keysize); if (!_cryptcurve25519_save_a_key(key_id, PUBLICKEY, newpub) || cryptframe_publickey_new(key_id, newpub) == NULL) { cryptcurve25519_purge_keypair(key_id); g_free(newpub); newpub = NULL; return FALSE; } if (DEBUG) { CryptFramePublicKey* pub = cryptframe_public_key_by_id(key_id); g_return_val_if_fail(pub != NULL, FALSE); g_return_val_if_fail(memcmp(public_key, pub->public_key, keysize) == 0, FALSE); } return TRUE; }
/// Associate the given key id with the given identity /// Note that it is OK to associate multiple key ids with a given identity /// but it is NOT OK to associate multiple identities with a given key id /// Return TRUE if we could make the association (it's OK to make /// the same valid association multiple times) WINEXPORT gboolean cryptframe_associate_identity(const char * identity, ///<[in] identity to associate key with const char * key_id) ///<[in] key to associate with identity { GHashTable* key_id_map; const char* found_identity; char* key_id_duplicate; char* identity_duplicate; INITMAPS; g_return_val_if_fail(key_id != NULL && identity != NULL, FALSE); if (cryptframe_public_key_by_id(key_id) == NULL) { g_critical("%s.%d: no public key associated with key id %s" , __FUNCTION__, __LINE__, key_id); return FALSE; } found_identity = cryptframe_whois_key_id(key_id); if (found_identity) { if (strcmp(found_identity, identity) != 0) { g_critical("%s.%d: Key id %s cannot be associated with identity %s." " Already associated with identity %s", __FUNCTION__, __LINE__ , key_id, identity, found_identity); return FALSE; } return TRUE; } key_id_duplicate = g_strdup(key_id); identity_duplicate = g_strdup(identity); g_hash_table_insert(identity_map_by_key_id, key_id_duplicate, identity_duplicate); key_id_map = cryptframe_key_ids_for(identity); if (NULL == key_id_map) { key_id_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); g_hash_table_insert(key_id_map_by_identity, identity_duplicate, key_id_map); } if (!g_hash_table_lookup((GHashTable*)key_id_map, key_id)) { g_hash_table_insert(key_id_map, key_id_duplicate, key_id_duplicate); } return TRUE; }
/// Create a new public key - or return the existing public key with this id WINEXPORT CryptFramePublicKey* cryptframe_publickey_new (const char *key_id, ///< Key id of the given public key gpointer public_key) ///< MALLOCed public key { AssimObj* aself; CryptFramePublicKey* self; INITMAPS; g_return_val_if_fail(key_id != NULL && public_key != NULL, NULL); self = cryptframe_public_key_by_id(key_id); if (self) { return self; } aself = assimobj_new(sizeof(CryptFramePublicKey)); aself->_finalize = _cryptframe_publickey_finalize; self = NEWSUBCLASS(CryptFramePublicKey, aself); self->key_id = g_strdup(key_id); self->key_size = crypto_box_PUBLICKEYBYTES; self->frame_type = FRAMETYPE_PUBKEYCURVE25519; self->public_key = public_key; g_hash_table_insert(public_key_map, self->key_id, self); DEBUGCKSUM3(self->key_id, public_key); return self; }
/// Save a curve25519 key to a file. FSTATIC gboolean _cryptcurve25519_save_a_key(const char * key_id,///<[in] key_id to save enum keytype ktype, ///<[in] type of key being saved gconstpointer key) ///<[in] pointer to key { ssize_t keysize; guint32 createmode; int fd; int rc; char* filename; if (!_is_legal_curve25519_key_id(key_id)) { g_warning("%s.%d: Key id %s is illegal", __FUNCTION__, __LINE__, key_id); return FALSE; } filename = curve25519_key_id_to_filename(key_id, ktype); if (PUBLICKEY == ktype) { keysize = crypto_box_PUBLICKEYBYTES; createmode = 0644; }else if (PRIVATEKEY == ktype) { keysize = crypto_box_SECRETKEYBYTES; createmode = 0600; }else{ g_error("%s.%d: Key type %d is illegal", __FUNCTION__, __LINE__, ktype); g_return_val_if_reached(FALSE); } // If it's a public key, it may exist but not be writable by us... if (PUBLICKEY == ktype && g_access(filename, R_OK) == 0) { // So, let's check and see if it's what we think it should be... if (_cache_curve25519_keypair(key_id)) { CryptFramePublicKey* pub = cryptframe_public_key_by_id(key_id); if (pub && memcmp(pub->public_key, key, keysize) == 0) { FREE(filename); filename = NULL; return TRUE; } } } fd = open(filename, O_WRONLY|O_CREAT, createmode); if (fd < 0 && (ENOENT == errno)) { char* dirname = _cache_curve25519_key_id_to_dirname(key_id, ktype); _cryptcurve25519_make_cryptdir(dirname); FREE(dirname); fd = open(filename, O_WRONLY|O_CREAT, createmode); } if (fd < 0) { g_warning("%s.%d: cannot create file %s [%s]", __FUNCTION__, __LINE__ , filename, g_strerror(errno)); g_free(filename); return FALSE; } DEBUGMSG4("Saving key to file %s", filename); DEBUGCKSUM4("Saved key:", key, keysize); rc = write(fd, key, keysize); if (rc != keysize) { g_warning("%s.%d: cannot write file %s: rc=%d [%s]", __FUNCTION__, __LINE__ , filename, rc, g_strerror(errno)); close(fd); g_unlink(filename); g_free(filename); return FALSE; } if (close(fd) < 0) { g_warning("%s.%d: Close of file %s failed.", __FUNCTION__, __LINE__, filename); g_unlink(filename); g_free(filename); return FALSE; } chmod(filename, createmode); // Ignore umask... DEBUGMSG1("%s.%d: file %s successfully created!", __FUNCTION__, __LINE__, filename); g_free(filename); return TRUE; }
/// Given marshalled packet data corresponding to an CryptCurve25519 frame /// return the corresponding Frame /// In other words, un-marshall the data... /// In our case, this means we decrypt it in-place into many other frames... WINEXPORT Frame* cryptcurve25519_tlvconstructor(gpointer tlvstart, ///<[in/out] Start of marshalled CStringFrame data gconstpointer pktend, ///<[in] Pointer to first invalid byte past 'tlvstart' gpointer* ignorednewpkt, ///<[ignored] replacement packet gpointer* ignoredpktend) ///<[ignored] end of replacement packet { guint8* valptr = get_generic_tlv_nonconst_value(tlvstart, pktend); guint8* nonce; guint8* cyphertext; const guint8* tlvend8 = valptr + get_generic_tlv_len(tlvstart, pktend); guint8* plaintext; CryptCurve25519* ret; guint namelen; gsize cypherlength; // The first key name is in sender's key name // The second key name is in receiver's key name CryptFramePublicKey * sender_public_key = NULL; CryptFramePrivateKey* receiver_secret_key = NULL; const char* sender_pubkey_id = NULL; const char* rcvr_seckey_id = NULL; int j; (void)ignorednewpkt; (void)ignoredpktend; valptr = get_generic_tlv_nonconst_value(tlvstart, pktend); for (j=0; j < 2; ++j) { char * key_id; g_return_val_if_fail((gpointer)(valptr+2) <= pktend, NULL); namelen = tlv_get_guint8(valptr, pktend); valptr += 1; g_return_val_if_fail((gpointer)(valptr+namelen) <= pktend, NULL); key_id = (char *)valptr; g_return_val_if_fail (strnlen(key_id, namelen) == namelen -1, NULL); g_return_val_if_fail(_is_valid_curve25519_key_id(key_id , 0 == j ? PUBLICKEY : PRIVATEKEY), NULL); if (0 == j) { sender_public_key = cryptframe_public_key_by_id(key_id); sender_pubkey_id = key_id; }else{ receiver_secret_key = cryptframe_private_key_by_id(key_id); rcvr_seckey_id = key_id; } g_return_val_if_fail(key_id != NULL, NULL); valptr += namelen; } if (NULL == sender_public_key) { g_warning("%s.%d: No access to sender %s public key" , __FUNCTION__, __LINE__, sender_pubkey_id); return NULL; } if (NULL == receiver_secret_key) { g_warning("%s.%d: No access to receiver %s private key" , __FUNCTION__, __LINE__, rcvr_seckey_id); return NULL; } g_return_val_if_fail((gpointer)(valptr + (crypto_box_NONCEBYTES+crypto_box_MACBYTES)) <= pktend, NULL); nonce = valptr; cyphertext = nonce + crypto_box_NONCEBYTES; plaintext = cyphertext + crypto_box_MACBYTES; cypherlength = tlvend8 - cyphertext; DEBUGCKSUM4("nonce:", nonce, crypto_box_NONCEBYTES); DEBUGCKSUM4("sender public key :", sender_public_key -> public_key, crypto_box_PUBLICKEYBYTES); DEBUGCKSUM4("receiver private key:", receiver_secret_key->private_key, crypto_box_SECRETKEYBYTES); DEBUGMSG4("cypher offset versus tlvstart: %ld", (long)(cyphertext-(guint8*)tlvstart)); DEBUGCKSUM4("cypher text:", cyphertext, cypherlength); if (crypto_box_open_easy(plaintext, cyphertext, cypherlength, nonce , sender_public_key->public_key, receiver_secret_key->private_key) != 0) { g_warning("%s.%d: could not decrypt %d byte message encrypted with key pair [pub:%s, sec:%s]" , __FUNCTION__, __LINE__, (int)cypherlength, sender_pubkey_id, rcvr_seckey_id); return NULL; } DEBUGCKSUM4("plain text:", plaintext, cypherlength-crypto_box_MACBYTES); // Note that our return value's size will determine where the beginning of the // decrypted data is (according to it's dataspace() member function) ret = cryptcurve25519_new(get_generic_tlv_type(tlvstart, pktend) , (const char *)sender_pubkey_id , rcvr_seckey_id, FALSE, 0); return (ret ? &(ret->baseclass.baseclass) : NULL); }
/// Construct a new CryptCurve25519 object (frame). CryptCurve25519* cryptcurve25519_new(guint16 frame_type, ///<[in] TLV type of CryptCurve25519 const char * sender_key_id, ///<[in] name of sender's key const char * receiver_key_id, ///<[in] name of receiver's key gboolean forsending, ///<[in] TRUE if this is for sending gsize objsize) ///<[in] sizeof(this object) - or zero for default { CryptFrame* baseframe; CryptCurve25519* ret; BINDDEBUG(CryptCurve25519); if (objsize < sizeof(CryptCurve25519)) { objsize = sizeof(CryptCurve25519); } if (NULL == sender_key_id) { sender_key_id = cryptframe_get_signing_key_id(); } DEBUGMSG2("%s.%d:(%s, %s, %d)", __FUNCTION__, __LINE__, sender_key_id, receiver_key_id , (int)objsize); g_return_val_if_fail(sender_key_id != NULL && receiver_key_id != NULL, NULL); if (!_is_valid_curve25519_key_id(receiver_key_id, PUBLICKEY)) { g_critical("%s.%d: public key name [%s] is invalid", __FUNCTION__, __LINE__, receiver_key_id); return NULL; } if (!_is_valid_curve25519_key_id(sender_key_id, PUBLICKEY)) { g_critical("%s.%d: public key name [%s] is invalid", __FUNCTION__, __LINE__, sender_key_id); return NULL; } baseframe = cryptframe_new(frame_type, sender_key_id, receiver_key_id, objsize); if (!_parentclass_finalize) { _parentclass_finalize = baseframe->baseclass.baseclass._finalize; } baseframe->baseclass.isvalid = _cryptcurve25519_default_isvalid; baseframe->baseclass.updatedata = _cryptcurve25519_updatedata; baseframe->baseclass.length = TLVLEN(receiver_key_id, sender_key_id); baseframe->baseclass.baseclass._finalize = _cryptcurve25519_finalize; ret = NEWSUBCLASS(CryptCurve25519, baseframe); ret->forsending = forsending; ret->private_key = cryptframe_private_key_by_id(forsending ? sender_key_id : receiver_key_id); ret->public_key = cryptframe_public_key_by_id(forsending ? receiver_key_id : sender_key_id); if (ret->private_key && ret->public_key) { DEBUGCKSUM3("private_key:", ret->private_key->private_key, crypto_box_SECRETKEYBYTES); DEBUGCKSUM3("public_key:", ret->public_key->public_key, crypto_box_PUBLICKEYBYTES); DUMP3(__FUNCTION__, &ret->baseclass.baseclass.baseclass, " is return value"); REF(ret->private_key); REF(ret->public_key); }else{ if (!ret->private_key) { g_warning("%s.%d: Sender private key is NULL for key id %s", __FUNCTION__, __LINE__ , sender_key_id); abort(); } if (!ret->public_key) { g_warning("%s.%d: Receiver public key is NULL for key id %s", __FUNCTION__, __LINE__ , receiver_key_id); } UNREF3(ret); return NULL; } return ret; }
/// Validate and cache the requested curve25519 keypair (or just public if no private) /// If it's already in memory (like a temporary key) we won't look for it on disk. FSTATIC gboolean _cache_curve25519_keypair(const char * key_id) ///< Key id of keypair to cache { GStatBuf statinfo; char * filename; gpointer public_key = NULL; gpointer secret_key = NULL; gboolean retval = TRUE; int fd = -1; int rc; /* * Coverity CID 1262413: * Coverity complains about this code with a Time of Check / Time of Use warning. * The file attributes we check in the stat calls below might have changed before * we read the key files. * * Although this is true, it is unlikely because of the permissions of the * directories and files involved. * * We only look at two attributes: * - file size * - file type * If the file size changes, we will detect that because we try and read one extra byte. * If the file type changes, we're screwed. But that's really unlikely and * unlikely to have the code succeed when reading a socket, directory, fifo or device. * It might cause the program to hang (on read), but I think that's nearly inevitable * and not worth the trouble to fix. * If the file type changes, the open might hang too... * So, we're pretty paranoid, and I judge us to be at least as paranoid as necessary. */ if (cryptframe_public_key_by_id(key_id) != NULL) { return TRUE; } filename = curve25519_key_id_to_filename(key_id, PUBLICKEY); if (g_stat(filename, &statinfo) < 0) { g_warning("%s.%d: g_stat error [%s] NOT Caching key id %s [%s]" , __FUNCTION__, __LINE__ , filename, key_id, g_strerror(errno)); retval = FALSE; goto getout; } if (statinfo.st_size != crypto_box_PUBLICKEYBYTES || !S_ISREG(statinfo.st_mode) || g_access(filename, R_OK) != 0) { retval = FALSE; g_warning("%s.%d: size/type/access error on %s NOT Caching key id %s" , __FUNCTION__, __LINE__, filename, key_id); goto getout; } fd = open(filename, O_RDONLY); if (fd < 0) { retval = FALSE; g_warning("%s.%d: open error on %s NOT Caching key id %s", __FUNCTION__, __LINE__ , filename, key_id); goto getout; } public_key = g_malloc(crypto_box_PUBLICKEYBYTES+1); rc = read(fd, public_key, crypto_box_PUBLICKEYBYTES+1); if (rc != crypto_box_PUBLICKEYBYTES) { g_warning("%s.%d: public key read on %s returned %d instead of %d [%s]" , __FUNCTION__, __LINE__, filename , rc, crypto_box_PUBLICKEYBYTES, g_strerror(errno)); retval = FALSE; goto getout; } close(fd); fd = -1; DEBUGCKSUM4(filename, public_key, crypto_box_PUBLICKEYBYTES); g_free(filename); filename = curve25519_key_id_to_filename(key_id, PRIVATEKEY); if (g_stat(filename, &statinfo) >= 0) { if (statinfo.st_size != crypto_box_SECRETKEYBYTES || !S_ISREG(statinfo.st_mode)) { g_warning("%s.%d: secret key stat on [%s] returned %d instead of %d [%s]" , __FUNCTION__, __LINE__, filename , (int)statinfo.st_size, crypto_box_SECRETKEYBYTES, g_strerror(errno)); goto getout; } if (g_access(filename, R_OK) != 0) { // Someone else's secret key... Not a problem... goto getout; } secret_key = g_malloc(crypto_box_SECRETKEYBYTES+1); fd = open(filename, O_RDONLY); if (fd < 0) { retval = FALSE; g_warning("%s.%d: open error on %s NOT Caching key id %s", __FUNCTION__, __LINE__ , filename, key_id); goto getout; } rc = read(fd, secret_key, crypto_box_SECRETKEYBYTES+1); if (rc != crypto_box_SECRETKEYBYTES) { g_warning("%s.%d: secret key read of %s returned %d instead of %d [%s]" , __FUNCTION__, __LINE__, filename , rc, crypto_box_SECRETKEYBYTES, g_strerror(errno)); retval = FALSE; goto getout; } DEBUGCKSUM4(filename, secret_key, crypto_box_SECRETKEYBYTES); close(fd); fd = -1; } getout: // Coverity: CID 1262410 - It doesn't like verifying non-NULL in this error leg // But it's a goto error leg, and in the future the code might change. // So I like it as is... if (filename != NULL) { g_free(filename); filename = NULL; } if (fd >= 0) { close(fd); fd = -1; } if (retval) { g_assert(public_key != NULL); (void)cryptframe_publickey_new(key_id, public_key); if (secret_key) { (void)cryptframe_privatekey_new(key_id, secret_key); } }else{ if (public_key) { g_free(public_key); public_key = NULL; } if (secret_key) { g_free(secret_key); secret_key = NULL; } } return retval; }