예제 #1
0
def multiply_privkeys(p1, p2, n):
    f1, f2 = get_privkey_format(p1), get_privkey_format(p2)
    p = decode_privkey(p1, f1)
    while n > 0:
        p = (p + decode_privkey(p2, f2)) % N
        n -= 1
    return encode_privkey(p, f1)
    def unseal_signup_data(cls, sealed_signup_data):
        """

        Args:
            sealed_signup_data: Sealed signup data that was returned
                previously in a EnclaveSignupInfo object from a call to
                create_signup_info

        Returns:
            A string The hex encoded PoET public key that was extracted from
            the sealed data
        """

        # Reverse the process we used in creating "sealed" signup info.
        # Specifically, we will do a base 32 decode, which gives us json
        # we can convert back to a dictionary we can use to get the
        # data we need
        signup_data = \
            json2dict(pybitcointools.base64.b32decode(sealed_signup_data))

        with cls._lock:
            cls._poet_public_key = \
                pybitcointools.decode_pubkey(
                    signup_data.get('poet_public_key'),
                    'hex')
            cls._poet_private_key = \
                pybitcointools.decode_privkey(
                    signup_data.get('poet_public_key'),
                    'hex')
            cls._active_wait_timer = None

            return signup_data.get('poet_public_key')
예제 #3
0
    def __init__(self,
                 base_url,
                 store_name=None,
                 name='SawtoothClient',
                 txntype_name=None,
                 msgtype_name=None,
                 keystring=None,
                 keyfile=None,
                 disable_client_validation=False):
        self._base_url = base_url
        self._message_type = msgtype_name
        self._transaction_type = txntype_name

        # an explicit store name takes precedence over a store name
        # implied by the transaction type
        self._store_name = None
        if store_name is not None:
            self._store_name = store_name.strip('/')
        elif txntype_name is not None:
            self._store_name = txntype_name.strip('/')

        self._communication = _Communication(base_url)
        self._last_transaction = None
        self._signing_key = None
        self._identifier = None
        self._update_batch = None
        self._disable_client_validation = disable_client_validation

        if keystring:
            LOGGER.debug("set signing key from string\n%s", keystring)
            self._signing_key = pybitcointools.decode_privkey(keystring, 'wif')
        elif keyfile:
            LOGGER.debug("set signing key from file %s", keyfile)
            try:
                self._signing_key = pybitcointools.decode_privkey(
                    open(keyfile, "r").read().strip(), 'wif')
            except IOError as ex:
                raise ClientException("Failed to load key file: {}".format(
                    str(ex)))

        if self._signing_key is not None:
            self._identifier = pybitcointools.pubtoaddr(
                pybitcointools.privtopub(self._signing_key))
