Merge pull request #3560 from gufe44/netbsd-rand-arc4random_buf-2.16

[Backport 2.16] Use arc4random_buf instead of rand on NetBSD
diff --git a/ChangeLog.d/bugfix_PR_2632.txt b/ChangeLog.d/bugfix_PR_2632.txt
new file mode 100644
index 0000000..1d54b4d
--- /dev/null
+++ b/ChangeLog.d/bugfix_PR_2632.txt
@@ -0,0 +1,4 @@
+Bugfix
+   * Avoid use of statically sized stack buffers for certificate writing.
+     This previously limited the maximum size of DER encoded certificates
+     in mbedtls_x509write_crt_der() to 2Kb. Reported by soccerGB in #2631.
diff --git a/ChangeLog.d/x509write_csr_heap_alloc.txt b/ChangeLog.d/x509write_csr_heap_alloc.txt
new file mode 100644
index 0000000..6223698
--- /dev/null
+++ b/ChangeLog.d/x509write_csr_heap_alloc.txt
@@ -0,0 +1,4 @@
+Bugfix
+   * Reduce the stack consumption of mbedtls_x509write_csr_der() which
+     previously could lead to stack overflow on constrained devices.
+     Contributed by Doru Gucea and Simon Leet in #3464.
diff --git a/include/mbedtls/pem.h b/include/mbedtls/pem.h
index 16b6101..1d44741 100644
--- a/include/mbedtls/pem.h
+++ b/include/mbedtls/pem.h
@@ -139,17 +139,27 @@
  * \brief           Write a buffer of PEM information from a DER encoded
  *                  buffer.
  *
- * \param header    header string to write
- * \param footer    footer string to write
- * \param der_data  DER data to write
- * \param der_len   length of the DER data
- * \param buf       buffer to write to
- * \param buf_len   length of output buffer
- * \param olen      total length written / required (if buf_len is not enough)
+ * \param header    The header string to write.
+ * \param footer    The footer string to write.
+ * \param der_data  The DER data to encode.
+ * \param der_len   The length of the DER data \p der_data in Bytes.
+ * \param buf       The buffer to write to.
+ * \param buf_len   The length of the output buffer \p buf in Bytes.
+ * \param olen      The address at which to store the total length written
+ *                  or required (if \p buf_len is not enough).
  *
- * \return          0 on success, or a specific PEM or BASE64 error code. On
- *                  MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL olen is the required
- *                  size.
+ * \note            You may pass \c NULL for \p buf and \c 0 for \p buf_len
+ *                  to request the length of the resulting PEM buffer in
+ *                  `*olen`.
+ *
+ * \note            This function may be called with overlapping \p der_data
+ *                  and \p buf buffers.
+ *
+ * \return          \c 0 on success.
+ * \return          #MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL if \p buf isn't large
+ *                  enough to hold the PEM buffer. In  this case, `*olen` holds
+ *                  the required minimum size of \p buf.
+ * \return          Another PEM or BASE64 error code on other kinds of failure.
  */
 int mbedtls_pem_write_buffer( const char *header, const char *footer,
                       const unsigned char *der_data, size_t der_len,
diff --git a/library/x509write_crt.c b/library/x509write_crt.c
index 0fc94fe..c389980 100644
--- a/library/x509write_crt.c
+++ b/library/x509write_crt.c
@@ -101,39 +101,44 @@
     mbedtls_platform_zeroize( ctx, sizeof( mbedtls_x509write_cert ) );
 }
 
-void mbedtls_x509write_crt_set_version( mbedtls_x509write_cert *ctx, int version )
+void mbedtls_x509write_crt_set_version( mbedtls_x509write_cert *ctx,
+                                        int version )
 {
     ctx->version = version;
 }
 
-void mbedtls_x509write_crt_set_md_alg( mbedtls_x509write_cert *ctx, mbedtls_md_type_t md_alg )
+void mbedtls_x509write_crt_set_md_alg( mbedtls_x509write_cert *ctx,
+                                       mbedtls_md_type_t md_alg )
 {
     ctx->md_alg = md_alg;
 }
 
