- djm@cvs.openbsd.org 2014/04/30 05:29:56
     [bufaux.c bufbn.c bufec.c buffer.c buffer.h sshbuf-getput-basic.c]
     [sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c sshbuf.h ssherr.c]
     [ssherr.h]
     New buffer API; the first installment of the conversion/replacement
     of OpenSSH's internals to make them usable as a standalone library.

     This includes a set of wrappers to make it compatible with the
     existing buffer API so replacement can occur incrementally.

     With and ok markus@

     Thanks also to Ben Hawkes, David Tomaschik, Ivan Fratric, Matthew
     Dempsky and Ron Bowes for a detailed review.
diff --git a/ChangeLog b/ChangeLog
index 50c83e6..5ffe464 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -52,6 +52,20 @@
      Don't attempt to append a nul quote char to the filename.  Should prevent
      fatal'ing with "el_insertstr failed" when there's a single quote char
      somewhere in the string.  bz#2238, ok markus@
+   - djm@cvs.openbsd.org 2014/04/30 05:29:56
+     [bufaux.c bufbn.c bufec.c buffer.c buffer.h sshbuf-getput-basic.c]
+     [sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c sshbuf.h ssherr.c]
+     [ssherr.h]
+     New buffer API; the first installment of the conversion/replacement
+     of OpenSSH's internals to make them usable as a standalone library.
+     
+     This includes a set of wrappers to make it compatible with the
+     existing buffer API so replacement can occur incrementally.
+     
+     With and ok markus@
+     
+     Thanks also to Ben Hawkes, David Tomaschik, Ivan Fratric, Matthew
+     Dempsky and Ron Bowes for a detailed review.
 
 20140430
  - (dtucker) [defines.h] Define __GNUC_PREREQ__ macro if we don't already
diff --git a/bufaux.c b/bufaux.c
index 320bc2c..aa9b892 100644
--- a/bufaux.c
+++ b/bufaux.c
@@ -1,68 +1,38 @@
-/* $OpenBSD: bufaux.c,v 1.59 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: bufaux.c,v 1.60 2014/04/30 05:29:56 djm Exp $ */
 /*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * Auxiliary functions for storing and retrieving various data types to/from
- * Buffers.
+ * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
  *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose.  Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- *
- * SSH2 packet format added by Markus Friedl
- * Copyright (c) 2000 Markus Friedl.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "includes.h"
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
 
 #include <sys/types.h>
 
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
-
-#include "xmalloc.h"
 #include "buffer.h"
 #include "log.h"
-#include "misc.h"
-
-/*
- * Returns integers from the buffer (msb first).
- */
+#include "ssherr.h"
 
 int
-buffer_get_short_ret(u_short *ret, Buffer *buffer)
+buffer_get_short_ret(u_short *v, Buffer *buffer)
 {
-	u_char buf[2];
+	int ret;
 
-	if (buffer_get_ret(buffer, (char *) buf, 2) == -1)
-		return (-1);
-	*ret = get_u16(buf);
-	return (0);
+	if ((ret = sshbuf_get_u16(buffer, v)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return -1;
+	}
+	return 0;
 }
 
 u_short
@@ -71,21 +41,21 @@
 	u_short ret;
 
 	if (buffer_get_short_ret(&ret, buffer) == -1)
-		fatal("buffer_get_short: buffer error");
+		fatal("%s: buffer error", __func__);
 
 	return (ret);
 }
 
 int
-buffer_get_int_ret(u_int *ret, Buffer *buffer)
+buffer_get_int_ret(u_int *v, Buffer *buffer)
 {
-	u_char buf[4];
+	int ret;
 
-	if (buffer_get_ret(buffer, (char *) buf, 4) == -1)
-		return (-1);
-	if (ret != NULL)
-		*ret = get_u32(buf);
-	return (0);
+	if ((ret = sshbuf_get_u32(buffer, v)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return -1;
+	}
+	return 0;
 }
 
 u_int
@@ -94,21 +64,21 @@
 	u_int ret;
 
 	if (buffer_get_int_ret(&ret, buffer) == -1)
-		fatal("buffer_get_int: buffer error");
+		fatal("%s: buffer error", __func__);
 
 	return (ret);
 }
 
 int
-buffer_get_int64_ret(u_int64_t *ret, Buffer *buffer)
+buffer_get_int64_ret(u_int64_t *v, Buffer *buffer)
 {
-	u_char buf[8];
+	int ret;
 
-	if (buffer_get_ret(buffer, (char *) buf, 8) == -1)
-		return (-1);
-	if (ret != NULL)
-		*ret = get_u64(buf);
-	return (0);
+	if ((ret = sshbuf_get_u64(buffer, v)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return -1;
+	}
+	return 0;
 }
 
 u_int64_t
@@ -117,78 +87,52 @@
 	u_int64_t ret;
 
 	if (buffer_get_int64_ret(&ret, buffer) == -1)
-		fatal("buffer_get_int: buffer error");
+		fatal("%s: buffer error", __func__);
 
 	return (ret);
 }
 