예제 #4
0
 def test_pbt_match(self):
     """
     Tests matching results between pybitcointools and native
     ECDSA key recovery
     """
     # This key has a small public key value which tests padding
     wifstr = '5JtMb6tmM9vT6QHyM7RR8pjMViqccukgMFNCPvG5xhLVf6CMoGx'
     priv = pbt.decode_privkey(wifstr, 'wif')
     msg = 'foo'
     sig = pbt.ecdsa_sign(msg, priv)
     native_recovered = gossip.signed_object.get_verifying_key(msg, sig)
     py_recovered = pbt.ecdsa_recover(msg, sig)
     self.assertEquals(native_recovered, py_recovered)
    def test_register_permissioned_validator_valid(self):
        signing_key = signed_object.generate_signing_key()

        try:
            priv_key1 = pybitcointools.decode_privkey(
                open('./tests/unit/keys/pv1.wif', "r")
                .read().strip(), 'wif')
            priv_key2 = pybitcointools.decode_privkey(
                open('./tests/unit/keys/pv2.wif', "r")
                .read().strip(), 'wif')
        except IOError as ex:
            raise Exception('IOError: {}'.format(str(ex)))

        pub_key1 = pybitcointools.encode_pubkey(
            pybitcointools.privtopub(priv_key1), 'hex')
        pub_key2 = pybitcointools.encode_pubkey(
            pybitcointools.privtopub(priv_key2), 'hex')
        permissioned_public_keys = [pub_key1, pub_key2]

        addr1 = signed_object.generate_identifier(priv_key1)
        addr2 = signed_object.generate_identifier(priv_key2)
        permissioned_addrs = [addr1, addr2]

        update = {
            'whitelist_name': "hyperledger.sawtooth-core.genesis-whitelist",
            'verb': 'reg',
            'permissioned_public_keys': permissioned_public_keys,
            'permissioned_addrs': permissioned_addrs
        }
        minfo = {'Update': update}
        transaction = PermissionedValidatorRegistryTransaction(minfo)
        transaction.sign_object(signing_key)

        store = ObjectStore()
        try:
            transaction.check_valid(store)
            transaction.apply(store)
        except InvalidTransactionError as e:
            self.fail('Failed valid transaction: {}'.format(e))
 def test_pbt_match(self):
     """
     Tests matching results between pybitcointools and native
     ECDSA key recovery
     """
     # This key has a small public key value which tests padding
     wifstr = '5JtMb6tmM9vT6QHyM7RR8pjMViqccukgMFNCPvG5xhLVf6CMoGx'
     priv = pbt.decode_privkey(wifstr, 'wif')
     msg = 'foo'
     sig = pbt.ecdsa_sign(msg, priv)
     native_recovered = gossip.signed_object.get_verifying_key(msg, sig)
     py_recovered = pbt.ecdsa_recover(msg, sig)
     self.assertEquals(native_recovered, py_recovered)
def generate_signing_key(wifstr=None):
    """Returns a decoded signing key associated with wifstr or generates
    a random signing key.

    Args:
        wifstr (str): A private key in wif format.

    Returns:
        str: a signing key.
    """
    if wifstr:
        return pybitcointools.decode_privkey(wifstr, 'wif')

    return pybitcointools.random_key()
예제 #8
0
def generate_signing_key(wifstr=None):
    """Returns a decoded signing key associated with wifstr or generates
    a random signing key.

    Args:
        wifstr (str): A private key in wif format.

    Returns:
        str: a signing key.
    """
    if wifstr:
        return pybitcointools.decode_privkey(wifstr, 'wif')

    return pybitcointools.random_key()
    def test_register_permissioned_validator_valid(self):
        signing_key = signed_object.generate_signing_key()

        try:
            priv_key1 = pybitcointools.decode_privkey(
                open('./tests/unit/keys/pv1.wif', "r").read().strip(), 'wif')
            priv_key2 = pybitcointools.decode_privkey(
                open('./tests/unit/keys/pv2.wif', "r").read().strip(), 'wif')
        except IOError as ex:
            raise Exception('IOError: {}'.format(str(ex)))

        pub_key1 = pybitcointools.encode_pubkey(
            pybitcointools.privtopub(priv_key1), 'hex')
        pub_key2 = pybitcointools.encode_pubkey(
            pybitcointools.privtopub(priv_key2), 'hex')
        permissioned_public_keys = [pub_key1, pub_key2]

        addr1 = signed_object.generate_identifier(priv_key1)
        addr2 = signed_object.generate_identifier(priv_key2)
        permissioned_addrs = [addr1, addr2]

        update = {
            'whitelist_name': "hyperledger.sawtooth-core.genesis-whitelist",
            'verb': 'reg',
            'permissioned_public_keys': permissioned_public_keys,
            'permissioned_addrs': permissioned_addrs
        }
        minfo = {'Update': update}
        transaction = PermissionedValidatorRegistryTransaction(minfo)
        transaction.sign_object(signing_key)

        store = ObjectStore()
        try:
            transaction.check_valid(store)
            transaction.apply(store)
        except InvalidTransactionError as e:
            self.fail('Failed valid transaction: {}'.format(e))
