/* * See management/management-notes.txt for more info on the * the dynamic challenge/response protocol implemented here. */ struct auth_challenge_info * get_auth_challenge (const char *auth_challenge, struct gc_arena *gc) { if (auth_challenge) { struct auth_challenge_info *ac; const int len = strlen (auth_challenge); char *work = (char *) gc_malloc (len+1, false, gc); char *cp; struct buffer b; buf_set_read (&b, (const uint8_t *)auth_challenge, len); ALLOC_OBJ_CLEAR_GC (ac, struct auth_challenge_info, gc); /* parse prefix */ if (!buf_parse(&b, ':', work, len)) return NULL; if (strcmp(work, "CRV1")) return NULL; /* parse flags */ if (!buf_parse(&b, ':', work, len)) return NULL; for (cp = work; *cp != '\0'; ++cp) { const char c = *cp; if (c == 'E') ac->flags |= CR_ECHO; else if (c == 'R') ac->flags |= CR_RESPONSE; } /* parse state ID */ if (!buf_parse(&b, ':', work, len)) return NULL; ac->state_id = string_alloc(work, gc); /* parse user name */ if (!buf_parse(&b, ':', work, len)) return NULL; ac->user = (char *) gc_malloc (strlen(work)+1, true, gc); openvpn_base64_decode(work, (void*)ac->user, -1); /* parse challenge text */ ac->challenge_text = string_alloc(BSTR(&b), gc); return ac; } else return NULL; }
/* sign arbitrary data */ static int rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { /* optional app data in rsa->meth->app_data; */ char *in_b64 = NULL; char *out_b64 = NULL; int ret = -1; int len; if (padding != RSA_PKCS1_PADDING) { RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); goto done; } /* convert 'from' to base64 */ if (openvpn_base64_encode (from, flen, &in_b64) <= 0) goto done; /* call MI for signature */ if (management) out_b64 = management_query_rsa_sig (management, in_b64); if (!out_b64) goto done; /* decode base64 signature to binary */ len = RSA_size(rsa); ret = openvpn_base64_decode (out_b64, to, len); /* verify length */ if (ret != len) ret = -1; done: if (in_b64) free (in_b64); if (out_b64) free (out_b64); return ret; }
void tls_crypt_v2_write_client_key_file(const char *filename, const char *b64_metadata, const char *server_key_file, const char *server_key_inline) { struct gc_arena gc = gc_new(); struct key_ctx server_key = { 0 }; struct buffer client_key_pem = { 0 }; struct buffer dst = alloc_buf_gc(TLS_CRYPT_V2_CLIENT_KEY_LEN + TLS_CRYPT_V2_MAX_WKC_LEN, &gc); struct key2 client_key = { 2 }; if (!rand_bytes((void *)client_key.keys, sizeof(client_key.keys))) { msg(M_FATAL, "ERROR: could not generate random key"); goto cleanup; } ASSERT(buf_write(&dst, client_key.keys, sizeof(client_key.keys))); struct buffer metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN, &gc); if (b64_metadata) { if (TLS_CRYPT_V2_MAX_B64_METADATA_LEN < strlen(b64_metadata)) { msg(M_FATAL, "ERROR: metadata too long (%d bytes, max %u bytes)", (int)strlen(b64_metadata), TLS_CRYPT_V2_MAX_B64_METADATA_LEN); } ASSERT(buf_write(&metadata, &TLS_CRYPT_METADATA_TYPE_USER, 1)); int decoded_len = openvpn_base64_decode(b64_metadata, BPTR(&metadata), BCAP(&metadata)); if (decoded_len < 0) { msg(M_FATAL, "ERROR: failed to base64 decode provided metadata"); goto cleanup; } ASSERT(buf_inc_len(&metadata, decoded_len)); } else { int64_t timestamp = htonll((uint64_t)now); ASSERT(buf_write(&metadata, &TLS_CRYPT_METADATA_TYPE_TIMESTAMP, 1)); ASSERT(buf_write(&metadata, ×tamp, sizeof(timestamp))); } tls_crypt_v2_init_server_key(&server_key, true, server_key_file, server_key_inline); if (!tls_crypt_v2_wrap_client_key(&dst, &client_key, &metadata, &server_key, &gc)) { msg(M_FATAL, "ERROR: could not wrap generated client key"); goto cleanup; } /* PEM-encode Kc || WKc */ if (!crypto_pem_encode(tls_crypt_v2_cli_pem_name, &client_key_pem, &dst, &gc)) { msg(M_FATAL, "ERROR: could not PEM-encode client key"); goto cleanup; } if (!buffer_write_file(filename, &client_key_pem)) { msg(M_FATAL, "ERROR: could not write client key file"); goto cleanup; } /* Sanity check: load client key (as "client") */ struct key_ctx_bi test_client_key; struct buffer test_wrapped_client_key; msg(D_GENKEY, "Testing client-side key loading..."); tls_crypt_v2_init_client_key(&test_client_key, &test_wrapped_client_key, filename, NULL); free_key_ctx_bi(&test_client_key); /* Sanity check: unwrap and load client key (as "server") */ struct buffer test_metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN, &gc); struct key2 test_client_key2 = { 0 }; free_key_ctx(&server_key); tls_crypt_v2_init_server_key(&server_key, false, server_key_file, server_key_inline); msg(D_GENKEY, "Testing server-side key loading..."); ASSERT(tls_crypt_v2_unwrap_client_key(&test_client_key2, &test_metadata, test_wrapped_client_key, &server_key)); secure_memzero(&test_client_key2, sizeof(test_client_key2)); free_buf(&test_wrapped_client_key); cleanup: secure_memzero(&client_key, sizeof(client_key)); free_key_ctx(&server_key); buf_clear(&client_key_pem); buf_clear(&dst); gc_free(&gc); }