int s2n_stuffer_read_base64()

in stuffer/s2n_stuffer_base64.c [75:146]


int s2n_stuffer_read_base64(struct s2n_stuffer *stuffer, struct s2n_stuffer *out)
{
    POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
    POSIX_PRECONDITION(s2n_stuffer_validate(out));
    int bytes_this_round = 3;
    s2n_stack_blob(o, 4, 4);

    do {
        if (s2n_stuffer_data_available(stuffer) < o.size) {
            break;
        }

        POSIX_GUARD(s2n_stuffer_read(stuffer, &o));

        uint8_t value1 = b64_inverse[o.data[0]];
        uint8_t value2 = b64_inverse[o.data[1]];
        uint8_t value3 = b64_inverse[o.data[2]];
        uint8_t value4 = b64_inverse[o.data[3]];

        /* We assume the entire thing is base64 data, thus, terminate cleanly if we encounter a non-base64 character */
        if (value1 == 255) {
            /* Undo the read */
            stuffer->read_cursor -= o.size;
            POSIX_BAIL(S2N_ERR_INVALID_BASE64);
        }

        /* The first two characters can never be '=' and in general
         * everything has to be a valid character.
         */
        POSIX_ENSURE(!(value1 == 64 || value2 == 64 || value2 == 255 || value3 == 255 || value4 == 255), S2N_ERR_INVALID_BASE64);

        if (o.data[2] == '=') {
            /* If there is only one output byte, then the second value
             * should have none of its bottom four bits set.
             */
            POSIX_ENSURE(!(o.data[3] != '=' || value2 & 0x0f), S2N_ERR_INVALID_BASE64);
            bytes_this_round = 1;
            value3 = 0;
            value4 = 0;
        } else if (o.data[3] == '=') {
            /* The last two bits of the final value should be unset */
            POSIX_ENSURE(!(value3 & 0x03), S2N_ERR_INVALID_BASE64);

            bytes_this_round = 2;
            value4 = 0;
        }

        /* Advance by bytes_this_round, and then fill in the data */
        POSIX_GUARD(s2n_stuffer_skip_write(out, bytes_this_round));
        uint8_t *ptr = out->blob.data + out->write_cursor - bytes_this_round;

        /* value1 maps to the first 6 bits of the first data byte */
        /* value2's top two bits are the rest */
        *ptr = ((value1 << 2) & 0xfc) | ((value2 >> 4) & 0x03);

        if (bytes_this_round > 1) {
            /* Put the next four bits in the second data byte */
            /* Put the next four bits in the third data byte */
            ptr++;
            *ptr = ((value2 << 4) & 0xf0) | ((value3 >> 2) & 0x0f);
        }

        if (bytes_this_round > 2) {
            /* Put the next two bits in the third data byte */
            /* Put the next six bits in the fourth data byte */
            ptr++;
            *ptr = ((value3 << 6) & 0xc0) | (value4 & 0x3f);
        }
    } while (bytes_this_round == 3);

    return S2N_SUCCESS;
}