A Clueless Agent Generator for Python 3.2


/ Published in: Python
Save to your folder(s)

This is an implementation of a clueless agent generator which creates self-decrypting clueless agents as described in "Environmental Key Generation towards Clueless Agents" by J. Riordan and B. Schneier.

It requires Python 3.2 and PyCrypto of a recent build (tested with 2.4 and higher).

To use, pass a python file (or other file) to be encrypted, followed by a series of "observations" on the command line. These observations are hashed to yield the encryption key. A signature is generated by hashing the key, and this signature will be expected to be present in the target environment. Pipe the resulting agent to a file or see the agent code directly on stdout. Additionally, there is an is_debug flag that can be specified (see the source) or tweaked in the resulting agent, to be more verbose.

To attempt decryption/execution of a clueless agent, simply run the generated python script (agent) and pass a set of observations on the command line. If the hash of the hash of the observations match the signature, the hash of the observations will be used as the decryption key. If the signature does not match, the agent will exit with no output.

The code previously directly exec()'d the resulting code, however, it simply outputs to stdout now. The resulting code would otherwise execute directly in-line, at that location in the program, which has many undesirable consequences. Piping it to a file and executing, piping it to a memory-backed temporary file and executing it, or placing the resulting code directly in memory afterward and then executing it, are all ways to run the code contained within. This makes it fundamentally little different from encrypting a file directly, except that the key is environmentally generated, perhaps by a daemon that feeds environmental observations on the command line to the agent.

Note, you can encrypt more than Python scripts, and agents can be made to contain themselves.

$ ./agent_generator.py plaincode.py 0 > cipheragent.py

$ ./agent_generator.py cipheragent.py some more observations > double_agent.py

$ ./double_agent.py wrong observations

--nothing here--

$ ./double_agent.py some more observations > cipheragent_2.py

--cipheragent_2.py now holds the same content as cipheragent.py--

$ ./cipheragent.py 0 > plaincode_2.py

--plaincode_2.py now holds the same content as plaincode.py--

$ ./plaincode_2.py

--should yield the same as--

$ ./plaincode.py


Copy this code and paste it in your HTML
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3.  
  4. # This code is public domain.
  5. #
  6. # usage: generate_agent plaincode.py [observation...] > agent.py
  7. #
  8. # This is an implementation of a clueless agent generator which creates self-decrypting clueless
  9. # agents as described in "Environmental Key Generation towards Clueless Agents" by J. Riordan
  10. # and B. Schneier.
  11. #
  12. # It requires Python 3.2 and PyCrypto of a recent build (tested with 2.4 and higher).
  13. # To use, pass a python file (or other file) to be encrypted, followed by a series of
  14. # "observations" on the command line. These observations are hashed to yield the encryption key.
  15. # A signature is generated by hashing the key, and this signature will be expected to be present
  16. # in the target environment. Pipe the resulting agent to a file or see the agent code directly
  17. # on stdout. Additionally, there is an is_debug flag that can be specified (see the source) or
  18. # tweaked in the resulting agent, to be more verbose.
  19. #
  20. # To attempt decryption/execution of a clueless agent, simply run the generated python script
  21. # (agent) and pass a set of observations on the command line. If the hash of the hash of the
  22. # observations match the signature, the hash of the observations will be used as the decryption
  23. # key. If the signature does not match, the agent will exit with no output.
  24. #
  25. # The code previously directly exec()'d the resulting code, however, it simply outputs to stdout
  26. # now. The resulting code would otherwise execute directly in-line, at that location in the
  27. # program, which has many undesirable consequences. Piping it to a file and executing, piping
  28. # it to a memory-backed temporary file and executing it, or placing the resulting code directly
  29. # in memory afterward and then executing it, are all ways to run the code contained within. This
  30. # makes it fundamentally little different from encrypting a file directly, except that the key
  31. # is environmentally generated, perhaps by a daemon that feeds environmental observations on the
  32. # command line to the agent.
  33. #
  34. # Note, that you can encrypt more than Python scripts, and agents can be made to contain themselves.
  35. #
  36. # $ ./agent_generator.py plaincode.py 0 > cipheragent.py
  37. # $ ./agent_generator.py cipheragent.py some more observations > double_agent.py
  38. # $ ./double_agent.py wrong observations
  39. # --nothing here--
  40. # $ ./double_agent.py some more observations > cipheragent_2.py
  41. # --cipheragent_2.py now holds the same content as cipheragent.py--
  42. # $ ./cipheragent.py 0 > plaincode_2.py
  43. # --plaincode_2.py now holds the same content as plaincode.py--
  44. # $ ./plaincode_2.py
  45. # --should yield the same as--
  46. # $ ./plaincode.py
  47.  
  48. import functools
  49. import base64
  50.  
  51. from Crypto.Hash import RIPEMD
  52. from Crypto.Cipher import ARC4
  53.  
  54. agent_template = """#!/usr/bin/env python
  55. # -*- coding: utf-8 -*-
  56.  
  57. # usage: python agent.py [observation...] > plaincode.py
  58.  
  59. is_debug = %s
  60.  
  61. import functools
  62. import base64
  63.  
  64. from Crypto.Hash import RIPEMD
  65. from Crypto.Cipher import ARC4
  66.  
  67. def execute_agent(observations, signature, ciphercode):
  68. key = RIPEMD.new()
  69. signature_check = RIPEMD.new()
  70.  
  71. key.update(functools.reduce(lambda a, b: a + b, observations))
  72. key_digest = key.hexdigest()
  73. signature_check.update(key_digest)
  74. signature_check_digest = bytes(signature_check.hexdigest(), encoding='utf-8')
  75.  
  76. if is_debug:
  77. print("expecting: %%s" %% (signature,))
  78. print("sig check: %%s" %% (signature_check_digest,))
  79.  
  80. if (signature_check_digest == signature):
  81. decrypter = ARC4.new(key_digest)
  82. plaincode = decrypter.decrypt(base64.b64decode(ciphercode))
  83.  
  84. if is_debug:
  85. print("key digest: %%s" %% (key_digest,))
  86. print("decrypted: %%s" %% (plaincode,))
  87.  
  88. print(plaincode)
  89.  
  90. if __name__ == "__main__":
  91. import sys
  92. observations = sys.argv[1:]
  93. signature = b'%s'
  94. ciphercode = %s
  95. execute_agent(observations, signature, ciphercode)
  96. """
  97.  
  98. def ciphercode_from_string(plaincode, observations):
  99. key = RIPEMD.new()
  100. signature = RIPEMD.new()
  101.  
  102. key.update(functools.reduce(lambda a, b: a + b, observations))
  103. key_digest = key.hexdigest()
  104. encrypter = ARC4.new(key_digest)
  105. ciphercode = base64.b64encode(encrypter.encrypt(plaincode))
  106. signature.update(key_digest)
  107.  
  108. return (signature.hexdigest(), ciphercode)
  109.  
  110. def generate_agent(plaincode, observations, is_debug=False):
  111. return agent_template % tuple([is_debug] + list(ciphercode_from_string(plaincode, observations)))
  112.  
  113. if __name__ == "__main__":
  114. import sys
  115.  
  116. with open(sys.argv[1], 'rb') as plaincode:
  117. print(generate_agent(plaincode.read(), sys.argv[2:], is_debug=False))

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.