Ace:IMSPython

From Adapt

Using the python suds library it's fairly trivial to request tokens from the ACE IMS service.

Validating ace tokens is described in on the ims overview page and token store example page

Request One Token

The ACE IMS provides a requestTokensImmediate call which can be used to request tokens for a supplied set of hash values. The tokens returned from this call can be used to validate the hash of any file hasn't been modified.

Code

from suds.client import Client

url='http://ims.umiacs.umd.edu:8080/ace-ims/IMSWebService?wsdl'
client = Client(url)
request = client.factory.create('tokenRequest')
request.hashValue = '4ed9ba3d9c7e3e092d0b0e3441f04574'
request.name = 'MyFile'

result = client.service.requestTokensImmediate('SHA-256-0',reqlist)
print result

The resulting token contains the name you supplied in the request, a proof for the hash value you submitted, and importantly the round ID and digest algorithm used to calculate the proof. You will use the round, digest algorithm and proof to later validate a file's hash value.

  • digestService - digest algorithm used to build proof
  • name - descriptive name you supplied in the request
  • proofElements - proof hashes used to validate file
  • roundID - which round were these tokens part of, used to validate proof result

The other items are also returned, but are not necessary to validate a proof

  • statusCode - 100 if success, some other number otherwise (see source code)
  • timestamp - exact time of token generation, useful, but should not be trusted as its not linked to the proof
  • tokenClassName - internal queue used to generate this queue, useful for debugging only

Output

[python] [toaster@loach ace-cli]$ python test2.py 
[(tokenResponse){
   digestService = "SHA-256"
   name = "somename"
   proofElements[] = 
      (proofElement){
         hashes[] = 
            "9129f93bc8ac2d93e35aa6206298fb8616690211a8563db51cf2ea1159682692",
         index = 0
      },
      (proofElement){
         hashes[] = 
            "34bd07cc18a7ab1a47467081dcb21a6ca1857b1d3bdc12106ba2fd538b3bafbd",
         index = 0
      },
      (proofElement){
         hashes[] = 
            "8a2042da9a114a41cf3738a841d65336af5b864ed6be8484c6bae4a4ac9e65a1",
         index = 0
      },
      (proofElement){
         hashes[] = 
            "e95826668c3f301bef729e60157bbd3dbc346859ceee71655a9a065106276d72",
         index = 1
      },
   roundId = 2892850
   statusCode = 100
   timestamp = 2011-01-07 12:59:36.000013
   tokenClassName = "SHA-256-0"
 }]

Generate a token for a file

Using hashlib, and binascii we can use python to both generate a digest and grab an ace token for that digest.

While this example only sends one request in a call, you should batch your requests prior to requesting a token. Just send a 'list' of tokenRequest objects to requestTokensImmediate. The IMS will support up to 10,000 requests per call.

Code

import hashlib
import binascii
from suds.client import Client

filename='test2.py'

digFile = open(filename,'rb')
hashAlg = hashlib.sha256()
hashAlg.update(digFile.read())
filedigest = binascii.b2a_hex(hashAlg.digest())

url='http://ims.umiacs.umd.edu:8080/ace-ims/IMSWebService?wsdl'
client = Client(url)

print  filename, ' ', filedigest

request = client.factory.create('tokenRequest')
request.hashValue = filedigest
request.name = filename

result = client.service.requestTokensImmediate('SHA-256-0',request)
print result

Output

[python] [toaster@loach ace-cli]$ python test2.py
test2.py   164182eef9792e2e1c5005cd9240ff508aef042b8fa344597431eae39370c784
[(tokenResponse){
   digestService = "SHA-256"
   name = "test2.py"
   proofElements[] = 
      (proofElement){
         hashes[] = 
            "c5e82872eeee3dfa539202a9757f8a5364b6fded4dfcb40b66084158f2b5c627",
         index = 0
      },
      (proofElement){
         hashes[] = 
            "6e16a71847403f4e586625463160993bfab189c0bba771d81354c03d9c3591fd",
         index = 0
      },
      (proofElement){
         hashes[] = 
            "0879b385c366d07142446a18dfb6d19c468a733991e9685fc75ce6f4b929b659",
         index = 0
      },
      (proofElement){
         hashes[] = 
            "e19dd18bd9eabf79a074d72231a7117bd2319a859d31a429575b4657e85d0c95",
         index = 1
      },
   roundId = 2893078
   statusCode = 100
   timestamp = 2011-01-07 13:08:27.000253
   tokenClassName = "SHA-256-0"
 }]


Round Requests

In order to validate the proof for any file, you need to recompute the proof result and compare that to the round hash stored on the IMS. The getRoundSummaries should be used to request the hash for previously generated rounds.