-void mbedtls_x509write_crt_set_subject_key( mbedtls_x509write_cert *ctx, mbedtls_pk_context *key )
+void mbedtls_x509write_crt_set_subject_key( mbedtls_x509write_cert *ctx,
+                                            mbedtls_pk_context *key )
 {
     ctx->subject_key = key;
 }
 
-void mbedtls_x509write_crt_set_issuer_key( mbedtls_x509write_cert *ctx, mbedtls_pk_context *key )
+void mbedtls_x509write_crt_set_issuer_key( mbedtls_x509write_cert *ctx,
+                                           mbedtls_pk_context *key )
 {
     ctx->issuer_key = key;
 }
 
 int mbedtls_x509write_crt_set_subject_name( mbedtls_x509write_cert *ctx,
-                                    const char *subject_name )
+                                            const char *subject_name )
 {
     return mbedtls_x509_string_to_names( &ctx->subject, subject_name );
 }
 
 int mbedtls_x509write_crt_set_issuer_name( mbedtls_x509write_cert *ctx,
-                                   const char *issuer_name )
+                                           const char *issuer_name )
 {
     return mbedtls_x509_string_to_names( &ctx->issuer, issuer_name );
 }
 
-int mbedtls_x509write_crt_set_serial( mbedtls_x509write_cert *ctx, const mbedtls_mpi *serial )
+int mbedtls_x509write_crt_set_serial( mbedtls_x509write_cert *ctx,
+                                      const mbedtls_mpi *serial )
 {
     int ret;
 
@@ -143,8 +148,9 @@
     return( 0 );
 }
 
-int mbedtls_x509write_crt_set_validity( mbedtls_x509write_cert *ctx, const char *not_before,
-                                const char *not_after )
+int mbedtls_x509write_crt_set_validity( mbedtls_x509write_cert *ctx,
+                                        const char *not_before,
+                                        const char *not_after )
 {
     if( strlen( not_before ) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1 ||
         strlen( not_after )  != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1 )
@@ -164,12 +170,12 @@
                                  int critical,
                                  const unsigned char *val, size_t val_len )
 {
-    return mbedtls_x509_set_extension( &ctx->extensions, oid, oid_len,
-                               critical, val, val_len );
+    return( mbedtls_x509_set_extension( &ctx->extensions, oid, oid_len,
+                                        critical, val, val_len ) );
 }
 
 int mbedtls_x509write_crt_set_basic_constraints( mbedtls_x509write_cert *ctx,
-                                         int is_ca, int max_pathlen )
+                                                 int is_ca, int max_pathlen )
 {
     int ret;
     unsigned char buf[9];
@@ -185,18 +191,21 @@
     {
         if( max_pathlen >= 0 )
         {
-            MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, buf, max_pathlen ) );
+            MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, buf,
+                                                               max_pathlen ) );
         }
         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_bool( &c, buf, 1 ) );
     }
 
     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED |
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf,
+                                                MBEDTLS_ASN1_CONSTRUCTED |
                                                 MBEDTLS_ASN1_SEQUENCE ) );
 
-    return mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_BASIC_CONSTRAINTS,
-                                        MBEDTLS_OID_SIZE( MBEDTLS_OID_BASIC_CONSTRAINTS ),
-                                        0, buf + sizeof(buf) - len, len );
+    return(
+        mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_BASIC_CONSTRAINTS,
+                             MBEDTLS_OID_SIZE( MBEDTLS_OID_BASIC_CONSTRAINTS ),
+                             0, buf + sizeof(buf) - len, len ) );
 }
 
 #if defined(MBEDTLS_SHA1_C)
@@ -208,7 +217,8 @@
     size_t len = 0;
 
     memset( buf, 0, sizeof(buf) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_pk_write_pubkey( &c, buf, ctx->subject_key ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+                mbedtls_pk_write_pubkey( &c, buf, ctx->subject_key ) );
 
     ret = mbedtls_sha1_ret( buf + sizeof( buf ) - len, len,
                             buf + sizeof( buf ) - 20 );
@@ -218,11 +228,13 @@
     len = 20;
 
     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_OCTET_STRING ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+            mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_OCTET_STRING ) );
 
