This is an automated email from the ASF dual-hosted git repository.

sandreoli pushed a commit to branch issue51
in repository https://gitbox.apache.org/repos/asf/incubator-milagro-crypto-c.git

commit 6662a6f16652d8526f0323f83535578a43329a05
Author: samuele-andreoli <[email protected]>
AuthorDate: Tue Nov 12 12:56:43 2019 +0000

    add benchmark and example
---
 benchmark/CMakeLists.txt          |   6 +
 benchmark/benchtest_paillier.c.in | 356 ++++++++++++++++++++++++++++++++++++++
 examples/CMakeLists.txt           |   5 +
 examples/example_paillier.c       | 280 ++++++++++++++++++++++++++++++
 4 files changed, 647 insertions(+)

diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt
index b1e7c37..af05ab7 100644
--- a/benchmark/CMakeLists.txt
+++ b/benchmark/CMakeLists.txt
@@ -50,3 +50,9 @@ foreach(level ${AMCL_RSA})
   add_executable(benchtest_rsa_${TFF} ${benchtest_rsa_${TFF}_GEN_SRCS})
   target_link_libraries(benchtest_rsa_${TFF} PRIVATE amcl_rsa_${TFF})
 endforeach()
+
+if(${BUILD_PAILLIER})
+  amcl_configure_file_core(benchtest_paillier.c.in benchtest_paillier.c 
benchtest_paillier_GEN_SRCS)
+  add_executable(benchtest_paillier ${benchtest_paillier_GEN_SRCS})
+  target_link_libraries(benchtest_paillier PRIVATE amcl_paillier)
+endif()
diff --git a/benchmark/benchtest_paillier.c.in 
b/benchmark/benchtest_paillier.c.in
new file mode 100644
index 0000000..2cfb594
--- /dev/null
+++ b/benchmark/benchtest_paillier.c.in
@@ -0,0 +1,356 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+*/
+
+/*
+   Benchmark Paillier crypto system.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "ff_8192.h"
+#include "ff_4096.h"
+#include "ff_2048.h"
+#include "randapi.h"
+#include "paillier.h"
+#include "amcl.h"
+#include "arch.h"
+
+#define MIN_TIME 5.0
+#define MIN_ITERS 10
+#define NTHREADS 2
+
+// https://sourceforge.net/p/predef/wiki/Compilers/
+static void print_compiler_info()
+{
+#if defined(__clang__)
+    printf("Compiler:\t\tclang (%s)\n", __clang_version__);
+#elif defined(__GNUC_PATCHLEVEL__)
+    printf("Compiler:\t\tgcc (%d.%d.%d)\n", __GNUC__, __GNUC_MINOR__, 
__GNUC_PATCHLEVEL__);
+#elif defined(__GNUC_MINOR__)
+    printf("Compiler:\t\tgcc (%d.%d)\n", __GNUC__, __GNUC_MINOR__);
+#elif defined(__INTEL_COMPILER)
+    printf("Compiler:\t\tIntel C/C++ (%d)\n", __INTEL_COMPILER);
+#elif defined(_MSC_FULL_VER)
+    printf("Compiler:\tMicrosoft C/C++ (%d)\n", _MSC_FULL_VER);
+#elif defined(__CC_ARM)
+    printf("Compiler:\t\tARM C/C++ (%d)\n", __ARMCC_VERSION);
+#else
+    printf("Compiler:\t\tUnknown"\n);
+#endif
+}
+
+#if defined(__ANDROID__)
+#include <android/api-level.h>
+#endif
+
+// https://sourceforge.net/p/predef/wiki/Architectures/
+// https://sourceforge.net/p/predef/wiki/OperatingSystems/
+static void print_platform_info()
+{
+#if defined(__linux__)
+    printf("Target platform:\tLinux ");
+    
+#if defined(__x86_64__)
+    printf("(64-bit)\n");
+#else
+    printf("(32-bit)\n");
+#endif
+
+#if defined(__ANDROID__)
+    printf("Target platform:\tAndroid %d\n", __ANDROID_API__);
+#endif
+
+#elif defined(_WIN64)
+    printf("Target platform:\tWindows (64-bit)\n");
+#elif defined(_WIN32)
+    printf("Target platform:\tWindows (32-bit)\n");
+#elif defined(__APPLE__)
+    printf("Target platform:\tMac OS\n");
+#else
+    printf("Target platform:\tUnknown\n");
+#endif
+}
+
+
+static void print_paillier_configuration()
+{
+    printf("Project Version:\t@BUILD_VERSION@\n");    
+    printf("CFLAGS:\t\t\t@CMAKE_C_FLAGS@\n");
+    printf("Build type:\t\t@CMAKE_BUILD_TYPE@\n");    
+    printf("AMCL 
Version:\t\t@AMCL_VERSION_MAJOR@.@AMCL_VERSION_MINOR@.@AMCL_VERSION_PATCH@\n");
+    printf("AMCL Detected OS:\t@OS@\n");
+    printf("AMCL CHUNK:\t\t%d\n", CHUNK);
+}
+
+static void print_system_info()
+{
+    printf("Configuration info\n");
+    printf("==================\n");
+    print_platform_info();
+    print_compiler_info();
+    print_paillier_configuration();
+    printf("\n");
+}
+
+
+int paillier(csprng *RNG)
+{
+    int rc;
+
+    int iterations;
+    clock_t start;
+    double elapsed;
+
+    char p[FS_2048];
+    octet P = {0,sizeof(p),p};
+    char q[FS_2048];
+    octet Q = {0,sizeof(q),q};
+
+    char p1[FS_2048];
+    octet P1 = {0,sizeof(p1),p1};
+    char q1[FS_2048];
+    octet Q1 = {0,sizeof(q1),q1};
+
+    char n[FS_2048] = {0};
+    octet N = {0,sizeof(n),n};
+    char g[FS_2048];
+    octet G = {0,sizeof(g),g};
+
+    char l[FS_2048] = {0};
+    octet L = {0,sizeof(l),l};
+
+    char m[FS_2048] = {0};
+    octet M = {0,sizeof(m),m};
+
+    // Plaintext to encrypt
+    char ptin[NTHREADS][FS_2048];
+    octet PTIN[NTHREADS];
+    char ptout[NTHREADS][FS_2048];
+    octet PTOUT[NTHREADS];
+
+    // Constant value for multiplication
+    char ptko[NTHREADS][FS_2048];
+    octet PTK[NTHREADS];
+
+    // Encrypted PTIN values
+    char cto[NTHREADS][FS_4096];
+    octet CT[NTHREADS];
+
+    // Homomorphic multiplicaton of plaintext by a constant ciphertext
+    char cta[NTHREADS][FS_4096];
+    octet CTA[NTHREADS];
+
+    // Homomorphic addition of ciphertext
+    char cto3[FS_4096] = {0};
+    octet CT3 = {0,sizeof(cto3),cto3};
+
+    // Output plaintext of addition of homomorphic multiplication values
+    char pto3[FS_2048] = {0};
+    octet PT3 = {sizeof(pto3),sizeof(pto3),pto3};
+
+    // Expected ouput is 26 / 0x1a i.e. 2*3 + 4*5
+    int values[NTHREADS] = {2,4};
+    int kvalues[NTHREADS] = {3,5};
+
+    print_system_info();
+
+    // Initialize octets
+    for(int i=0; i<NTHREADS; i++)
+    {
+        memset(ptin[i], 0, FS_2048*sizeof(ptin[i][0]));
+        PTIN[i].max = FS_2048;
+        PTIN[i].len = 0;
+        PTIN[i].val = ptin[i];
+
+        memset(ptout[i], 0, FS_2048*sizeof(ptout[i][0]));
+        PTOUT[i].max = FS_2048;
+        PTOUT[i].len = 0;
+        PTOUT[i].val = ptout[i];
+
+        memset(ptko[i], 0, FS_2048*sizeof(ptko[i][0]));
+        PTK[i].max = FS_2048;
+        PTK[i].len = 0;
+        PTK[i].val = ptko[i];
+
+        memset(cto[i], 0, FS_4096*sizeof(cto[i][0]));
+        CT[i].max = FS_4096;
+        CT[i].len = 0;
+        CT[i].val = cto[i];
+
+        memset(cta[i], 0, FS_4096*sizeof(cta[i][0]));
+        CTA[i].max = FS_4096;
+        CTA[i].len = 0;
+        CTA[i].val = cta[i];
+    }
+
+    printf("Timing info\n");
+    printf("===========\n");
+    
+    // Generating public/private key pair
+    iterations=0;
+    start=clock();
+    do
+    {
+        PAILLIER_KEY_PAIR(RNG, &P, &Q, &N, &G, &L, &M);
+        iterations++;
+        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
+    }
+    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
+    elapsed=1000.0*elapsed/iterations;
+    printf("PAILLIER_KEY_PAIR\t%8d iterations\t",iterations);
+    printf("%8.2lf ms per iteration\n",elapsed);
+
+    // Set plaintext values
+    for(int i=0; i<NTHREADS; i++)
+    {
+        BIG_1024_58 pt[FFLEN_2048];
+        FF_2048_init(pt, values[i],FFLEN_2048);
+        FF_2048_toOctet(&PTIN[i], pt, FFLEN_2048);
+
+        BIG_1024_58 ptk[FFLEN_2048];
+        FF_2048_init(ptk, kvalues[i],FFLEN_2048);
+        FF_2048_toOctet(&PTK[i], ptk, FFLEN_2048);
+    }
+
+    // Encrypt plaintext
+    iterations=0;
+    start=clock();
+    do
+    {
+        PAILLIER_ENCRYPT(RNG, &N, &G, &PTIN[0], &CT[0], NULL);
+        iterations++;
+        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
+    }
+    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
+    elapsed=1000.0*elapsed/iterations;
+    printf("PAILLIER_ENCRYPT\t%8d iterations\t",iterations);
+    printf("%8.2lf ms per iteration\n",elapsed);
+
+    rc = PAILLIER_ENCRYPT(RNG, &N, &G, &PTIN[1], &CT[1], NULL);
+    if (rc)
+    {
+        fprintf(stderr, "ERROR PAILLIER_ENCRYPT rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    // Multiple by constant
+    iterations=0;
+    start=clock();
+    do
+    {
+        PAILLIER_MULT(&N, &CT[0], &PTK[0], &CTA[0]);
+        iterations++;
+        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
+    }
+    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
+    elapsed=1000.0*elapsed/iterations;
+    printf("PAILLIER_MULT\t\t%8d iterations\t",iterations);
+    printf("%8.2lf ms per iteration\n",elapsed);
+
+    rc = PAILLIER_MULT(&N, &CT[1], &PTK[1], &CTA[1]);
+    if (rc)
+    {
+        fprintf(stderr, "ERROR PAILLIER_MULT rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    // Homomorphic addition
+    iterations=0;
+    start=clock();
+    do
+    {
+        PAILLIER_ADD(&N, &CTA[0], &CTA[1], &CT3);
+        iterations++;
+        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
+    }
+    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
+    elapsed=1000.0*elapsed/iterations;
+    printf("PAILLIER_ADD\t\t%8d iterations\t",iterations);
+    printf("%8.2lf ms per iteration\n",elapsed);
+
+    // Homomorphic addition
+    iterations=0;
+    start=clock();
+    do
+    {
+        PAILLIER_DECRYPT(&N, &L, &M, &CT3, &PT3);
+        iterations++;
+        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
+    }
+    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
+    elapsed=1000.0*elapsed/iterations;
+    printf("PAILLIER_DECRYPT\t%8d iterations\t",iterations);
+    printf("%8.2lf ms per iteration\n\n",elapsed);
+
+    printf("Size info\n");
+    printf("=========\n");
+    
+    printf("public key n\t%d bytes\n", FS_2048);
+    printf("public key g\t%d bytes\n", FS_2048);
+    printf("secret key l\t%d bytes\n", FS_2048);
+    printf("secret key m\t%d bytes\n", FS_2048);
+    printf("ciphertext\t%d bytes\n", FS_4096);
+
+#ifdef DEBUG
+    printf("PT3: ");
+    OCT_output(&PT3);
+    printf("\n");
+#endif
+
+    OCT_clear(&P);
+    OCT_clear(&Q);
+    OCT_clear(&P1);
+    OCT_clear(&Q1);
+    OCT_clear(&N);
+    OCT_clear(&G);
+    OCT_clear(&L);
+    OCT_clear(&M);
+    OCT_clear(&CT3);
+    OCT_clear(&PT3);
+    for(int i=0; i<NTHREADS; i++)
+    {
+        OCT_clear(&PTIN[i]);
+        OCT_clear(&PTOUT[i]);
+        OCT_clear(&CT[i]);
+        OCT_clear(&CTA[i]);
+    }
+    return 0;
+}
+
+int main()
+{
+    char* seedHex = "78d0fb6705ce77dee47d03eb5b9c5d30";
+    char seed[16] = {0};
+    octet SEED = {sizeof(seed),sizeof(seed),seed};
+
+    // CSPRNG
+    csprng RNG;
+
+    // fake random source
+    OCT_fromHex(&SEED,seedHex);
+
+    // initialise strong RNG
+    CREATE_CSPRNG(&RNG,&SEED);
+
+    paillier(&RNG);
+
+    KILL_CSPRNG(&RNG);
+}
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 785630e..d22cffe 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -66,3 +66,8 @@ foreach(level ${AMCL_RSA})
   target_link_libraries(example_rsa_${TFF} PRIVATE amcl_rsa_${TFF})
 
 endforeach()
+
+if(BUILD_PAILLIER)
+  add_executable(example_paillier example_paillier.c)
+  target_link_libraries(example_paillier PRIVATE amcl_paillier)
+endif()
diff --git a/examples/example_paillier.c b/examples/example_paillier.c
new file mode 100644
index 0000000..884929f
--- /dev/null
+++ b/examples/example_paillier.c
@@ -0,0 +1,280 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+*/
+
+/*
+   Example of Paillier crypto system.
+
+   Homomorphic multiplicaton of ciphertext by a constant and
+   homomorphic addition of ciphertexts
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "ff_8192.h"
+#include "ff_4096.h"
+#include "ff_2048.h"
+#include "randapi.h"
+#include "paillier.h"
+
+#define NTHREADS 2
+
+int paillier(csprng *RNG)
+{
+    int rc;
+    char p[FS_2048];
+    octet P = {0,sizeof(p),p};
+    char q[FS_2048];
+    octet Q = {0,sizeof(q),q};
+
+    char n[FS_2048] = {0};
+    octet N = {0,sizeof(n),n};
+    char g[FS_2048];
+    octet G = {0,sizeof(g),g};
+
+    char l[FS_2048] = {0};
+    octet L = {0,sizeof(l),l};
+
+    char m[FS_2048] = {0};
+    octet M = {0,sizeof(m),m};
+
+    // Plaintext to encrypt
+    char ptin[NTHREADS][FS_2048];
+    octet PTIN[NTHREADS];
+    char ptout[NTHREADS][FS_2048];
+    octet PTOUT[NTHREADS];
+
+    // Constant value for multiplication
+    char ptko[NTHREADS][FS_2048];
+    octet PTK[NTHREADS];
+
+    // Encrypted PTIN values
+    char cto[NTHREADS][FS_4096];
+    octet CT[NTHREADS];
+
+    // Homomorphic multiplicaton of plaintext by a constant ciphertext
+    char cta[NTHREADS][FS_4096];
+    octet CTA[NTHREADS];
+
+    // Homomorphic addition of ciphertext
+    char cto3[FS_4096] = {0};
+    octet CT3 = {0,sizeof(cto3),cto3};
+
+    // Output plaintext of addition of homomorphic multiplication values
+    char pto3[FS_2048] = {0};
+    octet PT3 = {sizeof(pto3),sizeof(pto3),pto3};
+
+    // Expected ouput is 26 / 0x1a i.e. 2*3 + 4*5
+    int values[NTHREADS] = {2,4};
+    int kvalues[NTHREADS] = {3,5};
+
+    // Initialize octets
+    for(int i=0; i<NTHREADS; i++)
+    {
+        memset(ptin[i], 0, FS_2048*sizeof(ptin[i][0]));
+        PTIN[i].max = FS_2048;
+        PTIN[i].len = 0;
+        PTIN[i].val = ptin[i];
+
+        memset(ptout[i], 0, FS_2048*sizeof(ptout[i][0]));
+        PTOUT[i].max = FS_2048;
+        PTOUT[i].len = 0;
+        PTOUT[i].val = ptout[i];
+
+        memset(ptko[i], 0, FS_2048*sizeof(ptko[i][0]));
+        PTK[i].max = FS_2048;
+        PTK[i].len = 0;
+        PTK[i].val = ptko[i];
+
+        memset(cto[i], 0, FS_4096*sizeof(cto[i][0]));
+        CT[i].max = FS_4096;
+        CT[i].len = 0;
+        CT[i].val = cto[i];
+
+        memset(cta[i], 0, FS_4096*sizeof(cta[i][0]));
+        CTA[i].max = FS_4096;
+        CTA[i].len = 0;
+        CTA[i].val = cta[i];
+    }
+
+    printf("Generating public/private key pair\n");
+    rc = PAILLIER_KEY_PAIR(RNG, &P, &Q, &N, &G, &L, &M);
+    if (rc)
+    {
+        fprintf(stderr, "ERROR PAILLIER_KEY_PAIR rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("P: ");
+    OCT_output(&P);
+    printf("\n");
+    printf("Q: ");
+    OCT_output(&Q);
+    printf("\n");
+
+    printf("Public Key \n");
+    printf("N: ");
+    OCT_output(&N);
+    printf("\n");
+    printf("G: ");
+    OCT_output(&G);
+    printf("\n");
+
+    printf("Secret Key \n");
+    printf("L: ");
+    OCT_output(&L);
+    printf("\n");
+    printf("M: ");
+    OCT_output(&M);
+    printf("\n");
+
+    // Set plaintext values
+    for(int i=0; i<NTHREADS; i++)
+    {
+        BIG_1024_58 pt[FFLEN_2048];
+        FF_2048_init(pt, values[i],FFLEN_2048);
+        FF_2048_toOctet(&PTIN[i], pt, FFLEN_2048);
+
+        BIG_1024_58 ptk[FFLEN_2048];
+        FF_2048_init(ptk, kvalues[i],FFLEN_2048);
+        FF_2048_toOctet(&PTK[i], ptk, FFLEN_2048);
+    }
+
+    for(int i=0; i<NTHREADS; i++)
+    {
+        printf("PTIN[%d] ", i);
+        OCT_output(&PTIN[i]);
+        printf("\n");
+    }
+
+    // Encrypt plaintext
+    for(int i=0; i<NTHREADS; i++)
+    {
+        rc = PAILLIER_ENCRYPT(RNG, &N, &G, &PTIN[i], &CT[i], NULL);
+        if (rc)
+        {
+            fprintf(stderr, "ERROR PAILLIER_ENCRYPT rc: %d\n", rc);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    for(int i=0; i<NTHREADS; i++)
+    {
+        printf("CT[%d] ", i);
+        OCT_output(&CT[i]);
+        printf("\n");
+    }
+
+    // Decrypt ciphertexts
+    for(int i=0; i<NTHREADS; i++)
+    {
+        rc = PAILLIER_DECRYPT(&N, &L, &M, &CT[i], &PTOUT[i]);
+        if (rc)
+        {
+            fprintf(stderr, "ERROR PAILLIER_DECRYPT rc: %d\n", rc);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    for(int i=0; i<NTHREADS; i++)
+    {
+        printf("PTOUT[%d] ", i);
+        OCT_output(&PTOUT[i]);
+        printf("\n");
+    }
+
+    for(int i=0; i<NTHREADS; i++)
+    {
+        rc = PAILLIER_MULT(&N, &CT[i], &PTK[i], &CTA[i]);
+        if (rc)
+        {
+            fprintf(stderr, "ERROR PAILLIER_MULT rc: %d\n", rc);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    rc = PAILLIER_ADD(&N, &CTA[0], &CTA[1], &CT3);
+    if (rc)
+    {
+        fprintf(stderr, "ERROR PAILLIER_ADD rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    for(int i=0; i<NTHREADS; i++)
+    {
+        printf("CTA[%d] ", i);
+        OCT_output(&CTA[i]);
+        printf("\n");
+    }
+    printf("CT3: ");
+    OCT_output(&CT3);
+    printf("\n");
+
+    rc = PAILLIER_DECRYPT(&N, &L, &M, &CT3, &PT3);
+    if (rc)
+    {
+        fprintf(stderr, "ERROR PAILLIER_DECRYPT rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("PT3: ");
+    OCT_output(&PT3);
+    printf("\n");
+
+    OCT_clear(&P);
+    OCT_clear(&Q);
+    OCT_clear(&N);
+    OCT_clear(&G);
+    OCT_clear(&L);
+    OCT_clear(&M);
+    OCT_clear(&CT3);
+    OCT_clear(&PT3);
+    for(int i=0; i<NTHREADS; i++)
+    {
+        OCT_clear(&PTIN[i]);
+        OCT_clear(&PTOUT[i]);
+        OCT_clear(&CT[i]);
+        OCT_clear(&CTA[i]);
+    }
+    return 0;
+}
+
+int main()
+{
+    char* seedHex = "78d0fb6705ce77dee47d03eb5b9c5d30";
+    char seed[16] = {0};
+    octet SEED = {sizeof(seed),sizeof(seed),seed};
+
+    // CSPRNG
+    csprng RNG;
+
+    // fake random source
+    OCT_fromHex(&SEED,seedHex);
+    printf("SEED: ");
+    OCT_output(&SEED);
+
+    // initialise strong RNG
+    CREATE_CSPRNG(&RNG,&SEED);
+
+    printf("\nPaillier example\n");
+    paillier(&RNG);
+
+    KILL_CSPRNG(&RNG);
+}

Reply via email to