예제 #10
0
def _decode_privkey(encoded_privkey, encoding_format='wif'):
    """
    Args:
        encoded_privkey: an encoded private key string
        encoding_format: string indicating format such as 'wif'

    Returns:
        private key object useable with this module
    """
    if encoding_format == 'wif':
        # base58 to int
        priv = pybitcointools.decode_privkey(encoded_privkey, encoding_format)
        # int to hex string
        priv = pybitcointools.encode_privkey(priv, 'hex')
        # hex string to bytes
        try:  # check python 3
            priv = priv.to_bytes(32, byteorder='big')
        except AttributeError:
            priv = binascii.unhexlify(priv)
    else:
        raise TypeError("unsupported private key format")

    return secp256k1.PrivateKey(priv)
예제 #11
0
class _PoetEnclaveSimulator(object):

    # The WIF-encoded enclave private seal key.  From it, we will create
    # private and public keys we can use for sealing and unsealing signup
    # info.
    __SEAL_PRIVATE_KEY_WIF = \
        '5KYsbooGBg51Gohakgq45enpXvCXmEBed1JivFfUZskmjLegHBG'

    _seal_private_key = \
        pybitcointools.decode_privkey(__SEAL_PRIVATE_KEY_WIF, 'wif')
    _seal_public_key = pybitcointools.privtopub(_seal_private_key)

    # Minimum duration for PoET 1 simulator is 30 seconds
    __MINIMUM_DURATTION = 30.0

    # The PoET keys will remain unset until signup info is either created or
    # unsealed
    _poet_public_key = None
    _poet_private_key = None
    _active_wait_timer = None

    @classmethod
    def create_signup_info(cls, originator_public_key):
        # First we need to create a public/private key pair for the PoET
        # enclave to use.
        cls._poet_private_key = pybitcointools.random_key()
        cls._poet_public_key = pybitcointools.privtopub(cls._poet_private_key)
        cls._active_wait_timer = None

        # We are going to fake out the sealing the signup data.
        signup_data = {
            'poet_public_key':
            pybitcointools.encode_pubkey(cls._poet_public_key, 'hex'),
            'poet_private_key':
            pybitcointools.encode_privkey(cls._poet_private_key, 'hex')
        }
        sealed_signup_data = \
            pybitcointools.base64.b32encode(dict2json(signup_data))

        # Create a fake report
        report_data = {
            'originator_public_key_hash':
            pybitcointools.sha256(
                pybitcointools.encode_pubkey(originator_public_key, 'hex')),
            'poet_public_key':
            pybitcointools.encode_pubkey(cls._poet_public_key, 'hex')
        }
        report = {'report_data': pybitcointools.sha256(dict2json(report_data))}
        report = pybitcointools.base64.b32encode(dict2json(report))

        # Fake our "proof" data.
        proof_data = {
            'attestation_evidence_payload':
            pybitcointools.sha256(report),
            'attestation_verification_report':
            pybitcointools.sha256('Shave and a haircut...Two bits!')
        }

        return \
            EnclaveSignupInfo(
                anti_sybil_id='Sally Field',
                poet_public_key=signup_data['poet_public_key'],
                proof_data=proof_data,
                sealed_signup_data=sealed_signup_data)

    @classmethod
    def unseal_signup_data(cls, sealed_signup_data):
        """

        Args:
            sealed_signup_data: Sealed signup data that was returned
                previously in a EnclaveSignupInfo object from a call to
                create_signup_info

        Returns:
            A string The hex encoded PoET public key that was extracted from
            the sealed data
        """

        # Reverse the process we used in creating "sealed" signup info.
        # Specifically, we will do a base 32 decode, which gives us json
        # we can convert back to a dictionary we can use to get the
        # data we need
        signup_data = \
            json2dict(pybitcointools.base64.b32decode(sealed_signup_data))

        cls._poet_public_key = \
            pybitcointools.decode_pubkey(
                signup_data.get('poet_public_key'),
                'hex')
        cls._poet_private_key = \
            pybitcointools.decode_privkey(
                signup_data.get('poet_public_key'),
                'hex')
        cls._active_wait_timer = None

        return signup_data.get('poet_public_key')

    @classmethod
    def verify_signup_info(cls, serialized_signup_info):
        # For now, we are going to always indicate that the signup information
        # is valid
        return True

    @classmethod
    def create_wait_timer(cls, previous_certificate_id, local_mean):
        # Create some value from the cert ID.  We are just going to use
        # the seal key to sign the cert ID.  We will then use the low-order
        # 64 bits to change that to a number [0, 1]
        tag = \
            pybitcointools.base64.b64decode(
                pybitcointools.ecdsa_sign(
                    previous_certificate_id,
                    cls._seal_private_key))

        tagd = float(struct.unpack('L', tag[-8:])[0]) / (2**64 - 1)

        # Now compute the duration
        duration = cls.__MINIMUM_DURATTION - local_mean * math.log(tagd)

        # Create and sign the wait timer
        wait_timer = \
            EnclaveWaitTimer(
                duration=duration,
                previous_certificate_id=previous_certificate_id,
                local_mean=local_mean)
        wait_timer.signature = \
            pybitcointools.ecdsa_sign(
                wait_timer.serialize(),
                cls._poet_private_key)

        # Keep track of the active wait timer
        cls._active_wait_timer = wait_timer

        return wait_timer

    @classmethod
    def deserialize_wait_timer(cls, serialized_timer, signature):
        return \
            EnclaveWaitTimer.wait_timer_from_serialized(
                serialized_timer=serialized_timer,
                signature=signature)

    @classmethod
    def create_wait_certificate(cls, timer, block_digest):
        # TO DO - implement PoET 1 create certificate logic

        # First create a new enclave wait certificate using the data provided
        # and then sign the certificate with the PoET private key
        wait_certificate = \
            EnclaveWaitCertificate.wait_certificate_with_timer(
                timer=timer,
                block_digest=block_digest)
        wait_certificate.signature = \
            pybitcointools.ecdsa_sign(
                wait_certificate.serialize(),
                cls._poet_private_key)

        return wait_certificate

    @classmethod
    def deserialize_wait_certificate(cls, serialized_certificate, signature):
        return \
            EnclaveWaitCertificate.wait_certificate_from_serialized(
                serialized_certificate=serialized_certificate,
                signature=signature)

    @classmethod
    def verify_wait_certificate(cls, certificate, encoded_poet_public_key):
        # poet_public_key = \
        #     pybitcointools.decode_pubkey(encoded_poet_public_key, 'hex')
        #
        # TO DO - implement PoET 1 create certificate logic
        # return \
        #     pybitcointools.ecdsa_verify(
        #         certificate.serialize(),
        #         certificate.signature,
        #         poet_public_key)

        return True
