add BLS Python wrapper
diff --git a/Dockerfile b/Dockerfile
index 83ab0a2..ae6c905 100755
--- a/Dockerfile
+++ b/Dockerfile
@@ -58,7 +58,7 @@
     cd incubator-milagro-crypto-c && \
     mkdir build && \
     cd build && \
-    cmake -D CMAKE_BUILD_TYPE=Release -D BUILD_SHARED_LIBS=ON -D AMCL_CHUNK=64 -D AMCL_CURVE="BLS381,SECP256K1" -D AMCL_RSA="" -D BUILD_PAILLIER=ON -D BUILD_PYTHON=ON -D BUILD_BLS=ON -D BUILD_WCC=OFF -D BUILD_MPIN=OFF -D BUILD_X509=OFF -D CMAKE_INSTALL_PREFIX=/usr/local .. && \
+    cmake -D CMAKE_BUILD_TYPE=Release -D BUILD_SHARED_LIBS=ON -D AMCL_CHUNK=64 -D AMCL_CURVE="BLS381,SECP256K1" -D AMCL_RSA="" -D BUILD_PAILLIER=ON -D BUILD_PYTHON=OFF -D BUILD_BLS=ON -D BUILD_WCC=OFF -D BUILD_MPIN=OFF -D BUILD_X509=OFF -D CMAKE_INSTALL_PREFIX=/usr/local .. && \
     make && \
     make test  ARGS=-j8 && \
     make install
diff --git a/python/amcl/bls.py b/python/amcl/bls.py
new file mode 100755
index 0000000..2a53ac4
--- /dev/null
+++ b/python/amcl/bls.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python3
+
+"""
+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.
+"""
+
+
+"""
+bls
+
+This module use cffi to access the c functions in the BLS library.
+
+There is also an example usage program in this file.
+
+"""
+
+import platform
+from amcl import core_utils
+
+_ffi = core_utils._ffi
+_ffi.cdef("""
+extern int BLS_BLS381_KEY_PAIR_GENERATE(csprng *RNG,octet* S,octet *W);
+extern int BLS_BLS381_SIGN(octet *SIG,octet *m,octet *S);
+extern int BLS_BLS381_VERIFY(octet *SIG,octet *m,octet *W);
+extern int BLS_BLS381_ADD_G1(octet *R1,octet *R2,octet *R);
+extern int BLS_BLS381_ADD_G2(octet *W1,octet *W2,octet *W);
+""")
+
+if (platform.system() == 'Windows'):
+    _libamcl_bls_BLS381 = _ffi.dlopen("libamcl_bls_BLS381.dll")
+elif (platform.system() == 'Darwin'):
+    _libamcl_bls_BLS381 = _ffi.dlopen("libamcl_bls_BLS381.dylib")
+else:
+    _libamcl_bls_BLS381 = _ffi.dlopen("libamcl_bls_BLS381.so")
+
+# Group Size
+BGS = 48
+# Field Size
+BFS = 48
+
+CURVE_SECURITY = 128
+
+G1LEN = BFS + 1
+
+if CURVE_SECURITY == 128:
+    G2LEN = 4 * BFS
+
+if CURVE_SECURITY == 192:
+    G2LEN = 8 * BFS
+
+if CURVE_SECURITY == 256:
+    G2LEN = 16 * BFS
+
+
+def key_pair_generate(rng, sk=None):
+    """Generate key pair
+
+    Generate key pair
+
+    Args::
+
+        rng: Pointer to cryptographically secure pseudo-random number generator instance
+        sk: secret key. Externally generated
+
+    Returns::
+
+        error_code: error from the C function
+        sk: secret key
+        pk: public key
+
+    Raises:
+
+    """
+    if sk:
+        sk1, sk1_val = core_utils.make_octet(None, sk)
+        rng = _ffi.NULL
+    else:
+        sk1, sk1val = core_utils.make_octet(BGS)
+
+    pk1, pk1val = core_utils.make_octet(G2LEN)
+    error_code = _libamcl_bls_BLS381.BLS_BLS381_KEY_PAIR_GENERATE(rng, sk1, pk1)
+
+    sk = core_utils.to_str(sk1)
+    pk = core_utils.to_str(pk1)
+
+    # clear memory
+    core_utils.clear_octet(sk1)
+    core_utils.clear_octet(pk1)
+
+    return error_code, sk, pk
+
+
+def sign(message, sk):
+    """Calculate a signature
+
+    Generate key pair
+
+    Args::
+
+        message: Message to sign
+        sk: BLS secret key
+
+    Returns::
+
+        error_code: Zero for success or else an error code
+        signature: BLS signature
+
+    Raises:
+
+    """
+    m, m_val = core_utils.make_octet(None, message)
+    sk1, sk1_val = core_utils.make_octet(None, sk)
+    signature1, signature1_val = core_utils.make_octet(G1LEN)
+    error_code = _libamcl_bls_BLS381.BLS_BLS381_SIGN(signature1, m, sk1)
+
+    signature = core_utils.to_str(signature1)
+
+    # clear memory
+    core_utils.clear_octet(sk1)
+    core_utils.clear_octet(signature1)
+
+    return error_code, signature
+
+
+def verify(signature, message, pk):
+    """Verify a signature
+
+    Verify a signature
+
+    Args::
+
+        message: Message to verify
+        signature: BLS signature
+        pk: BLS public key
+
+    Returns::
+
+        error_code: Zero for success or else an error code
+
+    Raises:
+
+    """
+    m, m_val = core_utils.make_octet(None, message)
+    pk1, pk1_val = core_utils.make_octet(None, pk)
+    signature1, signature1_val = core_utils.make_octet(None, signature)
+    error_code = _libamcl_bls_BLS381.BLS_BLS381_VERIFY(signature1, m, pk1)
+
+    # clear memory
+    core_utils.clear_octet(pk1)
+    core_utils.clear_octet(signature1)
+
+    return error_code
+
+
+def add_G1(R1, R2):
+    """Add two members from the group G1
+
+    Add two members from the group G1
+
+    Args::
+
+        R1:   member of G1
+        R2:   member of G1
+
+    Returns::
+
+        R:          member of G1. R = R1+R2
+        error_code: Zero for success or else an error code
+
+    Raises:
+
+    """
+    R11, R11_val = core_utils.make_octet(None, R1)
+    R21, R21_val = core_utils.make_octet(None, R2)
+    R1, R1_val = core_utils.make_octet(G1LEN)
+    error_code = _libamcl_bls_BLS381.BLS_BLS381_ADD_G1(R11, R21, R1)
+
+    R = core_utils.to_str(R1)
+
+    # clear memory
+    core_utils.clear_octet(R11)
+    core_utils.clear_octet(R21)
+    core_utils.clear_octet(R1)
+
+    return error_code, R
+
+
+def add_G2(R1, R2):
+    """Add two members from the group G2
+
+    Add two members from the group G2
+
+    Args::
+
+        R1:   member of G2
+        R2:   member of G2
+
+    Returns::
+
+        R:          member of G2. R = R1+R2
+        error_code: Zero for success or else an error code
+
+    Raises:
+
+    """
+    R11, R11_val = core_utils.make_octet(None, R1)
+    R21, R21_val = core_utils.make_octet(None, R2)
+    R1, R1_val = core_utils.make_octet(G2LEN)
+    error_code = _libamcl_bls_BLS381.BLS_BLS381_ADD_G2(R11, R21, R1)
+
+    R = core_utils.to_str(R1)
+
+    # clear memory
+    core_utils.clear_octet(R11)
+    core_utils.clear_octet(R21)
+    core_utils.clear_octet(R1)
+
+    return error_code, R
+
diff --git a/python/examples/example_bls.py b/python/examples/example_bls.py
new file mode 100755
index 0000000..33075d1
--- /dev/null
+++ b/python/examples/example_bls.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python3
+
+"""
+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.
+"""
+
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+from amcl import core_utils, bls
+
+if __name__ == "__main__":
+    # Print hex values
+    DEBUG = False
+
+    # Seed
+    seed_hex = "78d0fb6705ce77dee47d03eb5b9c5d30"
+    seed = bytes.fromhex(seed_hex)
+
+    # Message
+    message = b"test message"
+
+    # random number generator
+    rng = core_utils.create_csprng(seed)
+
+    # Generate key pairs
+    rtn, sk1, pktmp = bls.key_pair_generate(rng)
+    if rtn != 0:
+        print("Error: key_pair_generate {}".format(rtn))
+        raise SystemExit(0)
+    print("sk1: {}".format(sk1.hex()))
+    print("pktmp: {}".format(pktmp.hex()))
+
+    rtn, sk1, pk1 = bls.key_pair_generate(rng, sk1)
+    if rtn != 0:
+        print("Error: key_pair_generate {}".format(rtn))
+        raise SystemExit(0)
+    print("sk1: {}".format(sk1.hex()))
+    print("pk1: {}".format(pk1.hex()))
+
+    rtn, sk2, pk2 = bls.key_pair_generate(rng)
+    if rtn != 0:
+        print("Error: key_pair_generate {}".format(rtn))
+        raise SystemExit(0)
+    print("sk2: {}".format(sk2.hex()))
+    print("pk2: {}".format(pk2.hex()))
+
+    rtn, sk3, pk3 = bls.key_pair_generate(rng)
+    if rtn != 0:
+        print("Error: key_pair_generate {}".format(rtn))
+        raise SystemExit(0)
+    print("sk3: {}".format(sk3.hex()))
+    print("pk3: {}".format(pk3.hex()))
+
+    # Sign and verify
+    rtn, sig1 = bls.sign(message, sk1)
+    if rtn != 0:
+        print("Error: sign {}".format(rtn))
+        raise SystemExit(0)
+    print("sig1: {}".format(sig1.hex()))
+
+    rtn = bls.verify(sig1, message, pk1)
+    if rtn != 0:
+        print("Error: Invalid signature {}".format(rtn))
+        raise SystemExit(0)
+    print("Success: Signature is valid")
+
+    rtn, sig2 = bls.sign(message, sk2)
+    if rtn != 0:
+        print("Error: sign {}".format(rtn))
+        raise SystemExit(0)
+    print("sig2: {}".format(sig2.hex()))
+
+    rtn = bls.verify(sig2, message, pk2)
+    if rtn != 0:
+        print("Error: Invalid signature {}".format(rtn))
+        raise SystemExit(0)
+    print("Success: Signature is valid")
+
+    rtn, sig3 = bls.sign(message, sk3)
+    if rtn != 0:
+        print("Error: sign {}".format(rtn))
+        raise SystemExit(0)
+    print("sig3: {}".format(sig3.hex()))
+
+    rtn = bls.verify(sig3, message, pk3)
+    if rtn != 0:
+        print("Error: Invalid signature {}".format(rtn))
+        raise SystemExit(0)
+    print("Success: Signature is valid")
+
+    # Add Signatures
+    rtn, sig12 = bls.add_G1(sig1, sig2)
+    if rtn != 0:
+        print("Error: add_G1 {}".format(rtn))
+        raise SystemExit(0)
+    print("sig12: {}".format(sig12.hex()))
+
+    rtn, sig123 = bls.add_G1(sig12, sig3)
+    if rtn != 0:
+        print("Error: add_G1 {}".format(rtn))
+        raise SystemExit(0)
+    print("sig123: {}".format(sig123.hex()))
+
+    # Add Public keys
+    rtn, pk12 = bls.add_G2(pk1, pk2)
+    if rtn != 0:
+        print("Error: add_G2 {}".format(rtn))
+        raise SystemExit(0)
+    print("pk12: {}".format(pk12.hex()))
+
+    rtn, pk123 = bls.add_G2(pk12, pk3)
+    if rtn != 0:
+        print("Error: add_G2 {}".format(rtn))
+        raise SystemExit(0)
+    print("pk123: {}".format(pk123.hex()))
+
+    # Verify aggretated values
+    rtn = bls.verify(sig123, message, pk123)
+    if rtn != 0:
+        print("Error: Invalid aggregated signature {}".format(rtn))
+        raise SystemExit(0)
+    print("Success: Aggregated signature is valid")
+
+    # Clear memory
+    core_utils.kill_csprng(rng)
+    del sk1
+    del pk1
+    del sk2
+    del pk2
+    del sk3
+    del pk3
diff --git a/scripts/buildAMCL.sh b/scripts/buildAMCL.sh
index 137d09e..e612cb5 100755
--- a/scripts/buildAMCL.sh
+++ b/scripts/buildAMCL.sh
@@ -15,7 +15,7 @@
 cd incubator-milagro-crypto-c
 mkdir build
 cd build
