From c07ae5c8d29202466f066e4dcddbfd091513db7c Mon Sep 17 00:00:00 2001 From: TheJackiMonster Date: Thu, 5 Nov 2020 21:20:38 +0100 Subject: additional abstraction for identity keys Signed-off-by: TheJackiMonster --- src/conversation/conversation.h | 2 +- src/conversation/gnunet-service-conversation.c | 8 +- src/gnsrecord/gnsrecord_misc.c | 11 +- src/identity/identity_api.c | 164 +++++++++++++++++++ src/include/gnunet_identity_service.h | 215 +++++++++++++++++++++++++ 5 files changed, 389 insertions(+), 11 deletions(-) diff --git a/src/conversation/conversation.h b/src/conversation/conversation.h index 9eedbeb91..d244f5163 100644 --- a/src/conversation/conversation.h +++ b/src/conversation/conversation.h @@ -313,7 +313,7 @@ struct CadetPhoneRingMessage /** * Signature over a `struct CadetPhoneRingInfoPS` */ - struct GNUNET_CRYPTO_EcdsaSignature signature; + struct GNUNET_IDENTITY_Signature signature; }; diff --git a/src/conversation/gnunet-service-conversation.c b/src/conversation/gnunet-service-conversation.c index b1a629217..5c8b573a2 100644 --- a/src/conversation/gnunet-service-conversation.c +++ b/src/conversation/gnunet-service-conversation.c @@ -752,10 +752,10 @@ handle_cadet_ring_message (void *cls, const struct CadetPhoneRingMessage *msg) rs.expiration_time = msg->expiration_time; if (GNUNET_OK != - GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING, + GNUNET_IDENTITY_public_key_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING, &rs, &msg->signature, - &msg->caller_id.ecdsa_key)) + &msg->caller_id)) { GNUNET_break_op (0); destroy_line_cadet_channels (ch); @@ -1138,9 +1138,7 @@ handle_client_call_message (void *cls, const struct ClientCallMessage *msg) e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RING); GNUNET_IDENTITY_key_get_public (&msg->caller_id, &ring->caller_id); ring->expiration_time = rs.expiration_time; - GNUNET_CRYPTO_ecdsa_sign (&msg->caller_id.ecdsa_key, - &rs, - &ring->signature); + GNUNET_IDENTITY_private_key_sign(&msg->caller_id, &rs, &ring->signature); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending RING message via CADET\n"); GNUNET_MQ_send (ch->mq, e); GNUNET_SERVICE_client_continue (line->client); diff --git a/src/gnsrecord/gnsrecord_misc.c b/src/gnsrecord/gnsrecord_misc.c index ba8803850..2e00141a3 100644 --- a/src/gnsrecord/gnsrecord_misc.c +++ b/src/gnsrecord/gnsrecord_misc.c @@ -282,9 +282,9 @@ GNUNET_GNSRECORD_identity_from_data (const char *data, return GNUNET_SYSERR; if (data_size > sizeof (struct GNUNET_IDENTITY_PublicKey)) return GNUNET_SYSERR; - key->type = type; - memcpy (key, data, data_size); - return GNUNET_OK; + return (GNUNET_IDENTITY_read_key_from_buffer(key, data, data_size) == data_size? + GNUNET_OK : + GNUNET_SYSERR); } enum GNUNET_GenericReturnValue @@ -299,8 +299,9 @@ GNUNET_GNSRECORD_data_from_identity (const struct if (0 == *data_size) return GNUNET_SYSERR; *data = GNUNET_malloc (*data_size); - memcpy (*data, key, *data_size); - return GNUNET_OK; + return (GNUNET_IDENTITY_write_key_to_buffer(key, data, *data_size) == *data_size? + GNUNET_OK : + GNUNET_SYSERR); } diff --git a/src/identity/identity_api.c b/src/identity/identity_api.c index 213b6966e..242527c23 100644 --- a/src/identity/identity_api.c +++ b/src/identity/identity_api.c @@ -990,6 +990,170 @@ GNUNET_IDENTITY_key_get_length (const struct GNUNET_IDENTITY_PublicKey *key) } +ssize_t +GNUNET_IDENTITY_signature_get_length (const struct GNUNET_IDENTITY_Signature *sig) +{ + switch (ntohl (sig->type)) + { + case GNUNET_IDENTITY_TYPE_ECDSA: + return sizeof (sig->type) + sizeof (sig->ecdsa_signature); + break; + case GNUNET_IDENTITY_TYPE_EDDSA: + return sizeof (sig->type) + sizeof (sig->eddsa_signature); + break; + default: + GNUNET_break (0); + } + return -1; +} + + +ssize_t +GNUNET_IDENTITY_read_key_from_buffer (struct GNUNET_IDENTITY_PublicKey *key, + const void* buffer, + size_t len) +{ + if (len < sizeof (key->type)) + return -1; + GNUNET_memcpy(& (key->type), buffer, sizeof (key->type)); + const ssize_t length = GNUNET_IDENTITY_key_get_length(key); + if (len < length) + return -1; + if (length < 0) + return -2; + GNUNET_memcpy(key, buffer, length); + return length; +} + + +ssize_t +GNUNET_IDENTITY_write_key_to_buffer (const struct GNUNET_IDENTITY_PublicKey *key, + void* buffer, + size_t len) +{ + const ssize_t length = GNUNET_IDENTITY_key_get_length(key); + if (len < length) + return -1; + if (length < 0) + return -2; + GNUNET_memcpy(buffer, key, length); + return length; +} + + +int +GNUNET_IDENTITY_private_key_sign_ (const struct GNUNET_IDENTITY_PrivateKey *priv, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct GNUNET_IDENTITY_Signature *sig) +{ + sig->type = priv->type; + switch (ntohl (priv->type)) + { + case GNUNET_IDENTITY_TYPE_ECDSA: + return GNUNET_CRYPTO_ecdsa_sign_ (& (priv->ecdsa_key), purpose, & (sig->ecdsa_signature)); + break; + case GNUNET_IDENTITY_TYPE_EDDSA: + return GNUNET_CRYPTO_eddsa_sign_ (& (priv->eddsa_key), purpose, & (sig->eddsa_signature)); + break; + default: + GNUNET_break (0); + } + + return GNUNET_SYSERR; +} + + +int +GNUNET_IDENTITY_public_key_verify_ (uint32_t purpose, + const struct GNUNET_CRYPTO_EccSignaturePurpose *validate, + const struct GNUNET_IDENTITY_Signature *sig, + const struct GNUNET_IDENTITY_PublicKey *pub) +{ + /* check type matching of 'sig' and 'pub' */ + GNUNET_assert (ntohl (pub->type) == ntohl (sig->type)); + switch (ntohl (pub->type)) + { + case GNUNET_IDENTITY_TYPE_ECDSA: + return GNUNET_CRYPTO_ecdsa_verify_ (purpose, validate, & (sig->ecdsa_signature), & (pub->ecdsa_key)); + break; + case GNUNET_IDENTITY_TYPE_EDDSA: + return GNUNET_CRYPTO_eddsa_verify_ (purpose, validate, & (sig->eddsa_signature), & (pub->eddsa_key)); + break; + default: + GNUNET_break (0); + } + + return GNUNET_SYSERR; +} + + +ssize_t +GNUNET_IDENTITY_public_key_encrypt(const void *block, + size_t size, + const struct GNUNET_IDENTITY_PublicKey *pub, + struct GNUNET_CRYPTO_EcdhePublicKey *ecc, + void *result) +{ + struct GNUNET_CRYPTO_EcdhePrivateKey pk; + GNUNET_CRYPTO_ecdhe_key_create(&pk); + struct GNUNET_HashCode hash; + switch (ntohl (pub->type)) + { + case GNUNET_IDENTITY_TYPE_ECDSA: + if (GNUNET_CRYPTO_ecdh_ecdsa(&pk, &(pub->ecdsa_key), &hash) == GNUNET_SYSERR) + return -1; + break; + case GNUNET_IDENTITY_TYPE_EDDSA: + if (GNUNET_CRYPTO_ecdh_eddsa(&pk, &(pub->eddsa_key), &hash) == GNUNET_SYSERR) + return -1; + break; + default: + return -1; + } + GNUNET_CRYPTO_ecdhe_key_get_public(&pk, ecc); + GNUNET_CRYPTO_ecdhe_key_clear(&pk); + struct GNUNET_CRYPTO_SymmetricSessionKey key; + struct GNUNET_CRYPTO_SymmetricInitializationVector iv; + GNUNET_CRYPTO_hash_to_aes_key(&hash, &key, &iv); + GNUNET_CRYPTO_zero_keys(&hash, sizeof(hash)); + const ssize_t encrypted = GNUNET_CRYPTO_symmetric_encrypt(block, size, &key, &iv, result); + GNUNET_CRYPTO_zero_keys(&key, sizeof(key)); + GNUNET_CRYPTO_zero_keys(&iv, sizeof(iv)); + return encrypted; +} + + +ssize_t +GNUNET_IDENTITY_private_key_decrypt(const void *block, + size_t size, + const struct GNUNET_IDENTITY_PrivateKey *priv, + const struct GNUNET_CRYPTO_EcdhePublicKey *ecc, + void *result) { + struct GNUNET_HashCode hash; + switch (ntohl (priv->type)) + { + case GNUNET_IDENTITY_TYPE_ECDSA: + if (GNUNET_CRYPTO_ecdsa_ecdh(&(priv->ecdsa_key), ecc, &hash) == GNUNET_SYSERR) + return -1; + break; + case GNUNET_IDENTITY_TYPE_EDDSA: + if (GNUNET_CRYPTO_eddsa_ecdh(&(priv->eddsa_key), ecc, &hash) == GNUNET_SYSERR) + return -1; + break; + default: + return -1; + } + struct GNUNET_CRYPTO_SymmetricSessionKey key; + struct GNUNET_CRYPTO_SymmetricInitializationVector iv; + GNUNET_CRYPTO_hash_to_aes_key(&hash, &key, &iv); + GNUNET_CRYPTO_zero_keys(&hash, sizeof(hash)); + const ssize_t decrypted = GNUNET_CRYPTO_symmetric_decrypt(block, size, &key, &iv, result); + GNUNET_CRYPTO_zero_keys(&key, sizeof(key)); + GNUNET_CRYPTO_zero_keys(&iv, sizeof(iv)); + return decrypted; +} + + char * GNUNET_IDENTITY_public_key_to_string (const struct GNUNET_IDENTITY_PublicKey *key) diff --git a/src/include/gnunet_identity_service.h b/src/include/gnunet_identity_service.h index 17714fec4..8084a3a98 100644 --- a/src/include/gnunet_identity_service.h +++ b/src/include/gnunet_identity_service.h @@ -137,6 +137,33 @@ struct GNUNET_IDENTITY_PublicKey }; +/** + * An identity signature as per LSD0001. + */ +struct GNUNET_IDENTITY_Signature +{ + /** + * Type of signature. + * Defined by the GNS zone type value. + * In NBO. + */ + uint32_t type; + + union + { + /** + * An ECDSA signature + */ + struct GNUNET_CRYPTO_EcdsaSignature ecdsa_signature; + + /** + * AN EdDSA signature + */ + struct GNUNET_CRYPTO_EddsaSignature eddsa_signature; + }; +}; + + /** * Handle for an operation with the identity service. */ @@ -378,6 +405,194 @@ ssize_t GNUNET_IDENTITY_key_get_length (const struct GNUNET_IDENTITY_PublicKey *key); +/** + * Get the compacted length of a #GNUNET_IDENTITY_Signature. + * Compacted means that it returns the minimum number of bytes this + * signature is long, as opposed to the union structure inside + * #GNUNET_IDENTITY_Signature. + * Useful for compact serializations. + * + * @param sig the signature. + * @return -1 on error, else the compacted length of the signature. + */ +ssize_t +GNUNET_IDENTITY_signature_get_length (const struct GNUNET_IDENTITY_Signature *sig); + + +/** + * Reads a #GNUNET_IDENTITY_PublicKey from a compact buffer. + * The buffer has to contain at least the compacted length of + * a #GNUNET_IDENTITY_PublicKey bytes. + * If the buffer is too small, the function returns -1 as error. + * If the buffer does not contain a valid key, it returns -2 as error. + * + * @param key the key + * @param buffer the buffer + * @param len the length of buffer + * @return -1 or -2 on error, else the amount of bytes read from the buffer + */ +ssize_t +GNUNET_IDENTITY_read_key_from_buffer (struct GNUNET_IDENTITY_PublicKey *key, + const void* buffer, + size_t len); + + +/** + * Writes a #GNUNET_IDENTITY_PublicKey to a compact buffer. + * The buffer requires space for at least the compacted length of + * a #GNUNET_IDENTITY_PublicKey in bytes. + * If the buffer is too small, the function returns -1 as error. + * If the key is not valid, it returns -2 as error. + * + * @param key the key + * @param buffer the buffer + * @param len the length of buffer + * @return -1 or -2 on error, else the amount of bytes written to the buffer + */ +ssize_t +GNUNET_IDENTITY_write_key_to_buffer (const struct GNUNET_IDENTITY_PublicKey *key, + void* buffer, + size_t len); + + +/** + * @brief Sign a given block. + * + * The @a purpose data is the beginning of the data of which the signature is + * to be created. The `size` field in @a purpose must correctly indicate the + * number of bytes of the data structure, including its header. If possible, + * use #GNUNET_IDENTITY_private_key_sign() instead of this function. + * + * @param priv private key to use for the signing + * @param purpose what to sign (size, purpose) + * @param[out] sig where to write the signature + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +int +GNUNET_IDENTITY_private_key_sign_ (const struct GNUNET_IDENTITY_PrivateKey *priv, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct GNUNET_IDENTITY_Signature *sig); + + +/** + * @brief Sign a given block with #GNUNET_IDENTITY_PrivateKey. + * + * The @a ps data must be a fixed-size struct for which the signature is to be + * created. The `size` field in @a ps->purpose must correctly indicate the + * number of bytes of the data structure, including its header. + * + * @param priv private key to use for the signing + * @param ps packed struct with what to sign, MUST begin with a purpose + * @param[out] sig where to write the signature + */ +#define GNUNET_IDENTITY_private_key_sign(priv,ps,sig) do { \ + /* check size is set correctly */ \ + GNUNET_assert (htonl ((ps)->purpose.size) == sizeof (*(ps))); \ + /* check 'ps' begins with the purpose */ \ + GNUNET_static_assert (((void*) (ps)) == \ + ((void*) &(ps)->purpose)); \ + GNUNET_assert (GNUNET_OK == \ + GNUNET_IDENTITY_private_key_sign_ (priv, \ + &(ps)->purpose, \ + sig)); \ +} while (0) + + +/** + * @brief Verify a given signature. + * + * The @a validate data is the beginning of the data of which the signature + * is to be verified. The `size` field in @a validate must correctly indicate + * the number of bytes of the data structure, including its header. If @a + * purpose does not match the purpose given in @a validate (the latter must be + * in big endian), signature verification fails. If possible, + * use #GNUNET_IDENTITY_public_key_verify() instead of this function (only if @a validate + * is not fixed-size, you must use this function directly). + * + * @param purpose what is the purpose that the signature should have? + * @param validate block to validate (size, purpose, data) + * @param sig signature that is being validated + * @param pub public key of the signer + * @returns #GNUNET_OK if ok, #GNUNET_SYSERR if invalid + */ +int +GNUNET_IDENTITY_public_key_verify_ (uint32_t purpose, + const struct GNUNET_CRYPTO_EccSignaturePurpose *validate, + const struct GNUNET_IDENTITY_Signature *sig, + const struct GNUNET_IDENTITY_PublicKey *pub); + + +/** + * @brief Verify a given signature with #GNUNET_IDENTITY_PublicKey. + * + * The @a ps data must be a fixed-size struct for which the signature is to be + * created. The `size` field in @a ps->purpose must correctly indicate the + * number of bytes of the data structure, including its header. + * + * @param purp purpose of the signature, must match 'ps->purpose.purpose' + * (except in host byte order) + * @param ps packed struct with what to sign, MUST begin with a purpose + * @param sig where to read the signature from + * @param pub public key to use for the verifying + */ +#define GNUNET_IDENTITY_public_key_verify(purp,ps,sig,pub) ({ \ + /* check size is set correctly */ \ + GNUNET_assert (ntohl ((ps)->purpose.size) == sizeof (*(ps))); \ + /* check 'ps' begins with the purpose */ \ + GNUNET_static_assert (((void*) (ps)) == \ + ((void*) &(ps)->purpose)); \ + GNUNET_IDENTITY_public_key_verify_(purp, \ + &(ps)->purpose, \ + sig, \ + pub); \ + }) + + +/** + * Encrypt a block with #GNUNET_IDENTITY_PublicKey and derives a + * #GNUNET_CRYPTO_EcdhePublicKey which is required for decryption + * using ecdh to derive a symmetric key. + * + * @param block the block to encrypt + * @param size the size of the @a block + * @param pub public key to use for ecdh + * @param ecc where to write the ecc public key + * @param result the output parameter in which to store the encrypted result + * can be the same or overlap with @c block + * @returns the size of the encrypted block, -1 for errors. + * Due to the use of CFB and therefore an effective stream cipher, + * this size should be the same as @c len. + */ +ssize_t +GNUNET_IDENTITY_public_key_encrypt(const void *block, + size_t size, + const struct GNUNET_IDENTITY_PublicKey *pub, + struct GNUNET_CRYPTO_EcdhePublicKey *ecc, + void *result); + + +/** + * Decrypt a given block with #GNUNET_IDENTITY_PrivateKey and a given + * #GNUNET_CRYPTO_EcdhePublicKey using ecdh to derive a symmetric key. + * + * @param block the data to decrypt, encoded as returned by encrypt + * @param size the size of the @a block to decrypt + * @param priv private key to use for ecdh + * @param ecc the ecc public key + * @param result address to store the result at + * can be the same or overlap with @c block + * @return -1 on failure, size of decrypted block on success. + * Due to the use of CFB and therefore an effective stream cipher, + * this size should be the same as @c size. + */ +ssize_t +GNUNET_IDENTITY_private_key_decrypt(const void *block, + size_t size, + const struct GNUNET_IDENTITY_PrivateKey *priv, + const struct GNUNET_CRYPTO_EcdhePublicKey *ecc, + void *result); + + /** * Creates a (Base32) string representation of the public key. * The resulting string encodes a compacted representation of the key. -- cgit v1.2.3