예제 #1
0
static void mrb_ecdsa_free(mrb_state *mrb, void *ptr) {
  ecdsa_context *ecdsa = ptr;

  if (ecdsa != NULL) {
    ecdsa_free(ecdsa);
  }
}
예제 #2
0
파일: connection.c 프로젝트: AllardJ/Tomato
void free_connection(connection_t *c) {
	if(!c)
		return;

	cipher_close(c->incipher);
	digest_close(c->indigest);
	cipher_close(c->outcipher);
	digest_close(c->outdigest);

	sptps_stop(&c->sptps);
	ecdsa_free(c->ecdsa);
	rsa_free(c->rsa);

	free(c->hischallenge);

	buffer_clear(&c->inbuf);
	buffer_clear(&c->outbuf);

	io_del(&c->io);

	if(c->socket > 0)
		closesocket(c->socket);

	free(c->name);
	free(c->hostname);

	if(c->config_tree)
		exit_configuration(&c->config_tree);

	free(c);
}
예제 #3
0
static int eckey_verify_wrap( void *ctx, md_type_t md_alg,
                       const unsigned char *hash, size_t hash_len,
                       const unsigned char *sig, size_t sig_len )
{
    int ret;
    ecdsa_context ecdsa;

    ecdsa_init( &ecdsa );

    if( ( ret = ecdsa_from_keypair( &ecdsa, ctx ) ) == 0 )
        ret = ecdsa_verify_wrap( &ecdsa, md_alg, hash, hash_len, sig, sig_len );

    ecdsa_free( &ecdsa );

    return( ret );
}
예제 #4
0
static int eckey_sign_wrap( void *ctx, md_type_t md_alg,
                   const unsigned char *hash, size_t hash_len,
                   unsigned char *sig, size_t *sig_len,
                   int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
{
    int ret;
    ecdsa_context ecdsa;

    ecdsa_init( &ecdsa );

    if( ( ret = ecdsa_from_keypair( &ecdsa, ctx ) ) == 0 )
        ret = ecdsa_sign_wrap( &ecdsa, md_alg, hash, hash_len, sig, sig_len,
                               f_rng, p_rng );

    ecdsa_free( &ecdsa );

    return( ret );
}
예제 #5
0
static bool read_invitation_key(meshlink_handle_t *mesh) {
	FILE *fp;
	char filename[PATH_MAX];

	if(mesh->invitation_key) {
		ecdsa_free(mesh->invitation_key);
		mesh->invitation_key = NULL;
	}

	snprintf(filename,PATH_MAX, "%s" SLASH "invitations" SLASH "ecdsa_key.priv", mesh->confbase);

	fp = fopen(filename, "rb");

	if(fp) {
		mesh->invitation_key = ecdsa_read_pem_private_key(fp);
		fclose(fp);
		if(!mesh->invitation_key)
			logger(mesh, MESHLINK_ERROR, "Reading ECDSA private key file `%s' failed: %s", filename, strerror(errno));
	}

	return mesh->invitation_key;
}
예제 #6
0
void free_connection(connection_t *c) {
	if(!c)
		return;

	sptps_stop(&c->sptps);
	ecdsa_free(c->ecdsa);

	buffer_clear(&c->inbuf);
	buffer_clear(&c->outbuf);

	if(c->io.cb)
		abort();

	if(c->socket > 0)
		closesocket(c->socket);

	free(c->name);
	free(c->hostname);

	if(c->config_tree)
		exit_configuration(&c->config_tree);

	free(c);
}
예제 #7
0
static void ecdsa_free_wrap( void *ctx )
{
    ecdsa_free( (ecdsa_context *) ctx );
    polarssl_free( ctx );
}
예제 #8
0
int main( int argc, char *argv[] )
{
    int ret;
    ecdsa_context ctx_sign, ctx_verify;
    entropy_context entropy;
    ctr_drbg_context ctr_drbg;
    unsigned char hash[] = "This should be the hash of a message.";
    unsigned char sig[512];
    size_t sig_len;
    const char *pers = "ecdsa";
    ((void) argv);

    ecdsa_init( &ctx_sign );
    ecdsa_init( &ctx_verify );

    memset(sig, 0, sizeof( sig ) );
    ret = 1;

    if( argc != 1 )
    {
        polarssl_printf( "usage: ecdsa\n" );

#if defined(_WIN32)
        polarssl_printf( "\n" );
#endif

        goto exit;
    }

    /*
     * Generate a key pair for signing
     */
    polarssl_printf( "\n  . Seeding the random number generator..." );
    fflush( stdout );

    entropy_init( &entropy );
    if( ( ret = ctr_drbg_init( &ctr_drbg, entropy_func, &entropy,
                               (const unsigned char *) pers,
                               strlen( pers ) ) ) != 0 )
    {
        polarssl_printf( " failed\n  ! ctr_drbg_init returned %d\n", ret );
        goto exit;
    }

    polarssl_printf( " ok\n  . Generating key pair..." );
    fflush( stdout );

    if( ( ret = ecdsa_genkey( &ctx_sign, ECPARAMS,
                              ctr_drbg_random, &ctr_drbg ) ) != 0 )
    {
        polarssl_printf( " failed\n  ! ecdsa_genkey returned %d\n", ret );
        goto exit;
    }

    polarssl_printf( " ok (key size: %d bits)\n", (int) ctx_sign.grp.pbits );

    dump_pubkey( "  + Public key: ", &ctx_sign );

    /*
     * Sign some message hash
     */
    polarssl_printf( "  . Signing message..." );
    fflush( stdout );

    if( ( ret = ecdsa_write_signature( &ctx_sign,
                                       hash, sizeof( hash ),
                                       sig, &sig_len,
                                       ctr_drbg_random, &ctr_drbg ) ) != 0 )
    {
        polarssl_printf( " failed\n  ! ecdsa_genkey returned %d\n", ret );
        goto exit;
    }
    polarssl_printf( " ok (signature length = %u)\n", (unsigned int) sig_len );

    dump_buf( "  + Hash: ", hash, sizeof hash );
    dump_buf( "  + Signature: ", sig, sig_len );

    /*
     * Signature is serialized as defined by RFC 4492 p. 20,
     * but one can also access 'r' and 's' directly from the context
     */
#ifdef POLARSSL_FS_IO
    mpi_write_file( "    r = ", &ctx_sign.r, 16, NULL );
    mpi_write_file( "    s = ", &ctx_sign.s, 16, NULL );
#endif

    /*
     * Transfer public information to verifying context
     *
     * We could use the same context for verification and signatures, but we
     * chose to use a new one in order to make it clear that the verifying
     * context only needs the public key (Q), and not the private key (d).
     */
    polarssl_printf( "  . Preparing verification context..." );
    fflush( stdout );

    if( ( ret = ecp_group_copy( &ctx_verify.grp, &ctx_sign.grp ) ) != 0 )
    {
        polarssl_printf( " failed\n  ! ecp_group_copy returned %d\n", ret );
        goto exit;
    }

    if( ( ret = ecp_copy( &ctx_verify.Q, &ctx_sign.Q ) ) != 0 )
    {
        polarssl_printf( " failed\n  ! ecp_copy returned %d\n", ret );
        goto exit;
    }

    ret = 0;

    /*
     * Verify signature
     */
    polarssl_printf( " ok\n  . Verifying signature..." );
    fflush( stdout );

    if( ( ret = ecdsa_read_signature( &ctx_verify,
                                      hash, sizeof( hash ),
                                      sig, sig_len ) ) != 0 )
    {
        polarssl_printf( " failed\n  ! ecdsa_read_signature returned %d\n", ret );
        goto exit;
    }

    polarssl_printf( " ok\n" );

exit:

#if defined(_WIN32)
    polarssl_printf( "  + Press Enter to exit this program.\n" );
    fflush( stdout ); getchar();
#endif

    ecdsa_free( &ctx_verify );
    ecdsa_free( &ctx_sign );
    ctr_drbg_free( &ctr_drbg );
    entropy_free( &entropy );

    return( ret );
}
예제 #9
0
파일: invitation.c 프로젝트: xentec/tinc
int cmd_join(int argc, char *argv[]) {
	free(data);
	data = NULL;
	datalen = 0;

	if(argc > 2) {
		fprintf(stderr, "Too many arguments!\n");
		return 1;
	}

	// Make sure confbase exists and is accessible.
	if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
		fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
		return 1;
	}

	if(mkdir(confbase, 0777) && errno != EEXIST) {
		fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
		return 1;
	}

	if(access(confbase, R_OK | W_OK | X_OK)) {
		fprintf(stderr, "No permission to write in directory %s: %s\n", confbase, strerror(errno));
		return 1;
	}

	// If a netname or explicit configuration directory is specified, check for an existing tinc.conf.
	if((netname || confbasegiven) && !access(tinc_conf, F_OK)) {
		fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
		return 1;
	}

	// Either read the invitation from the command line or from stdin.
	char *invitation;

	if(argc > 1) {
		invitation = argv[1];
	} else {
		if(tty)
			fprintf(stderr, "Enter invitation URL: ");
		errno = EPIPE;
		if(!fgets(line, sizeof line, stdin)) {
			fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
			return false;
		}
		invitation = line;
	}

	// Parse the invitation URL.
	rstrip(line);

	char *slash = strchr(invitation, '/');
	if(!slash)
		goto invalid;

	*slash++ = 0;

	if(strlen(slash) != 48)
		goto invalid;

	char *address = invitation;
	char *port = NULL;
	if(*address == '[') {
		address++;
		char *bracket = strchr(address, ']');
		if(!bracket)
			goto invalid;
		*bracket = 0;
		if(bracket[1] == ':')
			port = bracket + 2;
	} else {
		port = strchr(address, ':');
		if(port)
			*port++ = 0;
	}

	if(!port || !*port)
		port = "655";

	if(!b64decode(slash, hash, 24) || !b64decode(slash + 24, cookie, 24))
		goto invalid;

	// Generate a throw-away key for the invitation.
	ecdsa_t *key = ecdsa_generate();
	if(!key)
		return 1;

	char *b64key = ecdsa_get_base64_public_key(key);

	// Connect to the tinc daemon mentioned in the URL.
	struct addrinfo *ai = str2addrinfo(address, port, SOCK_STREAM);
	if(!ai)
		return 1;

	struct addrinfo *aip = NULL;

next:
	if(!aip)
		aip = ai;
	else {
		aip = aip->ai_next;
		if(!aip)
			return 1;
	}

	sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
	if(sock <= 0) {
		fprintf(stderr, "Could not open socket: %s\n", strerror(errno));
		goto next;
	}

	if(connect(sock, aip->ai_addr, aip->ai_addrlen)) {
		char *addrstr, *portstr;
		sockaddr2str((sockaddr_t *)aip->ai_addr, &addrstr, &portstr);
		fprintf(stderr, "Could not connect to %s port %s: %s\n", addrstr, portstr, strerror(errno));
		free(addrstr);
		free(portstr);
		closesocket(sock);
		goto next;
	}

	fprintf(stderr, "Connected to %s port %s...\n", address, port);

	// Tell him we have an invitation, and give him our throw-away key.
	int len = snprintf(line, sizeof line, "0 ?%s %d.%d\n", b64key, PROT_MAJOR, PROT_MINOR);
	if(len <= 0 || len >= sizeof line)
		abort();

	if(!sendline(sock, "0 ?%s %d.%d", b64key, PROT_MAJOR, 1)) {
		fprintf(stderr, "Error sending request to %s port %s: %s\n", address, port, strerror(errno));
		closesocket(sock);
		goto next;
	}

	char hisname[4096] = "";
	int code, hismajor, hisminor = 0;

	if(!recvline(sock, line, sizeof line) || sscanf(line, "%d %s %d.%d", &code, hisname, &hismajor, &hisminor) < 3 || code != 0 || hismajor != PROT_MAJOR || !check_id(hisname) || !recvline(sock, line, sizeof line) || !rstrip(line) || sscanf(line, "%d ", &code) != 1 || code != ACK || strlen(line) < 3) {
		fprintf(stderr, "Cannot read greeting from peer\n");
		closesocket(sock);
		goto next;
	}

	// Check if the hash of the key he gave us matches the hash in the URL.
	char *fingerprint = line + 2;
	char hishash[64];
	if(sha512(fingerprint, strlen(fingerprint), hishash)) {
		fprintf(stderr, "Could not create digest\n%s\n", line + 2);
		return 1;
	}
	if(memcmp(hishash, hash, 18)) {
		fprintf(stderr, "Peer has an invalid key!\n%s\n", line + 2);
		return 1;

	}
	
	ecdsa_t *hiskey = ecdsa_set_base64_public_key(fingerprint);
	if(!hiskey)
		return 1;

	// Start an SPTPS session
	if(!sptps_start(&sptps, NULL, true, false, key, hiskey, "tinc invitation", 15, invitation_send, invitation_receive))
		return 1;

	// Feed rest of input buffer to SPTPS
	if(!sptps_receive_data(&sptps, buffer, blen))
		return 1;

	while((len = recv(sock, line, sizeof line, 0))) {
		if(len < 0) {
			if(errno == EINTR)
				continue;
			fprintf(stderr, "Error reading data from %s port %s: %s\n", address, port, strerror(errno));
			return 1;
		}

		char *p = line;
		while(len) {
			int done = sptps_receive_data(&sptps, p, len);
			if(!done)
				return 1;
			len -= done;
			p += done;
		}
	}
	
	sptps_stop(&sptps);
	ecdsa_free(hiskey);
	ecdsa_free(key);
	closesocket(sock);

	if(!success) {
		fprintf(stderr, "Connection closed by peer, invitation cancelled.\n");
		return 1;
	}

	return 0;

invalid:
	fprintf(stderr, "Invalid invitation URL.\n");
	return 1;
}
예제 #10
0
파일: invitation.c 프로젝트: xentec/tinc
static bool finalize_join(void) {
	char *name = xstrdup(get_value(data, "Name"));
	if(!name) {
		fprintf(stderr, "No Name found in invitation!\n");
		return false;
	}

	if(!check_id(name)) {
		fprintf(stderr, "Invalid Name found in invitation: %s!\n", name);
		return false;
	}

	if(!netname)
		netname = grep(data, "NetName");

	bool ask_netname = false;
	char temp_netname[32];

make_names:
	if(!confbasegiven) {
		free(confbase);
		confbase = NULL;
	}

	make_names(false);

	free(tinc_conf);
	free(hosts_dir);

	xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
	xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);

	if(!access(tinc_conf, F_OK)) {
		fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
		if(confbasegiven)
			return false;

		// Generate a random netname, ask for a better one later.
		ask_netname = true;
		snprintf(temp_netname, sizeof temp_netname, "join_%x", rand());
		netname = temp_netname;
		goto make_names;
	}	

	if(mkdir(confbase, 0777) && errno != EEXIST) {
		fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
		return false;
	}

	if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
		fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
		return false;
	}

	FILE *f = fopen(tinc_conf, "w");
	if(!f) {
		fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
		return false;
	}

	fprintf(f, "Name = %s\n", name);

	char filename[PATH_MAX];
	snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
	FILE *fh = fopen(filename, "w");
	if(!fh) {
		fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
		fclose(f);
		return false;
	}

	// Filter first chunk on approved keywords, split between tinc.conf and hosts/Name
	// Other chunks go unfiltered to their respective host config files
	const char *p = data;
	char *l, *value;

	while((l = get_line(&p))) {
		// Ignore comments
		if(*l == '#')
			continue;

		// Split line into variable and value
		int len = strcspn(l, "\t =");
		value = l + len;
		value += strspn(value, "\t ");
		if(*value == '=') {
			value++;
			value += strspn(value, "\t ");
		}
		l[len] = 0;

		// Is it a Name?
		if(!strcasecmp(l, "Name"))
			if(strcmp(value, name))
				break;
			else
				continue;
		else if(!strcasecmp(l, "NetName"))
			continue;

		// Check the list of known variables
		bool found = false;
		int i;
		for(i = 0; variables[i].name; i++) {
			if(strcasecmp(l, variables[i].name))
				continue;
			found = true;
			break;
		}

		// Ignore unknown and unsafe variables
		if(!found) {
			fprintf(stderr, "Ignoring unknown variable '%s' in invitation.\n", l);
			continue;
		} else if(!(variables[i].type & VAR_SAFE)) {
			fprintf(stderr, "Ignoring unsafe variable '%s' in invitation.\n", l);
			continue;
		}

		// Copy the safe variable to the right config file
		fprintf(variables[i].type & VAR_HOST ? fh : f, "%s = %s\n", l, value);
	}

	fclose(f);

	while(l && !strcasecmp(l, "Name")) {
		if(!check_id(value)) {
			fprintf(stderr, "Invalid Name found in invitation.\n");
			return false;
		}

		if(!strcmp(value, name)) {
			fprintf(stderr, "Secondary chunk would overwrite our own host config file.\n");
			return false;
		}

		snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, value);
		f = fopen(filename, "w");

		if(!f) {
			fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
			return false;
		}

		while((l = get_line(&p))) {
			if(!strcmp(l, "#---------------------------------------------------------------#"))
				continue;
			int len = strcspn(l, "\t =");
			if(len == 4 && !strncasecmp(l, "Name", 4)) {
				value = l + len;
				value += strspn(value, "\t ");
				if(*value == '=') {
					value++;
					value += strspn(value, "\t ");
				}
				l[len] = 0;
				break;
			}

			fputs(l, f);
			fputc('\n', f);
		}

		fclose(f);
	}

	// Generate our key and send a copy to the server
	ecdsa_t *key = ecdsa_generate();
	if(!key)
		return false;

	char *b64key = ecdsa_get_base64_public_key(key);
	if(!b64key)
		return false;

	snprintf(filename, sizeof filename, "%s" SLASH "ed25519_key.priv", confbase);
	f = fopenmask(filename, "w", 0600);
	if(!f)
		return false;

	if(!ecdsa_write_pem_private_key(key, f)) {
		fprintf(stderr, "Error writing private key!\n");
		ecdsa_free(key);
		fclose(f);
		return false;
	}

	fclose(f);

	fprintf(fh, "Ed25519PublicKey = %s\n", b64key);

	sptps_send_record(&sptps, 1, b64key, strlen(b64key));
	free(b64key);
	ecdsa_free(key);

	check_port(name);