-/*
- * Stores integers in the buffer, msb first.
- */
 void
 buffer_put_short(Buffer *buffer, u_short value)
 {
-	char buf[2];
+	int ret;
 
-	put_u16(buf, value);
-	buffer_append(buffer, buf, 2);
+	if ((ret = sshbuf_put_u16(buffer, value)) != 0)
+		fatal("%s: %s", __func__, ssh_err(ret));
 }
 
 void
 buffer_put_int(Buffer *buffer, u_int value)
 {
-	char buf[4];
+	int ret;
 
-	put_u32(buf, value);
-	buffer_append(buffer, buf, 4);
+	if ((ret = sshbuf_put_u32(buffer, value)) != 0)
+		fatal("%s: %s", __func__, ssh_err(ret));
 }
 
 void
 buffer_put_int64(Buffer *buffer, u_int64_t value)
 {
-	char buf[8];
+	int ret;
 
-	put_u64(buf, value);
-	buffer_append(buffer, buf, 8);
+	if ((ret = sshbuf_put_u64(buffer, value)) != 0)
+		fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-/*
- * Returns an arbitrary binary string from the buffer.  The string cannot
- * be longer than 256k.  The returned value points to memory allocated
- * with xmalloc; it is the responsibility of the calling function to free
- * the data.  If length_ptr is non-NULL, the length of the returned data
- * will be stored there.  A null character will be automatically appended
- * to the returned string, and is not counted in length.
- */
 void *
 buffer_get_string_ret(Buffer *buffer, u_int *length_ptr)
 {
+	size_t len;
+	int ret;
 	u_char *value;
-	u_int len;
 
-	/* Get the length. */
-	if (buffer_get_int_ret(&len, buffer) != 0) {
-		error("buffer_get_string_ret: cannot extract length");
-		return (NULL);
+	if ((ret = sshbuf_get_string(buffer, &value, &len)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return NULL;
 	}
-	if (len > 256 * 1024) {
-		error("buffer_get_string_ret: bad string length %u", len);
-		return (NULL);
-	}
-	/* Allocate space for the string.  Add one byte for a null character. */
-	value = xmalloc(len + 1);
-	/* Get the string. */
-	if (buffer_get_ret(buffer, value, len) == -1) {
-		error("buffer_get_string_ret: buffer_get failed");
-		free(value);
-		return (NULL);
-	}
-	/* Append a null character to make processing easier. */
-	value[len] = '\0';
-	/* Optionally return the length of the string. */
-	if (length_ptr)
-		*length_ptr = len;
-	return (value);
+	if (length_ptr != NULL)
+		*length_ptr = len;  /* Safe: sshbuf never stores len > 2^31 */
+	return value;
 }
 
 void *
@@ -197,31 +141,24 @@
 	void *ret;
 
 	if ((ret = buffer_get_string_ret(buffer, length_ptr)) == NULL)
-		fatal("buffer_get_string: buffer error");
+		fatal("%s: buffer error", __func__);
 	return (ret);
 }
 
 char *
 buffer_get_cstring_ret(Buffer *buffer, u_int *length_ptr)
 {
-	u_int length;
-	char *cp, *ret = buffer_get_string_ret(buffer, &length);
+	size_t len;
+	int ret;
+	char *value;
 
-	if (ret == NULL)
+	if ((ret = sshbuf_get_cstring(buffer, &value, &len)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
 		return NULL;
-	if ((cp = memchr(ret, '\0', length)) != NULL) {
-		/* XXX allow \0 at end-of-string for a while, remove later */
-		if (cp == ret + length - 1)
-			error("buffer_get_cstring_ret: string contains \\0");
-		else {
-			explicit_bzero(ret, length);
-			free(ret);
-			return NULL;
-		}
 	}
 	if (length_ptr != NULL)
-		*length_ptr = length;
-	return ret;
+		*length_ptr = len;  /* Safe: sshbuf never stores len > 2^31 */
+	return value;
 }
 
 char *
@@ -230,27 +167,24 @@
 	char *ret;
 
 	if ((ret = buffer_get_cstring_ret(buffer, length_ptr)) == NULL)
-		fatal("buffer_get_cstring: buffer error");
+		fatal("%s: buffer error", __func__);
 	return ret;
 }
 
 const void *
 buffer_get_string_ptr_ret(Buffer *buffer, u_int *length_ptr)
 {
-	void *ptr;
-	u_int len;
+	size_t len;
+	int ret;
+	const u_char *value;
 
-	if (buffer_get_int_ret(&len, buffer) != 0)
-		return NULL;
-	if (len > 256 * 1024) {
-		error("buffer_get_string_ptr: bad string length %u", len);
+	if ((ret = sshbuf_get_string_direct(buffer, &value, &len)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
 		return NULL;
 	}
-	ptr = buffer_ptr(buffer);
-	buffer_consume(buffer, len);
-	if (length_ptr)
-		*length_ptr = len;
-	return (ptr);
+	if (length_ptr != NULL)
+		*length_ptr = len;  /* Safe: sshbuf never stores len > 2^31 */
+	return value;
 }
 
 const void *
@@ -259,133 +193,65 @@
 	const void *ret;
 
 	if ((ret = buffer_get_string_ptr_ret(buffer, length_ptr)) == NULL)
-		fatal("buffer_get_string_ptr: buffer error");
+		fatal("%s: buffer error", __func__);
 	return (ret);
 }
 
-/*
- * Stores and arbitrary binary string in the buffer.
- */
 void
 buffer_put_string(Buffer *buffer, const void *buf, u_int len)
 {
-	buffer_put_int(buffer, len);
-	buffer_append(buffer, buf, len);
+	int ret;
+
+	if ((ret = sshbuf_put_string(buffer, buf, len)) != 0)
+		fatal("%s: %s", __func__, ssh_err(ret));
 }
+
 void
 buffer_put_cstring(Buffer *buffer, const char *s)
 {
-	if (s == NULL)
-		fatal("buffer_put_cstring: s == NULL");
-	buffer_put_string(buffer, s, strlen(s));
+	int ret;
+
+	if ((ret = sshbuf_put_cstring(buffer, s)) != 0)
+		fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-/*
- * Returns a character from the buffer (0 - 255).
- */
 int
-buffer_get_char_ret(u_char *ret, Buffer *buffer)
+buffer_get_char_ret(char *v, Buffer *buffer)
 {
-	if (buffer_get_ret(buffer, ret, 1) == -1) {
-		error("buffer_get_char_ret: buffer_get_ret failed");
-		return (-1);
+	int ret;
+
+	if ((ret = sshbuf_get_u8(buffer, (u_char *)v)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return -1;
 	}
-	return (0);
+	return 0;
 }
 
 int
 buffer_get_char(Buffer *buffer)
 {
-	u_char ch;
+	char ch;
 
 	if (buffer_get_char_ret(&ch, buffer) == -1)
-		fatal("buffer_get_char: buffer error");
-	return ch;
+		fatal("%s: buffer error", __func__);
+	return (u_char) ch;
 }
 
-/*
- * Stores a character in the buffer.
- */
 void
 buffer_put_char(Buffer *buffer, int value)
 {
-	char ch = value;
+	int ret;
 
-	buffer_append(buffer, &ch, 1);
+	if ((ret = sshbuf_put_u8(buffer, value)) != 0)
+		fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-/* Pseudo bignum functions */
-
-void *
-buffer_get_bignum2_as_string_ret(Buffer *buffer, u_int *length_ptr)
-{
-	u_int len;
-	u_char *bin, *p, *ret;
-
-	if ((p = bin = buffer_get_string_ret(buffer, &len)) == NULL) {
-		error("%s: invalid bignum", __func__);
-		return NULL;
-	}
-
-	if (len > 0 && (bin[0] & 0x80)) {
-		error("%s: negative numbers not supported", __func__);
-		free(bin);
-		return NULL;
-	}
-	if (len > 8 * 1024) {
-		error("%s: cannot handle BN of size %d", __func__, len);
-		free(bin);
-		return NULL;
-	}
-	/* Skip zero prefix on numbers with the MSB set */
-	if (len > 1 && bin[0] == 0x00 && (bin[1] & 0x80) != 0) {
-		p++;
-		len--;
-	}
-	ret = xmalloc(len);
-	memcpy(ret, p, len);
-	explicit_bzero(p, len);
-	free(bin);
-	return ret;
-}
-
-void *
-buffer_get_bignum2_as_string(Buffer *buffer, u_int *l)
-{
-	void *ret = buffer_get_bignum2_as_string_ret(buffer, l);
-
-	if (ret == NULL)
-		fatal("%s: buffer error", __func__);
-	return ret;
-}
-
-/*
- * Stores a string using the bignum encoding rules (\0 pad if MSB set).
- */
 void
 buffer_put_bignum2_from_string(Buffer *buffer, const u_char *s, u_int l)
 {
-	u_char *buf, *p;
-	int pad = 0;
+	int ret;
 
-	if (l > 8 * 1024)
-		fatal("%s: length %u too long", __func__, l);
-	/* Skip leading zero bytes */
-	for (; l > 0 && *s == 0; l--, s++)
-		;
-	p = buf = xmalloc(l + 1);
-	/*
-	 * If most significant bit is set then prepend a zero byte to
-	 * avoid interpretation as a negative number.
-	 */
-	if (l > 0 && (s[0] & 0x80) != 0) {
-		*p++ = '\0';
-		pad = 1;
-	}
-	memcpy(p, s, l);
-	buffer_put_string(buffer, buf, l + pad);
-	explicit_bzero(buf, l + pad);
-	free(buf);
+	if ((ret = sshbuf_put_bignum2_bytes(buffer, s, l)) != 0)
+		fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-
diff --git a/bufbn.c b/bufbn.c
index 1d2e012..0a519ed 100644
--- a/bufbn.c
+++ b/bufbn.c
@@ -1,229 +1,101 @@
-/* $OpenBSD: bufbn.c,v 1.11 2014/02/27 08:25:09 djm Exp $*/
+/* $OpenBSD: bufbn.c,v 1.12 2014/04/30 05:29:56 djm Exp $ */
+
 /*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * Auxiliary functions for storing and retrieving various data types to/from
- * Buffers.
+ * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
  *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose.  Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- *
- * SSH2 packet format added by Markus Friedl
- * Copyright (c) 2000 Markus Friedl.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "includes.h"
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
 
 #include <sys/types.h>
 
-#include <openssl/bn.h>
-
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
-
-#include "xmalloc.h"
 #include "buffer.h"
 #include "log.h"
-#include "misc.h"
+#include "ssherr.h"
 
-/*
- * Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed
- * by (bits+7)/8 bytes of binary data, msb first.
- */
 int
 buffer_put_bignum_ret(Buffer *buffer, const BIGNUM *value)
 {
-	int bits = BN_num_bits(value);
-	int bin_size = (bits + 7) / 8;
-	u_char *buf = xmalloc(bin_size);
-	int oi;
-	char msg[2];
+	int ret;
 
-	/* Get the value of in binary */
-	oi = BN_bn2bin(value, buf);
-	if (oi != bin_size) {
-		error("buffer_put_bignum_ret: BN_bn2bin() failed: oi %d != bin_size %d",
-		    oi, bin_size);
-		free(buf);
-		return (-1);
+	if ((ret = sshbuf_put_bignum1(buffer, value)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return -1;
 	}
-
-	/* Store the number of bits in the buffer in two bytes, msb first. */
-	put_u16(msg, bits);
-	buffer_append(buffer, msg, 2);
-	/* Store the binary data. */
-	buffer_append(buffer, buf, oi);
-
-	explicit_bzero(buf, bin_size);
-	free(buf);
-
-	return (0);
+	return 0;
 }
 
 void
 buffer_put_bignum(Buffer *buffer, const BIGNUM *value)
 {
 	if (buffer_put_bignum_ret(buffer, value) == -1)
-		fatal("buffer_put_bignum: buffer error");
+		fatal("%s: buffer error", __func__);
 }
 
-/*
- * Retrieves a BIGNUM from the buffer.
- */
 int
 buffer_get_bignum_ret(Buffer *buffer, BIGNUM *value)
 {
-	u_int bits, bytes;
-	u_char buf[2], *bin;
+	int ret;
 
-	/* Get the number of bits. */
-	if (buffer_get_ret(buffer, (char *) buf, 2) == -1) {
-		error("buffer_get_bignum_ret: invalid length");
-		return (-1);
+	if ((ret = sshbuf_get_bignum1(buffer, value)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return -1;
 	}
-	bits = get_u16(buf);
-	if (bits > 65535-7) {
-		error("buffer_get_bignum_ret: cannot handle BN of size %d",
-		    bits);
-		return (-1);
-	}
-	/* Compute the number of binary bytes that follow. */
-	bytes = (bits + 7) / 8;
-	if (bytes > 8 * 1024) {
-		error("buffer_get_bignum_ret: cannot handle BN of size %d", bytes);
-		return (-1);
-	}
-	if (buffer_len(buffer) < bytes) {
-		error("buffer_get_bignum_ret: input buffer too small");
-		return (-1);
-	}
-	bin = buffer_ptr(buffer);
-	if (BN_bin2bn(bin, bytes, value) == NULL) {
-		error("buffer_get_bignum_ret: BN_bin2bn failed");
-		return (-1);
-	}
-	if (buffer_consume_ret(buffer, bytes) == -1) {
-		error("buffer_get_bignum_ret: buffer_consume failed");
-		return (-1);
-	}
-	return (0);
+	return 0;
 }
 
 void
 buffer_get_bignum(Buffer *buffer, BIGNUM *value)
 {
 	if (buffer_get_bignum_ret(buffer, value) == -1)
-		fatal("buffer_get_bignum: buffer error");
+		fatal("%s: buffer error", __func__);
 }
 
-/*
- * Stores a BIGNUM in the buffer in SSH2 format.
- */
 int
 buffer_put_bignum2_ret(Buffer *buffer, const BIGNUM *value)
 {
-	u_int bytes;
-	u_char *buf;
-	int oi;
-	u_int hasnohigh = 0;
+	int ret;
 
-	if (BN_is_zero(value)) {
-		buffer_put_int(buffer, 0);
-		return 0;
+	if ((ret = sshbuf_put_bignum2(buffer, value)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return -1;
 	}
-	if (value->neg) {
-		error("buffer_put_bignum2_ret: negative numbers not supported");
-		return (-1);
-	}
-	bytes = BN_num_bytes(value) + 1; /* extra padding byte */
-	if (bytes < 2) {
-		error("buffer_put_bignum2_ret: BN too small");
-		return (-1);
-	}
-	buf = xmalloc(bytes);
-	buf[0] = 0x00;
-	/* Get the value of in binary */
-	oi = BN_bn2bin(value, buf+1);
-	if (oi < 0 || (u_int)oi != bytes - 1) {
-		error("buffer_put_bignum2_ret: BN_bn2bin() failed: "
-		    "oi %d != bin_size %d", oi, bytes);
-		free(buf);
-		return (-1);
-	}
-	hasnohigh = (buf[1] & 0x80) ? 0 : 1;
-	buffer_put_string(buffer, buf+hasnohigh, bytes-hasnohigh);
-	explicit_bzero(buf, bytes);
-	free(buf);
-	return (0);
+	return 0;
 }
 
 void
 buffer_put_bignum2(Buffer *buffer, const BIGNUM *value)
 {
 	if (buffer_put_bignum2_ret(buffer, value) == -1)
-		fatal("buffer_put_bignum2: buffer error");
+		fatal("%s: buffer error", __func__);
 }
 
 int
 buffer_get_bignum2_ret(Buffer *buffer, BIGNUM *value)
 {
-	u_int len;
-	u_char *bin;
+	int ret;
 
-	if ((bin = buffer_get_string_ret(buffer, &len)) == NULL) {
-		error("buffer_get_bignum2_ret: invalid bignum");
-		return (-1);
+	if ((ret = sshbuf_get_bignum2(buffer, value)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return -1;
 	}
-
-	if (len > 0 && (bin[0] & 0x80)) {
-		error("buffer_get_bignum2_ret: negative numbers not supported");
-		free(bin);
-		return (-1);
-	}
-	if (len > 8 * 1024) {
-		error("buffer_get_bignum2_ret: cannot handle BN of size %d",
-		    len);
-		free(bin);
-		return (-1);
-	}
-	if (BN_bin2bn(bin, len, value) == NULL) {
-		error("buffer_get_bignum2_ret: BN_bin2bn failed");
-		free(bin);
-		return (-1);
-	}
-	free(bin);
-	return (0);
+	return 0;
 }
 
 void
 buffer_get_bignum2(Buffer *buffer, BIGNUM *value)
 {
 	if (buffer_get_bignum2_ret(buffer, value) == -1)
-		fatal("buffer_get_bignum2: buffer error");
+		fatal("%s: buffer error", __func__);
 }
diff --git a/bufec.c b/bufec.c
index 89482b9..b33ede3 100644
--- a/bufec.c
+++ b/bufec.c
@@ -1,6 +1,7 @@
-/* $OpenBSD: bufec.c,v 1.3 2014/01/31 16:39:19 tedu Exp $ */
+/* $OpenBSD: bufec.c,v 1.4 2014/04/30 05:29:56 djm Exp $ */
+
 /*
- * Copyright (c) 2010 Damien Miller <djm@mindrot.org>
+ * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -15,73 +16,25 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "includes.h"
-
-#ifdef OPENSSL_HAS_ECC
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
 
 #include <sys/types.h>
 
-#include <openssl/bn.h>
-#include <openssl/ec.h>
-
-#include <string.h>
-#include <stdarg.h>
-
-#include "xmalloc.h"
 #include "buffer.h"
 #include "log.h"
-#include "misc.h"
+#include "ssherr.h"
 
-/*
- * Maximum supported EC GFp field length is 528 bits. SEC1 uncompressed
- * encoding represents this as two bitstring points that should each
- * be no longer than the field length, SEC1 specifies a 1 byte
- * point type header.
- * Being paranoid here may insulate us to parsing problems in
- * EC_POINT_oct2point.
- */
-#define BUFFER_MAX_ECPOINT_LEN ((528*2 / 8) + 1)
-
-/*
- * Append an EC_POINT to the buffer as a string containing a SEC1 encoded
- * uncompressed point. Fortunately OpenSSL handles the gory details for us.
- */
 int
 buffer_put_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
     const EC_POINT *point)
 {
-	u_char *buf = NULL;
-	size_t len;
-	BN_CTX *bnctx;
-	int ret = -1;
+	int ret;
 
-	/* Determine length */
-	if ((bnctx = BN_CTX_new()) == NULL)
-		fatal("%s: BN_CTX_new failed", __func__);
-	len = EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
-	    NULL, 0, bnctx);
-	if (len > BUFFER_MAX_ECPOINT_LEN) {
-		error("%s: giant EC point: len = %lu (max %u)",
-		    __func__, (u_long)len, BUFFER_MAX_ECPOINT_LEN);
-		goto out;
+	if ((ret = sshbuf_put_ec(buffer, point, curve)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return -1;
 	}
-	/* Convert */
-	buf = xmalloc(len);
-	if (EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
-	    buf, len, bnctx) != len) {
-		error("%s: EC_POINT_point2oct length mismatch", __func__);
-		goto out;
-	}
-	/* Append */
-	buffer_put_string(buffer, buf, len);
-	ret = 0;
- out:
-	if (buf != NULL) {
-		explicit_bzero(buf, len);
-		free(buf);
-	}
-	BN_CTX_free(bnctx);
-	return ret;
+	return 0;
 }
 
 void
@@ -96,43 +49,13 @@
 buffer_get_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
     EC_POINT *point)
 {
-	u_char *buf;
-	u_int len;
-	BN_CTX *bnctx;
-	int ret = -1;
+	int ret;
 
-	if ((buf = buffer_get_string_ret(buffer, &len)) == NULL) {
-		error("%s: invalid point", __func__);
+	if ((ret = sshbuf_get_ec(buffer, point, curve)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
 		return -1;
 	}
-	if ((bnctx = BN_CTX_new()) == NULL)
-		fatal("%s: BN_CTX_new failed", __func__);
-	if (len > BUFFER_MAX_ECPOINT_LEN) {
-		error("%s: EC_POINT too long: %u > max %u", __func__,
-		    len, BUFFER_MAX_ECPOINT_LEN);
-		goto out;
-	}
-	if (len == 0) {
-		error("%s: EC_POINT buffer is empty", __func__);
-		goto out;
-	}
-	if (buf[0] != POINT_CONVERSION_UNCOMPRESSED) {
-		error("%s: EC_POINT is in an incorrect form: "
-		    "0x%02x (want 0x%02x)", __func__, buf[0],
-		    POINT_CONVERSION_UNCOMPRESSED);
-		goto out;
-	}
-	if (EC_POINT_oct2point(curve, point, buf, len, bnctx) != 1) {
-		error("buffer_get_bignum2_ret: BN_bin2bn failed");
-		goto out;
-	}
-	/* EC_POINT_oct2point verifies that the point is on the curve for us */
-	ret = 0;
- out:
-	BN_CTX_free(bnctx);
-	explicit_bzero(buf, len);
-	free(buf);
-	return ret;
+	return 0;
 }
 
 void
@@ -143,4 +66,4 @@
 		fatal("%s: buffer error", __func__);
 }
 
-#endif /* OPENSSL_HAS_ECC */
+
diff --git a/buffer.c b/buffer.c
index d240f67..07bc186 100644
--- a/buffer.c
+++ b/buffer.c
@@ -1,253 +1,116 @@
-/* $OpenBSD: buffer.c,v 1.35 2014/02/02 03:44:31 djm Exp $ */
+/* $OpenBSD: buffer.c,v 1.36 2014/04/30 05:29:56 djm Exp $ */
+
 /*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * Functions for manipulating fifo buffers (that can grow if needed).
+ * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
  *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose.  Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "includes.h"
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
 
-#include <sys/param.h>
+#include <sys/types.h>
 
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
-
-#include "xmalloc.h"
 #include "buffer.h"
 #include "log.h"
-
-#define	BUFFER_MAX_CHUNK	0x100000
-#define	BUFFER_MAX_LEN		0xa00000
-#define	BUFFER_ALLOCSZ		0x008000
-
-/* Initializes the buffer structure. */
-
-void
-buffer_init(Buffer *buffer)
-{
-	const u_int len = 4096;
-
-	buffer->alloc = 0;
-	buffer->buf = xmalloc(len);
-	buffer->alloc = len;
-	buffer->offset = 0;
-	buffer->end = 0;
-}
-
-/* Frees any memory used for the buffer. */
-
-void
-buffer_free(Buffer *buffer)
-{
-	if (buffer->alloc > 0) {
-		explicit_bzero(buffer->buf, buffer->alloc);
-		buffer->alloc = 0;
-		free(buffer->buf);
-	}
-}
-
-/*
- * Clears any data from the buffer, making it empty.  This does not actually
- * zero the memory.
- */
-
-void
-buffer_clear(Buffer *buffer)
-{
-	buffer->offset = 0;
-	buffer->end = 0;
-}
-
-/* Appends data to the buffer, expanding it if necessary. */
+#include "ssherr.h"
 
 void
 buffer_append(Buffer *buffer, const void *data, u_int len)
 {
-	void *p;
-	p = buffer_append_space(buffer, len);
-	memcpy(p, data, len);
-}
+	int ret;
 
-static int
-buffer_compact(Buffer *buffer)
-{
-	/*
-	 * If the buffer is quite empty, but all data is at the end, move the
-	 * data to the beginning.
-	 */
-	if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) {
-		memmove(buffer->buf, buffer->buf + buffer->offset,
-			buffer->end - buffer->offset);
-		buffer->end -= buffer->offset;
-		buffer->offset = 0;
-		return (1);
-	}
-	return (0);
+	if ((ret = sshbuf_put(buffer, data, len)) != 0)
+		fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-/*
- * Appends space to the buffer, expanding the buffer if necessary. This does
- * not actually copy the data into the buffer, but instead returns a pointer
- * to the allocated region.
- */
-
 void *
 buffer_append_space(Buffer *buffer, u_int len)
 {
-	u_int newlen;
-	void *p;
+	int ret;
+	u_char *p;
 
-	if (len > BUFFER_MAX_CHUNK)
-		fatal("buffer_append_space: len %u not supported", len);
-
-	/* If the buffer is empty, start using it from the beginning. */
-	if (buffer->offset == buffer->end) {
-		buffer->offset = 0;
-		buffer->end = 0;
-	}
-restart:
-	/* If there is enough space to store all data, store it now. */
-	if (buffer->end + len < buffer->alloc) {
-		p = buffer->buf + buffer->end;
-		buffer->end += len;
-		return p;
-	}
-
-	/* Compact data back to the start of the buffer if necessary */
-	if (buffer_compact(buffer))
-		goto restart;
-
-	/* Increase the size of the buffer and retry. */
-	newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ);
-	if (newlen > BUFFER_MAX_LEN)
-		fatal("buffer_append_space: alloc %u not supported",
-		    newlen);
-	buffer->buf = xrealloc(buffer->buf, 1, newlen);
-	buffer->alloc = newlen;
-	goto restart;
-	/* NOTREACHED */
+	if ((ret = sshbuf_reserve(buffer, len, &p)) != 0)
+		fatal("%s: %s", __func__, ssh_err(ret));
+	return p;
 }
 
-/*
- * Check whether an allocation of 'len' will fit in the buffer
- * This must follow the same math as buffer_append_space
- */
 int
 buffer_check_alloc(Buffer *buffer, u_int len)
 {
-	if (buffer->offset == buffer->end) {
-		buffer->offset = 0;
-		buffer->end = 0;
-	}
- restart:
-	if (buffer->end + len < buffer->alloc)
-		return (1);
-	if (buffer_compact(buffer))
-		goto restart;
-	if (roundup(buffer->alloc + len, BUFFER_ALLOCSZ) <= BUFFER_MAX_LEN)
-		return (1);
-	return (0);
+	int ret = sshbuf_check_reserve(buffer, len);
+
+	if (ret == 0)
+		return 1;
+	if (ret == SSH_ERR_NO_BUFFER_SPACE)
+		return 0;
+	fatal("%s: %s", __func__, ssh_err(ret));
 }
 
-/* Returns the number of bytes of data in the buffer. */
-
-u_int
-buffer_len(const Buffer *buffer)
-{
-	return buffer->end - buffer->offset;
-}
-
-/* Gets data from the beginning of the buffer. */
-
 int
 buffer_get_ret(Buffer *buffer, void *buf, u_int len)
 {
-	if (len > buffer->end - buffer->offset) {
-		error("buffer_get_ret: trying to get more bytes %d than in buffer %d",
-		    len, buffer->end - buffer->offset);
-		return (-1);
+	int ret;
+
+	if ((ret = sshbuf_get(buffer, buf, len)) != 0) {
+		error("%s: %s", __func__, ssh_err(ret));
+		return -1;
 	}
-	memcpy(buf, buffer->buf + buffer->offset, len);
-	buffer->offset += len;
-	return (0);
+	return 0;
 }
 
 void
 buffer_get(Buffer *buffer, void *buf, u_int len)
 {
 	if (buffer_get_ret(buffer, buf, len) == -1)
-		fatal("buffer_get: buffer error");
+		fatal("%s: buffer error", __func__);
 }
 
-/* Consumes the given number of bytes from the beginning of the buffer. */
-
 int
 buffer_consume_ret(Buffer *buffer, u_int bytes)
 {
-	if (bytes > buffer->end - buffer->offset) {
-		error("buffer_consume_ret: trying to get more bytes than in buffer");
-		return (-1);
-	}
-	buffer->offset += bytes;
-	return (0);
+	int ret = sshbuf_consume(buffer, bytes);
+
+	if (ret == 0)
+		return 0;
+	if (ret == SSH_ERR_MESSAGE_INCOMPLETE)
+		return -1;
+	fatal("%s: %s", __func__, ssh_err(ret));
 }
 
 void
 buffer_consume(Buffer *buffer, u_int bytes)
 {
 	if (buffer_consume_ret(buffer, bytes) == -1)
-		fatal("buffer_consume: buffer error");
+		fatal("%s: buffer error", __func__);
 }
 
-/* Consumes the given number of bytes from the end of the buffer. */
-
 int
 buffer_consume_end_ret(Buffer *buffer, u_int bytes)
 {
-	if (bytes > buffer->end - buffer->offset)
-		return (-1);
-	buffer->end -= bytes;
-	return (0);
+	int ret = sshbuf_consume_end(buffer, bytes);
+
+	if (ret == 0)
+		return 0;
+	if (ret == SSH_ERR_MESSAGE_INCOMPLETE)
+		return -1;
+	fatal("%s: %s", __func__, ssh_err(ret));
 }
 
 void
 buffer_consume_end(Buffer *buffer, u_int bytes)
 {
 	if (buffer_consume_end_ret(buffer, bytes) == -1)
-		fatal("buffer_consume_end: trying to get more bytes than in buffer");
+		fatal("%s: buffer error", __func__);
 }
 
-/* Returns a pointer to the first used byte in the buffer. */
 
-void *
-buffer_ptr(const Buffer *buffer)
-{
-	return buffer->buf + buffer->offset;
-}
-
-/* Dumps the contents of the buffer to stderr. */
-
-void
-buffer_dump(const Buffer *buffer)
-{
-	u_int i;
-	u_char *ucp = buffer->buf;
-
-	for (i = buffer->offset; i < buffer->end; i++) {
-		fprintf(stderr, "%02x", ucp[i]);
-		if ((i-buffer->offset)%16==15)
-			fprintf(stderr, "\r\n");
-		else if ((i-buffer->offset)%2==1)
-			fprintf(stderr, " ");
-	}
-	fprintf(stderr, "\r\n");
-}
diff --git a/buffer.h b/buffer.h
index 74a7b81..9d853ed 100644
--- a/buffer.h
+++ b/buffer.h
@@ -1,57 +1,58 @@
-/* $OpenBSD: buffer.h,v 1.24 2014/04/28 03:09:18 djm Exp $ */
+/* $OpenBSD: buffer.h,v 1.25 2014/04/30 05:29:56 djm Exp $ */
 
 /*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * Code for manipulating FIFO buffers.
+ * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
  *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose.  Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */
+
 #ifndef BUFFER_H
 #define BUFFER_H
 
-typedef struct {
-	u_char	*buf;		/* Buffer for data. */
-	u_int	 alloc;		/* Number of bytes allocated for data. */
-	u_int	 offset;	/* Offset of first byte containing data. */
-	u_int	 end;		/* Offset of last byte containing data. */
-}       Buffer;
+#include "sshbuf.h"
 
-void	 buffer_init(Buffer *);
-void	 buffer_clear(Buffer *);
-void	 buffer_free(Buffer *);
+typedef struct sshbuf Buffer;
 
-u_int	 buffer_len(const Buffer *);
-void	*buffer_ptr(const Buffer *);
+#define buffer_init(b)		sshbuf_init(b)
+#define buffer_clear(b)		sshbuf_reset(b)
+#define buffer_free(b)		sshbuf_free(b)
+#define buffer_dump(b)		sshbuf_dump(b, stderr)
+
+/* XXX cast is safe: sshbuf never stores more than len 2^31 */
+#define buffer_len(b)		((u_int) sshbuf_len(b))
+#define	buffer_ptr(b)		sshbuf_mutable_ptr(b)
 
 void	 buffer_append(Buffer *, const void *, u_int);
 void	*buffer_append_space(Buffer *, u_int);
-
 int	 buffer_check_alloc(Buffer *, u_int);
-
 void	 buffer_get(Buffer *, void *, u_int);
 
 void	 buffer_consume(Buffer *, u_int);
 void	 buffer_consume_end(Buffer *, u_int);
 
-void     buffer_dump(const Buffer *);
 
 int	 buffer_get_ret(Buffer *, void *, u_int);
 int	 buffer_consume_ret(Buffer *, u_int);
 int	 buffer_consume_end_ret(Buffer *, u_int);
 
 #include <openssl/bn.h>
-
 void    buffer_put_bignum(Buffer *, const BIGNUM *);
 void    buffer_put_bignum2(Buffer *, const BIGNUM *);
 void	buffer_get_bignum(Buffer *, BIGNUM *);
 void	buffer_get_bignum2(Buffer *, BIGNUM *);
+void	buffer_put_bignum2_from_string(Buffer *, const u_char *, u_int);
 
 u_short	buffer_get_short(Buffer *);
 void	buffer_put_short(Buffer *, u_short);
@@ -71,8 +72,7 @@
 char   *buffer_get_cstring(Buffer *, u_int *);
 void	buffer_put_cstring(Buffer *, const char *);
 
-#define buffer_skip_string(b) \
-    do { u_int l = buffer_get_int(b); buffer_consume(b, l); } while (0)
+#define buffer_skip_string(b) (void)buffer_get_string_ptr(b, NULL);
 
 int	buffer_put_bignum_ret(Buffer *, const BIGNUM *);
 int	buffer_get_bignum_ret(Buffer *, BIGNUM *);
@@ -84,19 +84,15 @@
 void	*buffer_get_string_ret(Buffer *, u_int *);
 char	*buffer_get_cstring_ret(Buffer *, u_int *);
 const void *buffer_get_string_ptr_ret(Buffer *, u_int *);
-int	buffer_get_char_ret(u_char *, Buffer *);
-
-void *buffer_get_bignum2_as_string_ret(Buffer *, u_int *);
-void *buffer_get_bignum2_as_string(Buffer *, u_int *);
-void  buffer_put_bignum2_from_string(Buffer *, const u_char *, u_int);
+int	buffer_get_char_ret(char *, Buffer *);
 
 #ifdef OPENSSL_HAS_ECC
 #include <openssl/ec.h>
-
 int	buffer_put_ecpoint_ret(Buffer *, const EC_GROUP *, const EC_POINT *);
 void	buffer_put_ecpoint(Buffer *, const EC_GROUP *, const EC_POINT *);
 int	buffer_get_ecpoint_ret(Buffer *, const EC_GROUP *, EC_POINT *);
 void	buffer_get_ecpoint(Buffer *, const EC_GROUP *, EC_POINT *);
 #endif
 
-#endif				/* BUFFER_H */
+#endif	/* BUFFER_H */
+
diff --git a/sshbuf-getput-basic.c b/sshbuf-getput-basic.c
new file mode 100644
index 0000000..6b16b21
--- /dev/null
+++ b/sshbuf-getput-basic.c
@@ -0,0 +1,421 @@
+/*	$OpenBSD: sshbuf-getput-basic.c,v 1.1 2014/04/30 05:29:56 djm Exp $	*/
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ssherr.h"
+#define SSHBUF_INTERNAL
+#include "sshbuf.h"
+
+int
+sshbuf_get(struct sshbuf *buf, void *v, size_t len)
+{
+	const u_char *p = sshbuf_ptr(buf);
+	int r;
+
+	if ((r = sshbuf_consume(buf, len)) < 0)
+		return r;
+	if (v != NULL)
+		memcpy(v, p, len);
+	return 0;
+}
+
+int
+sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp)
+{
+	const u_char *p = sshbuf_ptr(buf);
+	int r;
+
+	if ((r = sshbuf_consume(buf, 8)) < 0)
+		return r;
+	if (valp != NULL)
+		*valp = PEEK_U64(p);
+	return 0;
+}
+
+int
+sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp)
+{
+	const u_char *p = sshbuf_ptr(buf);
+	int r;
+
+	if ((r = sshbuf_consume(buf, 4)) < 0)
+		return r;
+	if (valp != NULL)
+		*valp = PEEK_U32(p);
+	return 0;
+}
+
+int
+sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp)
+{
+	const u_char *p = sshbuf_ptr(buf);
+	int r;
+
+	if ((r = sshbuf_consume(buf, 2)) < 0)
+		return r;
+	if (valp != NULL)
+		*valp = PEEK_U16(p);
+	return 0;
+}
+
+int
+sshbuf_get_u8(struct sshbuf *buf, u_char *valp)
+{
+	const u_char *p = sshbuf_ptr(buf);
+	int r;
+
+	if ((r = sshbuf_consume(buf, 1)) < 0)
+		return r;
+	if (valp != NULL)
+		*valp = (u_int8_t)*p;
+	return 0;
+}
+
+int
+sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp)
+{
+	const u_char *val;
+	size_t len;
+	int r;
+
+	if (valp != NULL)
+		*valp = NULL;
+	if (lenp != NULL)
+		*lenp = 0;
+	if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0)
+		return r;
+	if (valp != NULL) {
+		if ((*valp = malloc(len + 1)) == NULL) {
+			SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL"));
+			return SSH_ERR_ALLOC_FAIL;
+		}
+		memcpy(*valp, val, len);
+		(*valp)[len] = '\0';
+	}
+	if (lenp != NULL)
+		*lenp = len;
+	return 0;
+}
+
+int
+sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp)
+{
+	size_t len;
+	const u_char *p;
+	int r;
+
+	if (valp != NULL)
+		*valp = NULL;
+	if (lenp != NULL)
+		*lenp = 0;
+	if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0)
+		return r;
+	if (valp != 0)
+		*valp = p;
+	if (lenp != NULL)
+		*lenp = len;
+	if (sshbuf_consume(buf, len + 4) != 0) {
+		/* Shouldn't happen */
+		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+		SSHBUF_ABORT();
+		return SSH_ERR_INTERNAL_ERROR;
+	}
+	return 0;
+}
+
+int
+sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp,
+    size_t *lenp)
+{
+	u_int32_t len;
+	const u_char *p = sshbuf_ptr(buf);
+
+	if (valp != NULL)
+		*valp = NULL;
+	if (lenp != NULL)
+		*lenp = 0;
+	if (sshbuf_len(buf) < 4) {
+		SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE"));
+		return SSH_ERR_MESSAGE_INCOMPLETE;
+	}
+	len = PEEK_U32(p);
+	if (len > SSHBUF_SIZE_MAX - 4) {
+		SSHBUF_DBG(("SSH_ERR_STRING_TOO_LARGE"));
+		return SSH_ERR_STRING_TOO_LARGE;
+	}
+	if (sshbuf_len(buf) - 4 < len) {
+		SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE"));
+		return SSH_ERR_MESSAGE_INCOMPLETE;
+	}
+	if (valp != 0)
+		*valp = p + 4;
+	if (lenp != NULL)
+		*lenp = len;
+	return 0;
+}
+
+int
+sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp)
+{
+	size_t len;
+	const u_char *p, *z;
+	int r;
+
+	if (valp != NULL)
+		*valp = NULL;
+	if (lenp != NULL)
+		*lenp = 0;
+	if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0)
+		return r;
+	/* Allow a \0 only at the end of the string */
+	if (len > 0 &&
+	    (z = memchr(p , '\0', len)) != NULL && z < p + len - 1) {
+		SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT"));
+		return SSH_ERR_INVALID_FORMAT;
+	}
+	if ((r = sshbuf_skip_string(buf)) != 0)
+		return -1;
+	if (valp != NULL) {
+		if ((*valp = malloc(len + 1)) == NULL) {
+			SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL"));
+			return SSH_ERR_ALLOC_FAIL;
+		}
+		memcpy(*valp, p, len);
+		(*valp)[len] = '\0';
+	}
+	if (lenp != NULL)
+		*lenp = (size_t)len;
+	return 0;
+}
+
+int
+sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v)
+{
+	u_int32_t len;
+	u_char *p;
+	int r;
+
+	/*
+	 * Use sshbuf_peek_string_direct() to figure out if there is
+	 * a complete string in 'buf' and copy the string directly
+	 * into 'v'.
+	 */
+	if ((r = sshbuf_peek_string_direct(buf, NULL, NULL)) != 0 ||
+	    (r = sshbuf_get_u32(buf, &len)) != 0 ||
+	    (r = sshbuf_reserve(v, len, &p)) != 0 ||
+	    (r = sshbuf_get(buf, p, len)) != 0)
+		return r;
+	return 0;
+}
+
+int
+sshbuf_put(struct sshbuf *buf, const void *v, size_t len)
+{
+	u_char *p;
+	int r;
+
+	if ((r = sshbuf_reserve(buf, len, &p)) < 0)
+		return r;
+	memcpy(p, v, len);
+	return 0;
+}
+
+int
+sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v)
+{
+	return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v));
+}
+
+int
+sshbuf_putf(struct sshbuf *buf, const char *fmt, ...)
+{
+	va_list ap;
+	int r;
+
+	va_start(ap, fmt);
+	r = sshbuf_putfv(buf, fmt, ap);
+	va_end(ap);
+	return r;
+}
+
+int
+sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap)
+{
+	va_list ap2;
+	int r, len;
+	u_char *p;
+
+	va_copy(ap2, ap);
+	if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) {
+		r = SSH_ERR_INVALID_ARGUMENT;
+		goto out;
+	}
+	if (len == 0) {
+		r = 0;
+		goto out; /* Nothing to do */
+	}
+	va_end(ap2);
+	va_copy(ap2, ap);
+	if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0)
+		goto out;
+	if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) {
+		r = SSH_ERR_INTERNAL_ERROR;
+		goto out; /* Shouldn't happen */
+	}
+	/* Consume terminating \0 */
+	if ((r = sshbuf_consume_end(buf, 1)) != 0)
+		goto out;
+	r = 0;
+ out:
+	va_end(ap2);
+	return r;
+}
+
+int
+sshbuf_put_u64(struct sshbuf *buf, u_int64_t val)
+{
+	u_char *p;
+	int r;
+
+	if ((r = sshbuf_reserve(buf, 8, &p)) < 0)
+		return r;
+	POKE_U64(p, val);
+	return 0;
+}
+
+int
+sshbuf_put_u32(struct sshbuf *buf, u_int32_t val)
+{
+	u_char *p;
+	int r;
+
+	if ((r = sshbuf_reserve(buf, 4, &p)) < 0)
+		return r;
+	POKE_U32(p, val);
+	return 0;
+}
+
+int
+sshbuf_put_u16(struct sshbuf *buf, u_int16_t val)
+{
+	u_char *p;
+	int r;
+
+	if ((r = sshbuf_reserve(buf, 2, &p)) < 0)
+		return r;
+	POKE_U16(p, val);
+	return 0;
+}
+
+int
+sshbuf_put_u8(struct sshbuf *buf, u_char val)
+{
+	u_char *p;
+	int r;
+
+	if ((r = sshbuf_reserve(buf, 1, &p)) < 0)
+		return r;
+	p[0] = val;
+	return 0;
+}
+
+int
+sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len)
+{
+	u_char *d;
+	int r;
+
+	if (len > SSHBUF_SIZE_MAX - 4) {
+		SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE"));
+		return SSH_ERR_NO_BUFFER_SPACE;
+	}
+	if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0)
+		return r;
+	POKE_U32(d, len);
+	memcpy(d + 4, v, len);
+	return 0;
+}
+
+int
+sshbuf_put_cstring(struct sshbuf *buf, const char *v)
+{
+	return sshbuf_put_string(buf, (u_char *)v, strlen(v));
+}
+
+int
+sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v)
+{
+	return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v));
+}
+
+int
+sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp)
+{
+	const u_char *p;
+	size_t len;
+	struct sshbuf *ret;
+	int r;
+
+	if (buf == NULL || bufp == NULL)
+		return SSH_ERR_INVALID_ARGUMENT;
+	*bufp = NULL;
+	if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0)
+		return r;
+	if ((ret = sshbuf_from(p, len)) == NULL)
+		return SSH_ERR_ALLOC_FAIL;
+	if ((r = sshbuf_consume(buf, len + 4)) != 0 ||  /* Shouldn't happen */
+	    (r = sshbuf_set_parent(ret, buf)) != 0) {
+		sshbuf_free(ret);
+		return r;
+	}
+	*bufp = ret;
+	return 0;
+}
+
+int
+sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len)
+{
+	u_char *d;
+	const u_char *s = (const u_char *)v;
+	int r, prepend;
+
+	if (len > SSHBUF_SIZE_MAX - 5) {
+		SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE"));
+		return SSH_ERR_NO_BUFFER_SPACE;
+	}
+	/* Skip leading zero bytes */
+	for (; len > 0 && *s == 0; len--, s++)
+		;
+	/*
+	 * If most significant bit is set then prepend a zero byte to
+	 * avoid interpretation as a negative number.
+	 */
+	prepend = len > 0 && (s[0] & 0x80) != 0;
+	if ((r = sshbuf_reserve(buf, len + 4 + prepend, &d)) < 0)
+		return r;
+	POKE_U32(d, len + prepend);
+	if (prepend)
+		d[4] = 0;
+	memcpy(d + 4 + prepend, s, len);
+	return 0;
+}
diff --git a/sshbuf-getput-crypto.c b/sshbuf-getput-crypto.c
new file mode 100644
index 0000000..9c801a4
--- /dev/null
+++ b/sshbuf-getput-crypto.c
@@ -0,0 +1,233 @@
+/*	$OpenBSD: sshbuf-getput-crypto.c,v 1.1 2014/04/30 05:29:56 djm Exp $	*/
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+
+#include "ssherr.h"
+#define SSHBUF_INTERNAL
+#include "sshbuf.h"
+
+int
+sshbuf_get_bignum2(struct sshbuf *buf, BIGNUM *v)
+{
+	const u_char *d;
+	size_t len;
+	int r;
+
+	if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0)
+		return r;
+	/* Refuse negative (MSB set) and overlong bignums */
+	if ((len != 0 && (*d & 0x80) != 0))
+		return SSH_ERR_BIGNUM_IS_NEGATIVE;
+	if (len > SSHBUF_MAX_BIGNUM)
+		return SSH_ERR_BIGNUM_TOO_LARGE;
+	if (v != NULL && BN_bin2bn(d, len, v) == NULL)
+		return SSH_ERR_ALLOC_FAIL;
+	/* Consume the string */
+	if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) {
+		/* Shouldn't happen */
+		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+		SSHBUF_ABORT();
+		return SSH_ERR_INTERNAL_ERROR;
+	}
+	return 0;
+}
+
+int
+sshbuf_get_bignum1(struct sshbuf *buf, BIGNUM *v)
+{
+	const u_char *d = sshbuf_ptr(buf);
+	u_int16_t len_bits;
+	size_t len_bytes;
+
+	/* Length in bits */
+	if (sshbuf_len(buf) < 2)
+		return SSH_ERR_MESSAGE_INCOMPLETE;
+	len_bits = PEEK_U16(d);
+	len_bytes = (len_bits + 7) >> 3;
+	if (len_bytes > SSHBUF_MAX_BIGNUM + 1)
+		return SSH_ERR_BIGNUM_TOO_LARGE;
+	if (sshbuf_len(buf) < 2 + len_bytes)
+		return SSH_ERR_MESSAGE_INCOMPLETE;
+	if (v != NULL && BN_bin2bn(d + 2, len_bytes, v) == NULL)
+		return SSH_ERR_ALLOC_FAIL;
+	if (sshbuf_consume(buf, 2 + len_bytes) != 0) {
+		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+		SSHBUF_ABORT();
+		return SSH_ERR_INTERNAL_ERROR;
+	}
+	return 0;
+}
+
+#ifdef OPENSSL_HAS_ECC
+static int
+get_ec(const u_char *d, size_t len, EC_POINT *v, const EC_GROUP *g)
+{
+	/* Refuse overlong bignums */
+	if (len == 0 || len > SSHBUF_MAX_ECPOINT)
+		return SSH_ERR_ECPOINT_TOO_LARGE;
+	/* Only handle uncompressed points */
+	if (*d != POINT_CONVERSION_UNCOMPRESSED)
+		return SSH_ERR_INVALID_FORMAT;
+	if (v != NULL && EC_POINT_oct2point(g, v, d, len, NULL) != 1)
+		return SSH_ERR_INVALID_FORMAT; /* XXX assumption */
+	return 0;
+}
+
+int
+sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g)
+{
+	const u_char *d;
+	size_t len;
+	int r;
+
+	if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0)
+		return r;
+	if ((r = get_ec(d, len, v, g)) != 0)
+		return r;
+	/* Skip string */
+	if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) {
+		/* Shouldn't happen */
+		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+		SSHBUF_ABORT();
+		return SSH_ERR_INTERNAL_ERROR;
+	}
+	return 0;
+}
+
+int
+sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v)
+{
+	EC_POINT *pt = EC_POINT_new(EC_KEY_get0_group(v));
+	int r;
+	const u_char *d;
+	size_t len;
+
+	if (pt == NULL) {
+		SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL"));
+		return SSH_ERR_ALLOC_FAIL;
+	}
+	if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) {
+		EC_POINT_free(pt);
+		return r;
+	}
+	if ((r = get_ec(d, len, pt, EC_KEY_get0_group(v))) != 0) {
+		EC_POINT_free(pt);
+		return r;
+	}
+	if (EC_KEY_set_public_key(v, pt) != 1) {
+		EC_POINT_free(pt);
+		return SSH_ERR_ALLOC_FAIL; /* XXX assumption */
+	}
+	EC_POINT_free(pt);
+	/* Skip string */
+	if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) {
+		/* Shouldn't happen */
+		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+		SSHBUF_ABORT();
+		return SSH_ERR_INTERNAL_ERROR;
+	}
+	return 0;	
+}
+#endif /* OPENSSL_HAS_ECC */
+
+int
+sshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v)
+{
+	u_char d[SSHBUF_MAX_BIGNUM + 1];
+	int len = BN_num_bytes(v), prepend = 0, r;
+
+	if (len < 0 || len > SSHBUF_MAX_BIGNUM)
+		return SSH_ERR_INVALID_ARGUMENT;
+	*d = '\0';
+	if (BN_bn2bin(v, d + 1) != len)
+		return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */
+	/* If MSB is set, prepend a \0 */
+	if (len > 0 && (d[1] & 0x80) != 0)
+		prepend = 1;
+	if ((r = sshbuf_put_string(buf, d + 1 - prepend, len + prepend)) < 0) {
+		bzero(d, sizeof(d));
+		return r;
+	}
+	bzero(d, sizeof(d));
+	return 0;
+}
+
+int
+sshbuf_put_bignum1(struct sshbuf *buf, const BIGNUM *v)
+{
+	int r, len_bits = BN_num_bits(v);
+	size_t len_bytes = (len_bits + 7) / 8;
+	u_char d[SSHBUF_MAX_BIGNUM], *dp;
+
+	if (len_bits < 0 || len_bytes > SSHBUF_MAX_BIGNUM)
+		return SSH_ERR_INVALID_ARGUMENT;
+	if (BN_bn2bin(v, d) != (int)len_bytes)
+		return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */
+	if ((r = sshbuf_reserve(buf, len_bytes + 2, &dp)) < 0) {
+		bzero(d, sizeof(d));
+		return r;
+	}
+	POKE_U16(dp, len_bits);
+	memcpy(dp + 2, d, len_bytes);
+	bzero(d, sizeof(d));
+	return 0;
+}
+
+#ifdef OPENSSL_HAS_ECC
+int
+sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g)
+{
+	u_char d[SSHBUF_MAX_ECPOINT];
+	BN_CTX *bn_ctx;
+	size_t len;
+	int ret;
+
+	if ((bn_ctx = BN_CTX_new()) == NULL)
+		return SSH_ERR_ALLOC_FAIL;
+	if ((len = EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED,
+	    NULL, 0, bn_ctx)) > SSHBUF_MAX_ECPOINT) {
+		BN_CTX_free(bn_ctx);
+		return SSH_ERR_INVALID_ARGUMENT;
+	}
+	if (EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED,
+	    d, len, bn_ctx) != len) {
+		BN_CTX_free(bn_ctx);
+		return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */
+	}
+	BN_CTX_free(bn_ctx);
+	ret = sshbuf_put_string(buf, d, len);
+	bzero(d, len);
+	return ret;
+}
+
+int
+sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v)
+{
+	return sshbuf_put_ec(buf, EC_KEY_get0_public_key(v),
+	    EC_KEY_get0_group(v));
+}
+#endif /* OPENSSL_HAS_ECC */
+
diff --git a/sshbuf-misc.c b/sshbuf-misc.c
new file mode 100644
index 0000000..22dbfd5
--- /dev/null
+++ b/sshbuf-misc.c
@@ -0,0 +1,129 @@
+/*	$OpenBSD: sshbuf-misc.c,v 1.1 2014/04/30 05:29:56 djm Exp $	*/
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <resolv.h>
+#include <ctype.h>
+
+#include "ssherr.h"
+#define SSHBUF_INTERNAL
+#include "sshbuf.h"
+
+void
+sshbuf_dump(struct sshbuf *buf, FILE *f)
+{
+	const u_char *p = sshbuf_ptr(buf);
+	size_t i, j, len = sshbuf_len(buf);
+
+	fprintf(f, "buffer %p len = %zu\n", buf, len);
+	for (i = 0; i < len; i += 16) {
+		fprintf(f, "%.4zd: ", i);
+		for (j = i; j < i + 16; j++) {
+			if (j < len)
+				fprintf(f, "%02x ", p[j]);
+			else
+				fprintf(f, "   ");
+		}
+		fprintf(f, " ");
+		for (j = i; j < i + 16; j++) {
+			if (j < len) {
+				if  (isascii(p[j]) && isprint(p[j]))
+					fprintf(f, "%c", p[j]);
+				else
+					fprintf(f, ".");
+			}
+		}
+		fprintf(f, "\n");
+	}
+}
+
+char *
+sshbuf_dtob16(struct sshbuf *buf)
+{
+	size_t i, j, len = sshbuf_len(buf);
+	const u_char *p = sshbuf_ptr(buf);
+	char *ret;
+	const char hex[] = "0123456789abcdef";
+
+	if (len == 0)
+		return strdup("");
+	if (SIZE_MAX / 2 <= len || (ret = malloc(len * 2 + 1)) == NULL)
+		return NULL;
+	for (i = j = 0; i < len; i++) {
+		ret[j++] = hex[(p[i] >> 4) & 0xf];
+		ret[j++] = hex[p[i] & 0xf];
+	}
+	ret[j] = '\0';
+	return ret;
+}
+
+char *
+sshbuf_dtob64(struct sshbuf *buf)
+{
+	size_t len = sshbuf_len(buf), plen;
+	const u_char *p = sshbuf_ptr(buf);
+	char *ret;
+	int r;
+
+	if (len == 0)
+		return strdup("");
+	plen = ((len + 2) / 3) * 4 + 1;
+	if (SIZE_MAX / 2 <= len || (ret = malloc(plen)) == NULL)
+		return NULL;
+	if ((r = b64_ntop(p, len, ret, plen)) == -1) {
+		bzero(ret, plen);
+		free(ret);
+		return NULL;
+	}
+	return ret;
+}
+
+int
+sshbuf_b64tod(struct sshbuf *buf, const char *b64)
+{
+	size_t plen = strlen(b64);
+	int nlen, r;
+	u_char *p;
+
+	if (plen == 0)
+		return 0;
+	if ((p = malloc(plen)) == NULL)
+		return SSH_ERR_ALLOC_FAIL;
+	if ((nlen = b64_pton(b64, p, plen)) < 0) {
+		bzero(p, plen);
+		free(p);
+		return SSH_ERR_INVALID_FORMAT;
+	}
+	if ((r = sshbuf_put(buf, p, nlen)) < 0) {
+		bzero(p, plen);
+		free(p);
+		return r;
+	}
+	bzero(p, plen);
+	free(p);
+	return 0;
+}
+
diff --git a/sshbuf.c b/sshbuf.c
new file mode 100644
index 0000000..11d8d41
--- /dev/null
+++ b/sshbuf.c
@@ -0,0 +1,405 @@
+/*	$OpenBSD: sshbuf.c,v 1.1 2014/04/30 05:29:56 djm Exp $	*/
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ssherr.h"
+#define SSHBUF_INTERNAL
+#include "sshbuf.h"
+
+static inline int
+sshbuf_check_sanity(const struct sshbuf *buf)
+{
+	SSHBUF_TELL("sanity");
+	if (__predict_false(buf == NULL ||
+	    (!buf->readonly && buf->d != buf->cd) ||
+	    buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX ||
+	    buf->cd == NULL ||
+	    (buf->dont_free && (buf->readonly || buf->parent != NULL)) ||
+	    buf->max_size > SSHBUF_SIZE_MAX ||
+	    buf->alloc > buf->max_size ||
+	    buf->size > buf->alloc ||
+	    buf->off > buf->size)) {
+		/* Do not try to recover from corrupted buffer internals */
+		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
+		raise(SIGSEGV);
+		return SSH_ERR_INTERNAL_ERROR;
+	}
+	return 0;
+}
+
+static void
+sshbuf_maybe_pack(struct sshbuf *buf, int force)
+{
+	SSHBUF_DBG(("force %d", force));
+	SSHBUF_TELL("pre-pack");
+	if (buf->off == 0 || buf->readonly || buf->refcount > 1)
+		return;
+	if (force ||
+	    (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) {
+		memmove(buf->d, buf->d + buf->off, buf->size - buf->off);
+		buf->size -= buf->off;
+		buf->off = 0;
+		SSHBUF_TELL("packed");
+	}
+}
+
+struct sshbuf *
+sshbuf_new(void)
+{
+	struct sshbuf *ret;
+
+	if ((ret = calloc(sizeof(*ret), 1)) == NULL)
+		return NULL;
+	ret->alloc = SSHBUF_SIZE_INIT;
+	ret->max_size = SSHBUF_SIZE_MAX;
+	ret->readonly = 0;
+	ret->refcount = 1;
+	ret->parent = NULL;
+	if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) {
+		free(ret);
+		return NULL;
+	}
+	return ret;
+}
+
+struct sshbuf *
+sshbuf_from(const void *blob, size_t len)
+{
+	struct sshbuf *ret;
+
+	if (blob == NULL || len > SSHBUF_SIZE_MAX ||
+	    (ret = calloc(sizeof(*ret), 1)) == NULL)
+		return NULL;
+	ret->alloc = ret->size = ret->max_size = len;
+	ret->readonly = 1;
+	ret->refcount = 1;
+	ret->parent = NULL;
+	ret->cd = blob;
+	ret->d = NULL;
+	return ret;
+}
+
+int
+sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent)
+{
+	int r;
+
+	if ((r = sshbuf_check_sanity(child)) != 0 ||
+	    (r = sshbuf_check_sanity(parent)) != 0)
+		return r;
+	child->parent = parent;
+	child->parent->refcount++;
+	return 0;
+}
+
+struct sshbuf *
+sshbuf_fromb(struct sshbuf *buf)
+{
+	struct sshbuf *ret;
+
+	if (sshbuf_check_sanity(buf) != 0)
+		return NULL;
+	if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL)
+		return NULL;
+	if (sshbuf_set_parent(ret, buf) != 0) {
+		sshbuf_free(ret);
+		return NULL;
+	}
+	return ret;
+}
+
+void
+sshbuf_init(struct sshbuf *ret)
+{
+	bzero(ret, sizeof(*ret));
+	ret->alloc = SSHBUF_SIZE_INIT;
+	ret->max_size = SSHBUF_SIZE_MAX;
+	ret->readonly = 0;
+	ret->dont_free = 1;
+	ret->refcount = 1;
+	if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL)
+		ret->alloc = 0;
+}
+
+void
+sshbuf_free(struct sshbuf *buf)
+{
+	int dont_free = 0;
+
+	if (buf == NULL)
+		return;
+	/*
+	 * The following will leak on insane buffers, but this is the safest
+	 * course of action - an invalid pointer or already-freed pointer may
+	 * have been passed to us and continuing to scribble over memory would
+	 * be bad.
+	 */
+	if (sshbuf_check_sanity(buf) != 0)
+		return;
+	/*
+	 * If we are a child, the free our parent to decrement its reference
+	 * count and possibly free it.
+	 */
+	if (buf->parent != NULL) {
+		sshbuf_free(buf->parent);
+		buf->parent = NULL;
+	}
+	/*
+	 * If we are a parent with still-extant children, then don't free just
+	 * yet. The last child's call to sshbuf_free should decrement our
+	 * refcount to 0 and trigger the actual free.
+	 */
+	buf->refcount--;
+	if (buf->refcount > 0)
+		return;
+	dont_free = buf->dont_free;
+	if (!buf->readonly) {
+		bzero(buf->d, buf->alloc);
+		free(buf->d);
+	}
+	bzero(buf, sizeof(*buf));
+	if (!dont_free)
+		free(buf);
+}
+
+void
+sshbuf_reset(struct sshbuf *buf)
+{
+	u_char *d;
+
+	if (buf->readonly || buf->refcount > 1) {
+		/* Nonsensical. Just make buffer appear empty */
+		buf->off = buf->size;
+		return;
+	}
+	if (sshbuf_check_sanity(buf) == 0)
+		bzero(buf->d, buf->alloc);
+	buf->off = buf->size = 0;
+	if (buf->alloc != SSHBUF_SIZE_INIT) {
+		if ((d = realloc(buf->d, SSHBUF_SIZE_INIT)) != NULL) {
+			buf->cd = buf->d = d;
+			buf->alloc = SSHBUF_SIZE_INIT;
+		}
+	}
+}
+
+size_t
+sshbuf_max_size(const struct sshbuf *buf)
+{
+	return buf->max_size;
+}
+
+size_t
+sshbuf_alloc(const struct sshbuf *buf)
+{
+	return buf->alloc;
+}
+
+const struct sshbuf *
+sshbuf_parent(const struct sshbuf *buf)
+{
+	return buf->parent;
+}
+
+u_int
+sshbuf_refcount(const struct sshbuf *buf)
+{
+	return buf->refcount;
+}
+
+int
+sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
+{
+	size_t rlen;
+	u_char *dp;
+	int r;
+
+	SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size));
+	if ((r = sshbuf_check_sanity(buf)) != 0)
+		return r;
+	if (max_size == buf->max_size)
+		return 0;
+	if (buf->readonly || buf->refcount > 1)
+		return SSH_ERR_BUFFER_READ_ONLY;
+	if (max_size > SSHBUF_SIZE_MAX)
+		return SSH_ERR_NO_BUFFER_SPACE;
+	/* pack and realloc if necessary */
+	sshbuf_maybe_pack(buf, max_size < buf->size);
+	if (max_size < buf->alloc && max_size > buf->size) {
+		if (buf->size < SSHBUF_SIZE_INIT)
+			rlen = SSHBUF_SIZE_INIT;
+		else
+			rlen = roundup(buf->size, SSHBUF_SIZE_INC);
+		if (rlen > max_size)
+			rlen = max_size;
+		bzero(buf->d + buf->size, buf->alloc - buf->size);
+		SSHBUF_DBG(("new alloc = %zu", rlen));
+		if ((dp = realloc(buf->d, rlen)) == NULL)
+			return SSH_ERR_ALLOC_FAIL;
+		buf->cd = buf->d = dp;
+		buf->alloc = rlen;
+	}
+	SSHBUF_TELL("new-max");
+	if (max_size < buf->alloc)
+		return SSH_ERR_NO_BUFFER_SPACE;
+	buf->max_size = max_size;
+	return 0;
+}
+
+size_t
+sshbuf_len(const struct sshbuf *buf)
+{
+	if (sshbuf_check_sanity(buf) != 0)
+		return 0;
+	return buf->size - buf->off;
+}
+
+size_t
+sshbuf_avail(const struct sshbuf *buf)
+{
+	if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
+		return 0;
+	return buf->max_size - (buf->size - buf->off);
+}
+
+const u_char *
+sshbuf_ptr(const struct sshbuf *buf)
+{
+	if (sshbuf_check_sanity(buf) != 0)
+		return NULL;
+	return buf->cd + buf->off;
+}
+
+u_char *
+sshbuf_mutable_ptr(const struct sshbuf *buf)
+{
+	if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
+		return NULL;
+	return buf->d + buf->off;
+}
+
+int
+sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
+{
+	int r;
+
+	if ((r = sshbuf_check_sanity(buf)) != 0)
+		return r;
+	if (buf->readonly || buf->refcount > 1)
+		return SSH_ERR_BUFFER_READ_ONLY;
+	SSHBUF_TELL("check");
+	/* Check that len is reasonable and that max_size + available < len */
+	if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
+		return SSH_ERR_NO_BUFFER_SPACE;
+	return 0;
+}
+
+int
+sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
+{
+	size_t rlen, need;
+	u_char *dp;
+	int r;
+
+	if (dpp != NULL)
+		*dpp = NULL;
+
+	SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
+	if ((r = sshbuf_check_reserve(buf, len)) != 0)
+		return r;
+	/*
+	 * If the requested allocation appended would push us past max_size
+	 * then pack the buffer, zeroing buf->off.
+	 */
+	sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
+	SSHBUF_TELL("reserve");
+	if (len + buf->size > buf->alloc) {
+		/*
+		 * Prefer to alloc in SSHBUF_SIZE_INC units, but
+		 * allocate less if doing so would overflow max_size.
+		 */
+		need = len + buf->size - buf->alloc;
+		rlen = roundup(buf->alloc + need, SSHBUF_SIZE_INC);
+		SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
+		if (rlen > buf->max_size)
+			rlen = buf->alloc + need;
+		SSHBUF_DBG(("adjusted rlen %zu", rlen));
+		if ((dp = realloc(buf->d, rlen)) == NULL) {
+			SSHBUF_DBG(("realloc fail"));
+			if (dpp != NULL)
+				*dpp = NULL;
+			return SSH_ERR_ALLOC_FAIL;
+		}
+		buf->alloc = rlen;
+		buf->cd = buf->d = dp;
+		if ((r = sshbuf_check_reserve(buf, len)) < 0) {
+			/* shouldn't fail */
+			if (dpp != NULL)
+				*dpp = NULL;
+			return r;
+		}
+	}
+	dp = buf->d + buf->size;
+	buf->size += len;
+	SSHBUF_TELL("done");
+	if (dpp != NULL)
+		*dpp = dp;
+	return 0;
+}
+
+int
+sshbuf_consume(struct sshbuf *buf, size_t len)
+{
+	int r;
+
+	SSHBUF_DBG(("len = %zu", len));
+	if ((r = sshbuf_check_sanity(buf)) != 0)
+		return r;
+	if (len == 0)
+		return 0;
+	if (len > sshbuf_len(buf))
+		return SSH_ERR_MESSAGE_INCOMPLETE;
+	buf->off += len;
+	SSHBUF_TELL("done");
+	return 0;
+}
+
+int
+sshbuf_consume_end(struct sshbuf *buf, size_t len)
+{
+	int r;
+
+	SSHBUF_DBG(("len = %zu", len));
+	if ((r = sshbuf_check_sanity(buf)) != 0)
+		return r;
+	if (len == 0)
+		return 0;
+	if (len > sshbuf_len(buf))
+		return SSH_ERR_MESSAGE_INCOMPLETE;
+	buf->size -= len;
+	SSHBUF_TELL("done");
+	return 0;
+}
+
diff --git a/sshbuf.h b/sshbuf.h
new file mode 100644
index 0000000..1d7da31
--- /dev/null
+++ b/sshbuf.h
@@ -0,0 +1,325 @@
+/*	$OpenBSD: sshbuf.h,v 1.1 2014/04/30 05:29:56 djm Exp $	*/
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SSHBUF_H
+#define _SSHBUF_H
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+
+#define SSHBUF_SIZE_MAX		0x8000000	/* Hard maximum size */
+#define SSHBUF_REFS_MAX		0x100000	/* Max child buffers */
+#define SSHBUF_MAX_BIGNUM	(16384 / 8)	/* Max bignum *bytes* */
+#define SSHBUF_MAX_ECPOINT	((528 * 2 / 8) + 1) /* Max EC point *bytes* */
+
+/*
+ * NB. do not depend on the internals of this. It will be made opaque
+ * one day.
+ */
+struct sshbuf {
+	u_char *d;		/* Data */
+	const u_char *cd;	/* Const data */
+	size_t off;		/* First available byte is buf->d + buf->off */
+	size_t size;		/* Last byte is buf->d + buf->size - 1 */
+	size_t max_size;	/* Maximum size of buffer */
+	size_t alloc;		/* Total bytes allocated to buf->d */
+	int readonly;		/* Refers to external, const data */
+	int dont_free;		/* Kludge to support sshbuf_init */
+	u_int refcount;		/* Tracks self and number of child buffers */
+	struct sshbuf *parent;	/* If child, pointer to parent */
+};
+
+#ifndef SSHBUF_NO_DEPREACTED
+/*
+ * NB. Please do not use sshbuf_init() in new code. Please use sshbuf_new()
+ * instead. sshbuf_init() is deprectated and will go away soon (it is
+ * only included to allow compat with buffer_* in OpenSSH)
+ */
+void sshbuf_init(struct sshbuf *buf);
+#endif
+
+/*
+ * Create a new sshbuf buffer.
+ * Returns pointer to buffer on success, or NULL on allocation failure.
+ */
+struct sshbuf *sshbuf_new(void);
+
+/*
+ * Create a new, read-only sshbuf buffer from existing data.
+ * Returns pointer to buffer on success, or NULL on allocation failure.
+ */
+struct sshbuf *sshbuf_from(const void *blob, size_t len);
+
+/*
+ * Create a new, read-only sshbuf buffer from the contents of an existing
+ * buffer. The contents of "buf" must not change in the lifetime of the
+ * resultant buffer.
+ * Returns pointer to buffer on success, or NULL on allocation failure.
+ */
+struct sshbuf *sshbuf_fromb(struct sshbuf *buf);
+
+/*
+ * Create a new, read-only sshbuf buffer from the contents of a string in
+ * an existing buffer (the string is consumed in the process).
+ * The contents of "buf" must not change in the lifetime of the resultant
+ * buffer.
+ * Returns pointer to buffer on success, or NULL on allocation failure.
+ */
+int	sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp);
+
+/*
+ * Clear and free buf
+ */
+void	sshbuf_free(struct sshbuf *buf);
+
+/*
+ * Reset buf, clearing its contents. NB. max_size is preserved.
+ */
+void	sshbuf_reset(struct sshbuf *buf);
+
+/*
+ * Return the maximum size of buf
+ */
+size_t	sshbuf_max_size(const struct sshbuf *buf);
+
+/*
+ * Set the maximum size of buf
+ * Returns 0 on success, or a negative SSH_ERR_* error code on failure.
+ */
+int	sshbuf_set_max_size(struct sshbuf *buf, size_t max_size);
+
+/*
+ * Returns the length of data in buf
+ */
+size_t	sshbuf_len(const struct sshbuf *buf);
+
+/*
+ * Returns number of bytes left in buffer before hitting max_size.
+ */
+size_t	sshbuf_avail(const struct sshbuf *buf);
+
+/*
+ * Returns a read-only pointer to the start of the the data in buf
+ */
+const u_char *sshbuf_ptr(const struct sshbuf *buf);
+
+/*
+ * Returns a mutable pointer to the start of the the data in buf, or
+ * NULL if the buffer is read-only.
+ */
+u_char *sshbuf_mutable_ptr(const struct sshbuf *buf);
+
+/*
+ * Check whether a reservation of size len will succeed in buf
+ * Safer to use than direct comparisons again sshbuf_avail as it copes
+ * with unsigned overflows correctly.
+ * Returns 0 on success, or a negative SSH_ERR_* error code on failure.
+ */
+int	sshbuf_check_reserve(const struct sshbuf *buf, size_t len);
+
+/*
+ * Reserve len bytes in buf.
+ * Returns 0 on success and a pointer to the first reserved byte via the
+ * optional dpp parameter or a negative * SSH_ERR_* error code on failure.
+ */
+int	sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp);
+
+/*
+ * Consume len bytes from the start of buf
+ * Returns 0 on success, or a negative SSH_ERR_* error code on failure.
+ */
+int	sshbuf_consume(struct sshbuf *buf, size_t len);
+
+/*
+ * Consume len bytes from the end of buf
+ * Returns 0 on success, or a negative SSH_ERR_* error code on failure.
+ */
+int	sshbuf_consume_end(struct sshbuf *buf, size_t len);
+
+/* Extract or deposit some bytes */
+int	sshbuf_get(struct sshbuf *buf, void *v, size_t len);
+int	sshbuf_put(struct sshbuf *buf, const void *v, size_t len);
+int	sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v);
+
+/* Append using a printf(3) format */
+int	sshbuf_putf(struct sshbuf *buf, const char *fmt, ...)
+	    __attribute__((format(printf, 2, 3)));
+int	sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap);
+
+/* Functions to extract or store big-endian words of various sizes */
+int	sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp);
+int	sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp);
+int	sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp);
+int	sshbuf_get_u8(struct sshbuf *buf, u_char *valp);
+int	sshbuf_put_u64(struct sshbuf *buf, u_int64_t val);
+int	sshbuf_put_u32(struct sshbuf *buf, u_int32_t val);
+int	sshbuf_put_u16(struct sshbuf *buf, u_int16_t val);
+int	sshbuf_put_u8(struct sshbuf *buf, u_char val);
+
+/*
+ * Functions to extract or store SSH wire encoded strings (u32 len || data)
+ * The "cstring" variants admit no \0 characters in the string contents.
+ * Caller must free *valp.
+ */
+int	sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp);
+int	sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp);
+int	sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v);
+int	sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len);
+int	sshbuf_put_cstring(struct sshbuf *buf, const char *v);
+int	sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v);
+
+/*
+ * "Direct" variant of sshbuf_get_string, returns pointer into the sshbuf to
+ * avoid an malloc+memcpy. The pointer is guaranteed to be valid until the
+ * next sshbuf-modifying function call. Caller does not free.
+ */
+int	sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp,
+	    size_t *lenp);
+
+/* Skip past a string */
+#define sshbuf_skip_string(buf) sshbuf_get_string_direct(buf, NULL, NULL)
+
+/* Another variant: "peeks" into the buffer without modifying it */
+int	sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp,
+	    size_t *lenp);
+
+/*
+ * Functions to extract or store SSH wire encoded bignums and elliptic
+ * curve points.
+ */
+int	sshbuf_get_bignum2(struct sshbuf *buf, BIGNUM *v);
+int	sshbuf_get_bignum1(struct sshbuf *buf, BIGNUM *v);
+int	sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g);
+int	sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v);
+int	sshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v);
+int	sshbuf_put_bignum1(struct sshbuf *buf, const BIGNUM *v);
+int	sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g);
+int	sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v);
+int	sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len);
+
+/* Dump the contents of the buffer to stderr in a human-readable format */
+void	sshbuf_dump(struct sshbuf *buf, FILE *f);
+
+/* Return the hexadecimal representation of the contents of the buffer */
+char	*sshbuf_dtob16(struct sshbuf *buf);
+
+/* Encode the contents of the buffer as base64 */
+char	*sshbuf_dtob64(struct sshbuf *buf);
+
+/* Decode base64 data and append it to the buffer */
+int	sshbuf_b64tod(struct sshbuf *buf, const char *b64);
+
+/* Macros for decoding/encoding integers */
+#define PEEK_U64(p) \
+	(((u_int64_t)(((u_char *)(p))[0]) << 56) | \
+	 ((u_int64_t)(((u_char *)(p))[1]) << 48) | \
+	 ((u_int64_t)(((u_char *)(p))[2]) << 40) | \
+	 ((u_int64_t)(((u_char *)(p))[3]) << 32) | \
+	 ((u_int64_t)(((u_char *)(p))[4]) << 24) | \
+	 ((u_int64_t)(((u_char *)(p))[5]) << 16) | \
+	 ((u_int64_t)(((u_char *)(p))[6]) << 8) | \
+	  (u_int64_t)(((u_char *)(p))[7]))
+#define PEEK_U32(p) \
+	(((u_int32_t)(((u_char *)(p))[0]) << 24) | \
+	 ((u_int32_t)(((u_char *)(p))[1]) << 16) | \
+	 ((u_int32_t)(((u_char *)(p))[2]) << 8) | \
+	  (u_int32_t)(((u_char *)(p))[3]))
+#define PEEK_U16(p) \
+	(((u_int16_t)(((u_char *)(p))[0]) << 8) | \
+	  (u_int16_t)(((u_char *)(p))[1]))
+
+#define POKE_U64(p, v) \
+	do { \
+		((u_char *)(p))[0] = (((u_int64_t)(v)) >> 56) & 0xff; \
+		((u_char *)(p))[1] = (((u_int64_t)(v)) >> 48) & 0xff; \
+		((u_char *)(p))[2] = (((u_int64_t)(v)) >> 40) & 0xff; \
+		((u_char *)(p))[3] = (((u_int64_t)(v)) >> 32) & 0xff; \
+		((u_char *)(p))[4] = (((u_int64_t)(v)) >> 24) & 0xff; \
+		((u_char *)(p))[5] = (((u_int64_t)(v)) >> 16) & 0xff; \
+		((u_char *)(p))[6] = (((u_int64_t)(v)) >> 8) & 0xff; \
+		((u_char *)(p))[7] = ((u_int64_t)(v)) & 0xff; \
+	} while (0)
+#define POKE_U32(p, v) \
+	do { \
+		((u_char *)(p))[0] = (((u_int64_t)(v)) >> 24) & 0xff; \
+		((u_char *)(p))[1] = (((u_int64_t)(v)) >> 16) & 0xff; \
+		((u_char *)(p))[2] = (((u_int64_t)(v)) >> 8) & 0xff; \
+		((u_char *)(p))[3] = ((u_int64_t)(v)) & 0xff; \
+	} while (0)
+#define POKE_U16(p, v) \
+	do { \
+		((u_char *)(p))[0] = (((u_int64_t)(v)) >> 8) & 0xff; \
+		((u_char *)(p))[1] = ((u_int64_t)(v)) & 0xff; \
+	} while (0)
+
+/* Internal definitions follow. Exposed for regress tests */
+#ifdef SSHBUF_INTERNAL
+
+/*
+ * Return the allocation size of buf
+ */
+size_t	sshbuf_alloc(const struct sshbuf *buf);
+
+/*
+ * Increment the reference count of buf.
+ */
+int	sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent);
+
+/*
+ * Return the parent buffer of buf, or NULL if it has no parent.
+ */
+const struct sshbuf *sshbuf_parent(const struct sshbuf *buf);
+
+/*
+ * Return the reference count of buf
+ */
+u_int	sshbuf_refcount(const struct sshbuf *buf);
+
+# define SSHBUF_SIZE_INIT	256		/* Initial allocation */
+# define SSHBUF_SIZE_INC	256		/* Preferred increment length */
+# define SSHBUF_PACK_MIN	8192		/* Minimim packable offset */
+
+/* # define SSHBUF_ABORT abort */
+/* # define SSHBUF_DEBUG */
+
+# ifndef SSHBUF_ABORT
+#  define SSHBUF_ABORT()
+# endif
+
+# ifdef SSHBUF_DEBUG
+#  define SSHBUF_TELL(what) do { \
+		printf("%s:%d %s: %s size %zu alloc %zu off %zu max %zu\n", \
+		    __FILE__, __LINE__, __func__, what, \
+		    buf->size, buf->alloc, buf->off, buf->max_size); \
+		fflush(stdout); \
+	} while (0)
+#  define SSHBUF_DBG(x) do { \
+		printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \
+		printf x; \
+		printf("\n"); \
+		fflush(stdout); \
+	} while (0)
+# else
+#  define SSHBUF_TELL(what)
+#  define SSHBUF_DBG(x)
+# endif
+#endif /* SSHBUF_INTERNAL */
+
+#endif /* _SSHBUF_H */
diff --git a/ssherr.c b/ssherr.c
new file mode 100644
index 0000000..49fbb71
--- /dev/null
+++ b/ssherr.c
@@ -0,0 +1,131 @@
+/*	$OpenBSD: ssherr.c,v 1.1 2014/04/30 05:29:56 djm Exp $	*/
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include "ssherr.h"
+
+const char *
+ssh_err(int n)
+{
+	switch (n) {
+	case SSH_ERR_SUCCESS:
+		return "success";
+	case SSH_ERR_INTERNAL_ERROR:
+		return "unexpected internal error";
+	case SSH_ERR_ALLOC_FAIL:
+		return "memory allocation failed";
+	case SSH_ERR_MESSAGE_INCOMPLETE:
+		return "incomplete message";
+	case SSH_ERR_INVALID_FORMAT:
+		return "invalid format";
+	case SSH_ERR_BIGNUM_IS_NEGATIVE:
+		return "bignum is negative";
+	case SSH_ERR_STRING_TOO_LARGE:
+		return "string is too large";
+	case SSH_ERR_BIGNUM_TOO_LARGE:
+		return "bignum is too large";
+	case SSH_ERR_ECPOINT_TOO_LARGE:
+		return "elliptic curve point is too large";
+	case SSH_ERR_NO_BUFFER_SPACE:
+		return "insufficient buffer space";
+	case SSH_ERR_INVALID_ARGUMENT:
+		return "invalid argument";
+	case SSH_ERR_KEY_BITS_MISMATCH:
+		return "key bits do not match";
+	case SSH_ERR_EC_CURVE_INVALID:
+		return "invalid elliptic curve";
+	case SSH_ERR_KEY_TYPE_MISMATCH:
+		return "key type does not match";
+	case SSH_ERR_KEY_TYPE_UNKNOWN:
+		return "unknown or unsupported key type";
+	case SSH_ERR_EC_CURVE_MISMATCH:
+		return "elliptic curve does not match";
+	case SSH_ERR_EXPECTED_CERT:
+		return "plain key provided where certificate required";
+	case SSH_ERR_KEY_LACKS_CERTBLOB:
+		return "key lacks certificate data";
+	case SSH_ERR_KEY_CERT_UNKNOWN_TYPE:
+		return "unknown/unsupported certificate type";
+	case SSH_ERR_KEY_CERT_INVALID_SIGN_KEY:
+		return "invalid certificate signing key";
+	case SSH_ERR_KEY_INVALID_EC_VALUE:
+		return "invalid elliptic curve value";
+	case SSH_ERR_SIGNATURE_INVALID:
+		return "incorrect signature";
+	case SSH_ERR_LIBCRYPTO_ERROR:
+		return "error in libcrypto";  /* XXX fetch and return */
+	case SSH_ERR_UNEXPECTED_TRAILING_DATA:
+		return "unexpected bytes remain after decoding";
+	case SSH_ERR_SYSTEM_ERROR:
+		return strerror(errno);
+	case SSH_ERR_KEY_CERT_INVALID:
+		return "invalid certificate";
+	case SSH_ERR_AGENT_COMMUNICATION:
+		return "communication with agent failed";
+	case SSH_ERR_AGENT_FAILURE:
+		return "agent refused operation";
+	case SSH_ERR_DH_GEX_OUT_OF_RANGE:
+		return "DH GEX group out of range";
+	case SSH_ERR_DISCONNECTED:
+		return "disconnected";
+	case SSH_ERR_MAC_INVALID:
+		return "message authentication code incorrect";
+	case SSH_ERR_NO_CIPHER_ALG_MATCH:
+		return "no matching cipher found";
+	case SSH_ERR_NO_MAC_ALG_MATCH:
+		return "no matching MAC found";
+	case SSH_ERR_NO_COMPRESS_ALG_MATCH:
+		return "no matching compression method found";
+	case SSH_ERR_NO_KEX_ALG_MATCH:
+		return "no matching key exchange method found";
+	case SSH_ERR_NO_HOSTKEY_ALG_MATCH:
+		return "no matching host key type found";
+	case SSH_ERR_PROTOCOL_MISMATCH:
+		return "protocol version mismatch";
+	case SSH_ERR_NO_PROTOCOL_VERSION:
+		return "could not read protocol version";
+	case SSH_ERR_NO_HOSTKEY_LOADED:
+		return "could not load host key";
+	case SSH_ERR_NEED_REKEY:
+		return "rekeying not supported by peer";
+	case SSH_ERR_PASSPHRASE_TOO_SHORT:
+		return "passphrase is too short (minimum four characters)";
+	case SSH_ERR_FILE_CHANGED:
+		return "file changed while reading";
+	case SSH_ERR_KEY_UNKNOWN_CIPHER:
+		return "key encrypted using unsupported cipher";
+	case SSH_ERR_KEY_WRONG_PASSPHRASE:
+		return "incorrect passphrase supplied to decrypt private key";
+	case SSH_ERR_KEY_BAD_PERMISSIONS:
+		return "bad permissions";
+	case SSH_ERR_KEY_CERT_MISMATCH:
+		return "certificate does not match key";
+	case SSH_ERR_KEY_NOT_FOUND:
+		return "key not found";
+	case SSH_ERR_AGENT_NOT_PRESENT:
+		return "agent not present";
+	case SSH_ERR_AGENT_NO_IDENTITIES:
+		return "agent contains no identities";
+	case SSH_ERR_KRL_BAD_MAGIC:
+		return "KRL file has invalid magic number";
+	case SSH_ERR_KEY_REVOKED:
+		return "Key is revoked";
+	default:
+		return "unknown error";
+	}
+}
diff --git a/ssherr.h b/ssherr.h
new file mode 100644
index 0000000..106f786
--- /dev/null
+++ b/ssherr.h
@@ -0,0 +1,80 @@
+/*	$OpenBSD: ssherr.h,v 1.1 2014/04/30 05:29:56 djm Exp $	*/
+/*
+ * Copyright (c) 2011 Damien Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SSHERR_H
+#define _SSHERR_H
+
+/* XXX are these too granular? not granular enough? I can't decide - djm */
+
+/* Error codes */
+#define SSH_ERR_SUCCESS				0
+#define SSH_ERR_INTERNAL_ERROR			-1
+#define SSH_ERR_ALLOC_FAIL			-2
+#define SSH_ERR_MESSAGE_INCOMPLETE		-3
+#define SSH_ERR_INVALID_FORMAT			-4
+#define SSH_ERR_BIGNUM_IS_NEGATIVE		-5
+#define SSH_ERR_STRING_TOO_LARGE		-6
+#define SSH_ERR_BIGNUM_TOO_LARGE		-7
+#define SSH_ERR_ECPOINT_TOO_LARGE		-8
+#define SSH_ERR_NO_BUFFER_SPACE			-9
+#define SSH_ERR_INVALID_ARGUMENT		-10
+#define SSH_ERR_KEY_BITS_MISMATCH		-11
+#define SSH_ERR_EC_CURVE_INVALID		-12
+#define SSH_ERR_KEY_TYPE_MISMATCH		-13
+#define SSH_ERR_KEY_TYPE_UNKNOWN		-14 /* XXX UNSUPPORTED? */
+#define SSH_ERR_EC_CURVE_MISMATCH		-15
+#define SSH_ERR_EXPECTED_CERT			-16
+#define SSH_ERR_KEY_LACKS_CERTBLOB		-17
+#define SSH_ERR_KEY_CERT_UNKNOWN_TYPE		-18
+#define SSH_ERR_KEY_CERT_INVALID_SIGN_KEY	-19
+#define SSH_ERR_KEY_INVALID_EC_VALUE		-20
+#define SSH_ERR_SIGNATURE_INVALID		-21
+#define SSH_ERR_LIBCRYPTO_ERROR			-22
+#define SSH_ERR_UNEXPECTED_TRAILING_DATA	-23
+#define SSH_ERR_SYSTEM_ERROR			-24
+#define SSH_ERR_KEY_CERT_INVALID		-25
+#define SSH_ERR_AGENT_COMMUNICATION		-26
+#define SSH_ERR_AGENT_FAILURE			-27
+#define SSH_ERR_DH_GEX_OUT_OF_RANGE		-28
+#define SSH_ERR_DISCONNECTED			-29
+#define SSH_ERR_MAC_INVALID			-30
+#define SSH_ERR_NO_CIPHER_ALG_MATCH		-31
+#define SSH_ERR_NO_MAC_ALG_MATCH		-32
+#define SSH_ERR_NO_COMPRESS_ALG_MATCH		-33
+#define SSH_ERR_NO_KEX_ALG_MATCH		-34
+#define SSH_ERR_NO_HOSTKEY_ALG_MATCH		-35
+#define SSH_ERR_NO_HOSTKEY_LOADED		-36
+#define SSH_ERR_PROTOCOL_MISMATCH		-37
+#define SSH_ERR_NO_PROTOCOL_VERSION		-38
+#define SSH_ERR_NEED_REKEY			-39
+#define SSH_ERR_PASSPHRASE_TOO_SHORT		-40
+#define SSH_ERR_FILE_CHANGED			-41
+#define SSH_ERR_KEY_UNKNOWN_CIPHER		-42
+#define SSH_ERR_KEY_WRONG_PASSPHRASE		-43
+#define SSH_ERR_KEY_BAD_PERMISSIONS		-44
+#define SSH_ERR_KEY_CERT_MISMATCH		-45
+#define SSH_ERR_KEY_NOT_FOUND			-46
+#define SSH_ERR_AGENT_NOT_PRESENT		-47
+#define SSH_ERR_AGENT_NO_IDENTITIES		-48
+#define SSH_ERR_BUFFER_READ_ONLY		-49
+#define SSH_ERR_KRL_BAD_MAGIC			-50
+#define SSH_ERR_KEY_REVOKED			-51
+
+/* Translate a numeric error code to a human-readable error string */
+const char *ssh_err(int n);
+
+#endif /* _SSHERR_H */