You can find the id for the round you need to retrieve in the roundId field in an ace token.

Code

from suds.client import Client

url='http://ims.umiacs.umd.edu:8080/ace-ims/IMSWebService?wsdl'
client = Client(url)

result = client.service.getRoundSummaries(2893078)
print result[0].id
print result[0].hashValue

Output

[python] [toaster@loach ace-cli]$ python test2.py
2893078
1324d496da42e04347c74001f8bd948b31fa296419ee49246ba5494970b16752

Complete Example, file to round

This example will read a file, compute its digest and request a token. It will then recompute the round hash using the

Code

import hashlib
import binascii
from suds.client import Client

filename='test2.py'

digFile = open(filename,'rb')
hashAlg = hashlib.sha256()
hashAlg.update(digFile.read())
binarydigest = hashAlg.digest()
filedigest = binascii.b2a_hex(binarydigest)

url='http://ims.umiacs.umd.edu:8080/ace-ims/IMSWebService?wsdl'
client = Client(url)

print '---File to secure:'
print filename, ' ', filedigest


print '\n---Token Response from IMS'
request = client.factory.create('tokenRequest')
request.hashValue = filedigest
request.name = filename
token = client.service.requestTokensImmediate('SHA-256-0',request)
print 'Round:',  token[0].roundId, ' Date:', token[0].timestamp
print token[0].proofElements


print '\n---Computing proof'

level = 0
prevhash = binarydigest
for element in token[0].proofElements:
    i = 0
    hashAlg = hashlib.sha256()
    # create level by converting hashes to bytes and inserting 
    # previous level where necessary, first level uses file hash
    for strhash in element.hashes:
        if i == element.index:
            hashAlg.update(prevhash)
        hashAlg.update(binascii.a2b_hex(strhash))
        i = i + 1

    # in case previous level is to be inserted at end
    if i == element.index:
        hashAlg.update(prevhash)
    prevhash = hashAlg.digest()
    print 'Level:',level, '( index:',element.index,') ', binascii.b2a_hex(prevhash)
    level = level + 1

print '\n---Requesting Round Hash for',token[0].roundId
rounds = client.service.getRoundSummaries(token[0].roundId)
print 'Round hash:', rounds[0].hashValue

print '\n---Comparing Round Hash to computed proof hash'
print rounds[0].hashValue
print binascii.b2a_hex(prevhash)
print 'Equal:',binascii.b2a_hex(prevhash) == rounds[0].hashValue

Output

[python] [toaster@loach ace-cli]$ python test2.py
---File to secure:
test2.py   1f9bf12fc4e402d6cad8b36f9f6e0482af14add1b00f0f5aff2197309483a199

---Token Response from IMS
Round: 2895642  Date: 2011-01-07 14:54:21.000883
[(proofElement){
   hashes[] = 
      "31ed6c99ea3932bd2cbc8db0e42fc7bd028a773ac7e2359785f55fe076a6492b",
   index = 0
 }, (proofElement){
   hashes[] = 
      "be071a2271d0be7633e986c4463cbc3fc39e244d75948000c41583e78d0398e3",
   index = 0
 }, (proofElement){
   hashes[] = 
      "2cb53579297ff25fa6492e6f9b4cc130acf379f1061e31d2e5fd65da734c0f91",
   index = 0
 }, (proofElement){
   hashes[] = 
      "439e40fbeae4635d014091ed93324cd467c61a3848516ee99d2c0bb20e02e7cf",
   index = 1
 }]

---Computing proof
Level: 0 ( index: 0 )  f91649510d117b361d6023b8846f12f273de0e25fab257d94be774a77bd222c6
Level: 1 ( index: 0 )  1d56960d990765a1581559786df5e77dbdcf25e6c53b79f215375001ac761f88
Level: 2 ( index: 0 )  31473c279b3d0d4714b096fe59c0aaa2b36286cbf3e82dbc9afb73a988a33f3e
Level: 3 ( index: 1 )  1ccba7f2302c71615f20e5b5768a118fbf44781e40bd2d7e5479ddd11a46d44c

---Requesting Round Hash for 2895642
Round hash: 1ccba7f2302c71615f20e5b5768a118fbf44781e40bd2d7e5479ddd11a46d44c

---Comparing Round Hash to computed proof hash
1ccba7f2302c71615f20e5b5768a118fbf44781e40bd2d7e5479ddd11a46d44c
1ccba7f2302c71615f20e5b5768a118fbf44781e40bd2d7e5479ddd11a46d44c
Equal: True

Challenge Round Data