-    return mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER,
-                                        MBEDTLS_OID_SIZE( MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER ),
-                                        0, buf + sizeof(buf) - len, len );
+    return mbedtls_x509write_crt_set_extension( ctx,
+                 MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER,
+                 MBEDTLS_OID_SIZE( MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER ),
+                 0, buf + sizeof(buf) - len, len );
 }
 
 int mbedtls_x509write_crt_set_authority_key_identifier( mbedtls_x509write_cert *ctx )
@@ -233,7 +245,8 @@
     size_t len = 0;
 
     memset( buf, 0, sizeof(buf) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_pk_write_pubkey( &c, buf, ctx->issuer_key ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+                          mbedtls_pk_write_pubkey( &c, buf, ctx->issuer_key ) );
 
     ret = mbedtls_sha1_ret( buf + sizeof( buf ) - len, len,
                             buf + sizeof( buf ) - 20 );
@@ -243,15 +256,19 @@
     len = 20;
 
     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0 ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+        mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0 ) );
 
     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                MBEDTLS_ASN1_SEQUENCE ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+                          mbedtls_asn1_write_tag( &c, buf,
+                                                  MBEDTLS_ASN1_CONSTRUCTED |
+                                                  MBEDTLS_ASN1_SEQUENCE ) );
 
-    return mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER,
-                                   MBEDTLS_OID_SIZE( MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER ),
-                                   0, buf + sizeof( buf ) - len, len );
+    return mbedtls_x509write_crt_set_extension(
+        ctx, MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER,
+        MBEDTLS_OID_SIZE( MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER ),
+        0, buf + sizeof( buf ) - len, len );
 }
 #endif /* MBEDTLS_SHA1_C */
 
@@ -298,8 +315,8 @@
         return( MBEDTLS_ERR_X509_INVALID_FORMAT );
 
     ret = mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_KEY_USAGE,
-                                       MBEDTLS_OID_SIZE( MBEDTLS_OID_KEY_USAGE ),
-                                       1, c, (size_t)ret );
+                                   MBEDTLS_OID_SIZE( MBEDTLS_OID_KEY_USAGE ),
+                                   1, c, (size_t)ret );
     if( ret != 0 )
         return( ret );
 
@@ -325,8 +342,8 @@
         return( ret );
 
     ret = mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_NS_CERT_TYPE,
-                                       MBEDTLS_OID_SIZE( MBEDTLS_OID_NS_CERT_TYPE ),
-                                       0, c, (size_t)ret );
+                                   MBEDTLS_OID_SIZE( MBEDTLS_OID_NS_CERT_TYPE ),
+                                   0, c, (size_t)ret );
     if( ret != 0 )
         return( ret );
 
@@ -348,7 +365,8 @@
                                              (const unsigned char *) t + 2,
                                              size - 2 ) );
         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( p, start, len ) );
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start, MBEDTLS_ASN1_UTC_TIME ) );
+        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start,
+                                             MBEDTLS_ASN1_UTC_TIME ) );
     }
     else
     {
@@ -356,15 +374,17 @@
                                                   (const unsigned char *) t,
                                                   size ) );
         MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( p, start, len ) );
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start, MBEDTLS_ASN1_GENERALIZED_TIME ) );
+        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start,
+                                             MBEDTLS_ASN1_GENERALIZED_TIME ) );
     }
 
     return( (int) len );
 }
 
