Add an example of RSA signature (#348)

diff --git a/cmake/scripts/test.sh b/cmake/scripts/test.sh
index 87e48a8..f437d02 100755
--- a/cmake/scripts/test.sh
+++ b/cmake/scripts/test.sh
@@ -163,6 +163,7 @@
   python3 builtin_gbdt_train.py
   python3 builtin_online_decrypt.py
   python3 builtin_private_join_and_compute.py
+  python3 builtin_rsa_sign.py
   popd
 
   # kill all background services
diff --git a/examples/python/builtin_rsa_sign.py b/examples/python/builtin_rsa_sign.py
new file mode 100644
index 0000000..7130262
--- /dev/null
+++ b/examples/python/builtin_rsa_sign.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+
+import sys
+
+from teaclave import (AuthenticationService, FrontendService,
+                      AuthenticationClient, FrontendClient, FunctionInput,
+                      FunctionOutput, OwnerList, DataMap)
+from utils import (AUTHENTICATION_SERVICE_ADDRESS, FRONTEND_SERVICE_ADDRESS,
+                   AS_ROOT_CA_CERT_PATH, ENCLAVE_INFO_PATH, USER_ID,
+                   USER_PASSWORD)
+
+
+def get_client(user_id, user_password):
+    auth_client = AuthenticationService(
+        AUTHENTICATION_SERVICE_ADDRESS, AS_ROOT_CA_CERT_PATH,
+        ENCLAVE_INFO_PATH).connect().get_client()
+
+    print("[+] registering user")
+    auth_client.user_register(user_id, user_password)
+
+    print("[+] login")
+    token = auth_client.user_login(user_id, user_password)
+
+    client = FrontendService(FRONTEND_SERVICE_ADDRESS, AS_ROOT_CA_CERT_PATH,
+                             ENCLAVE_INFO_PATH).connect().get_client()
+    metadata = {"id": user_id, "token": token}
+    client.metadata = metadata
+    return client
+
+
+def register_input_file(client):
+    """
+    Commands when encrypting input files:
+        ./teaclave_cli encrypt
+        --algorithm teaclave-file-128
+        --input-file ./tests/fixtures/functions/rsa_sign/key.der
+        --key 00000000000000000000000000000003
+        --output-file ./tests/fixtures/functions/rsa_sign/rsakey.enc
+        --cmac-flag
+    """
+    url = "http://localhost:6789/fixtures/functions/rsa_sign/rsakey.enc"
+    cmac = "4de3bb77327c82923640835c6e5ada66"
+    schema = "teaclave-file-128"
+    key = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3]
+    iv = []
+    key_data_id = client.register_input_file(url, schema, key, iv, cmac)
+
+    return key_data_id
+
+
+def register_func(client):
+    function_id = client.register_function(
+        name="builtin-rsa-sign",
+        description="Native Rsa Signing Function",
+        executor_type="builtin",
+        arguments=["data"],
+        inputs=[FunctionInput("rsa_key", "Input key file.")])
+
+    return function_id
+
+
+def create_task(client, function_id, input_file_user):
+    task_id = client.create_task(
+        function_id=function_id,
+        executor="builtin",
+        function_arguments=({
+            "data": "test data",
+        }),
+        inputs_ownership=[OwnerList("rsa_key", [input_file_user])])
+
+    return task_id
+
+
+def rsa_task():
+    client1 = get_client("rsa_sign_user1", "password1")
+    client2 = get_client("rsa_sign_user2", "password2")
+
+    print("[+] registering key file")
+    key_id = register_input_file(client1)
+    print("[+] key file id" + key_id)
+
+    print("[+] registering function")
+    function_id = register_func(client2)
+    print("[+] function id" + function_id)
+
+    print("[+] creating task")
+    task_id = create_task(client2, function_id, "rsa_sign_user1")
+    print("[+] task id" + task_id)
+
+    print("[+] assigning data to task")
+    client1.assign_data_to_task(task_id, [DataMap("rsa_key", key_id)], [])
+
+    print("[+] user1 approving task")
+    client1.approve_task(task_id)
+
+    print("[+] user2 approving task")
+    client2.approve_task(task_id)
+
+    print("[+] invoking task")
+    client2.invoke_task(task_id)
+
+    print("[+] getting result")
+    result = client2.get_task_result(task_id)
+    print("[+] done")
+
+    return bytes(result)
+
+
+def main():
+    rt = rsa_task()
+    print("[+] function return: ", rt)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/executor/Cargo.toml b/executor/Cargo.toml
index 3755037..c102d44 100644
--- a/executor/Cargo.toml
+++ b/executor/Cargo.toml
@@ -33,6 +33,7 @@
   "builtin_logistic_regression_train",
   "builtin_online_decrypt",
   "builtin_private_join_and_compute",