-cmake -D CMAKE_BUILD_TYPE=Debug -D BUILD_SHARED_LIBS=ON -D AMCL_CHUNK=64 -D AMCL_CURVE="BLS381,SECP256K1" -D AMCL_RSA="" -D BUILD_PAILLIER=ON -D BUILD_PYTHON=ON -D BUILD_BLS=ON -D BUILD_WCC=OFF -D BUILD_MPIN=ON -D BUILD_X509=OFF -D CMAKE_INSTALL_PREFIX=/usr/local ..
+cmake -D CMAKE_BUILD_TYPE=Debug -D BUILD_SHARED_LIBS=ON -D AMCL_CHUNK=64 -D AMCL_CURVE="BLS381,SECP256K1" -D AMCL_RSA="" -D BUILD_PAILLIER=ON -D BUILD_PYTHON=OFF -D BUILD_BLS=ON -D BUILD_WCC=OFF -D BUILD_MPIN=ON -D BUILD_X509=OFF -D CMAKE_INSTALL_PREFIX=/usr/local ..
 make
 make test ARGS=-j8
 sudo make install
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1311df2..f6c45db 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -39,3 +39,10 @@
   OWNER_WRITE OWNER_READ OWNER_EXECUTE
   GROUP_READ GROUP_EXECUTE
   WORLD_READ WORLD_EXECUTE)
+
+if(BUILD_PYTHON)
+  message(STATUS "Copy ${target} library to python directory for testing")
+  add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/lib*" "${PROJECT_BINARY_DIR}/python/test")
+  add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/lib*" "${PROJECT_BINARY_DIR}/python/benchmark")
+  add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/lib*" "${PROJECT_BINARY_DIR}/python/examples")  
+endif(BUILD_PYTHON)