The above example shows how to assert a digest you have hasn't been tampered with. Now, how much do you trust the IMS? Using an external witness value, you can challenge the IMS to provide a proof for any round to that day's witness value. Checking the proof for a witness identical to checking an individual file except you start with the round as your base and end up with a witness instead of starting at a file's digest and ending with a round.

There is a caveat, as the witness is only generated once a day, you will have to wait until the next day after a witness has been created to run a challenge.

The createWitnessProofForRound call will generate a proof which links that round to the daily witness value. These calls take a little bit of time to run since the IMS will compute this proof on demand for you.

Code

import hashlib
import binascii
from suds.client import Client
#
# Witness value retrieved from:
# http://groups.google.com/group/ace-ims-witness
#
roundid = 2855147
trustedwitnessvalue='d85e36d6af2246d76c9a4fa0ef22eb10a5215eae5747504241b92b18f2c22467'

url='http://ims.umiacs.umd.edu:8080/ace-ims/IMSWebService?wsdl'
client = Client(url)

print '\n---Requesting Round Hash for',roundid
rounds = client.service.getRoundSummaries(roundid)
print 'Round hash:', rounds[0].hashValue

print '\n---Requesting proof to witness for round',roundid
witnessProof = client.service.createWitnessProofForRound(roundid)
print 'Witness ID:', witnessProof[0].witnessId, 'Timestamp:',witnessProof[0].roundTimestamp, witnessProof[0].tokenClassName, witnessProof[0].digestService
print witnessProof[0].proofElements

print '\n---Calculating round to witness proof',roundid
level = 0
prevhash = binascii.a2b_hex(rounds[0].hashValue)
for element in witnessProof[0].proofElements:
    i = 0
    hashAlg = hashlib.sha256()
    # create level by converting hashes to bytes and inserting 
    # previous level where necessary, first level uses file hash
    for strhash in element.hashes:
        if i == element.index:
            hashAlg.update(prevhash)
        hashAlg.update(binascii.a2b_hex(strhash))
        i = i + 1

    # in case previous level is to be inserted at end
    if i == element.index:
        hashAlg.update(prevhash)
    prevhash = hashAlg.digest()
    print 'Level:',level, '( index:',element.index,') ', binascii.b2a_hex(prevhash)
    level = level + 1

imswitnessvalue = binascii.b2a_hex(prevhash)
print '\n---Comparing trusted value to IMS proof result'
print 'calculated',imswitnessvalue
print 'trusted   ',trustedwitnessvalue
print 'Equal:',imswitnessvalue == trustedwitnessvalue

Output

[python] [toaster@loach ace-cli]$ python test3.py

---Requesting Round Hash for 2855147
Round hash: 476106066c46a15f96be17327c7054fdf3c87821db73ff88daeb4ca83f53cb57