+  "builtin_rsa_sign",
 ]
 
 builtin_echo = []
@@ -42,6 +43,7 @@
 builtin_logistic_regression_train = []
 builtin_online_decrypt = []
 builtin_private_join_and_compute = []
+builtin_rsa_sign = []
 
 [dependencies]
 log           = { version = "0.4.6", features = ["release_max_level_info"] }
diff --git a/executor/src/builtin.rs b/executor/src/builtin.rs
index ef4c413..5874d74 100644
--- a/executor/src/builtin.rs
+++ b/executor/src/builtin.rs
@@ -20,7 +20,7 @@
 
 use teaclave_function::{
     Echo, GbdtPredict, GbdtTrain, LogisticRegressionPredict, LogisticRegressionTrain,
-    OnlineDecrypt, PrivateJoinAndCompute,
+    OnlineDecrypt, PrivateJoinAndCompute, RsaSign,
 };
 use teaclave_types::{FunctionArguments, FunctionRuntime, TeaclaveExecutor};
 
@@ -54,6 +54,8 @@
             OnlineDecrypt::NAME => OnlineDecrypt::new().run(arguments, runtime),
             #[cfg(feature = "builtin_private_join_and_compute")]
             PrivateJoinAndCompute::NAME => PrivateJoinAndCompute::new().run(arguments, runtime),
+            #[cfg(feature = "builtin_rsa_sign")]
+            RsaSign::NAME => RsaSign::new().run(arguments, runtime),
             _ => bail!("Function not found."),
         }
     }
diff --git a/function/README.md b/function/README.md
index 42df464..91bed63 100644
--- a/function/README.md
+++ b/function/README.md
@@ -20,6 +20,7 @@
   - `builtin-logistic-regression-predict`: LR prediction with input model and input test data.
   - `builtin-private-join-and-compute`: Find intersection of muti-parties' input
     data and compute sum of the common items.
+  - `builtin-rsa-sign`: Signing data with RSA key.
   
 The function arguments are in JSON format and can be serialized to a Rust struct
 very easily. You can learn more about supported arguments in the implementation
diff --git a/function/src/lib.rs b/function/src/lib.rs
index 0e83885..2d43640 100644
--- a/function/src/lib.rs
+++ b/function/src/lib.rs
@@ -29,6 +29,7 @@
 mod logistic_regression_train;
 mod online_decrypt;
 mod private_join_and_compute;
+mod rsa_sign;
 
 pub use echo::Echo;
 pub use gbdt_predict::GbdtPredict;
@@ -37,6 +38,7 @@
 pub use logistic_regression_train::LogisticRegressionTrain;
 pub use online_decrypt::OnlineDecrypt;
 pub use private_join_and_compute::PrivateJoinAndCompute;
+pub use rsa_sign::RsaSign;
 
 #[cfg(feature = "enclave_unit_test")]
 pub mod tests {
@@ -52,6 +54,7 @@
             logistic_regression_predict::tests::run_tests(),
             online_decrypt::tests::run_tests(),
             private_join_and_compute::tests::run_tests(),
+            rsa_sign::tests::run_tests(),
         )
     }
 }
