Welcome to ksi-python’s documentation!¶
KSI Python SDK¶
This is a thin wrapper on top of KSI C SDK. Experimental, non-supported code.
Synopsis¶
Example of synchronous singing and verification.
import ksi
import hashlib
# Instantiate service parameters from the environment
KSI = ksi.KSI(**ksi.ksi_env())
# Sign a text string
sig = KSI.sign_hash(hashlib.sha256(b"Tere!"))
# Print some signature properties
print(sig.get_signing_time(), sig.get_signer_id())
# Now verify this text string, first obtaining a data hasher
h = sig.get_hasher()
h.update(b"Tere!")
print(KSI.verify_hash(sig, h))
# Obtain a binary blob which can be stored for long term
serialized_signature = sig.get_binary()
# Some time have passed, fetch the signature and verify again
sig2 = KSI.new_signature_object(serialized_signature)
print(KSI.verify_hash(sig2, h))
Note that asynchronous signing can provide a significant speedup when multiple signatures are requested. Example of asynchronous singing with gevent.
import ksi
import hashlib
from gevent.pool import Pool
# Instantiate service parameters from the environment
KSI = ksi.KSI(**ksi.ksi_env())
# Multiple strings to be signed
string_list = ["This", "is", "a", "list", "of", "strings"]
# Define a signer function.
def sign_hash(h):
sig = KSI.sign_hash(h)
# Verification and/or storing could be done here
print(KSI.verify_hash(sig, h))
# Create a gevent pool. Note that for optimal efficiency
# pool size should not be smaller than
# ``KSI.get_async_config()['max_pending_count']``
pool = Pool(100)
# Sign all strings asynchronously
for string in string_list:
pool.spawn(sign_hash, hashlib.sha256(string.encode()))
pool.join()
Client-side aggregation (signing in blocks) is also possible if the KSI Gateway allows it. This means that multiple hashes can be individually signed by a single request to the Gateway server. In addition, if block signing is allowed, the asynchronous signing service will by default dynamically sign in blocks depending on if the signing requests demand becomes too large to efficiently sign them one-by-one. Example of signing a block of hashes synchronously (asynchronous block signing is supported as well).
import ksi
import hashlib
# Instantiate service parameters from the environment
KSI = ksi.KSI(**ksi.ksi_env())
# Multiple strings to be signed
string_list = ["This", "is", "a", "list", "of", "strings"]
# Hashes of strings
hash_list = [hashlib.sha256(string.encode()) for string in string_list]
# Sign hashes in a block
sigs = KSI.sign_hash_list(hash_list)
# Verify hashes
for i in range(len(sigs)):
print(KSI.verify_hash(sigs[i], hash_list[i]))
Install¶
Requirements: Python 2.7+ or Python 3.1+. Jython, IronPython are not supported.
Install fresh libksi aka KSI C SDK; see https://github.com/guardtime/libksi/
Install python-devel package
Run:
> pip install ksi-python
or
> easy_install ksi-python
Tests¶
Specify KSI Gateway access parameters and run
> python setup.py test
To test if KSI Python SDK signs asynchronously with gevent, make sure gevent is installed.
Documentation¶
http://guardtime.github.io/ksi-python/
Type:
> pydoc ksi
to read the documentation after installation. Generating html or pdf documentation:
make sure that dependencies like sphinx (pip install sphinx
) are installed, extension
is built (python setup.py build
) and run:
> cd docs
> make html
or
> make latexpdf
License¶
Apache 2.0. Please contact Guardtime for supported options.
API Reference¶
class KSI¶
-
class
ksi.
KSI
(signing_service, extending_service, publications_file='http://verify.guardtime.com/ksi-publications.bin', publications_cnstr={'email': 'publications@guardtime.com'})¶ KSI service provider class. Holds context and service parameters.
Parameters: - signing_service (dict) – Signing/aggregation service access parameters. A dictionary with following keys: {‘url’: .., ‘user’: …, ‘pass’: …}
- extending_service (dict) – Extending service access parameters. A dictionary with following keys: {‘url’: .., ‘user’: …, ‘pass’: …}
- publications_file (str, optional) – Publications file download URL. Default one works with Guardtime commercial KSI service.
- publications_cnstr (dict, optional) –
Publications file verification constraints: signing certificate (must be issued under a root in global system truststore) fields are validated against provided rules, e.g.:
{"email" : "publications@guardtime.com"}
Default one works with Guardtime commercial KSI service.
- Properties:
- verification: constants specifying verification policies. see
set_verification_policy()
-
extend
(sig)¶ Extend the signature to the latest available publication.
Modifies the signature in-place. Extender sever must be specified earlier. Possible, if there is at least 1 publication created after signing, i.e. quite safe to try if 35 days have passed from signing.
Extending a signature is very useful before long-term archiving. This makes distant-future verification possible without access to extender service.
Parameters: KsiSignature – Returns: True if extending was successful, False if no suitable publication was found. Raises: ksi.Error
– or its subclass on KSI errors
-
get_async_config
()¶ Get the current async configuration.
The maximum aggregator request count (from
KSI.get_async_config
) is the maximum number of requests the server is willing to respond to per round. Setting amax_req_count
significantly larger than that will most probably cause failures and the async service to unnecessarily resend often failing requests in cache.See
set_async_config()
Returns: {“max_req_count”: (int), “max_pending_count”: (int), “max_aggregator_req_count”: (int)} Return type: dict
-
get_async_state
()¶ Get the async state.
See
KsiAsync.get_state()
-
get_verification_policy
()¶ Retrieves the current verification policy.
Returns: Identifier. see set_verification_policy()
for a list of values.Return type: int
-
new_signature_object
(blob)¶ Instantiates a KsiSignature object from earlier serialized binary representation.
Parameters: str/buffer – binary representation of signature data. Returns: A newly instantiated KsiSignature
object.Return type: KsiSignature Raises: ksi.Error
– or its subclass on KSI errors
-
set_async_config
(config)¶ Set the async configuration.
Note
max_pending_count
cannot be assigned a smaller value than previously set. Setting it significantly higher will cause request timeouts (meaning a high rate of request resending) and possiblyAsyncServiceError
instances.Note that if
max_pending_count
is reached then new requests will be cached in a queue until the number of pending requests becomes smaller. The user is responsible for memory safety of the queued requests, possibly using gevent.pool with a max size ofmax_pending_count
+ queue max size (depending on the amount of memory the program should have access to).Note
The asynchronous service maximum request count is initially capped at 250 responses per round (one round lasts for 1 second) and maximum pending count at 500. Even if the maximum pending count is allowed to be set higher, network errors may occur, as the host may not be able to send/receive all requests on time, depending on the host’s NIC, CPU, etc.
Parameters: dict – {‘max_req_count’: (int), ‘max_pending_count’: (int)}
Raises: ValueError
– when input contains invalid values or if ‘max_pending_count’ is smaller than previously setTypeError
– when input contains invalid keys
-
set_verification_policy
(new_policy)¶ Specify a verification policy for future verifications.
Note that it is hard to fail with the default one. Change only if necessary. Suffix_EXT
instructs verification to transparently extend the signature if possible.Parameters: policy_id – A property from the KSI object:
KSI.verification.POLICY_GENERAL KSI.verification.POLICY_GENERAL_EXT KSI.verification.POLICY_KEY_BASED KSI.verification.POLICY_CALENDAR_BASED KSI.verification.POLICY_PUBLICATIONS_FILE_BASED KSI.verification.POLICY_PUBLICATIONS_FILE_BASED_EXT
-
sign_blob
(blob)¶ Signs a data blob.
Note
- In case of gevent use (function run by spawned greenlet):
- Signing is an asynchronous operation.
- Otherwise:
- Signing blocks for 1..2 seconds.
Parameters: str/buffer – data to be signed. Returns: see KsiSignature
class in this module.Return type: KsiSignature Raises: ksi.Error
– or its subclass on KSI errors
-
sign_hash
(data_hash)¶ Signs a data hash.
Note
- In case of gevent use (function run by spawned greenlet):
- Signing is an asynchronous operation.
- Otherwise:
- Signing blocks for 1..2 seconds.
When using gevent, the state of the async service can be monitored by calling
KSI.get_async_state()
. Spawning greenlets does not automatically guarantee that that the async state is updated as greenlets may not run instantaneously. It is recommended to also monitor how many greenlets are being spawned or even use gevent.pool in accordance with the async service configuration (seeKSI.get_async_config()
) for increased memory safety.Parameters: Object – hash created using a hasher from Python
hashlib
.Returns: see
KsiSignature
class in this module.Return type: Raises: ksi.Error
– or its subclass on KSI errorsksi.AsyncServiceError
– when the request has waited over a minute to be sent out or when the maximum retry count has been reached due to previous errors. Related to too many asynchronous requests or incorrect async service config
-
sign_hash_list
(data_hashes)¶ Signs a list of hashes in one block.
Note
- In case of gevent use (function run by spawned greenlet):
- Signing is an asynchronous operation.
- Otherwise:
- Signing blocks for 1..2 seconds.
Parameters: list – hashes created using a hasher from Python
hashlib
.Returns: list of
KsiSignature
instancesReturn type: Raises: ksi.Error
– or its subclass on KSI errorsksi.AsyncServiceError
– when this function is called, but block signing is not allowed
-
verify
(sig)¶ Verify signature consistency. No document content check.
No return value.
Raises: ksi.Error
– or its subclass on KSI errors
-
verify_blob
(sig, blob)¶ Verify a data blob and its signature.
Parameters: - KsiSignature – Signature object.
- blob – A string or buffer containing binary data bytes.
Returns: - A 3-tuple with following data:
True if signature is OK; False if there is conslusive evidence that signature is invalid and further action can not change the situation. Code: error code as string. See https://github.com/guardtime/libksi/blob/v3.16.2482/src/ksi/policy.h#L70. Reason: Human readable message.
Return type: (result (boolean), code (str), reason (str))
Raises: ValueError
– Final decision is not possible given current input data. Changing something, e.g. verification policy, might help to find a conclusive answer.ksi.Error
– or its subclass on KSI errors
-
verify_hash
(sig, data_hash)¶ Verify a signature and a provided data hash of signed data.
Parameters: - KsiSignature – Signature object.
- data_hash – A hasher from
hashlib
, after processing its input data.
Returns: - A 3-tuple with following data:
True if signature is OK; False if there is conslusive evidence that signature is invalid and further action can not change the situation. Code: error code as string. See https://github.com/guardtime/libksi/blob/v3.16.2482/src/ksi/policy.h#L70. Reason: Human readable message.
Return type: (result (boolean), code (str), reason (str))
Raises: ValueError
– Final decision is not possible given current input data. Changing something, e.g. verification policy, might help to find a conclusive answer.ksi.Error
– or its subclass on KSI errors
class KsiSignature¶
-
class
ksi.
KsiSignature
(ksi, sig)¶ KSI Signature object.
Exists only in instantiated, integrity-checked form. Created by the KSI class only. Do not initialize directly.
Parameters: - ksi (class KSI) – KSI class. Necessary to avoid early garbage collection.
- sig – serialized signature.
-
get_binary
()¶ Returns a serialized signature, ready to be passed to C language binding or stored into a file or db field.
-
get_data_hash
()¶ Returns the hash algorithm and hash value of signed data
See the note about get_hash_algorithm()
Returns: A tuple of hash algorithm name and a binary data hash. Return type: (str, str/buffer) Raises: ksi.Error
– or its subclass on KSI errors
-
get_hash_algorithm
()¶ Returns the hash algorithm name used for hashing data during signing.
It is important to use exactly the same hash algorithm for hashing data during signing and during signature verification.
Note
Returns algorithm names like
SHA2-256
. Some older libraries, like Python’s hashlib and OpenSSL use names likeSHA256
. Use some processing to get legacy compatible name, for example:sig.get_hash_algorithm().replace(“SHA2-“, “sha”)Returns: hash algorithm name Return type: str Raises: ksi.Error
– or its subclass on KSI errors
-
get_hasher
()¶ Returns a data hasher object from hashlib.
Used hash algorithm matches the one which was used during the signature creation.
Returns: a new hashing object from hashlib
Return type: Object
Raises: ksi.Error
– or its subclass on KSI errorsValueError
– hashlib does not support used hash algorithm
-
get_publication_data
()¶ Returns publication data used for extending this signature.
Returned data can be used to validate the signature in strongest possible form, using an independent publication. Publications references refer to the choice of mediums; publication code can be compared with one printed in chosen medium.
Returns: None if publication record is not available. A dictionary on success, see example below: { 'refs': [ u'ref1', u'Financial Times, ISSN: 0307-1766, 2015-06-17', u'https://twitter.com/Guardtime/status/611103980870070272' ], 'publication': 'AAAAAA-CVPYKY-AANVCV-Q7IJFM-ZJ5YBK-BNXRKG-IWIFER-5XXL73-4NXXLS-D6CROT-QUMAHA-JDWBQI', 'publishing_time_t': 1434326400L, 'publishing_time': datetime.datetime(2015, 6, 15, 0, 0) }
Raises: ksi.Error
– or its subclass on KSI errors
-
get_signer_id
()¶ Extract cryptographically protected data signer’s identity.
This identifier contains hierarchical namespace of KSI aggregators, gateway and end user. May look like
GT :: GT :: Company :: user.name
.Returns: a signer’s id prefixed with full aggregator hierarchy. Return type: str Raises: ksi.Error
– or its subclass on KSI errors
-
get_signing_time
()¶ Extract cryptographically protected signing time from a signature.
Returns: datetime.datetime
object, in local timezone.Return type: Object Raises: ksi.Error
– or its subclass on KSI errors
-
get_signing_time_utc
()¶ Extract cryptographically protected signing time from a signature.
Returns: datetime.datetime
object, using UTC timezone.Return type: Object Raises: ksi.Error
– or its subclass on KSI errors
-
is_extended
()¶ Checks if signature is extened and contains a publication record.
Returns: True if extended, False if not. Return type: Bool Raises: ksi.Error
– or its subclass on KSI errors
-
set_binary
(sig)¶ Sets a new serialized signature. Called after successfully extending the prior signature. Internal use.
class KsiAsync¶
-
class
ksi.
KsiAsync
(ctx, surl, suser, skey)¶ Async service class. Handles the KSI asynchronous signing service.
Created by the KSI class only. Do not initialize or call methods directly.
Note that this class is optimized to be used with gevent. Requests called without spawned greenlets (e.g. by gevent.spawn or gevent.Pool.spawn) will be blocking.
-
get_config
()¶ Returns async service configuration
Note
- Mutable entries (see
KsiAsync.set_config
): - ‘max_req_count’, ‘max_pending_count’, ‘dynamic_block_signing’
- Immutable entries:
- ‘server_max_req_count’:
- maximum number of requests the aggregator is willing to respond to per round
- ‘max_block_size’:
- capped at 2^16, max block size gives the maximum number of hashes one block can contain
- ‘block_signing’:
- if block signing is available for the current ksi context. If not,
‘dynamic_block_signing’ cannot be set to True and
KSI.sign_hash_list
raisesKSI.AsyncServiceError
Returns: - {“server_max_req_count”: int, “max_req_count”: int,
- ”max_pending_count”: int, “max_block_size”: int, “dynamic_block_signing”: bool, “block_signing”: bool}
Return type: dict - Mutable entries (see
-
get_response
(request_id)¶ Returns the serialized signature
Removes request and response data from instance and returns the received signature.
Parameters: uuid – id of request Returns: signature of the hash with given uuid Return type: sig
-
get_state
()¶ Returns async service state
- state dictionary key/value pairs:
- “pending requests”:
- (int) number of requests which have been sent for
signing. Cannot be larger than
KsiAsync.max_pending_count
- “responses ready”:
- (int) number of signatures which have been received
but not returned by
KSI.sign_hash
- “waiting in queue”:
- (int) number of signatures which have not yet been sent
for signing as
KsiAsync.max_pending_count
has been reached (if a request stays in queue for more than 1 minute, thenAsyncServiceError
is raised) - “requests in cache”:
- (int) number of active
KSI.sign_hash
andKSI.sign_hash_list
instances
Returns: dict (state)
-
is_pending
(request_id)¶ Checks if request hash has been signed
Parameters: uuid – id of request Returns: True if requested hash has been signed Return type: bool
-
new_block_request
(hash_names, hash_digests, leaf_ids=None)¶ Creates block signing request and sends to the service.
Parameters: - [string] – list of hashing function names
- [Object] – list of hashes created by
hashlib
- [uuid] – list of hash uuids if block signing request is dynamically initialized by KsiAsync
Returns: id of the request so its progress can be tracked
Return type: uuid
Raises: ksi.AsyncServiceError
– requested block size exceeds limitksi.Error
– or its subclass on KSI errors
-
new_request
(hash_name, hash_digest)¶ Creates request and sends to the service.
Parameters: - string – name of the hashing function which created this hash.
- Object – hash created using a hasher from Python
hashlib
Returns: id of the request so its progress can be tracked
Return type: uuid
Raises: ksi.Error
– or its subclass on KSI errors
-
run_service
(request_id)¶ Runs the async signing service
_ksi.ksi_run_async_service
returns:- dict/bool:
- {uuid: signature} in case the async response handle returned a signature response
- int:
- is the number of responses yet to be received
- list/bool:
- [response_id, error_string] if the async response handle returned an error
Parameters: uuid – in order to track current request
Raises: ksi.Error
– or its subclass on KSI errorsksi.AsyncServiceError
– if too many retry requests have been sent with current uuidksi.HashError
– if problems with the request hash
-
set_config
(max_req_count=None, max_pending_count=None, dynamic_block_signing=None)¶ Saves new async service configuration
Parameters: - int – maximum request count per round, should not be larger than the server maximum request count per round, as timeouts and HTTP errors will occur
- int – maximum number of requests to be cached at one time, must not be smaller than last configured value
- bool – sign with blocks explicitly or in case demand exceeds max_pending_count, not permitted
to use if
config['block_signing'] == False
Raises: ValueError
– when parameters are incorrect
-
Helpers¶
-
ksi.
ksi_env
()¶ Helper for initializing a KSI class with KSI service parameters from the environment.
- System environment parameters should look like:
- Required:
- KSI_AGGREGATOR=’url=http://url user=name pass=xyz’ KSI_EXTENDER=’url=http://url user=name pass=xyz’
- Required if using Guardtime commerical KSI service:
- KSI_PUBFILE=’http://verify.guardtime.com/ksi-publications.bin’ KSI_PUBFILE_CNSTR=’{“email” : “publications@guardtime.com”}’
Example
KSI = ksi.KSI(**ksi.ksi_env())
Raises: ksi.FormatError
– when an environment variable misses required component.
Exceptions¶
-
exception
ksi.
AsyncServiceError
¶ Bases:
ksi.Error
- Request max retry count reached.
- HTTP error (possibly caused by requesting significantly more responses than the
aggregator can handle, see
KSI.set_async_config()
). - Network error (possibly caused by sening too many requests and receiving timeout
errors, see
KSI.set_async_config()
). - Async service request cache full.
- Async connection was closed.
- Async service has not finished.
- Request timeout (possibly caused by too many pending requests or initializing a synchronous sign requests on top of a full request queue).
- If
KSI.sign_hash_list
is called, but block signing is not allowed.
-
exception
ksi.
CryptoError
¶ Bases:
ksi.Error
- Cryptographic operation could not be performed. Likely causes are unsupported cryptographic algorithms, invalid keys and lack of resources.
-
exception
ksi.
Error
¶ Bases:
exceptions.Exception
Unspecfied KSI error
-
exception
ksi.
FormatError
¶ Bases:
ksi.Error
Invalid argument, format, or parameter. Untrusted or unavailable hash algorithm.
-
exception
ksi.
InvalidSignatureError
¶ Bases:
ksi.Error
- Invalid KSI signature.
- Invalid PKI signature.
- The PKI signature is not trusted by the API.
- The objects used are in an invalid state.
-
exception
ksi.
NetworkError
¶ Bases:
ksi.Error
- A network error occurred.
- A network connection timeout occurred.
- A network send timeout occurred.
- A network receive timeout occurred.
- A HTTP error occurred.
-
exception
ksi.
PublicationError
¶ Bases:
ksi.Error
Problems with publication based verification.
- The extender returned a wrong calendar chain.
- No suitable publication to extend to.
- The publication in the signature was not found in the publications file.
- Invalid publication.
- The publications file is not signed.
-
exception
ksi.
ServiceAuthError
¶ Bases:
ksi.Error
- The request could not be authenticated (missing or unknown login identifier, MAC check failure, etc).
-
exception
ksi.
ServiceError
¶ Bases:
ksi.Error
Various KSI service related errors.
- The request is still pending.
- The request ID in response does not match with request ID in request.
- Pattern for errors with client request.
- The request contained invalid payload (unknown payload type, missing mandatory elements, unknown critical elements, etc).
- The server encountered an unspecified internal error.
- The server encountered unspecified critical errors connecting to upstream servers.
- No response from upstream aggregators.
- The extender returned an error.
- The request indicated client-side aggregation tree larger than allowed for the client (retrying would not succeed either).
- The request combined with other requests from the same client in the same round would create an aggregation sub-tree larger than allowed for the client (retrying in a later round could succeed).
- Too many requests from the client in the same round (retrying in a later round could succeed)
- Input hash value in the client request is longer than the server allows.
- Received PDU v2 response to PDU v1 request. Configure the SDK to use PDU v2 format for the given aggregator.
- Received PDU v1 response to PDU v2 request. Configure the SDK to use PDU v1 format for the given aggregator.
-
exception
ksi.
ServiceExtenderBlockchainError
¶ Bases:
ksi.ServiceError
The Extender server does not have required period of Calendar Blockchain.
- The request asked for a hash chain going backwards in time
- Pattern for local errors in the server.
- The server misses the internal database needed to service the request (most likely it has not been initialized yet).
- The server’s internal database is in an inconsistent state.
- The request asked for hash values older than the oldest round in the server’s database.
- The request asked for hash values newer than the newest round in the server’s database.