예제 #12
0
def decode_privkey(privkey, encoding_format='wif'):
    return pybitcointools.decode_privkey(privkey, encoding_format)
예제 #13
0
import pybitcointools

# Generate a random private key
valid_private_key = False
while not valid_private_key:
    private_key = pybitcointools.random_key()
    decoded_private_key = pybitcointools.decode_privkey(private_key, 'hex')
    valid_private_key = 0 < decoded_private_key < pybitcointools.N

print "Private Key (hex) is:", private_key
print "Private Key (decimal) is:", decoded_private_key

# Convert private key to WIF format
wif_encoded_private_key = pybitcointools.encode_privkey(
    decoded_private_key, 'wif')
print "Private Key (WIF) is:", wif_encoded_private_key

# Add suffix "01" to indicate a compressed private key
compressed_private_key = private_key + '01'
print "Private Key Compressed (hex) is:", compressed_private_key

# Generate a WIF format from the compressed private key (WIF-)
예제 #14
0
def get_address_from_private_key_wif(key):
    return pybitcointools.privtoaddr(pybitcointools.decode_privkey(key, 'wif'))
예제 #15
0
def get_address_from_private_key_wif(key):
    return pybitcointools.privtoaddr(pybitcointools.decode_privkey(key, 'wif'))
예제 #16
0
import pybitcointools
import sys

if __name__ == "__main__":
    if len(sys.argv) >= 2:
        priv_key = sys.argv[
            1]  #"LmB72UZvRmJ5cUPZWpxqYWW5KkCgASa53GZQNhWNTPzJ9J1R4T8x"
        priv_data = pybitcointools.decode_privkey(priv_key)
        print(pybitcointools.encode_privkey(priv_data, "wif_compressed"))
        print(pybitcointools.privtoaddr(priv_key))
    else:
        print("Usage: %s [private_key]" % sys.argv[0])
예제 #17
0
#     - (see https://github.com/vbuterin/pybitcointools/issues/153 for the reason of the fork; there are conflicts with "import bitcoin")
#
#     my pip freeze:
#        bitcoin==1.1.42
#        pybitcointools==1.1.42     (installed as above)
#        python-bitcoinlib==0.10.1
#

from __future__ import print_function
import pybitcointools

# Generate a random private key
valid_private_key = False
while not valid_private_key:
    private_key = pybitcointools.random_key()
    decoded_private_key = pybitcointools.decode_privkey(private_key, 'hex')
    valid_private_key = 0 < decoded_private_key < pybitcointools.N

print("Private Key (hex) is: ", private_key)
print("Private Key (decimal) is: ", decoded_private_key)

# Convert private key to WIF format
wif_encoded_private_key = pybitcointools.encode_privkey(
    decoded_private_key, 'wif')
print("Private Key (WIF) is: ", wif_encoded_private_key)

# Add suffix "01" to indicate a compressed private key
compressed_private_key = private_key + '01'
print("Private Key Compressed (hex) is: ", compressed_private_key)