-int mbedtls_x509write_crt_der( mbedtls_x509write_cert *ctx, unsigned char *buf, size_t size,
-                       int (*f_rng)(void *, unsigned char *, size_t),
-                       void *p_rng )
+int mbedtls_x509write_crt_der( mbedtls_x509write_cert *ctx,
+                               unsigned char *buf, size_t size,
+                               int (*f_rng)(void *, unsigned char *, size_t),
+                               void *p_rng )
 {
     int ret;
     const char *sig_oid;
@@ -372,15 +392,14 @@
     unsigned char *c, *c2;
     unsigned char hash[64];
     unsigned char sig[SIGNATURE_MAX_SIZE];
-    unsigned char tmp_buf[2048];
     size_t sub_len = 0, pub_len = 0, sig_and_oid_len = 0, sig_len;
     size_t len = 0;
     mbedtls_pk_type_t pk_alg;
 
     /*
-     * Prepare data to be signed in tmp_buf
+     * Prepare data to be signed at the end of the target buffer
      */
-    c = tmp_buf + sizeof( tmp_buf );
+    c = buf + size;
 
     /* Signature algorithm needed in TBS, and later for actual signature */
 
@@ -406,27 +425,36 @@
     /* Only for v3 */
     if( ctx->version == MBEDTLS_X509_CRT_VERSION_3 )
     {
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_extensions( &c, tmp_buf, ctx->extensions ) );
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                           MBEDTLS_ASN1_SEQUENCE ) );
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC |
-                                                           MBEDTLS_ASN1_CONSTRUCTED | 3 ) );
+        MBEDTLS_ASN1_CHK_ADD( len,
+                              mbedtls_x509_write_extensions( &c,
+                                                      buf, ctx->extensions ) );
+        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
+        MBEDTLS_ASN1_CHK_ADD( len,
+                              mbedtls_asn1_write_tag( &c, buf,
+                                                      MBEDTLS_ASN1_CONSTRUCTED |
+                                                      MBEDTLS_ASN1_SEQUENCE ) );
+        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
+        MBEDTLS_ASN1_CHK_ADD( len,
+                              mbedtls_asn1_write_tag( &c, buf,
+                                               MBEDTLS_ASN1_CONTEXT_SPECIFIC |
+                                               MBEDTLS_ASN1_CONSTRUCTED | 3 ) );
     }
 
     /*
      *  SubjectPublicKeyInfo
      */
-    MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_pk_write_pubkey_der( ctx->subject_key,
-                                                tmp_buf, c - tmp_buf ) );
+    MBEDTLS_ASN1_CHK_ADD( pub_len,
+                          mbedtls_pk_write_pubkey_der( ctx->subject_key,
+                                                       buf, c - buf ) );
     c -= pub_len;
     len += pub_len;
 
     /*
      *  Subject  ::=  Name
      */
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_names( &c, tmp_buf, ctx->subject ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+                          mbedtls_x509_write_names( &c, buf,
+                                                    ctx->subject ) );
 
     /*
      *  Validity ::= SEQUENCE {
@@ -435,32 +463,39 @@
      */
     sub_len = 0;
 
-    MBEDTLS_ASN1_CHK_ADD( sub_len, x509_write_time( &c, tmp_buf, ctx->not_after,
-                                            MBEDTLS_X509_RFC5280_UTC_TIME_LEN ) );
+    MBEDTLS_ASN1_CHK_ADD( sub_len,
+                          x509_write_time( &c, buf, ctx->not_after,
+                                        MBEDTLS_X509_RFC5280_UTC_TIME_LEN ) );
 
