Last active
December 4, 2017 17:21
-
-
Save rfde/ea6c255b75863ff0632b1994ddae956a to your computer and use it in GitHub Desktop.
Proof of concept: python script to obtain a trusted time stamp from a public time stamp server (rfc3161, e.g. zeitstempel.dfn.de or tsa.safecreative.org)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
#### TODO: Flexibilität: https://docs.python.org/3.3/library/argparse.html#argparse.ArgumentParser.add_argument | |
import random | |
import hashlib | |
import urllib.request | |
import logging | |
from subprocess import call | |
from pyasn1.type import univ, namedtype, namedval, tag | |
from pyasn1.codec.der import encoder as der_encoder | |
# log "info" messages to stdout, include date/time | |
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.INFO) | |
##################################################################### | |
# ASN.1 definitions from RFC3161 and RFC5912 | |
# | |
# TimeStampReq ::= SEQUENCE { | |
# version INTEGER { v1(1) }, | |
# messageImprint MessageImprint, | |
# --a hash algorithm OID and the hash value of the data to be | |
# --time-stamped | |
# reqPolicy TSAPolicyId OPTIONAL, | |
# nonce INTEGER OPTIONAL, | |
# certReq BOOLEAN DEFAULT FALSE, | |
# extensions [0] IMPLICIT Extensions OPTIONAL } | |
# | |
# MessageImprint ::= SEQUENCE { | |
# hashAlgorithm AlgorithmIdentifier, | |
# hashedMessage OCTET STRING } | |
# | |
# TSAPolicyId ::= OBJECT IDENTIFIER | |
# | |
# AlgorithmIdentifier ::= SEQUENCE { | |
# algorithm OBJECT IDENTIFIER, | |
# parameters ANY DEFINED BY algorithm OPTIONAL } | |
# | |
# id-sha512 OBJECT IDENTIFIER ::= { | |
# joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) | |
# csor(3) nistalgorithm(4) hashalgs(2) 3 } | |
##################################################################### | |
# ASN.1: classes | |
class AlgorithmIdentifier(univ.Sequence): | |
componentType = namedtype.NamedTypes( | |
namedtype.NamedType('algorithm', univ.ObjectIdentifier()), | |
namedtype.OptionalNamedType('parameters', univ.Any()) | |
) | |
class MessageImprint(univ.Sequence): | |
componentType = namedtype.NamedTypes( | |
namedtype.NamedType('hashAlgorithm', AlgorithmIdentifier()), | |
namedtype.NamedType('hashedMessage', univ.OctetString()) | |
) | |
class TimeStampReq(univ.Sequence): | |
componentType = namedtype.NamedTypes( | |
namedtype.NamedType('version', univ.Integer()), | |
namedtype.NamedType('messageImprint', MessageImprint()), | |
namedtype.OptionalNamedType('reqPolicy', univ.ObjectIdentifier()), | |
namedtype.OptionalNamedType('nonce', univ.Integer()), | |
namedtype.DefaultedNamedType('certReq', univ.Boolean(False)) | |
# namedtype.OptionalNamedType('extensions', Extensions()) | |
) | |
## Frist step: generate SHA512 hash from given file | |
hasher = hashlib.sha512() | |
with open('blank.pdf', 'rb') as f: | |
buf = f.read() | |
hasher.update(buf) | |
logging.info("File hash: " + hasher.hexdigest().upper()) | |
filehash = hasher.digest() | |
## Second step: assemble TimeStampRequest (ASN.1 Sequence) locally | |
myRequest = TimeStampReq() | |
myRequest['version'] = univ.Integer(1) # Version: always 1 | |
myRequest['messageImprint'] = MessageImprint() | |
myRequest['messageImprint']['hashAlgorithm'] = AlgorithmIdentifier() | |
myRequest['messageImprint']['hashAlgorithm']['algorithm'] = univ.ObjectIdentifier((2, 16, 840, 1, 101, 3, 4, 2, 3)) # Algorithm = sha512 (constant) | |
myRequest['messageImprint']['hashedMessage'] = filehash # the actual sha512 hash | |
myRequest['nonce'] = random.getrandbits(64) # Nonce: 64bit random integer | |
logging.info('My nonce: ' + hex(myRequest['nonce'])) | |
myRequest['certReq'] = univ.Boolean(True) | |
# encode request in DER format | |
binRequest = der_encoder.encode(myRequest) | |
# save request to file | |
with open('a.tsq', 'wb') as f: | |
f.write(binRequest) | |
logging.info("Wrote request to file: " + "a.tsq") | |
# call openssl to display the request | |
print("-- OpenSSL --") | |
call(["openssl", "ts", "-query", "-in", "./a.tsq", "-text"]) | |
# Read request from file | |
# binRequest = None | |
# with open("a.tsq", "rb") as f: | |
# binRequest = bytearray(f.read()) | |
## Third step: Request Timestamp from server | |
# assemble HTTP POST request | |
postRequest = urllib.request.Request( | |
url="http://tsa.safecreative.org", | |
data=binRequest, # encoded TimeStampReq | |
headers={"Content-Type" : "application/timestamp-query"}, | |
method="POST" | |
) | |
# send HTTP POST request to request time stamp | |
with urllib.request.urlopen(postRequest) as response: | |
# write response to file | |
with open("resp.tsr", "wb") as f: | |
f.write(response.read()) | |
logging.info("Wrote response to file: " + "resp.tsr") | |
# call openssl to display the response | |
print("-- OpenSSL --") | |
call(["openssl", "ts", "-reply", "-in", "./resp.tsr", "-text"]) | |
logging.info("Done.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment