diff --git a/base64-decode.c b/base64-decode.c
index c332aa22c3582c398deba52626ca8e7b25afc521..c1c0d2f08ec41015535caf15d3b779cb92d3edda 100644
--- a/base64-decode.c
+++ b/base64-decode.c
@@ -57,30 +57,26 @@ decode_table[0x100] =
 void
 base64_decode_init(struct base64_decode_ctx *ctx)
 {
-  ctx->word = ctx->bits = 0;
-  ctx->status = BASE64_DECODE_OK;
+  ctx->word = ctx->bits = ctx->padding = 0;
 }
 
-unsigned
+int
 base64_decode_single(struct base64_decode_ctx *ctx,
 		     uint8_t *dst,
 		     uint8_t src)
 {
   int data;
   
-  if (ctx->status == BASE64_DECODE_ERROR)
-    return 0;
-
   data = decode_table[src];
 
   switch(data)
     {
     default:
       assert(data >= 0 && data < 0x40);
-	  
-      if (ctx->status != BASE64_DECODE_OK)
-	goto invalid;
-	  
+
+      if (ctx->padding)
+	return -1;
+      
       ctx->word = ctx->word << 6 | data;
       ctx->bits += 6;
 
@@ -93,56 +89,60 @@ base64_decode_single(struct base64_decode_ctx *ctx,
       else return 0;
 
     case TABLE_INVALID:
-    invalid:
-      ctx->status = BASE64_DECODE_ERROR;
-      /* Fall through */
-      
+      return -1;
+
     case TABLE_SPACE:
       return 0;
       
     case TABLE_END:
-      if (!ctx->bits)
-	goto invalid;
+      /* There can be at most two padding characters. */
+      if (!ctx->bits || ctx->padding > 2)
+	return -1;
+      
       if (ctx->word & ( (1<<ctx->bits) - 1))
 	/* We shouldn't have any leftover bits */
-	goto invalid;
-      
-      ctx->status = BASE64_DECODE_END;
+	return -1;
+
+      ctx->padding++;
       ctx->bits -= 2;
       return 0;
     }
 }
 
-unsigned
+int
 base64_decode_update(struct base64_decode_ctx *ctx,
+		     unsigned *dst_length,
 		     uint8_t *dst,
-		     unsigned length,
+		     unsigned src_length,
 		     const uint8_t *src)
 {
   unsigned done;
   unsigned i;
-  
-  if (ctx->status == BASE64_DECODE_ERROR)
-    return 0;
 
-  for (i = 0, done = 0; i<length; i++)
-    done += base64_decode_single(ctx, dst + done, src[i]);
-
-  assert(done <= BASE64_DECODE_LENGTH(length));
+  assert(*dst_length >= BASE64_DECODE_LENGTH(src_length));
+  
+  for (i = 0, done = 0; i<src_length; i++)
+    switch(base64_decode_single(ctx, dst + done, src[i]))
+      {
+      case -1:
+	return 0;
+      case 1:
+	done++;
+	/* Fall through */
+      case 0:
+	break;
+      default:
+	abort();
+      }
   
-  return done;
+  assert(done <= BASE64_DECODE_LENGTH(src_length));
+
+  *dst_length = done;
+  return 1;
 }
 
 int
-base64_decode_status(struct base64_decode_ctx *ctx)
+base64_decode_final(struct base64_decode_ctx *ctx)
 {
-  switch (ctx->status)
-    {
-    case BASE64_DECODE_END:
-    case BASE64_DECODE_OK:
-      return ctx->bits == 0;
-    case BASE64_DECODE_ERROR:
-      return 0;
-    }
-  abort();
+  return ctx->bits == 0;
 }
diff --git a/base64.h b/base64.h
index 3084bbff6329a8350feb7b2bdda9128e57b319a8..9532e54fd0897a3c172ee704e4d6a0cba0e72a47 100644
--- a/base64.h
+++ b/base64.h
@@ -95,36 +95,40 @@ base64_encode_group(uint8_t *dst, uint32_t group);
 
 struct base64_decode_ctx
 {
-  enum
-    {
-      BASE64_DECODE_OK,
-      BASE64_DECODE_ERROR,
-      BASE64_DECODE_END
-    } status; 
   unsigned word;   /* Leftover bits */
   unsigned bits;   /* Number buffered bits */
+
+  /* Number of padding characters encountered */
+  unsigned padding;
 };
 
 void
 base64_decode_init(struct base64_decode_ctx *ctx);
 
-/* Decodes a single byte. Returns amount of output (always 0 or 1).
- * FIXME: What to return on errors? */
-unsigned
+/* Decodes a single byte. Returns amount of output (0 or 1), or -1 on
+ * errors. */
+int
 base64_decode_single(struct base64_decode_ctx *ctx,
 		     uint8_t *dst,
 		     uint8_t src);
 
-/* Returns the number of output characters. DST should point to an
- * area of size at least BASE64_DECODE_LENGTH(length). */
-unsigned
+/* Returns 1 on success, 0 on error. DST should point to an area of
+ * size at least BASE64_DECODE_LENGTH(length), and for sanity
+ * checking, *DST_LENGTH should be initialized to the size of that
+ * area before the call. *DST_LENGTH is updated to the amount of
+ * decoded output. */
+
+/* FIXME: Currently results in an assertion failure if *DST_LENGTH is
+ * too small. Return some error instead? */
+int
 base64_decode_update(struct base64_decode_ctx *ctx,
+		     unsigned *dst_length,
 		     uint8_t *dst,
-		     unsigned length,
+		     unsigned src_length,
 		     const uint8_t *src);
 
 /* Returns 1 on success. */
 int
-base64_decode_status(struct base64_decode_ctx *ctx);
+base64_decode_final(struct base64_decode_ctx *ctx);
 
 #endif /* NETTLE_BASE64_H_INCLUDED */