-    MBEDTLS_ASN1_CHK_ADD( sub_len, x509_write_time( &c, tmp_buf, ctx->not_before,
-                                            MBEDTLS_X509_RFC5280_UTC_TIME_LEN ) );
+    MBEDTLS_ASN1_CHK_ADD( sub_len,
+                          x509_write_time( &c, buf, ctx->not_before,
+                                        MBEDTLS_X509_RFC5280_UTC_TIME_LEN ) );
 
     len += sub_len;
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, sub_len ) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                    MBEDTLS_ASN1_SEQUENCE ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, sub_len ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+                          mbedtls_asn1_write_tag( &c, buf,
+                                                  MBEDTLS_ASN1_CONSTRUCTED |
+                                                  MBEDTLS_ASN1_SEQUENCE ) );
 
     /*
      *  Issuer  ::=  Name
      */
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_names( &c, tmp_buf, ctx->issuer ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_names( &c, buf,
+                                                         ctx->issuer ) );
 
     /*
      *  Signature   ::=  AlgorithmIdentifier
      */
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_algorithm_identifier( &c, tmp_buf,
-                       sig_oid, strlen( sig_oid ), 0 ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+                          mbedtls_asn1_write_algorithm_identifier( &c, buf,
+                                              sig_oid, strlen( sig_oid ), 0 ) );
 
     /*
      *  Serial   ::=  INTEGER
      */
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &c, tmp_buf, &ctx->serial ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &c, buf,
+                                                       &ctx->serial ) );
 
     /*
      *  Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
@@ -470,48 +505,67 @@
     if( ctx->version != MBEDTLS_X509_CRT_VERSION_1 )
     {
         sub_len = 0;
-        MBEDTLS_ASN1_CHK_ADD( sub_len, mbedtls_asn1_write_int( &c, tmp_buf, ctx->version ) );
+        MBEDTLS_ASN1_CHK_ADD( sub_len,
+                              mbedtls_asn1_write_int( &c, buf, ctx->version ) );
         len += sub_len;
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, sub_len ) );
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC |
-                                                           MBEDTLS_ASN1_CONSTRUCTED | 0 ) );
+        MBEDTLS_ASN1_CHK_ADD( len,
+                              mbedtls_asn1_write_len( &c, buf, sub_len ) );
+        MBEDTLS_ASN1_CHK_ADD( len,
+                              mbedtls_asn1_write_tag( &c, buf,
+                                               MBEDTLS_ASN1_CONTEXT_SPECIFIC |
+                                               MBEDTLS_ASN1_CONSTRUCTED | 0 ) );
     }
 
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                       MBEDTLS_ASN1_SEQUENCE ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+                mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED |
+                                                     MBEDTLS_ASN1_SEQUENCE ) );
 
     /*
      * Make signature
      */
+
+    /* Compute hash of CRT. */
     if( ( ret = mbedtls_md( mbedtls_md_info_from_type( ctx->md_alg ), c,
                             len, hash ) ) != 0 )
     {
         return( ret );
     }
 
-    if( ( ret = mbedtls_pk_sign( ctx->issuer_key, ctx->md_alg, hash, 0, sig, &sig_len,
-                         f_rng, p_rng ) ) != 0 )
+    if( ( ret = mbedtls_pk_sign( ctx->issuer_key, ctx->md_alg,
+                                 hash, 0, sig, &sig_len,
+                                 f_rng, p_rng ) ) != 0 )
     {
         return( ret );
     }
 
-    /*
-     * Write data to output buffer
-     */
+    /* Move CRT to the front of the buffer to have space
+     * for the signature. */
+    memmove( buf, c, len );
+    c = buf + len;
+
+    /* Add signature at the end of the buffer,
+     * making sure that it doesn't underflow
+     * into the CRT buffer. */
     c2 = buf + size;
-    MBEDTLS_ASN1_CHK_ADD( sig_and_oid_len, mbedtls_x509_write_sig( &c2, buf,
+    MBEDTLS_ASN1_CHK_ADD( sig_and_oid_len, mbedtls_x509_write_sig( &c2, c,
                                         sig_oid, sig_oid_len, sig, sig_len ) );
 
-    if( len > (size_t)( c2 - buf ) )
-        return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
+    /*
+     * Memory layout after this step:
+     *
+     * buf       c=buf+len                c2            buf+size
+     * [CRT0,...,CRTn, UNUSED, ..., UNUSED, SIG0, ..., SIGm]
+     */
 
-    c2 -= len;
-    memcpy( c2, c, len );
+    /* Move raw CRT to just before the signature. */
+    c = c2 - len;
+    memmove( c, buf, len );
 
     len += sig_and_oid_len;
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c2, buf, len ) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c2, buf, MBEDTLS_ASN1_CONSTRUCTED |
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf,
+                                                 MBEDTLS_ASN1_CONSTRUCTED |
                                                  MBEDTLS_ASN1_SEQUENCE ) );
 
     return( (int) len );
