binary FEATURE accept base64 values with newlines

Fixes #1793
diff --git a/src/plugins_types/binary.c b/src/plugins_types/binary.c
index 114a17d..5317e01 100644
--- a/src/plugins_types/binary.c
+++ b/src/plugins_types/binary.c
@@ -211,6 +211,51 @@
     return LY_SUCCESS;
 }
 
+/**
+ * @brief Remove all newlines from a base64 string if present.
+ *
+ * @param[in,out] value Value, may be dynamic and modified.
+ * @param[in,out] value_len Length of @p value, is updated.
+ * @param[in,out] options Type options, are updated.
+ * @param[out] err Error information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_newlines(char **value, size_t *value_len, uint32_t *options, struct ly_err_item **err)
+{
+    char *val;
+    size_t len;
+
+    if ((*value_len < 65) || ((*value)[64] != '\n')) {
+        /* no newlines */
+        return LY_SUCCESS;
+    }
+
+    if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
+        /* make the value dynamic so we can modify it */
+        *value = strndup(*value, *value_len);
+        LY_CHECK_RET(!*value, LY_EMEM);
+        *options |= LYPLG_TYPE_STORE_DYNAMIC;
+    }
+
+    val = *value;
+    len = *value_len;
+    while (len > 64) {
+        if (val[64] != '\n') {
+            /* missing, error */
+            return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
+        }
+
+        /* remove the newline */
+        memmove(val + 64, val + 65, len - 64);
+        --(*value_len);
+        val += 64;
+        len -= 65;
+    }
+
+    return LY_SUCCESS;
+}
+
 LIBYANG_API_DEF LY_ERR
 lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
@@ -252,8 +297,12 @@
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
     LY_CHECK_GOTO(ret, cleanup);
 
-    /* validate */
     if (format != LY_VALUE_CANON) {
+        /* accept newline every 64 characters (PEM data) */
+        ret = binary_base64_newlines((char **)&value, &value_len, &options, err);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* validate */
         ret = binary_base64_validate(value, value_len, type_bin, err);
         LY_CHECK_GOTO(ret, cleanup);
     }
diff --git a/tests/utests/types/binary.c b/tests/utests/types/binary.c
index 2df1cc9..e96efab 100644
--- a/tests/utests/types/binary.c
+++ b/tests/utests/types/binary.c
@@ -125,6 +125,34 @@
     assert_ptr_equal(value.realtype, lysc_type);
     type->free(UTEST_LYCTX, &value);
 
+    /* newlines after every 64 chars */
+    val = "MIIEAzCCAuugAwIBAgIURc4sipHvJSlNrQIhRhZilBvV4RowDQYJKoZIhvcNAQEL\n"
+            "BQAwgZAxCzAJBgNVBAYTAkNaMRYwFAYDVQQIDA1Tb3V0aCBNb3JhdmlhMQ0wCwYD\n"
+            "VQQHDARCcm5vMRgwFgYDVQQKDA9DRVNORVQgei5zLnAuby4xDDAKBgNVBAsMA1RN\n"
+            "QzETMBEGA1UEAwwKZXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBs\n"
+            "ZS5vcmcwHhcNMjEwOTAzMTAyMTAxWhcNMzEwOTAxMTAyMTAxWjCBkDELMAkGA1UE\n"
+            "BhMCQ1oxFjAUBgNVBAgMDVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xGDAW\n"
+            "BgNVBAoMD0NFU05FVCB6LnMucC5vLjEMMAoGA1UECwwDVE1DMRMwEQYDVQQDDApl\n"
+            "eGFtcGxlIENBMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLm9yZzCCASIwDQYJ\n"
+            "KoZIhvcNAQEBBQADggEPADCCAQoCggEBAN4Ld3JDDocyy9KXNJhEUPeZpQW3UdUN\n"
+            "Xloeh5n/bxasgThkBuQ7oF/nKyVUe517U1CJA993ZIc0jhIWThAnqXkz70DX5EZ7\n"
+            "ancPd01MidA6T8k1RYYJWr+vyIRYYBYzK7LSnU6wMWqPTgzZB+KMWwb065ooLEB5\n"
+            "XwqAeTIMPLRqM1Galewl4ZSuRJnrXxRjfF3AWNyC9dZw6wIg8cppvoLdBGQiFJQf\n"
+            "9SgiVy+HyedAytFEixqKAAIgQUJwhCgbEd6jGFbeaL8HT4MFp1VmaaUBQMkZj/Gn\n"
+            "KBwCk5BEMu76EN1pzHc4Dd6DabQNGCnsqOPe31yhQGmNFy9R6zNnWZMCAwEAAaNT\n"
+            "MFEwHQYDVR0OBBYEFM7w/pO8vk5oowvWPoCKo0RW/JcnMB8GA1UdIwQYMBaAFM7w\n"
+            "/pO8vk5oowvWPoCKo0RW/JcnMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL\n"
+            "BQADggEBAG/xfYuRKnCyiwYC/K7kAjHmCNnLCr1mx8P1ECsSJPme3OThDTNeGf8i\n"
+            "N2952tGmMFDa+DaAwPc6Gt3cWTb/NYMTLWlt2yj5rJAcLXxIU0SMafBf+F7E/R8A\n"
+            "b/HDDjs0pQaJ0EJhQJVkMdfj3Wq9l0dJT5iEBUrUQflDufiMdEJEIGKZh86MgzEL\n"
+            "bcn1QX8dlLc91M2OifWStqLzXPicG+jjuoPUceC0flMQDb2qx03sxvJKfYfS5ArA\n"
+            "CqvdWyXLoP7DI9THJrMI/vBHJKpl4Wtmsh2OLn9VHauFMzPSGke5GwjXCpbXGepj\n"
+            "9qWN8Gd/FWgSDH2OBvZ6aHdB1pPjN9k=";
+    assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+            0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+    assert_ptr_equal(value.realtype, lysc_type);
+    type->free(UTEST_LYCTX, &value);
+
     /* empty value */
     val = "";
     assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),