# Generate a WIF format from the compressed private key (WIF-compressed)
class _PoetEnclaveSimulator(object):
    # A lock to protect threaded access
    _lock = threading.Lock()

    # The WIF-encoded enclave private seal key.  From it, we will create
    # private and public keys we can use for sealing and unsealing signup
    # info.
    __SEAL_PRIVATE_KEY_WIF = \
        '5KYsbooGBg51Gohakgq45enpXvCXmEBed1JivFfUZskmjLegHBG'

    _seal_private_key = \
        pybitcointools.decode_privkey(__SEAL_PRIVATE_KEY_WIF, 'wif')
    _seal_public_key = pybitcointools.privtopub(_seal_private_key)

    # The WIF-encoded private report key.  From it, we will create private
    # key we can use for signing attestation verification reports.
    __REPORT_PRIVATE_KEY_WIF = \
        '5Jz5Kaiy3kCiHE537uXcQnJuiNJshf2bZZn43CrALMGoCd3zRuo'

    _report_private_key = \
        pybitcointools.decode_privkey(__REPORT_PRIVATE_KEY_WIF, 'wif')
    _report_public_key = pybitcointools.privtopub(_report_private_key)

    # Minimum duration for PoET 1 simulator is 30 seconds
    __MINIMUM_DURATTION = 30.0

    # The anti-sybil ID for this particular validator.  This will get set when
    # the enclave is initialized
    _anti_sybil_id = None

    # The PoET keys will remain unset until signup info is either created or
    # unsealed
    _poet_public_key = None
    _poet_private_key = None
    _active_wait_timer = None

    @classmethod
    def initialize(cls, **kwargs):
        # Create an anti-Sybil ID that is unique for this validator
        cls._anti_sybil_id = \
            pybitcointools.sha256(kwargs.get('NodeName', 'validator'))

    @classmethod
    def create_signup_info(cls,
                           originator_public_key,
                           validator_network_basename,
                           most_recent_wait_certificate_id):
        with cls._lock:
            # First we need to create a public/private key pair for the PoET
            # enclave to use.
            cls._poet_private_key = pybitcointools.random_key()
            cls._poet_public_key = \
                pybitcointools.privtopub(cls._poet_private_key)
            cls._active_wait_timer = None

            # We are going to fake out the sealing the signup data.
            signup_data = {
                'poet_public_key':
                    pybitcointools.encode_pubkey(cls._poet_public_key, 'hex'),
                'poet_private_key':
                    pybitcointools.encode_privkey(
                        cls._poet_private_key,
                        'hex')
            }
            sealed_signup_data = \
                pybitcointools.base64.b32encode(dict2json(signup_data))

            # Create a fake report
            report_data = {
                'originator_public_key_hash':
                    pybitcointools.sha256(
                        pybitcointools.encode_pubkey(
                            originator_public_key,
                            'hex')),
                'poet_public_key':
                    pybitcointools.encode_pubkey(cls._poet_public_key, 'hex')
            }
            report = {
                'report_data': pybitcointools.sha256(dict2json(report_data)),
                'validator_network_basename': validator_network_basename
            }

            # Fake our "proof" data.
            attestation_evidence_payload = {
                'enclave_quote':
                    pybitcointools.base64.b64encode(dict2json(report)),
                'pse_manifest':
                    pybitcointools.base64.b64encode(
                        pybitcointools.sha256(
                            'manifest destiny')),
                'nonce': most_recent_wait_certificate_id
            }

            attestation_verification_report = {
                'attestation_evidence_payload': attestation_evidence_payload,
                'anti_sybil_id': cls._anti_sybil_id
            }

            proof_data = {
                'attestation_verification_report':
                    attestation_verification_report,
                'signature':
                    pybitcointools.ecdsa_sign(
                        dict2json(attestation_verification_report),
                        cls._report_private_key)
            }

            return \
                EnclaveSignupInfo(
                    poet_public_key=signup_data['poet_public_key'],
                    proof_data=proof_data,
                    sealed_signup_data=sealed_signup_data)

    @classmethod
    def deserialize_signup_info(cls, serialized_signup_info):
        return \
            EnclaveSignupInfo.signup_info_from_serialized(
                serialized_signup_info=serialized_signup_info)

    @classmethod
    def unseal_signup_data(cls, sealed_signup_data):
        """

        Args:
            sealed_signup_data: Sealed signup data that was returned
                previously in a EnclaveSignupInfo object from a call to
                create_signup_info

        Returns:
            A string The hex encoded PoET public key that was extracted from
            the sealed data
        """

        # Reverse the process we used in creating "sealed" signup info.
        # Specifically, we will do a base 32 decode, which gives us json
        # we can convert back to a dictionary we can use to get the
        # data we need
        signup_data = \
            json2dict(pybitcointools.base64.b32decode(sealed_signup_data))

        with cls._lock:
            cls._poet_public_key = \
                pybitcointools.decode_pubkey(
                    signup_data.get('poet_public_key'),
                    'hex')
            cls._poet_private_key = \
                pybitcointools.decode_privkey(
                    signup_data.get('poet_public_key'),
                    'hex')
            cls._active_wait_timer = None

            return signup_data.get('poet_public_key')

    @classmethod
    def verify_signup_info(cls,
                           signup_info,
                           originator_public_key,
                           validator_network_basename,
                           most_recent_wait_certificate_id):
        # Verify the attestation verification report signature
        attestation_verification_report = \
            signup_info.proof_data.get('attestation_verification_report')
        if attestation_verification_report is None:
            raise \
                SignupInfoError(
                    'Attestation verification report is missing from proof '
                    'data')

        if not pybitcointools.ecdsa_verify(
                dict2json(attestation_verification_report),
                signup_info.proof_data.get('signature'),
                cls._report_public_key):
            raise \
                SignupInfoError(
                    'Attestation verification report signature is invalid')

        # Verify the presence of the anti-Sybil ID
        anti_sybil_id = attestation_verification_report.get('anti_sybil_id')
        if anti_sybil_id is None:
            raise \
                SignupInfoError(
                    'Attestation verification report does not contain an '
                    'anti-Sybil ID')

        # Verify that the report data field in the report contains the SHA256
        # digest of the originator's public key SHA 256 digest and the PoET
        # public key.
        attestation_evidence_payload = \
            attestation_verification_report.get(
                'attestation_evidence_payload')
        if attestation_evidence_payload is None:
            raise \
                SignupInfoError(
                    'Attestation verification report does not contain '
                    'attestation evidence payload')

        enclave_quote = attestation_evidence_payload.get('enclave_quote')
        if enclave_quote is None:
            raise \
                SignupInfoError(
                    'Attestation evidence payload does not contain an '
                    'enclave quote')

        report = json2dict(pybitcointools.base64.b64decode(enclave_quote))
        report_data = report.get('report_data')
        if report_data is None:
            raise \
                SignupInfoError('Enclave quote does not contain report data')

        target_report_data = {
            'originator_public_key_hash':
                pybitcointools.sha256(
                    pybitcointools.encode_pubkey(
                        originator_public_key,
                        'hex')),
            'poet_public_key': signup_info.poet_public_key
        }
        target_report_data_digest = \
            pybitcointools.sha256(dict2json(target_report_data))

        if report_data != target_report_data_digest:
            raise SignupInfoError('Enclave quote report data is invalid')

        # Verify that the validator base name in the enclave quote report
        # matches the provided validator network basename
        validator_net_basename = report.get('validator_network_basename')
        if validator_net_basename is None:
            raise \
                SignupInfoError(
                    'Enclave quote report does not have a validator network '
                    'basename')

        if validator_net_basename != validator_network_basename:
            raise \
                SignupInfoError(
                    'Enclave quote report validator network basename [{0}] '
                    'does not match [{1}]'.format(
                        validator_net_basename,
                        validator_network_basename))

        # NOTE - this check is currently not performed as a transaction
        #        does not have a good way to obtaining the most recent
        #        wait certificate ID.
        #
        # Verify that the wait certificate ID in the attestation evidence
        # payload matches the provided wait certificate ID.  The wait
        # certificate ID is stored in the AEP nonce field.
        # nonce = attestation_evidence_payload.get('nonce')
        # if nonce is None:
        #     raise \
        #         SignupInfoError(
        #             'Attestation evidence payload does not have a nonce')
        #
        # if nonce != most_recent_wait_certificate_id:
        #     raise \
        #         SignupInfoError(
        #             'Attestation evidence payload nonce {0} does not match '
        #             'most-recently-committed wait certificate ID {1}'.format(
        #                 nonce,
        #                 most_recent_wait_certificate_id))

    @classmethod
    def create_wait_timer(cls, previous_certificate_id, local_mean):
        with cls._lock:
            # If we don't have a PoET private key, then the enclave has not
            # been properly initialized (either by calling create_signup_info
            # or unseal_signup_data)
            if cls._poet_private_key is None:
                raise \
                    WaitTimerError(
                        'Enclave must be initialized before attempting to '
                        'create a wait timer')

            # Create some value from the cert ID.  We are just going to use
            # the seal key to sign the cert ID.  We will then use the
            # low-order 64 bits to change that to a number [0, 1]
            tag = \
                pybitcointools.base64.b64decode(
                    pybitcointools.ecdsa_sign(
                        previous_certificate_id,
                        cls._seal_private_key))

            tagd = float(struct.unpack('L', tag[-8:])[0]) / (2**64 - 1)

            # Now compute the duration
            duration = cls.__MINIMUM_DURATTION - local_mean * math.log(tagd)

            # Create and sign the wait timer
            wait_timer = \
                EnclaveWaitTimer(
                    duration=duration,
                    previous_certificate_id=previous_certificate_id,
                    local_mean=local_mean)
            wait_timer.signature = \
                pybitcointools.ecdsa_sign(
                    wait_timer.serialize(),
                    cls._poet_private_key)

            # Keep track of the active wait timer
            cls._active_wait_timer = wait_timer

            return wait_timer

    @classmethod
    def deserialize_wait_timer(cls, serialized_timer, signature):
        with cls._lock:
            # Verify the signature before trying to deserialize
            if not pybitcointools.ecdsa_verify(
                    serialized_timer,
                    signature,
                    cls._poet_public_key):
                return None

        return \
            EnclaveWaitTimer.wait_timer_from_serialized(
                serialized_timer=serialized_timer,
                signature=signature)

    @classmethod
    def create_wait_certificate(cls, block_digest):
        with cls._lock:
            # If we don't have a PoET private key, then the enclave has not
            # been properly initialized (either by calling create_signup_info
            # or unseal_signup_data)
            if cls._poet_private_key is None:
                raise \
                    WaitCertificateError(
                        'Enclave must be initialized before attempting to '
                        'create a wait certificate')

            # Several criteria we need to be met before we can create a wait
            # certificate:
            # 1. We have an active timer
            # 2. The active timer has expired
            # 3. The active timer has not timed out
            if cls._active_wait_timer is None:
                raise \
                    WaitCertificateError(
                        'Enclave active wait timer has not been initialized')

            # HACK ALERT!!  HACK ALERT!!  HACK ALERT!!  HACK ALERT!!
            #
            # Today, without the genesis utility we cannot make these checks.
            # Once we have the genesis utility, this code needs to change to
            # Depend upon the timer not being expired or timed out.  The
            # Original specification requires this check.
            #
            # HACK ALERT!!  HACK ALERT!!  HACK ALERT!!  HACK ALERT!!
            #
            # if not cls._active_wait_timer.has_expired():
            #     raise \
            #         WaitCertificateError(
            #             'Cannot create wait certificate because timer has '
            #             'not expired')
            # if wait_timer.has_timed_out():
            #     raise \
            #         WaitCertificateError(
            #             'Cannot create wait certificate because timer '
            #             'has timed out')

            # Create a random nonce for the certificate.  For our "random"
            # nonce we will take the timer signature, concat that with the
            # current time, JSON-ize it and create a SHA-256 hash over it.
            # Probably not considered random by security professional
            # standards, but it is good enough for the simulator.
            random_string = \
                dict2json({
                    'wait_timer_signature': cls._active_wait_timer.signature,
                    'now': datetime.datetime.utcnow().isoformat()
                })
            nonce = pybitcointools.sha256(random_string)

            # First create a new enclave wait certificate using the data
            # provided and then sign the certificate with the PoET private key
            wait_certificate = \
                EnclaveWaitCertificate.wait_certificate_with_wait_timer(
                    wait_timer=cls._active_wait_timer,
                    nonce=nonce,
                    block_digest=block_digest)
            wait_certificate.signature = \
                pybitcointools.ecdsa_sign(
                    wait_certificate.serialize(),
                    cls._poet_private_key)

            # Now that we have created the certificate, we no longer have an
            # active timer
            cls._active_wait_timer = None

            return wait_certificate

    @classmethod
    def deserialize_wait_certificate(cls, serialized_certificate, signature):
        return \
            EnclaveWaitCertificate.wait_certificate_from_serialized(
                serialized_certificate=serialized_certificate,
                signature=signature)

    @classmethod
    def verify_wait_certificate(cls, certificate, encoded_poet_public_key):
        # poet_public_key = \
        #     pybitcointools.decode_pubkey(encoded_poet_public_key, 'hex')
        #
        # return \
        #     pybitcointools.ecdsa_verify(
        #         certificate.serialize(),
        #         certificate.signature,
        #         poet_public_key)

        return True