@@ -521,23 +575,23 @@
 #define PEM_END_CRT             "-----END CERTIFICATE-----\n"
 
 #if defined(MBEDTLS_PEM_WRITE_C)
-int mbedtls_x509write_crt_pem( mbedtls_x509write_cert *crt, unsigned char *buf, size_t size,
-                       int (*f_rng)(void *, unsigned char *, size_t),
-                       void *p_rng )
+int mbedtls_x509write_crt_pem( mbedtls_x509write_cert *crt,
+                               unsigned char *buf, size_t size,
+                               int (*f_rng)(void *, unsigned char *, size_t),
+                               void *p_rng )
 {
     int ret;
-    unsigned char output_buf[4096];
-    size_t olen = 0;
+    size_t olen;
 
-    if( ( ret = mbedtls_x509write_crt_der( crt, output_buf, sizeof(output_buf),
+    if( ( ret = mbedtls_x509write_crt_der( crt, buf, size,
                                    f_rng, p_rng ) ) < 0 )
     {
         return( ret );
     }
 
     if( ( ret = mbedtls_pem_write_buffer( PEM_BEGIN_CRT, PEM_END_CRT,
-                                  output_buf + sizeof(output_buf) - ret,
-                                  ret, buf, size, &olen ) ) != 0 )
+                                          buf + size - ret, ret,
+                                          buf, size, &olen ) ) != 0 )
     {
         return( ret );
     }
diff --git a/library/x509write_csr.c b/library/x509write_csr.c
index d1b0716..ac5eaaa 100644
--- a/library/x509write_csr.c
+++ b/library/x509write_csr.c
@@ -81,6 +81,14 @@
 #define SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
 #endif
 
+#if defined(MBEDTLS_PLATFORM_C)
+#include "mbedtls/platform.h"
+#else
+#include <stdlib.h>
+#define mbedtls_calloc    calloc
+#define mbedtls_free      free
+#endif
+
 void mbedtls_x509write_csr_init( mbedtls_x509write_csr *ctx )
 {
     memset( ctx, 0, sizeof( mbedtls_x509write_csr ) );
@@ -187,71 +195,85 @@
     return( 0 );
 }
 
-int mbedtls_x509write_csr_der( mbedtls_x509write_csr *ctx, unsigned char *buf, size_t size,
-                       int (*f_rng)(void *, unsigned char *, size_t),
-                       void *p_rng )
+static int x509write_csr_der_internal( mbedtls_x509write_csr *ctx,
+                                 unsigned char *buf,
+                                 size_t size,
+                                 unsigned char *sig,
+                                 int (*f_rng)(void *, unsigned char *, size_t),
+                                 void *p_rng )
 {
     int ret;
     const char *sig_oid;
     size_t sig_oid_len = 0;
     unsigned char *c, *c2;
     unsigned char hash[64];
-    unsigned char sig[SIGNATURE_MAX_SIZE];
-    unsigned char tmp_buf[2048];
     size_t pub_len = 0, sig_and_oid_len = 0, sig_len;
     size_t len = 0;
     mbedtls_pk_type_t pk_alg;
 
-    /*
-     * Prepare data to be signed in tmp_buf
-     */
-    c = tmp_buf + sizeof( tmp_buf );
+    /* Write the CSR backwards starting from the end of buf */
+    c = buf + size;
 
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_extensions( &c, tmp_buf, ctx->extensions ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_extensions( &c, buf,
+                                                           ctx->extensions ) );
 
     if( len )
     {
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                        MBEDTLS_ASN1_SEQUENCE ) );
+        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
+        MBEDTLS_ASN1_CHK_ADD( len,
+            mbedtls_asn1_write_tag(
+                &c, buf,
+                MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) );
 
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                        MBEDTLS_ASN1_SET ) );
+        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
+        MBEDTLS_ASN1_CHK_ADD( len,
+            mbedtls_asn1_write_tag(
+                &c, buf,
+                MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET ) );
 
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_oid( &c, tmp_buf, MBEDTLS_OID_PKCS9_CSR_EXT_REQ,
-                                          MBEDTLS_OID_SIZE( MBEDTLS_OID_PKCS9_CSR_EXT_REQ ) ) );
+        MBEDTLS_ASN1_CHK_ADD( len,
+            mbedtls_asn1_write_oid(
+                &c, buf, MBEDTLS_OID_PKCS9_CSR_EXT_REQ,
+                MBEDTLS_OID_SIZE( MBEDTLS_OID_PKCS9_CSR_EXT_REQ ) ) );
 
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
-        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                        MBEDTLS_ASN1_SEQUENCE ) );
+        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
+        MBEDTLS_ASN1_CHK_ADD( len,
+            mbedtls_asn1_write_tag(
+                &c, buf,
+                MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) );
     }
 
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                    MBEDTLS_ASN1_CONTEXT_SPECIFIC ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+        mbedtls_asn1_write_tag(
+            &c, buf,
+            MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC ) );
 
     MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_pk_write_pubkey_der( ctx->key,
-                                                tmp_buf, c - tmp_buf ) );
+                                                              buf, c - buf ) );
     c -= pub_len;
     len += pub_len;
 
     /*
      *  Subject  ::=  Name
      */
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_names( &c, tmp_buf, ctx->subject ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_names( &c, buf,
+                                                         ctx->subject ) );
 
     /*
      *  Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
      */
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, tmp_buf, 0 ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, buf, 0 ) );
 
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, tmp_buf, len ) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, tmp_buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                    MBEDTLS_ASN1_SEQUENCE ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+        mbedtls_asn1_write_tag(
+            &c, buf,
+            MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) );
 
     /*
-     * Prepare signature
+     * Sign the written CSR data into the sig buffer
+     * Note: hash errors can happen only after an internal error
      */
     ret = mbedtls_md( mbedtls_md_info_from_type( ctx->md_alg ), c, len, hash );
     if( ret != 0 )
