+ return ret;
+}
+
+static void longswap(void *_ptr, size_t len)
+{
+ const union {
+ long one;
+ char little;
+ } is_endian = {
+ 1
+ };
+
+ if (is_endian.little) {
+ size_t i;
+ unsigned char *p = _ptr, c;
+
+ for (i = 0; i < len; i += 4) {
+ c = p[0], p[0] = p[3], p[3] = c;
+ c = p[1], p[1] = p[2], p[2] = c;
+ }
+ }
+}
+
+static int sig_out(BIO *b)
+{
+ BIO_OK_CTX *ctx;
+ EVP_MD_CTX *md;
+ const EVP_MD *digest;
+ int md_size;
+ void *md_data;
+
+ ctx = BIO_get_data(b);
+ md = ctx->md;
+ digest = EVP_MD_CTX_md(md);
+ md_size = EVP_MD_size(digest);
+ md_data = EVP_MD_CTX_md_data(md);
+
+ if (ctx->buf_len + 2 * md_size > OK_BLOCK_SIZE)
+ return 1;
+
+ if (!EVP_DigestInit_ex(md, digest, NULL))
+ goto berr;
+ /*
+ * FIXME: there's absolutely no guarantee this makes any sense at all,
+ * particularly now EVP_MD_CTX has been restructured.
+ */
+ if (RAND_bytes(md_data, md_size) <= 0)
+ goto berr;
+ memcpy(&(ctx->buf[ctx->buf_len]), md_data, md_size);
+ longswap(&(ctx->buf[ctx->buf_len]), md_size);
+ ctx->buf_len += md_size;
+
+ if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
+ goto berr;
+ if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
+ goto berr;
+ ctx->buf_len += md_size;
+ ctx->blockout = 1;
+ ctx->sigio = 0;
+ return 1;
+ berr:
+ BIO_clear_retry_flags(b);
+ return 0;
+}
+
+static int sig_in(BIO *b)
+{
+ BIO_OK_CTX *ctx;
+ EVP_MD_CTX *md;
+ unsigned char tmp[EVP_MAX_MD_SIZE];
+ int ret = 0;
+ const EVP_MD *digest;
+ int md_size;
+ void *md_data;
+
+ ctx = BIO_get_data(b);
+ md = ctx->md;
+ digest = EVP_MD_CTX_md(md);
+ md_size = EVP_MD_size(digest);
+ md_data = EVP_MD_CTX_md_data(md);
+
+ if ((int)(ctx->buf_len - ctx->buf_off) < 2 * md_size)
+ return 1;
+
+ if (!EVP_DigestInit_ex(md, digest, NULL))
+ goto berr;
+ memcpy(md_data, &(ctx->buf[ctx->buf_off]), md_size);
+ longswap(md_data, md_size);
+ ctx->buf_off += md_size;
+
+ if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
+ goto berr;
+ if (!EVP_DigestFinal_ex(md, tmp, NULL))
+ goto berr;
+ ret = memcmp(&(ctx->buf[ctx->buf_off]), tmp, md_size) == 0;
+ ctx->buf_off += md_size;
+ if (ret == 1) {
+ ctx->sigio = 0;
+ if (ctx->buf_len != ctx->buf_off) {
+ memmove(ctx->buf, &(ctx->buf[ctx->buf_off]),
+ ctx->buf_len - ctx->buf_off);
+ }
+ ctx->buf_len -= ctx->buf_off;
+ ctx->buf_off = 0;
+ } else {
+ ctx->cont = 0;
+ }
+ return 1;
+ berr:
+ BIO_clear_retry_flags(b);
+ return 0;
+}
+
+static int block_out(BIO *b)
+{
+ BIO_OK_CTX *ctx;
+ EVP_MD_CTX *md;
+ unsigned long tl;
+ const EVP_MD *digest;
+ int md_size;
+
+ ctx = BIO_get_data(b);
+ md = ctx->md;
+ digest = EVP_MD_CTX_md(md);
+ md_size = EVP_MD_size(digest);
+
+ tl = ctx->buf_len - OK_BLOCK_BLOCK;
+ ctx->buf[0] = (unsigned char)(tl >> 24);
+ ctx->buf[1] = (unsigned char)(tl >> 16);
+ ctx->buf[2] = (unsigned char)(tl >> 8);
+ ctx->buf[3] = (unsigned char)(tl);
+ if (!EVP_DigestUpdate(md,
+ (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
+ goto berr;
+ if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
+ goto berr;
+ ctx->buf_len += md_size;
+ ctx->blockout = 1;
+ return 1;
+ berr:
+ BIO_clear_retry_flags(b);
+ return 0;
+}
+
+static int block_in(BIO *b)
+{
+ BIO_OK_CTX *ctx;
+ EVP_MD_CTX *md;
+ unsigned long tl = 0;
+ unsigned char tmp[EVP_MAX_MD_SIZE];
+ int md_size;
+
+ ctx = BIO_get_data(b);
+ md = ctx->md;
+ md_size = EVP_MD_size(EVP_MD_CTX_md(md));
+
+ assert(sizeof(tl) >= OK_BLOCK_BLOCK); /* always true */
+ tl = ctx->buf[0];
+ tl <<= 8;
+ tl |= ctx->buf[1];
+ tl <<= 8;
+ tl |= ctx->buf[2];
+ tl <<= 8;
+ tl |= ctx->buf[3];
+
+ if (ctx->buf_len < tl + OK_BLOCK_BLOCK + md_size)
+ return 1;
+
+ if (!EVP_DigestUpdate(md,
+ (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
+ goto berr;
+ if (!EVP_DigestFinal_ex(md, tmp, NULL))
+ goto berr;
+ if (memcmp(&(ctx->buf[tl + OK_BLOCK_BLOCK]), tmp, md_size) == 0) {
+ /* there might be parts from next block lurking around ! */
+ ctx->buf_off_save = tl + OK_BLOCK_BLOCK + md_size;
+ ctx->buf_len_save = ctx->buf_len;
+ ctx->buf_off = OK_BLOCK_BLOCK;
+ ctx->buf_len = tl + OK_BLOCK_BLOCK;
+ ctx->blockout = 1;
+ } else {
+ ctx->cont = 0;
+ }
+ return 1;
+ berr:
+ BIO_clear_retry_flags(b);
+ return 0;
+}