diff --git a/function/src/rsa_sign.rs b/function/src/rsa_sign.rs
new file mode 100644
index 0000000..48188fc
--- /dev/null
+++ b/function/src/rsa_sign.rs
@@ -0,0 +1,114 @@
+// 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.
+
+#[cfg(feature = "mesalock_sgx")]
+use std::prelude::v1::*;
+use std::vec;
+
+use ring::{rand, signature};
+
+use std::convert::TryFrom;
+use teaclave_types::{FunctionArguments, FunctionRuntime};
+
+const IN_DATA: &str = "rsa_key";
+
+#[derive(serde::Deserialize)]
+struct RsaSignArguments {
+    data: String,
+}
+
+impl TryFrom<FunctionArguments> for RsaSignArguments {
+    type Error = anyhow::Error;
+
+    fn try_from(arguments: FunctionArguments) -> Result<Self, Self::Error> {
+        use anyhow::Context;
+        serde_json::from_str(&arguments.into_string()).context("Cannot deserialize arguments")
+    }
+}
+
+#[derive(Default)]
+pub struct RsaSign;
+
+impl RsaSign {
+    pub const NAME: &'static str = "builtin-rsa-sign";
+
+    pub fn new() -> Self {
+        Default::default()
+    }
+
+    pub fn run(
+        &self,
+        arguments: FunctionArguments,
+        runtime: FunctionRuntime,
+    ) -> anyhow::Result<String> {
+        let args = RsaSignArguments::try_from(arguments)?;
+
+        let mut key = Vec::new();
+        let mut f = runtime.open_input(IN_DATA)?;
+        f.read_to_end(&mut key)?;
+        let key_pair = signature::RsaKeyPair::from_der(&key)?;
+        let mut sig = vec![0; key_pair.public_modulus_len()];
+        let rng = rand::SystemRandom::new();
+        key_pair.sign(
+            &signature::RSA_PKCS1_SHA256,
+            &rng,
+            args.data.as_bytes(),
+            &mut sig,
+        )?;
+
+        let output_base64 = base64::encode(&sig);
+        Ok(output_base64)
+    }
+}
+
+#[cfg(feature = "enclave_unit_test")]
+pub mod tests {
+    use super::*;
+    use serde_json::json;
+    use std::untrusted::fs;
+    use teaclave_crypto::*;
+    use teaclave_runtime::*;
+    use teaclave_test_utils::*;
+    use teaclave_types::*;
+
+    pub fn run_tests() -> bool {
+        run_tests!(test_rsa_sign)
+    }
+
+    fn test_rsa_sign() {
+        let arguments = FunctionArguments::from_json(json!({
+            "data": "test data",
+        }))
+        .unwrap();
+
+        let plain_input = "fixtures/functions/rsa_sign/key.der";
+        let expected_output = "fixtures/functions/rsa_sign/expected_rsasign.txt";
+
+        let input_files = StagedFiles::new(hashmap!(
+            IN_DATA =>
+            StagedFileInfo::new(plain_input, TeaclaveFile128Key::random(), FileAuthTag::mock())
+        ));
+
+        let output_files = StagedFiles::default();
+        let runtime = Box::new(RawIoRuntime::new(input_files, output_files));
+
+        let summary = RsaSign::new().run(arguments, runtime).unwrap();
+        let mut expected_string = fs::read_to_string(&expected_output).unwrap();
+        expected_string = expected_string.replace("\n", "");
+        assert_eq!(summary, expected_string);
+    }
+}
diff --git a/services/proto/src/teaclave_frontend_service.rs b/services/proto/src/teaclave_frontend_service.rs
index abc30e5..e784f59 100644
--- a/services/proto/src/teaclave_frontend_service.rs
+++ b/services/proto/src/teaclave_frontend_service.rs
@@ -553,7 +553,6 @@
             .crypto_info
             .ok_or_else(|| anyhow!("missing crypto_info"))?
             .try_into()?;
-
         Ok(RegisterInputFileRequest {
             url,
             cmac,
diff --git a/tests/fixtures/functions/rsa_sign/expected_rsasign.txt b/tests/fixtures/functions/rsa_sign/expected_rsasign.txt
new file mode 100644
index 0000000..28599f2
--- /dev/null
+++ b/tests/fixtures/functions/rsa_sign/expected_rsasign.txt
@@ -0,0 +1 @@
+tmgoOYVBmGOQNvb1Rrf13L0bchIlbRx2rFf14udRmZrXzaGrIH3z26tSjD6a3RvKGh/vgW8bjbr7caSdjUxNPI/LJEk/TXX0ilkhfV957fBgtOtB2YFqbKGFR6zoHHC2zgOWDUXidGJqMQFD/SY5tBe0t4bFhHPkxnU/lTTwK+sg8ECNCXTvblUsI7dMVzMHV3q29NwE43DvrM+9pbt9QGm8sGatAnpcK/RVp1nk60FQJvwe0zUQXvpPMpCv8vO+/kKEZkwXw8WwPOvCNI5jE/8gGnQHCH/3UgvZjfgp978htHOky7BNskYIHsc87dClIjMhjfMto5pm14SdCpmmHw==
diff --git a/tests/fixtures/functions/rsa_sign/key.der b/tests/fixtures/functions/rsa_sign/key.der
new file mode 100644
index 0000000..3857aa4
--- /dev/null
+++ b/tests/fixtures/functions/rsa_sign/key.der
Binary files differ
diff --git a/tests/fixtures/functions/rsa_sign/rsakey.enc b/tests/fixtures/functions/rsa_sign/rsakey.enc
new file mode 100644
index 0000000..1989b38
--- /dev/null
+++ b/tests/fixtures/functions/rsa_sign/rsakey.enc
Binary files differ