@@ -271,32 +293,68 @@
         return( MBEDTLS_ERR_X509_INVALID_ALG );
 
     if( ( ret = mbedtls_oid_get_oid_by_sig_alg( pk_alg, ctx->md_alg,
-                                                &sig_oid, &sig_oid_len ) ) != 0 )
+                                              &sig_oid, &sig_oid_len ) ) != 0 )
     {
         return( ret );
     }
 
     /*
-     * Write data to output buffer
+     * Move the written CSR data to the start of buf to create space for
+     * writing the signature into buf.
+     */
+    memmove( buf, c, len );
+
+    /*
+     * Write sig and its OID into buf backwards from the end of buf.
+     * Note: mbedtls_x509_write_sig will check for c2 - ( buf + len ) < sig_len
+     * and return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL if needed.
      */
     c2 = buf + size;
-    MBEDTLS_ASN1_CHK_ADD( sig_and_oid_len, mbedtls_x509_write_sig( &c2, buf,
-                                        sig_oid, sig_oid_len, sig, sig_len ) );
+    MBEDTLS_ASN1_CHK_ADD( sig_and_oid_len,
+        mbedtls_x509_write_sig( &c2, buf + len, sig_oid, sig_oid_len,
+                                sig, sig_len ) );
 
-    if( len > (size_t)( c2 - buf ) )
-        return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
-
+    /*
+     * Compact the space between the CSR data and signature by moving the
+     * CSR data to the start of the signature.
+     */
     c2 -= len;
-    memcpy( c2, c, len );
+    memmove( c2, buf, len );
 
+    /* ASN encode the total size and tag the CSR data with it. */
     len += sig_and_oid_len;
     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c2, buf, len ) );