ask_netname:
	if(ask_netname && tty) {
		fprintf(stderr, "Enter a new netname: ");
		if(!fgets(line, sizeof line, stdin)) {
			fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
			return false;
		}
		if(!*line || *line == '\n')
			goto ask_netname;

		line[strlen(line) - 1] = 0;

		char newbase[PATH_MAX];
		snprintf(newbase, sizeof newbase, CONFDIR SLASH "tinc" SLASH "%s", line);
		if(rename(confbase, newbase)) {
			fprintf(stderr, "Error trying to rename %s to %s: %s\n", confbase, newbase, strerror(errno));
			goto ask_netname;
		}

		netname = line;
		make_names(false);
	}

	fprintf(stderr, "Configuration stored in: %s\n", confbase);

	return true;
}
예제 #11
0
파일: sptps_speed.c 프로젝트: xentec/tinc
int main(int argc, char *argv[]) {
	ecdsa_t *key1, *key2;
	ecdh_t *ecdh1, *ecdh2;
	sptps_t sptps1, sptps2;
	char buf1[4096], buf2[4096], buf3[4096];
	double duration = argc > 1 ? atof(argv[1]) : 10;

	crypto_init();

	randomize(buf1, sizeof buf1);
	randomize(buf2, sizeof buf2);
	randomize(buf3, sizeof buf3);

	// Key generation

	fprintf(stderr, "Generating keys for %lg seconds: ", duration);
	for(clock_start(); clock_countto(duration);)
		ecdsa_free(ecdsa_generate());
	fprintf(stderr, "%17.2lf op/s\n", rate);

	key1 = ecdsa_generate();
	key2 = ecdsa_generate();

	// Ed25519 signatures

	fprintf(stderr, "Ed25519 sign for %lg seconds: ", duration);
	for(clock_start(); clock_countto(duration);)
		if(!ecdsa_sign(key1, buf1, 256, buf2))
			return 1;
	fprintf(stderr, "%20.2lf op/s\n", rate);

	fprintf(stderr, "Ed25519 verify for %lg seconds: ", duration);
	for(clock_start(); clock_countto(duration);)
		if(!ecdsa_verify(key1, buf1, 256, buf2)) {
			fprintf(stderr, "Signature verification failed\n");
			return 1;
		}
	fprintf(stderr, "%18.2lf op/s\n", rate);

	ecdh1 = ecdh_generate_public(buf1);
	fprintf(stderr, "ECDH for %lg seconds: ", duration);
	for(clock_start(); clock_countto(duration);) {
		ecdh2 = ecdh_generate_public(buf2);
		if(!ecdh2)
			return 1;
		if(!ecdh_compute_shared(ecdh2, buf1, buf3))
			return 1;
	}
	fprintf(stderr, "%28.2lf op/s\n", rate);
	ecdh_free(ecdh1);

	// SPTPS authentication phase

	int fd[2];
	if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
		fprintf(stderr, "Could not create a UNIX socket pair: %s\n", sockstrerror(sockerrno));
		return 1;
	}

	struct pollfd pfd[2] = {{fd[0], POLLIN}, {fd[1], POLLIN}};

	fprintf(stderr, "SPTPS/TCP authenticate for %lg seconds: ", duration);
	for(clock_start(); clock_countto(duration);) {
		sptps_start(&sptps1, fd + 0, true, false, key1, key2, "sptps_speed", 11, send_data, receive_record);
		sptps_start(&sptps2, fd + 1, false, false, key2, key1, "sptps_speed", 11, send_data, receive_record);
		while(poll(pfd, 2, 0)) {
			if(pfd[0].revents)
				receive_data(&sptps1);
			if(pfd[1].revents)
				receive_data(&sptps2);
		}
		sptps_stop(&sptps1);
		sptps_stop(&sptps2);
	}
	fprintf(stderr, "%10.2lf op/s\n", rate * 2);

	// SPTPS data

	sptps_start(&sptps1, fd + 0, true, false, key1, key2, "sptps_speed", 11, send_data, receive_record);
	sptps_start(&sptps2, fd + 1, false, false, key2, key1, "sptps_speed", 11, send_data, receive_record);
	while(poll(pfd, 2, 0)) {
		if(pfd[0].revents)
			receive_data(&sptps1);
		if(pfd[1].revents)
			receive_data(&sptps2);
	}
	fprintf(stderr, "SPTPS/TCP transmit for %lg seconds: ", duration);
	for(clock_start(); clock_countto(duration);) {
		if(!sptps_send_record(&sptps1, 0, buf1, 1451))
			abort();
		receive_data(&sptps2);
	}
	rate *= 2 * 1451 * 8;
	if(rate > 1e9)
		fprintf(stderr, "%14.2lf Gbit/s\n", rate / 1e9);
	else if(rate > 1e6)
		fprintf(stderr, "%14.2lf Mbit/s\n", rate / 1e6);
	else if(rate > 1e3)
		fprintf(stderr, "%14.2lf kbit/s\n", rate / 1e3);
	sptps_stop(&sptps1);
	sptps_stop(&sptps2);

	// SPTPS datagram authentication phase

	close(fd[0]);
	close(fd[1]);

	if(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd)) {
		fprintf(stderr, "Could not create a UNIX socket pair: %s\n", sockstrerror(sockerrno));
		return 1;
	}

	fprintf(stderr, "SPTPS/UDP authenticate for %lg seconds: ", duration);
	for(clock_start(); clock_countto(duration);) {
		sptps_start(&sptps1, fd + 0, true, true, key1, key2, "sptps_speed", 11, send_data, receive_record);
		sptps_start(&sptps2, fd + 1, false, true, key2, key1, "sptps_speed", 11, send_data, receive_record);
		while(poll(pfd, 2, 0)) {
			if(pfd[0].revents)
				receive_data(&sptps1);
			if(pfd[1].revents)
				receive_data(&sptps2);
		}
		sptps_stop(&sptps1);
		sptps_stop(&sptps2);
	}
	fprintf(stderr, "%10.2lf op/s\n", rate * 2);

	// SPTPS datagram data

	sptps_start(&sptps1, fd + 0, true, true, key1, key2, "sptps_speed", 11, send_data, receive_record);
	sptps_start(&sptps2, fd + 1, false, true, key2, key1, "sptps_speed", 11, send_data, receive_record);
	while(poll(pfd, 2, 0)) {
		if(pfd[0].revents)
			receive_data(&sptps1);
		if(pfd[1].revents)
			receive_data(&sptps2);
	}
	fprintf(stderr, "SPTPS/UDP transmit for %lg seconds: ", duration);
	for(clock_start(); clock_countto(duration);) {
		if(!sptps_send_record(&sptps1, 0, buf1, 1451))
			abort();
		receive_data(&sptps2);
	}
	rate *= 2 * 1451 * 8;
	if(rate > 1e9)
		fprintf(stderr, "%14.2lf Gbit/s\n", rate / 1e9);
	else if(rate > 1e6)
		fprintf(stderr, "%14.2lf Mbit/s\n", rate / 1e6);
	else if(rate > 1e3)
		fprintf(stderr, "%14.2lf kbit/s\n", rate / 1e3);
	sptps_stop(&sptps1);
	sptps_stop(&sptps2);

	// Clean up

	close(fd[0]);
	close(fd[1]);
	ecdsa_free(key1);
	ecdsa_free(key2);
	crypto_exit();

	return 0;
}