Finally a fully working pair-pin-setup!

This commit is contained in:
F. Duncanh
2023-11-24 00:10:26 -05:00
parent 4241694252
commit bcc7244fb6
4 changed files with 309 additions and 5 deletions

View File

@@ -388,7 +388,6 @@ int extract_evp_private_key(unsigned char *privkey, int keylen, EVP_PKEY *key) {
if ('a' <= *data && *data <= 'f') val = 10 + *data - 'a';
if ('A' <= *data && *data <= 'F') val = 10 + *data - 'A';
if (val == 64) {
//printf("[%c]\n", *data);
data++;
continue;
}
@@ -396,7 +395,6 @@ int extract_evp_private_key(unsigned char *privkey, int keylen, EVP_PKEY *key) {
part = part% 2;
switch (part) {
case 1:
//printf("%d %d [%c] %u\n", i, part, *data, val);
part1 = val;
data++;
break;

View File

@@ -12,17 +12,17 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*==================================================================
* modified by fduncanh 2021
* modified by fduncanh 2021, 2023
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <openssl/sha.h> // for SHA512_DIGEST_LENGTH
#include "pairing.h"
#include "crypto.h"
#include "srp.h"
#define SALT_KEY "Pair-Verify-AES-Key"
#define SALT_IV "Pair-Verify-AES-IV"
@@ -31,6 +31,12 @@ struct pairing_s {
ed25519_key_t *ed;
};
typedef struct srp_user_s {
char username[SRP_USERNAME_SIZE];
unsigned char salt[SRP_SALT_SIZE];
unsigned char verifier[SRP_VERIFIER_SIZE];
} srp_user_t;
typedef enum {
STATUS_INITIAL,
STATUS_SETUP,
@@ -47,6 +53,11 @@ struct pairing_session_s {
x25519_key_t *ecdh_ours;
x25519_key_t *ecdh_theirs;
unsigned char ecdh_secret[X25519_KEY_SIZE];
/* srp items */
srp_user_t *srp_user;
unsigned char srp_session_key[SRP_SESSION_KEY_SIZE];
};
static int
@@ -291,6 +302,131 @@ random_pin() {
return (int) random_short;
}
int
srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_id, const char *pin,
const char **salt, int *len_salt, const char **pk, int *len_pk) {
if (strlen(device_id) >= SRP_USERNAME_SIZE) {
return -1;
}
if (session->srp_user) {
free (session->srp_user);
}
session->srp_user = (srp_user_t *) malloc(sizeof(srp_user_t));
if (!session->srp_user) {
return -2;
}
memset(session->srp_user, 0, sizeof(srp_user_t));
strncpy(session->srp_user->username, device_id, strlen(device_id) + 1);
const unsigned char *srp_b = srp_private_key(pairing);
unsigned char * srp_B;
unsigned char * srp_s;
unsigned char * srp_v;
int len_b = ED25519_KEY_SIZE;
int len_B;
int len_s;
int len_v;
srp_create_salted_verification_key(SRP_SHA, SRP_NG, device_id,
(const unsigned char *) pin, strlen (pin),
(const unsigned char **) &srp_s, &len_s,
(const unsigned char **) &srp_v, &len_v,
NULL, NULL);
if (len_s != SRP_SALT_SIZE || len_v != SRP_VERIFIER_SIZE) {
return -3;
}
memcpy(session->srp_user->salt, srp_s, SRP_SALT_SIZE);
memcpy(session->srp_user->verifier, srp_v, SRP_VERIFIER_SIZE);
*salt = (char *) session->srp_user->salt;
*len_salt = len_s;
srp_create_server_ephemeral_key(SRP_SHA, SRP_NG,
srp_v, len_v,
srp_b, len_b,
(const unsigned char **) &srp_B, &len_B,
NULL, NULL, 1);
*pk = (char *) srp_B;
*len_pk = len_B;
return 0;
}
int
srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigned char *A,
int len_A, unsigned char *proof, int client_proof_len, int proof_len) {
int authenticated = 0;
const unsigned char *B = NULL;
const unsigned char *b = srp_private_key(pairing);
int len_b = ED25519_KEY_SIZE;
int len_B = 0;
int len_K = 0;
const unsigned char *session_key = NULL;
const unsigned char *M2 = NULL;
struct SRPVerifier *verifier = srp_verifier_new(SRP_SHA, SRP_NG, (const char *) session->srp_user->username,
(const unsigned char *) session->srp_user->salt, SRP_SALT_SIZE,
(const unsigned char *) session->srp_user->verifier, SRP_VERIFIER_SIZE,
A, len_A,
b, len_b,
&B, &len_B, NULL, NULL, 1);
srp_verifier_verify_session(verifier, proof, &M2);
authenticated = srp_verifier_is_authenticated(verifier);
if (authenticated == 0) {
/* HTTP 470 should be sent to client if not verified.*/
srp_verifier_delete(verifier);
free (session->srp_user);
session->srp_user = NULL;
return -1;
}
session_key = srp_verifier_get_session_key(verifier, &len_K);
if (len_K != SRP_SESSION_KEY_SIZE) {
return -2;
}
memcpy(session->srp_session_key, session_key, len_K);
memcpy(proof, M2, proof_len);
srp_verifier_delete(verifier);
return 0;
}
int
srp_confirm_pair_setup(pairing_session_t *session, const unsigned char *pk,
unsigned char *epk, unsigned char *auth_tag) {
unsigned char aesKey[16], aesIV[16];
unsigned char hash[SHA512_DIGEST_LENGTH];
unsigned char pk_client[ED25519_KEY_SIZE];
int pk_len_client, epk_len;
/* decrypt client epk to get client pk, authenticate with auth_tag*/
const char *salt = "Pair-Setup-AES-Key";
sha_ctx_t *ctx = sha_init();
sha_update(ctx, (const unsigned char *) salt, strlen(salt));
sha_update(ctx, session->srp_session_key, SRP_SESSION_KEY_SIZE);
sha_final(ctx, hash, NULL);
sha_destroy(ctx);
memcpy(aesKey, hash, 16);
salt = "Pair-Setup-AES-IV";
ctx = sha_init();
sha_update(ctx, (const unsigned char *) salt, strlen(salt));
sha_update(ctx, session->srp_session_key, SRP_SESSION_KEY_SIZE);
sha_final(ctx, hash, NULL);
sha_destroy(ctx);
memcpy(aesIV, hash, 16);
aesIV[15]++;
pk_len_client = gcm_decrypt(epk, ED25519_KEY_SIZE, pk_client, aesKey, aesIV, auth_tag);
if (pk_len_client <= 0) {
/* authentication failed */
return pk_len_client;
}
/* the previously undocumented necessary "nonce" */
aesIV[15]++;
epk_len = gcm_encrypt(pk, ED25519_KEY_SIZE, epk, aesKey, aesIV, auth_tag);
return epk_len;
}

View File

@@ -20,6 +20,18 @@
#define PAIRING_SIG_SIZE (2 * X25519_KEY_SIZE)
#define SRP_USERNAME_SIZE 24 /* accomodates up to an 8-octet MAC address */
#define SRP_SESSION_KEY_SIZE 40
#define SRP_VERIFIER_SIZE 256
#define SRP_SALT_SIZE 16
#define SRP_PK_SIZE 256
#define SRP_SHA SRP_SHA1
#define SRP_NG SRP_NG_2048
#define SRP_M2_SIZE 64
#define GCM_AUTHTAG_SIZE 16
#define SHA512_KEY_LENGTH 64
typedef struct pairing_s pairing_t;
typedef struct pairing_session_s pairing_session_t;
@@ -41,4 +53,10 @@ void pairing_destroy(pairing_t *pairing);
int pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[X25519_KEY_SIZE]);
int srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_id, const char *pin,
const char **salt, int *len_salt, const char **pk, int *len_pk);
int srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigned char *A,
int len_A, unsigned char *proof, int client_proof_len, int proof_len);
int srp_confirm_pair_setup(pairing_session_t *session, const unsigned char *pk,
unsigned char *epk, unsigned char *auth_tag);
#endif