---Requesting proof to witness for round 2855147
Witness ID: 1759 Timestamp: 2011-01-06 07:00:54 SHA-256 SHA-256
[(proofElement){
   hashes[] = 
      "1671688d58c4d8e99f373db6d3796a21fc127e3f987b13cf76e589bf12c1123e",
      "d1ec79775ae3ba83a3f856500867a15881094d3cb316363d98beaf32c3703227",
   index = 2
 }, (proofElement){
   hashes[] = 
      "ddca1a9842a99566159a9f7d37f023017c54a641f1b08812037629aea33413f6",
      "eb7248c5a7f7dfbb02a5275a17207acf3246a43bb10cce2b89dfae7a2f45b6c1",
   index = 0
 }, (proofElement){
   hashes[] = 
      "7ab730b239cf7090f8780623831e4e4630262560228238dbec9f10b7ace5c6de",
      "b48bb4a7d92c7fe235e5e3c621ef92ccb6f9a62d7f85b65c4a460d042770daf4",
   index = 0
 }, (proofElement){
   hashes[] = 
      "5d2aa0ebc2169fd8b4f9f2776e2dec9bd2d6a2e169a38102e5ae865870db117b",
   index = 0
 }, (proofElement){
   hashes[] = 
      "10790984f6a92b6f174a361f2aad4fc8fb0c04da3e61153dafc3618ab157c169",
      "948293675f27d4936f3ed22e8e0796438354db7b2b846eec91b330a8b197cd6f",
   index = 0
 }, (proofElement){
   hashes[] = 
      "68cff51aec86801f471d97175531463a42a89d12109f7d75c11094d53ec64103",
      "8a18326494ebee7c157c16e5ab0fcc1fbf072557a21a409b576d04e6c1658f07",
   index = 0
 }, (proofElement){
   hashes[] = 
      "6d63317567c24842e9e3d44c31adf00178417a62706ac7cf22e1d4ce9b533a3a",
   index = 0
 }, (proofElement){
   hashes[] = 
      "dfe600269076511a438f0cbe9a01f9aa953995f00b763ba8f77fd812215493cb",
      "8cc299dd7b44ec61bb88281913cb765fb4bbdfabac6e1fd150b81ba85fa42ba0",
   index = 0
 }, (proofElement){
   hashes[] = 
      "f989203eeb71a2eb0b4695ecc0c68844eb81b77adcb0fb996f31d43c0241172d",
      "caf20c717052f249caa3cbb31edc3ad0dd89f67cefdce390ff87abcd6ec38fc8",
   index = 0
 }, (proofElement){
   hashes[] = 
      "8bffee5e9d2ff42c58eafcce8f58f0177ce7c8ca87523e1e050f21d521463036",
      "5ec4c85c0ebaf1f05fd75dc23553782d489187bca2717565c7594e03f254a959",
   index = 0
 }, (proofElement){
   hashes[] = 
      "b3bf6e304d5854100d5308eca1fe249e90ca22df43ad8ce63b863bdcd8eecdd8",
      "2118cc520d42975cec0883a15ac8821e4122b3c92128c6c3455fe925f7f4d398",
   index = 0
 }, (proofElement){
   hashes[] = 
      "774fe5a3e1baee2c4ed07a3f6bc5b4634b4959a9f9f5fc52743d2ebd50eed453",
      "058cb6172362b4081ac7ee80c2fb49c65afe97aa7f4f0740f68128201899b2d8",
   index = 0
 }, (proofElement){
   hashes[] = 
      "a53a90c997334f7b42a418d46df3c9f66957ad649d11250ca306f7baaa50a62c",
   index = 0
 }, (proofElement){
   hashes[] = 
      "3b6e1c9a642c22cbd902bc3a5acfdc31537ee00669bb707ea6065a378741ac1f",
   index = 0
 }, (proofElement){
   hashes[] = 
      "cdf23c541a7196d3eceb609d85f897990a5c111bc05f87eb45854bcaf3611b4f",
   index = 1
 }]

---Calculating round to witness proof 2855147
Level: 0 ( index: 2 )  fab1ec8cf671971f6c0814bebcc293f706d50bed8ef943cdb16e0f4f1286070c
Level: 1 ( index: 0 )  054a29efa2926be0c192020b1fb21879b63f51f77bc5760db8a3f66d1d5079af
Level: 2 ( index: 0 )  e8de10ebb57a3b7737fce745098e9e2556fa4e843984cfbdc9348f9cfa51127a
Level: 3 ( index: 0 )  bf3fb03228aaf5585ca056b24c2582ba91e2e012823e7f0bb0f6b1feab14b65c
Level: 4 ( index: 0 )  c885cba7f7e01c9d1db515d8b949201fcfbea164789075aef6ec1bbf5cf8fb04
Level: 5 ( index: 0 )  f6367c8e4fc62deb1fc43448c71062fde34f563820df713d26514a4550d2576b
Level: 6 ( index: 0 )  baedb56e2afdf0565736078d739c9cb1fb609ed0bf8d9846a1a190a3f971ca8e
Level: 7 ( index: 0 )  4c35406d87d7cb495cb08527d5900811a7e5c0a287d795c570d7f8515e9c9958
Level: 8 ( index: 0 )  b7f4d6813489f6ee45b058466595db24d443bfb331082e7363e7ccd42f76970d
Level: 9 ( index: 0 )  f74dfcfea8e8b3f1394eaf4e8c5e455add53a8907b7f1f9113067a042661fd0b
Level: 10 ( index: 0 )  b12da578de58bd6150c37a865de23d881de4bf56ba010d0f8d20e5b70dc82656
Level: 11 ( index: 0 )  9d3b4d729a0036232e2121c24d7ea5211f30536de22d792c4f64256173149ea4
Level: 12 ( index: 0 )  6e00cd2d65d663f4686c7ec7046b362adcda70139ce7a6d164163affc50598d1
Level: 13 ( index: 0 )  296cee71935d5f925cb04a31b67c6dc12d91f0fc6d413af0f9fc313311f494f3
Level: 14 ( index: 1 )  d85e36d6af2246d76c9a4fa0ef22eb10a5215eae5747504241b92b18f2c22467

---Comparing trusted value to IMS proof result
calculated d85e36d6af2246d76c9a4fa0ef22eb10a5215eae5747504241b92b18f2c22467
trusted    d85e36d6af2246d76c9a4fa0ef22eb10a5215eae5747504241b92b18f2c22467
Equal: True