-    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c2, buf, MBEDTLS_ASN1_CONSTRUCTED |
-                                                 MBEDTLS_ASN1_SEQUENCE ) );
+    MBEDTLS_ASN1_CHK_ADD( len,
+        mbedtls_asn1_write_tag(
+            &c2, buf,
+            MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) );
+
+    /* Zero the unused bytes at the start of buf */
+    memset( buf, 0, c2 - buf);
 
     return( (int) len );
 }
 
+int mbedtls_x509write_csr_der( mbedtls_x509write_csr *ctx, unsigned char *buf,
+                               size_t size,
+                               int (*f_rng)(void *, unsigned char *, size_t),
+                               void *p_rng )
+{
+    int ret;
+    unsigned char *sig;
+
+    if( ( sig = mbedtls_calloc( 1, SIGNATURE_MAX_SIZE ) ) == NULL )
+    {
+        return( MBEDTLS_ERR_X509_ALLOC_FAILED );
+    }
+
+    ret = x509write_csr_der_internal( ctx, buf, size, sig, f_rng, p_rng );
+
+    mbedtls_free( sig );
+
+    return( ret );
+}
+
 #define PEM_BEGIN_CSR           "-----BEGIN CERTIFICATE REQUEST-----\n"
 #define PEM_END_CSR             "-----END CERTIFICATE REQUEST-----\n"
 
diff --git a/tests/git-scripts/pre-commit.sh b/tests/git-scripts/pre-commit.sh
new file mode 100755
index 0000000..4365686
--- /dev/null
+++ b/tests/git-scripts/pre-commit.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+# pre-commit.sh
+#
+# Copyright (c) 2017, ARM Limited, All Rights Reserved
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This file is part of Mbed TLS (https://tls.mbed.org)
+
+# Purpose
+#
+# This script does quick sanity checks before commiting:
+#   - check that generated files are up-to-date.
+#
+# It is meant to be called as a git pre-commit hook, see README.md.
+#
+# From the git sample pre-commit hook:
+#   Called by "git commit" with no arguments.  The hook should
+#   exit with non-zero status after issuing an appropriate message if
+#   it wants to stop the commit.
+
+set -eu
+
+tests/scripts/check-generated-files.sh
diff --git a/tests/scripts/check-generated-files.sh b/tests/scripts/check-generated-files.sh
index 516437e..5776daa 100755
--- a/tests/scripts/check-generated-files.sh
+++ b/tests/scripts/check-generated-files.sh
@@ -50,11 +50,29 @@
 
 set -eu
 
+if [ $# -ne 0 ] && [ "$1" = "--help" ]; then
+    cat <<EOF
+$0 [-u]
+This script checks that all generated file are up-to-date. If some aren't, by
+default the scripts reports it and exits in error; with the -u option, it just
+updates them instead.
+
+  -u    Update the files rather than return an error for out-of-date files.
+EOF
+    exit
+fi
+
 if [ -d library -a -d include -a -d tests ]; then :; else
     echo "Must be run from mbed TLS root" >&2
     exit 1
 fi
 
+UPDATE=
+if [ $# -ne 0 ] && [ "$1" = "-u" ]; then
+    shift
+    UPDATE='y'
+fi
+
 check()
 {
     SCRIPT=$1
@@ -80,9 +98,15 @@
     for FILE in $FILES; do
         if ! diff $FILE $FILE.bak >/dev/null 2>&1; then
             echo "'$FILE' was either modified or deleted by '$SCRIPT'"
-            exit 1
+            if [ -z "$UPDATE" ]; then
+                exit 1
+            fi
         fi
-        mv $FILE.bak $FILE
+        if [ -z "$UPDATE" ]; then
+            mv $FILE.bak $FILE
+        else
+            rm $FILE.bak
+        fi
 
         if [ -d $TO_CHECK ]; then
             # Create a grep regular expression that we can check against the
@@ -99,7 +123,9 @@
         # Check if there are any new files
         if ls -1 $TO_CHECK | grep -v "$PATTERN" >/dev/null 2>&1; then
             echo "Files were created by '$SCRIPT'"
-            exit 1
+            if [ -z "$UPDATE" ]; then
+                exit 1
+            fi
         fi
     fi
 }