View File

@@ -200,9 +200,161 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) {
/* does nothing yet */
const char *request_data;
int request_datalen;
bool data_is_plist = false;
bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
request_data = http_request_get_data(request, &request_datalen);
logger_log(conn->raop->logger, LOGGER_INFO, "client requested pair-setup-pin, datalen = %d", request_datalen);
if (request_datalen > 0) {
char *header_str= NULL;
http_request_get_header_string(request, &header_str);
logger_log(conn->raop->logger, LOGGER_INFO, "request header: %s", header_str);
data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL);
free(header_str);
}
if (!data_is_plist) {
logger_log(conn->raop->logger, LOGGER_INFO, "did not receive expected plist from client, request_datalen = %d");
goto authentication_failed;
}
/* process the pair-setup-pin request */
plist_t req_root_node = NULL;
plist_from_bin(request_data, request_datalen, &req_root_node);
plist_t req_method_node = plist_dict_get_item(req_root_node, "method");
plist_t req_user_node = plist_dict_get_item(req_root_node, "user");
plist_t req_pk_node = plist_dict_get_item(req_root_node, "pk");
plist_t req_proof_node = plist_dict_get_item(req_root_node, "proof");
plist_t req_epk_node = plist_dict_get_item(req_root_node, "epk");
plist_t req_authtag_node = plist_dict_get_item(req_root_node, "authTag");
if (PLIST_IS_STRING(req_method_node) && PLIST_IS_STRING(req_user_node)) {
/* this is the initial pair-setup-pin request */
const char *salt;
char pin[6];
const char *pk;
int len_pk, len_salt;
char *method = NULL;
char *user = NULL;
plist_get_string_val(req_method_node, &method);
if (strncmp(method, "pin", strlen (method))) {
logger_log(conn->raop->logger, LOGGER_ERR, "error, required method is \"pin\", client requested \"%s\"", method);
*response_data = NULL;
response_datalen = 0;
free (method);
plist_free (req_root_node);
return;
}
free (method);
plist_get_string_val(req_user_node, &user);
logger_log(conn->raop->logger, LOGGER_INFO, "pair-setup-pin: device_id = %s", user);
snprintf(pin, 6, "%04u", conn->raop->pin);
conn->raop->pin = 0;
int ret = srp_new_user(conn->pairing, conn->raop->pairing, (const char *) user,
(const char *) pin, &salt, &len_salt, &pk, &len_pk);
free(user);
plist_free(req_root_node);
if (ret < 0) {
logger_log(conn->raop->logger, LOGGER_ERR, "failed to create user, err = %d", ret);
goto authentication_failed;
}
plist_t res_root_node = plist_new_dict();
plist_t res_salt_node = plist_new_data(salt, len_salt);
plist_t res_pk_node = plist_new_data(pk, len_pk);
plist_dict_set_item(res_root_node, "pk", res_pk_node);
plist_dict_set_item(res_root_node, "salt", res_salt_node);
plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
plist_free(res_root_node);
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
return;
} else if (PLIST_IS_DATA(req_pk_node) && PLIST_IS_DATA(req_proof_node)) {
/* this is the second part of pair-setup-pin request */
char *client_pk = NULL;
char *client_proof = NULL;
unsigned char proof[64];
memset(proof, 0, sizeof(proof));
uint64_t client_pk_len;
uint64_t client_proof_len;
plist_get_data_val(req_pk_node, &client_pk, &client_pk_len);
plist_get_data_val(req_proof_node, &client_proof, &client_proof_len);
if (logger_debug) {
char *str = utils_data_to_string((const unsigned char *) client_proof, client_proof_len, 20);
logger_log(conn->raop->logger, LOGGER_DEBUG, "client SRP6a proof <M> :\n%s", str);
free (str);
}
memcpy(proof, client_proof, (int) client_proof_len);
free (client_proof);
int ret = srp_validate_proof(conn->pairing, conn->raop->pairing, (const unsigned char *) client_pk,
(int) client_pk_len, proof, (int) client_proof_len, (int) sizeof(proof));
free (client_pk);
plist_free(req_root_node);
if (ret < 0) {
logger_log(conn->raop->logger, LOGGER_ERR, "Client Authentication Failure (client proof not validated)");
goto authentication_failed;
}
if (logger_debug) {
char *str = utils_data_to_string((const unsigned char *) proof, sizeof(proof), 20);
logger_log(conn->raop->logger, LOGGER_DEBUG, "server SRP6a proof <M1> :\n%s", str);
free (str);
}
plist_t res_root_node = plist_new_dict();
plist_t res_proof_node = plist_new_data((const char *) proof, 20);
plist_dict_set_item(res_root_node, "proof", res_proof_node);
plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
plist_free(res_root_node);
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
return;
} else if (PLIST_IS_DATA(req_epk_node) && PLIST_IS_DATA(req_authtag_node)) {
/* this is the third part of pair-setup-pin request */
char *client_epk = NULL;
char *client_authtag = NULL;
uint64_t client_epk_len;
uint64_t client_authtag_len;
unsigned char epk[ED25519_KEY_SIZE];
unsigned char authtag[GCM_AUTHTAG_SIZE];
unsigned char public_key[32];
int ret;
plist_get_data_val(req_epk_node, &client_epk, &client_epk_len);
plist_get_data_val(req_authtag_node, &client_authtag, &client_authtag_len);
if (logger_debug) {
char *str = utils_data_to_string((const unsigned char *) client_epk, client_epk_len, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "client_epk %d:\n%s\n", (int) client_epk_len, str);
str = utils_data_to_string((const unsigned char *) client_authtag, client_authtag_len, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "client_authtag %d:\n%s\n", (int) client_authtag_len, str);
free (str);
}
memcpy(epk, client_epk, ED25519_KEY_SIZE);
memcpy(authtag, client_authtag, GCM_AUTHTAG_SIZE);
free (client_authtag);
free (client_epk);
plist_free(req_root_node);
pairing_get_public_key(conn->raop->pairing, public_key);
ret = srp_confirm_pair_setup(conn->pairing, public_key, epk, authtag);
if (ret < 0) {
logger_log(conn->raop->logger, LOGGER_ERR, "pair-pin-setup (step 3): client authentication failed\n");
goto authentication_failed;
} else {
logger_log(conn->raop->logger, LOGGER_INFO, "pair-pin-setup success\n");
}
pairing_session_set_setup_status(conn->pairing);
plist_t res_root_node = plist_new_dict();
plist_t res_epk_node = plist_new_data((const char *) epk, 32);
plist_t res_authtag_node = plist_new_data((const char *) authtag, 16);
plist_dict_set_item(res_root_node, "epk", res_epk_node);
plist_dict_set_item(res_root_node, "authTag", res_authtag_node);
plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
plist_free(res_root_node);
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
return;
}
authentication_failed:;
http_response_destroy(response);
response = http_response_init("RTSP/1.0", 470, "Client Authentication Failure");
const char *cseq = http_request_get_header(request, "CSeq");
http_response_add_header(response, "CSeq", cseq);
http_response_add_header(response, "Server", "AirTunes/"GLOBAL_VERSION);
*response_data = NULL;
response_datalen = 0;
return;