Implemented public Base64 interface
Base64 is required for vCard avatars. It will also be useful for an authentication mechanism with future xmpp_connect_raw() interface.
This commit is contained in:
@@ -442,7 +442,7 @@ static int _handle_scram_sha1_challenge(xmpp_conn_t * const conn,
|
|||||||
if (!text)
|
if (!text)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
challenge = (char *)base64_decode(conn->ctx, text, strlen(text));
|
challenge = xmpp_base64_decode_str(conn->ctx, text, strlen(text));
|
||||||
xmpp_free(conn->ctx, text);
|
xmpp_free(conn->ctx, text);
|
||||||
if (!challenge)
|
if (!challenge)
|
||||||
goto err;
|
goto err;
|
||||||
@@ -639,8 +639,8 @@ static void _auth(xmpp_conn_t * const conn)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
str = (char *)base64_encode(conn->ctx, (unsigned char *)scram_init,
|
str = xmpp_base64_encode(conn->ctx, (unsigned char *)scram_init,
|
||||||
strlen(scram_init));
|
strlen(scram_init));
|
||||||
if (!str) {
|
if (!str) {
|
||||||
xmpp_free(conn->ctx, scram_init);
|
xmpp_free(conn->ctx, scram_init);
|
||||||
xmpp_stanza_release(auth);
|
xmpp_stanza_release(auth);
|
||||||
|
|||||||
279
src/crypto.c
279
src/crypto.c
@@ -16,6 +16,9 @@
|
|||||||
/** @defgroup Digests Message digests
|
/** @defgroup Digests Message digests
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/** @defgroup Encodings Encodings
|
||||||
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h> /* memset, memcpy */
|
#include <string.h> /* memset, memcpy */
|
||||||
|
|
||||||
@@ -182,3 +185,279 @@ void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest)
|
|||||||
assert(SHA1_DIGEST_SIZE == XMPP_SHA1_DIGEST_SIZE);
|
assert(SHA1_DIGEST_SIZE == XMPP_SHA1_DIGEST_SIZE);
|
||||||
memcpy(digest, sha1->digest, SHA1_DIGEST_SIZE);
|
memcpy(digest, sha1->digest, SHA1_DIGEST_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Base64 encoding routines. Implemented according to RFC 3548. */
|
||||||
|
|
||||||
|
/* map of all byte values to the base64 values, or to
|
||||||
|
'65' which indicates an invalid character. '=' is '64' */
|
||||||
|
static const unsigned char _base64_invcharmap[256] = {
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,62, 65,65,65,63,
|
||||||
|
52,53,54,55, 56,57,58,59, 60,61,65,65, 65,64,65,65,
|
||||||
|
65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
||||||
|
15,16,17,18, 19,20,21,22, 23,24,25,65, 65,65,65,65,
|
||||||
|
65,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||||
|
41,42,43,44, 45,46,47,48, 49,50,51,65, 65,65,65,65,
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||||
|
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65
|
||||||
|
};
|
||||||
|
|
||||||
|
/* map of all 6-bit values to their corresponding byte
|
||||||
|
in the base64 alphabet. Padding char is the value '64' */
|
||||||
|
static const char _base64_charmap[65] = {
|
||||||
|
'A','B','C','D', 'E','F','G','H',
|
||||||
|
'I','J','K','L', 'M','N','O','P',
|
||||||
|
'Q','R','S','T', 'U','V','W','X',
|
||||||
|
'Y','Z','a','b', 'c','d','e','f',
|
||||||
|
'g','h','i','j', 'k','l','m','n',
|
||||||
|
'o','p','q','r', 's','t','u','v',
|
||||||
|
'w','x','y','z', '0','1','2','3',
|
||||||
|
'4','5','6','7', '8','9','+','/',
|
||||||
|
'='
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t base64_encoded_len(const size_t len)
|
||||||
|
{
|
||||||
|
/* encoded steam is 4 bytes for every three, rounded up */
|
||||||
|
return ((len + 2)/3) << 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *base64_encode(xmpp_ctx_t *ctx,
|
||||||
|
const unsigned char * const buffer, const size_t len)
|
||||||
|
{
|
||||||
|
size_t clen;
|
||||||
|
char *cbuf, *c;
|
||||||
|
uint32_t word, hextet;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
clen = base64_encoded_len(len);
|
||||||
|
cbuf = xmpp_alloc(ctx, clen + 1);
|
||||||
|
if (cbuf != NULL) {
|
||||||
|
c = cbuf;
|
||||||
|
/* loop over data, turning every 3 bytes into 4 characters */
|
||||||
|
for (i = 0; i + 2 < len; i += 3) {
|
||||||
|
word = buffer[i] << 16 | buffer[i+1] << 8 | buffer[i+2];
|
||||||
|
hextet = (word & 0x00FC0000) >> 18;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = (word & 0x0003F000) >> 12;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = (word & 0x00000FC0) >> 6;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = (word & 0x000003F);
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
}
|
||||||
|
/* zero, one or two bytes left */
|
||||||
|
switch (len - i) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
hextet = (buffer[len-1] & 0xFC) >> 2;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = (buffer[len-1] & 0x03) << 4;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
*c++ = _base64_charmap[64]; /* pad */
|
||||||
|
*c++ = _base64_charmap[64]; /* pad */
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
hextet = (buffer[len-2] & 0xFC) >> 2;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = ((buffer[len-2] & 0x03) << 4) |
|
||||||
|
((buffer[len-1] & 0xF0) >> 4);
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = (buffer[len-1] & 0x0F) << 2;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
*c++ = _base64_charmap[64]; /* pad */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* add a terminal null */
|
||||||
|
*c = '\0';
|
||||||
|
}
|
||||||
|
return cbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t base64_decoded_len(const char * const buffer, const size_t len)
|
||||||
|
{
|
||||||
|
size_t nudge = 0;
|
||||||
|
unsigned char c;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (len < 4) return 0;
|
||||||
|
|
||||||
|
/* count the padding characters for the remainder */
|
||||||
|
for (i = len; i > 0; --i) {
|
||||||
|
c = _base64_invcharmap[(unsigned char)buffer[i-1]];
|
||||||
|
if (c < 64) break;
|
||||||
|
if (c == 64) ++nudge;
|
||||||
|
if (c > 64) return 0;
|
||||||
|
}
|
||||||
|
if (nudge > 2) return 0;
|
||||||
|
|
||||||
|
/* decoded steam is 3 bytes for every four */
|
||||||
|
return 3 * (len >> 2) - nudge;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void base64_decode(xmpp_ctx_t *ctx,
|
||||||
|
const char * const buffer, const size_t len,
|
||||||
|
unsigned char **out, size_t *outlen)
|
||||||
|
{
|
||||||
|
size_t dlen;
|
||||||
|
unsigned char *dbuf, *d;
|
||||||
|
uint32_t word, hextet = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* len must be a multiple of 4 */
|
||||||
|
if (len & 0x03) goto _base64_error;
|
||||||
|
|
||||||
|
dlen = base64_decoded_len(buffer, len);
|
||||||
|
if (dlen == 0) goto _base64_error;
|
||||||
|
|
||||||
|
dbuf = xmpp_alloc(ctx, dlen + 1);
|
||||||
|
if (dbuf != NULL) {
|
||||||
|
d = dbuf;
|
||||||
|
/* loop over each set of 4 characters, decoding 3 bytes */
|
||||||
|
for (i = 0; i + 3 < len; i += 4) {
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[i]];
|
||||||
|
if (hextet & 0xC0) break;
|
||||||
|
word = hextet << 18;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[i+1]];
|
||||||
|
if (hextet & 0xC0) break;
|
||||||
|
word |= hextet << 12;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[i+2]];
|
||||||
|
if (hextet & 0xC0) break;
|
||||||
|
word |= hextet << 6;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[i+3]];
|
||||||
|
if (hextet & 0xC0) break;
|
||||||
|
word |= hextet;
|
||||||
|
*d++ = (word & 0x00FF0000) >> 16;
|
||||||
|
*d++ = (word & 0x0000FF00) >> 8;
|
||||||
|
*d++ = (word & 0x000000FF);
|
||||||
|
}
|
||||||
|
if (hextet > 64) goto _base64_decode_error;
|
||||||
|
/* handle the remainder */
|
||||||
|
switch (dlen % 3) {
|
||||||
|
case 0:
|
||||||
|
/* nothing to do */
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* redo the last quartet, checking for correctness */
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len-4]];
|
||||||
|
if (hextet & 0xC0) goto _base64_decode_error;
|
||||||
|
word = hextet << 2;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len-3]];
|
||||||
|
if (hextet & 0xC0) goto _base64_decode_error;
|
||||||
|
word |= hextet >> 4;
|
||||||
|
*d++ = word & 0xFF;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len-2]];
|
||||||
|
if (hextet != 64) goto _base64_decode_error;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len-1]];
|
||||||
|
if (hextet != 64) goto _base64_decode_error;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* redo the last quartet, checking for correctness */
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len-4]];
|
||||||
|
if (hextet & 0xC0) goto _base64_decode_error;
|
||||||
|
word = hextet << 10;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len-3]];
|
||||||
|
if (hextet & 0xC0) goto _base64_decode_error;
|
||||||
|
word |= hextet << 4;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len-2]];
|
||||||
|
if (hextet & 0xC0) goto _base64_decode_error;
|
||||||
|
word |= hextet >> 2;
|
||||||
|
*d++ = (word & 0xFF00) >> 8;
|
||||||
|
*d++ = (word & 0x00FF);
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len-1]];
|
||||||
|
if (hextet != 64) goto _base64_decode_error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*d = '\0';
|
||||||
|
}
|
||||||
|
*out = dbuf;
|
||||||
|
*outlen = dbuf == NULL ? 0 : dlen;
|
||||||
|
return;
|
||||||
|
|
||||||
|
_base64_decode_error:
|
||||||
|
/* invalid character; abort decoding! */
|
||||||
|
xmpp_free(ctx, dbuf);
|
||||||
|
_base64_error:
|
||||||
|
*out = NULL;
|
||||||
|
*outlen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base64 encoding routine
|
||||||
|
* Returns an allocated string which must be freed with xmpp_free().
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context
|
||||||
|
* @param data buffer to encode
|
||||||
|
* @param len size of the data buffer
|
||||||
|
*
|
||||||
|
* @return an allocated null-terminated string or NULL on error
|
||||||
|
*
|
||||||
|
* @ingroup Encodings
|
||||||
|
*/
|
||||||
|
char *xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len)
|
||||||
|
{
|
||||||
|
return base64_encode(ctx, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base64 decoding routine
|
||||||
|
* Returns an allocated string which must be freed with xmpp_free(). User
|
||||||
|
* calls this function when the result must be a string. When decoded buffer
|
||||||
|
* contains '\0' NULL is returned.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context
|
||||||
|
* @param base64 encoded buffer
|
||||||
|
* @param len size of the buffer
|
||||||
|
*
|
||||||
|
* @return an allocated null-terminated string or NULL on error
|
||||||
|
*
|
||||||
|
* @ingroup Encodings
|
||||||
|
*/
|
||||||
|
char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len)
|
||||||
|
{
|
||||||
|
unsigned char *buf = NULL;
|
||||||
|
size_t buflen;
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
/* handle empty string */
|
||||||
|
buf = xmpp_alloc(ctx, 1);
|
||||||
|
if (buf)
|
||||||
|
buf[0] = '\0';
|
||||||
|
buflen = 0;
|
||||||
|
} else {
|
||||||
|
base64_decode(ctx, base64, len, &buf, &buflen);
|
||||||
|
}
|
||||||
|
if (buf) {
|
||||||
|
if (buflen != strlen((char *)buf)) {
|
||||||
|
xmpp_free(ctx, buf);
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (char *)buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base64 decoding routine
|
||||||
|
* Returns an allocated buffer which must be freed with xmpp_free().
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context
|
||||||
|
* @param base64 encoded buffer
|
||||||
|
* @param len size of the encoded buffer
|
||||||
|
* @param out allocated buffer is stored here
|
||||||
|
* @param outlen size of the allocated buffer
|
||||||
|
*
|
||||||
|
* @note on an error the `*out` will be NULL
|
||||||
|
*
|
||||||
|
* @ingroup Encodings
|
||||||
|
*/
|
||||||
|
void xmpp_base64_decode_bin(xmpp_ctx_t *ctx, const char *base64, size_t len,
|
||||||
|
unsigned char **out, size_t *outlen)
|
||||||
|
{
|
||||||
|
base64_decode(ctx, base64, len, out, outlen);
|
||||||
|
}
|
||||||
|
|||||||
278
src/sasl.c
278
src/sasl.c
@@ -32,7 +32,8 @@
|
|||||||
|
|
||||||
/** generate authentication string for the SASL PLAIN mechanism */
|
/** generate authentication string for the SASL PLAIN mechanism */
|
||||||
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
|
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
|
||||||
int idlen, passlen;
|
size_t idlen, passlen;
|
||||||
|
size_t msglen;
|
||||||
char *result = NULL;
|
char *result = NULL;
|
||||||
char *msg;
|
char *msg;
|
||||||
|
|
||||||
@@ -41,13 +42,14 @@ char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
|
|||||||
|
|
||||||
idlen = strlen(authid);
|
idlen = strlen(authid);
|
||||||
passlen = strlen(password);
|
passlen = strlen(password);
|
||||||
msg = xmpp_alloc(ctx, 2 + idlen + passlen);
|
msglen = 2 + idlen + passlen;
|
||||||
|
msg = xmpp_alloc(ctx, msglen);
|
||||||
if (msg != NULL) {
|
if (msg != NULL) {
|
||||||
msg[0] = '\0';
|
msg[0] = '\0';
|
||||||
memcpy(msg+1, authid, idlen);
|
memcpy(msg+1, authid, idlen);
|
||||||
msg[1+idlen] = '\0';
|
msg[1+idlen] = '\0';
|
||||||
memcpy(msg+1+idlen+1, password, passlen);
|
memcpy(msg+1+idlen+1, password, passlen);
|
||||||
result = base64_encode(ctx, (unsigned char *)msg, 2 + idlen + passlen);
|
result = xmpp_base64_encode(ctx, (unsigned char *)msg, msglen);
|
||||||
xmpp_free(ctx, msg);
|
xmpp_free(ctx, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +95,7 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
|
|||||||
char *key, *value;
|
char *key, *value;
|
||||||
unsigned char *s, *t;
|
unsigned char *s, *t;
|
||||||
|
|
||||||
text = base64_decode(ctx, msg, strlen(msg));
|
text = (unsigned char *)xmpp_base64_decode_str(ctx, msg, strlen(msg));
|
||||||
if (text == NULL) {
|
if (text == NULL) {
|
||||||
xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
|
xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -346,7 +348,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
hash_release(table); /* also frees value strings */
|
hash_release(table); /* also frees value strings */
|
||||||
|
|
||||||
/* reuse response for the base64 encode of our result */
|
/* reuse response for the base64 encode of our result */
|
||||||
response = base64_encode(ctx, (unsigned char *)result, strlen(result));
|
response = xmpp_base64_encode(ctx, (unsigned char *)result, strlen(result));
|
||||||
xmpp_free(ctx, result);
|
xmpp_free(ctx, result);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
@@ -398,11 +400,11 @@ char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
sval = (char *)base64_decode(ctx, s, strlen(s));
|
sval = xmpp_base64_decode_str(ctx, s, strlen(s));
|
||||||
if (!sval) {
|
if (!sval) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
sval_len = base64_decoded_len(ctx, s, strlen(s));
|
sval_len = strlen(sval);
|
||||||
ival = strtol(i, &saveptr, 10);
|
ival = strtol(i, &saveptr, 10);
|
||||||
|
|
||||||
auth_len = 10 + strlen(r) + strlen(first_bare) + strlen(challenge);
|
auth_len = 10 + strlen(r) + strlen(first_bare) + strlen(challenge);
|
||||||
@@ -428,7 +430,7 @@ char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
sign[j] ^= key[j];
|
sign[j] ^= key[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
sign_b64 = base64_encode(ctx, sign, sizeof(sign));
|
sign_b64 = xmpp_base64_encode(ctx, sign, sizeof(sign));
|
||||||
if (!sign_b64) {
|
if (!sign_b64) {
|
||||||
goto out_response;
|
goto out_response;
|
||||||
}
|
}
|
||||||
@@ -441,8 +443,8 @@ char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
strcat(response, sign_b64);
|
strcat(response, sign_b64);
|
||||||
xmpp_free(ctx, sign_b64);
|
xmpp_free(ctx, sign_b64);
|
||||||
|
|
||||||
response_b64 = base64_encode(ctx, (unsigned char *)response,
|
response_b64 = xmpp_base64_encode(ctx, (unsigned char *)response,
|
||||||
strlen(response));
|
strlen(response));
|
||||||
if (!response_b64) {
|
if (!response_b64) {
|
||||||
goto out_response;
|
goto out_response;
|
||||||
}
|
}
|
||||||
@@ -458,259 +460,3 @@ out:
|
|||||||
xmpp_free(ctx, tmp);
|
xmpp_free(ctx, tmp);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Base64 encoding routines. Implemented according to RFC 3548 */
|
|
||||||
|
|
||||||
/** map of all byte values to the base64 values, or to
|
|
||||||
'65' which indicates an invalid character. '=' is '64' */
|
|
||||||
static const char _base64_invcharmap[256] = {
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,62, 65,65,65,63,
|
|
||||||
52,53,54,55, 56,57,58,59, 60,61,65,65, 65,64,65,65,
|
|
||||||
65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
|
||||||
15,16,17,18, 19,20,21,22, 23,24,25,65, 65,65,65,65,
|
|
||||||
65,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
|
||||||
41,42,43,44, 45,46,47,48, 49,50,51,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65
|
|
||||||
};
|
|
||||||
|
|
||||||
/** map of all 6-bit values to their corresponding byte
|
|
||||||
in the base64 alphabet. Padding char is the value '64' */
|
|
||||||
static const char _base64_charmap[65] = {
|
|
||||||
'A','B','C','D', 'E','F','G','H',
|
|
||||||
'I','J','K','L', 'M','N','O','P',
|
|
||||||
'Q','R','S','T', 'U','V','W','X',
|
|
||||||
'Y','Z','a','b', 'c','d','e','f',
|
|
||||||
'g','h','i','j', 'k','l','m','n',
|
|
||||||
'o','p','q','r', 's','t','u','v',
|
|
||||||
'w','x','y','z', '0','1','2','3',
|
|
||||||
'4','5','6','7', '8','9','+','/',
|
|
||||||
'='
|
|
||||||
};
|
|
||||||
|
|
||||||
int base64_encoded_len(xmpp_ctx_t *ctx, const unsigned len)
|
|
||||||
{
|
|
||||||
/* encoded steam is 4 bytes for every three, rounded up */
|
|
||||||
return ((len + 2)/3) << 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *base64_encode(xmpp_ctx_t *ctx,
|
|
||||||
const unsigned char * const buffer, const unsigned len)
|
|
||||||
{
|
|
||||||
int clen;
|
|
||||||
char *cbuf, *c;
|
|
||||||
uint32_t word, hextet;
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
clen = base64_encoded_len(ctx, len);
|
|
||||||
cbuf = xmpp_alloc(ctx, clen + 1);
|
|
||||||
if (cbuf != NULL) {
|
|
||||||
c = cbuf;
|
|
||||||
/* loop over data, turning every 3 bytes into 4 characters */
|
|
||||||
for (i = 0; i + 2 < len; i += 3) {
|
|
||||||
word = buffer[i] << 16 | buffer[i+1] << 8 | buffer[i+2];
|
|
||||||
hextet = (word & 0x00FC0000) >> 18;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = (word & 0x0003F000) >> 12;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = (word & 0x00000FC0) >> 6;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = (word & 0x000003F);
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
}
|
|
||||||
/* zero, one or two bytes left */
|
|
||||||
switch (len - i) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
hextet = (buffer[len-1] & 0xFC) >> 2;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = (buffer[len-1] & 0x03) << 4;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
*c++ = _base64_charmap[64]; /* pad */
|
|
||||||
*c++ = _base64_charmap[64]; /* pad */
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
hextet = (buffer[len-2] & 0xFC) >> 2;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = ((buffer[len-2] & 0x03) << 4) |
|
|
||||||
((buffer[len-1] & 0xF0) >> 4);
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = (buffer[len-1] & 0x0F) << 2;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
*c++ = _base64_charmap[64]; /* pad */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* add a terminal null */
|
|
||||||
*c = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
return cbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
int base64_decoded_len(xmpp_ctx_t *ctx,
|
|
||||||
const char * const buffer, const unsigned len)
|
|
||||||
{
|
|
||||||
int nudge;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
if (len < 4) return 0;
|
|
||||||
|
|
||||||
/* count the padding characters for the remainder */
|
|
||||||
nudge = -1;
|
|
||||||
c = _base64_invcharmap[(int)buffer[len-1]];
|
|
||||||
if (c < 64) nudge = 0;
|
|
||||||
else if (c == 64) {
|
|
||||||
c = _base64_invcharmap[(int)buffer[len-2]];
|
|
||||||
if (c < 64) nudge = 1;
|
|
||||||
else if (c == 64) {
|
|
||||||
c = _base64_invcharmap[(int)buffer[len-3]];
|
|
||||||
if (c < 64) nudge = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nudge < 0) return 0; /* reject bad coding */
|
|
||||||
|
|
||||||
/* decoded steam is 3 bytes for every four */
|
|
||||||
return 3 * (len >> 2) - nudge;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *base64_decode(xmpp_ctx_t *ctx,
|
|
||||||
const char * const buffer, const unsigned len)
|
|
||||||
{
|
|
||||||
int dlen;
|
|
||||||
unsigned char *dbuf, *d;
|
|
||||||
uint32_t word, hextet = 0;
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
/* len must be a multiple of 4 */
|
|
||||||
if (len & 0x03) return NULL;
|
|
||||||
|
|
||||||
dlen = base64_decoded_len(ctx, buffer, len);
|
|
||||||
dbuf = xmpp_alloc(ctx, dlen + 1);
|
|
||||||
if (dbuf != NULL) {
|
|
||||||
d = dbuf;
|
|
||||||
/* loop over each set of 4 characters, decoding 3 bytes */
|
|
||||||
for (i = 0; i + 3 < len; i += 4) {
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[i]];
|
|
||||||
if (hextet & 0xC0) break;
|
|
||||||
word = hextet << 18;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[i+1]];
|
|
||||||
if (hextet & 0xC0) break;
|
|
||||||
word |= hextet << 12;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[i+2]];
|
|
||||||
if (hextet & 0xC0) break;
|
|
||||||
word |= hextet << 6;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[i+3]];
|
|
||||||
if (hextet & 0xC0) break;
|
|
||||||
word |= hextet;
|
|
||||||
*d++ = (word & 0x00FF0000) >> 16;
|
|
||||||
*d++ = (word & 0x0000FF00) >> 8;
|
|
||||||
*d++ = (word & 0x000000FF);
|
|
||||||
}
|
|
||||||
if (hextet > 64) goto _base64_decode_error;
|
|
||||||
/* handle the remainder */
|
|
||||||
switch (dlen % 3) {
|
|
||||||
case 0:
|
|
||||||
/* nothing to do */
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/* redo the last quartet, checking for correctness */
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-4]];
|
|
||||||
if (hextet & 0xC0) goto _base64_decode_error;
|
|
||||||
word = hextet << 2;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-3]];
|
|
||||||
if (hextet & 0xC0) goto _base64_decode_error;
|
|
||||||
word |= hextet >> 4;
|
|
||||||
*d++ = word & 0xFF;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-2]];
|
|
||||||
if (hextet != 64) goto _base64_decode_error;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-1]];
|
|
||||||
if (hextet != 64) goto _base64_decode_error;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/* redo the last quartet, checking for correctness */
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-4]];
|
|
||||||
if (hextet & 0xC0) goto _base64_decode_error;
|
|
||||||
word = hextet << 10;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-3]];
|
|
||||||
if (hextet & 0xC0) goto _base64_decode_error;
|
|
||||||
word |= hextet << 4;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-2]];
|
|
||||||
if (hextet & 0xC0) goto _base64_decode_error;
|
|
||||||
word |= hextet >> 2;
|
|
||||||
*d++ = (word & 0xFF00) >> 8;
|
|
||||||
*d++ = (word & 0x00FF);
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-1]];
|
|
||||||
if (hextet != 64) goto _base64_decode_error;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*d = '\0';
|
|
||||||
}
|
|
||||||
return dbuf;
|
|
||||||
|
|
||||||
_base64_decode_error:
|
|
||||||
/* invalid character; abort decoding! */
|
|
||||||
xmpp_free(ctx, dbuf);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** self tests ***/
|
|
||||||
#ifdef TEST
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int test_charmap_identity(void)
|
|
||||||
{
|
|
||||||
int i, v, u;
|
|
||||||
|
|
||||||
for (i = 0; i < 65; i++) {
|
|
||||||
v = _base64_charmap[i];
|
|
||||||
if (v > 255) return 1;
|
|
||||||
u = _base64_invcharmap[v];
|
|
||||||
/* printf("map: %d -> %d -> %d\n", i, v, u); */
|
|
||||||
if (u != i) return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_charmap_range(void)
|
|
||||||
{
|
|
||||||
int i, v;
|
|
||||||
|
|
||||||
for (i = 64; i < 256; i++) {
|
|
||||||
v = _base64_invcharmap[i];
|
|
||||||
if (i < 64) return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
printf("testing charmap identity...");
|
|
||||||
ret = test_charmap_identity();
|
|
||||||
if (ret) return ret;
|
|
||||||
printf(" ok.\n");
|
|
||||||
|
|
||||||
printf("testing charmap range...");
|
|
||||||
ret = test_charmap_range();
|
|
||||||
if (ret) return ret;
|
|
||||||
printf(" ok.\n");
|
|
||||||
|
|
||||||
printf("no error\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* TEST */
|
|
||||||
|
|||||||
14
src/sasl.h
14
src/sasl.h
@@ -27,18 +27,4 @@ char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
const char *first_bare, const char *jid,
|
const char *first_bare, const char *jid,
|
||||||
const char *password);
|
const char *password);
|
||||||
|
|
||||||
|
|
||||||
/** Base64 encoding routines. Implemented according to RFC 3548 */
|
|
||||||
|
|
||||||
int base64_encoded_len(xmpp_ctx_t *ctx, const unsigned len);
|
|
||||||
|
|
||||||
char *base64_encode(xmpp_ctx_t *ctx,
|
|
||||||
const unsigned char * const buffer, const unsigned len);
|
|
||||||
|
|
||||||
int base64_decoded_len(xmpp_ctx_t *ctx,
|
|
||||||
const char * const buffer, const unsigned len);
|
|
||||||
|
|
||||||
unsigned char *base64_decode(xmpp_ctx_t *ctx,
|
|
||||||
const char * const buffer, const unsigned len);
|
|
||||||
|
|
||||||
#endif /* _LIBXMPP_SASL_H__ */
|
#endif /* _LIBXMPP_SASL_H__ */
|
||||||
|
|||||||
@@ -407,6 +407,13 @@ char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen);
|
|||||||
char *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1);
|
char *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1);
|
||||||
void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest);
|
void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest);
|
||||||
|
|
||||||
|
/* Base64 */
|
||||||
|
|
||||||
|
char *xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len);
|
||||||
|
char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len);
|
||||||
|
void xmpp_base64_decode_bin(xmpp_ctx_t *ctx, const char *base64, size_t len,
|
||||||
|
unsigned char **out, size_t *outlen);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,8 +14,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "strophe.h"
|
#include "strophe.h"
|
||||||
#include "common.h"
|
|
||||||
#include "sasl.h"
|
|
||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
@@ -95,13 +93,48 @@ static const struct {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const unsigned char bin_data[] = {
|
||||||
|
0xda, 0xa8, 0x81, 0x80, 0x00, 0x01, 0x00, 0x05,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x0c, 0x5f, 0x78, 0x6d,
|
||||||
|
0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||||
|
0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x67,
|
||||||
|
0x6d, 0x61, 0x69, 0x6c, 0x03, 0x63, 0x6f, 0x6d,
|
||||||
|
0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00,
|
||||||
|
0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00,
|
||||||
|
0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04,
|
||||||
|
0x61, 0x6c, 0x74, 0x31, 0x04, 0x78, 0x6d, 0x70,
|
||||||
|
0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67,
|
||||||
|
0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0xc0,
|
||||||
|
0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02,
|
||||||
|
0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14,
|
||||||
|
0x66, 0x04, 0x61, 0x6c, 0x74, 0x34, 0x04, 0x78,
|
||||||
|
0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f,
|
||||||
|
0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d,
|
||||||
|
0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00,
|
||||||
|
0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00,
|
||||||
|
0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x32,
|
||||||
|
0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06,
|
||||||
|
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
|
||||||
|
0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x19, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x14, 0x66, 0x04, 0x78, 0x6d,
|
||||||
|
0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f,
|
||||||
|
0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
|
||||||
|
0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00,
|
||||||
|
0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00,
|
||||||
|
0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x33, 0x04,
|
||||||
|
0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67,
|
||||||
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f,
|
||||||
|
0x6d, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx;
|
xmpp_ctx_t *ctx;
|
||||||
unsigned char *dec;
|
unsigned char *udec;
|
||||||
|
char *dec;
|
||||||
char *enc;
|
char *enc;
|
||||||
size_t len;
|
size_t len;
|
||||||
int ret = 0;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
printf("BASE64 tests.\n");
|
printf("BASE64 tests.\n");
|
||||||
@@ -114,22 +147,33 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(tests); ++i) {
|
for (i = 0; i < ARRAY_SIZE(tests); ++i) {
|
||||||
printf("Test #%d: ", (int)i + 1);
|
printf("Test #%d: ", (int)i + 1);
|
||||||
enc = base64_encode(ctx, (unsigned char *)tests[i].raw,
|
enc = xmpp_base64_encode(ctx, (unsigned char *)tests[i].raw,
|
||||||
strlen(tests[i].raw));
|
strlen(tests[i].raw));
|
||||||
assert(enc != NULL);
|
assert(enc != NULL);
|
||||||
COMPARE(tests[i].base64, enc);
|
COMPARE(tests[i].base64, enc);
|
||||||
xmpp_free(ctx, enc);
|
xmpp_free(ctx, enc);
|
||||||
|
|
||||||
dec = base64_decode(ctx, tests[i].base64, strlen(tests[i].base64));
|
dec = xmpp_base64_decode_str(ctx, tests[i].base64,
|
||||||
|
strlen(tests[i].base64));
|
||||||
assert(dec != NULL);
|
assert(dec != NULL);
|
||||||
len = (size_t)base64_decoded_len(ctx, tests[i].base64,
|
COMPARE_BUF(tests[i].raw, strlen(tests[i].raw), dec, strlen(dec));
|
||||||
strlen(tests[i].base64));
|
|
||||||
COMPARE_BUF(tests[i].raw, strlen(tests[i].raw), dec, len);
|
|
||||||
xmpp_free(ctx, dec);
|
xmpp_free(ctx, dec);
|
||||||
printf("ok\n");
|
printf("ok\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("Test with binary data: ");
|
||||||
|
enc = xmpp_base64_encode(ctx, bin_data, sizeof(bin_data));
|
||||||
|
assert(enc != NULL);
|
||||||
|
xmpp_base64_decode_bin(ctx, enc, strlen(enc), &udec, &len);
|
||||||
|
assert(udec != NULL);
|
||||||
|
assert(len != 0);
|
||||||
|
assert(len == sizeof(bin_data));
|
||||||
|
COMPARE_BUF(bin_data, sizeof(bin_data), udec, len);
|
||||||
|
xmpp_free(ctx, udec);
|
||||||
|
xmpp_free(ctx, enc);
|
||||||
|
printf("ok\n");
|
||||||
|
|
||||||
xmpp_ctx_free(ctx);
|
xmpp_ctx_free(ctx);
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user