diff options
115 files changed, 30504 insertions, 0 deletions
diff --git a/.bzrignore b/.bzrignore new file mode 100644 index 0000000..f945743 --- /dev/null +++ b/.bzrignore @@ -0,0 +1,4 @@ +project.log +tailor.state +tailor.state.old +tailor.state.journal
\ No newline at end of file diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..81f21a6 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,112 @@ + +Changes since 1.1a1 +=================== + * Most importantly, the distribution has been broken into two +parts: exportable, and export-controlled. The exportable part +contains all the hashing algorithms, signature-only public key +algorithms, chaffing & winnowing, random number generation, various +utility modules, and the documentation. + + The export-controlled part contains public-key encryption +algorithms such as RSA and ElGamal, and bulk encryption algorithms +like DES, IDEA, or Skipjack. Getting this code still requires that +you go through an access control CGI script, and denies you access if +you're outside the US or Canada. + + * Added the RIPEMD hashing algorithm. (Contributed by +Hirendra Hindocha.) + + * Implemented the recently declassified Skipjack block +encryption algorithm. My implementation runs at 864 K/sec on a +PII/266, which isn't particularly fast, but you're probably better off +using another algorithm anyway. :) + + * A simple XOR cipher has been added, mostly for use by the +chaffing/winnowing code. (Contributed by Barry Warsaw.) + + * Added Protocol.Chaffing and Hash.HMAC.py. (Contributed by +Barry Warsaw.) + + Protocol.Chaffing implements chaffing and winnowing, recently +proposed by R. Rivest, which hides a message (the wheat) by adding +many noise messages to it (the chaff). The chaff can be discarded by +the receiver through a message authentication code. The neat thing +about this is that it allows secret communication without actually +having an encryption algorithm, and therefore this falls within the +exportable subset. + + * Tidied up randpool.py, and removed its use of a block +cipher; this makes it work with only the export-controlled subset +available. + + * Various renamings and reorganizations, mostly internal. + +Changes since 1.0.2 +=================== + + * Changed files to work with Python 1.5; everything has been +re-arranged into a hierarchical package. (Not backward compatible.) +The package organization is: +Crypto. + Hash. + MD2, MD4, MD5, SHA, HAVAL + Cipher. + ARC2, ARC4, Blowfish, CAST, DES, DES3, Diamond, + IDEA, RC5, Sapphire + PublicKey. + DSA, ElGamal, qNEW, RSA + Util. + number, randpool, RFC1751 + + Since this is backward-incompatible anyway, I also changed +module names from all lower-case to mixed-case: diamond -> Diamond, +rc5 -> RC5, etc. That had been an annoying inconsistency for a while. + + * Added CAST5 module contributed by <wiml@hhhh.org>. + + * Added qNEW digital signature algorithm (from the digisign.py +I advertised a while back). (If anyone would like to suggest new +algorithms that should be implemented, please do; I think I've got +everything that's really useful at the moment, but...) + + * Support for keyword arguments has been added. This allowed +removing the obnoxious key handling for Diamond and RC5, where the +first few bytes of the key indicated the number of rounds to use, and +various other parameters. Now you need only do something like: + +from Crypto.Cipher import RC5 +obj = RC5.new(key, RC5.ECB, rounds=8) + +(Not backward compatible.) + + * Various function names have been changed, and parameter +names altered. None of these were part of the public interface, so it +shouldn't really matter much. + + * Various bugs fixed, the test suite has been expanded, and +the build process simplified. + + * Updated the documentation accordingly. + +Changes since 1.0.1: +==================== + + * Changed files to work with Python 1.4 . + + * The DES and DES3 modules now automatically correct the +parity of their keys. + + * Added R. Rivest's DES test (see http://theory.lcs.mit.edu/~rivest/destest.txt) + + Changes since 1.0.0: + + * REDOC III succumbed to differential cryptanalysis, and has +been removed. + + * The crypt and rotor modules have been dropped; they're still +available in the standard Python distribution. + + * The Ultra-Fast crypt() module has been placed in a separate +distribution; see <http://starship.skyport.net/crew/amk/maintained/crypto.html>. + + * Various bugs fixed. diff --git a/Cipher/__init__.py b/Cipher/__init__.py new file mode 100644 index 0000000..dffec17 --- /dev/null +++ b/Cipher/__init__.py @@ -0,0 +1,6 @@ + +__all__ = ['ARC2', 'Blowfish', 'DES', 'DES3', 'IDEA', 'RC5', 'Diamond', 'CAST', + 'Skipjack', 'ARC4', 'Sapphire', 'XOR' ] + + + diff --git a/Demo/README b/Demo/README new file mode 100644 index 0000000..0e48d7c --- /dev/null +++ b/Demo/README @@ -0,0 +1,29 @@ +This directory contains demonstration files that use the modules +included in the Python Cryptography Toolkit. + +Note: These programs have version numbers of their own, which are not +necessarily the same as the version number of the Toolkit package. + +cipher Encrypt and decrypt sensitive files; type 'cipher -h' + for a usage message. + +voice Allows secure voice communication over a TCP/IP link. + Currently this is Linux-specific; changes to make it + run on other systems would be greatly appreciated. + +RSAgen.py Generates a new RSA key. Demonstrates using + randpool.py, and maintains a file of random data in + "randseed". Requires that the IDEA and MD5 modules + are installed. + +testkey.py RSA public/private key pair used by example programs. + +Secure importing of Python modules: + + sign.py Sign all *.pyc files in a directory, using the + key defined in testkey.py. + + secimp.py Implementation of the secure 'import' command. + + + diff --git a/Demo/cipher b/Demo/cipher new file mode 100755 index 0000000..79c92da --- /dev/null +++ b/Demo/cipher @@ -0,0 +1,150 @@ +#!/usr/bin/env python +# -*-Python-*- +# Cipher 1.00 +# +# Part of the Python Cryptography Toolkit, version 1.1 +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# + +import sys, getopt, os + +# Determine the name of this executable +executable = os.path.basename(sys.argv[0]) +if executable=='': executable='cipher' +cipher = '' # Unknown ciphering algorithm +key = (0, '') # Empty key +magic = 'ctx\001' # Magic string prefixed to the data +NoInputFile = '' # Exceptions raised on file errors +NoOutputFile = '' + +def PrintUsage(): + print 'Usage: cipher [OPTIONS] file1 file2 ...' + print '\n -c ciphername Force use of ciphername to encrypt/decrypt' + print ' -k key Key to use for encryption/decryption' + print '\nThe default cipher algorithm is IDEA; if no key is set on the command' + print 'line, you will be prompted to enter a key.' + print 'Files are read completely into memory, so do not try to encrypt' + print 'very large files.' + +def GenerateIV(length): + import whrandom + IV='' + for i in range(0, length): + IV=IV + chr(int(256*whrandom.random())) + return IV + +def Encipher(filename, cipher, key): + if (cipher==''): cipher='IDEA' + try: + exec ('from Crypto.Cipher import '+cipher) + module=eval(cipher) + except ImportError: + print executable+ ':', cipher, ': Cipher does not exist.' + sys.exit(1) + import Crypto.Hash.MD5 + try: + input=open(filename, 'r') + except IOError: + raise NoInputFile + try: + output=open(filename+'.cip', 'w') + except IOError: + raise NoOutputFile, filename+'.cip' + + if (key[0]==0): + key=raw_input('Enter encryption key for '+ filename+ ':') + else: key=key[1] + key=Crypto.Hash.MD5.new(key).digest() + IV='' + for i in range(0, module.blocksize): IV=IV+'A' + if (module.keysize==0): + cipherobj=module.new(key, module.CBC, IV) + else: + cipherobj=module.new(key[0:module.keysize], module.CBC, IV) + output.write(magic+cipher+'\0') + data = GenerateIV(module.blocksize) + filedata=input.read() + data = data + magic + str(len(filedata))+'\0'+filename+'\0' + data = data + filedata + input.close() + padding=module.blocksize - (len(data) % module.blocksize) + for i in range(0, padding): + data = data + chr(i) + ciphertext=cipherobj.encrypt(data) + output.write(ciphertext) + output.close() + +def Decipher(filename, cipher, key): + import Crypto.Hash.MD5, string + try: + input=open(filename, 'r') + except IOError: + raise NoInputFile + if (input.read(len(magic))!=magic): + print executable+':', filename+': Does not seem to be a ciphered file' + return + t='' + while (1): + c=input.read(1) + if (ord(c)==0): break + t=t+c + if (cipher==''): cipher=t + try: + from Crypto.Cipher import * + module=eval(cipher) + except ImportError: + print executable+ ':', cipher, ': Cipher does not exist.' + sys.exit(1) + if (key[0]==0): + key=raw_input('Enter encryption key for '+ filename+ ':') + else: key=key[1] + key=Crypto.Hash.MD5.new(key).digest() + IV = '' + for i in range(0, module.blocksize): IV=IV+'A' + data=input.read() + if (module.keysize==0): + cipherobj=module.new(key, module.CBC, IV) + else: + cipherobj=module.new(key[0:module.keysize], module.CBC, IV) + plain=cipherobj.decrypt(data) # Decrypt the data + plain=plain[module.blocksize:] # Discard first block of random data + if (plain[0:len(magic)]!=magic): + print executable+':', filename+': Incorrect key or cipher algorithm' + return + else: plain=plain[len(magic):] + i=string.find(plain, '\0') + length=string.atoi(plain[0:i]) + j=string.find(plain, '\0', i+1) + newfilename=plain[i+1:j] + try: + output=open(newfilename, 'w') + except IOError: + raise NoOutputFile, newfilename + output.write(plain[j+1:j+1+length]) + output.close() + +if len(sys.argv)==1: PrintUsage() ; sys.exit(0) + +options, args=getopt.getopt(sys.argv[1:], 'c:k:hH') +for opt in options: + letter, param = opt + if (letter=='-c'): cipher = param + if (letter=='-k'): key = (1, param) + if (letter=='-h' or letter=='-H'): + PrintUsage() + sys.exit(0) + +for file in args: + try: + if (file[-4:]=='.cip'): + Decipher(file, cipher, key) + else: + Encipher(file, cipher, key) + except NoInputFile: + print executable+ ':', file+ ': No such file.' + except NoOutputFile, filename: + print executable+ ':', filename+ ': Cannot open file' + diff --git a/Demo/secimp/README b/Demo/secimp/README new file mode 100644 index 0000000..94a6e30 --- /dev/null +++ b/Demo/secimp/README @@ -0,0 +1,24 @@ + +This is a simple demonstration of adding an import hook that verifies +a digital signature on a Python code object before allowing it to be +imported. There are three files: + + * sign.py, which signs all the *.pyc files in the directories +listed on the command line. The contents of the .pyc file is stored +along with the signature in a file whose name ends with .pys . + + * secimp.py, which implements a secimport() function which +will use *.pys files. + + * testkey.py is the key used to sign and verify *.pys files. + +To try it out: + 1. Run "sign.py ." to compile and sign all the *.py files in +the current directory. + + 2. Run secimp.py from the command-line; it will try to +securely import testkey.pys, which should succeed. + + 3. Fire up your favorite editor, and change a single byte in a +string somewhere in testkey.pys. Run secimp.py again; it should raise +an exception when the signature can't be verified. diff --git a/Demo/secimp/secimp.py b/Demo/secimp/secimp.py new file mode 100644 index 0000000..a1ecb1d --- /dev/null +++ b/Demo/secimp/secimp.py @@ -0,0 +1,84 @@ +#!/usr/local/bin/python + +import sys ; sys.path = ['../../../'] + sys.path + +import imp, os +from sys import modules + +# Secure import: +def secimport(name, globals=None, locals=None, fromlist=None): + # Fast path: let's see if it's already in sys.modules. + # Two speed optimizations are worth mentioning: + # - We use 'modules' instead of 'sys.modules'; this saves a + # dictionary look-up per call. + # - It's also faster to use a try-except statement than + # to use modules.has_key(name) to check if it's there. + try: + return modules[name] + except KeyError: + pass + + # See if it's a built-in module + m = imp.init_builtin(name) + if m: + return m + + # See if it's a frozen module + m = imp.init_frozen(name) + if m: + return m + + # Search the default path (i.e. sys.path). + # If this raises an exception, the module is not found -- + # let the caller handle the exception. + fp, pathname, (suffix, mode, type) = imp.find_module(name) + + # See what we got... + # Note that fp will be closed automatically when we return. + + # Extensions are written in C, and can just be loaded. + if type == imp.C_EXTENSION: + return imp.load_dynamic(name, pathname) + + # For a compiled or source file, we'll check if there is a *.pys file + # present in the same directory. + if type == imp.PY_COMPILED or type == imp.PY_SOURCE: + root, ext = os.path.splitext(pathname) + testfile = root + '.pys' + try: + print testfile + secfile=open(testfile, 'rb') + except IOError, tuple: + if (tuple[0]==2): pass # Ignore 'file not found' error + else: raise IOError, tuple + else: + # Check the signature (a signed hash of the code object). + # We could sign the whole code object, but that would + # require a huge key and would double the size of the + # *.pys file. + import marshal + from Crypto.Hash import MD5 + fp.close() # Close the original *.pyc file + from testkey import * # Get the key for verification + signature=marshal.load(secfile) # Read signature + position=secfile.tell() # Save position + data=secfile.read() # Read code object + hash=MD5.new(data).digest() # Compute its hash value + ##print 'sigcheck:', key.verify(hash, signature) + if (not key.verify(hash, signature)): + raise ImportError, 'Signature check of '+ testfile + ' failed' + secfile.seek(position) # Rewind pointer to the + # beginning of the code object + fp=secfile + del secfile + # Now we can happily import the compiled code object. + return imp.load_compiled(name, pathname, fp) + + # Shouldn't get here at all. + raise ImportError, '%s: unknown module type (%d)' % (name, type) + +if __name__=='__main__': + # A sample invocation of the secure import looks like this: + print 'Attempting secure import' + r=secimport('testkey') + print 'Secure import succeeded' diff --git a/Demo/secimp/sign.py b/Demo/secimp/sign.py new file mode 100755 index 0000000..727bef6 --- /dev/null +++ b/Demo/secimp/sign.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +# Using the public key defined in testkey.py, sign all *.pyc files in +# the listed directories. + +from testkey import * +from Crypto.Hash import MD5 +import os, glob, sys +import marshal, compileall + +filelist = [] +if (len(sys.argv)>1): + for dir in sys.argv[1:]: + dir=os.path.join(dir, '') + compileall.compile_dir(dir) + filelist=filelist + glob.glob(dir + '*.pyc') +else: + print "Usage: sign.py dir1 dir2 dir3 ..." + print " All *.pyc files in the listed directories will be signed," + print "leaving the signatures in *.pys files." + sys.exit(0) + +if len(filelist)==0: + print "No *.pyc files found" + sys.exit(0) + +for file in filelist: + input=open(file, 'rb') + try: + os.unlink(file[:-4]+'.pys') # Delete any existing signed file + except os.error, tuple: + if (tuple[0]==2): pass # Ignore 'file not found' error + else: raise os.error, tuple + output=open(file[:-4]+'.pys', 'wb') + data=input.read() + hash=MD5.new(data).digest() # Compute hash of the code object + K = "random bytes" + signature=key.sign(hash, K) # Sign the hash value + marshal.dump(signature, output) # Save signature to the file + output.write(data) # Copy code object to signed file + input.close() + output.close() + print os.path.basename(file)+ ' processed.' + + + diff --git a/Demo/secimp/testkey.py b/Demo/secimp/testkey.py new file mode 100644 index 0000000..d32d284 --- /dev/null +++ b/Demo/secimp/testkey.py @@ -0,0 +1,36 @@ + +import sys +#sys.path = ['../../..', sys.path] + +import Crypto.PublicKey.DSA + +key = Crypto.PublicKey.DSA.construct(( + # y + 0x43E9162E224CBD1B66D7C27EB7E795392310B5E7AC6E0B1F60021F5E03F90E851CB7F76603FAE73907154371AE04EDBF0D9D557DF03488F34C18324B8DFEF5D2L, + # g + 0x4D6DB63479E55D0BE31CF1BEA58AB9365FC5EA267FFCD8424B56390E6EE7DD9BF788F696EED8475516353E61F37B8441137FA4F8DC82A9F84FA52BCD37517C32L, + # p + 0x8000011124427A59DC0AF8AC982B490C75B1B3E94042F50F500E0636391C6FCC8C13E628528B4B75E158618A34592D5A68CA684371F9678BBA54DD40C0020F25L, + # q + 0x9B128544B02353FF961E1774D2FA94E52E078F5DL, + # x + 0x991386B7B92C221E42B1386D61255F5C58FD79A7L, +)) + +if __name__ == '__main__': + # Running this script directly will generate a new key and print it out + from Crypto.PublicKey import DSA + from Crypto.Util.randpool import KeyboardRandomPool + + pool = KeyboardRandomPool(numbytes = 64) + pool.Randomize() + + key = DSA.generate(512, pool.getBytes, sys.stdout.write) + print "key = Crypto.PublicKey.DSA.construct((" + for field in key.keydata: + print " #", field + print " " + hex(getattr(key, field)) + "," + print '))' + + + diff --git a/Doc/pycrypt.tex b/Doc/pycrypt.tex new file mode 100644 index 0000000..365ed70 --- /dev/null +++ b/Doc/pycrypt.tex @@ -0,0 +1,1458 @@ +\documentclass{howto} + +\title{The Python Cryptography Modules} + +\release{1.1.0} + +\author{A.M. Kuchling} +\authoraddress{\email{akuchling@acm.org}} + +\begin{document} +\maketitle + +\begin{abstract} +\noindent +This document describes a package containing various cryptographic +modules available for the Python programming language. It assumes you +have some basic knowledge about the Python language and about +cryptography in general. +\end{abstract} + +\tableofcontents + +\section{Introduction} + +\subsection{Design Goals} +The Python cryptography modules are intended to provide a reliable and +stable base for writing Python programs that require +cryptographic functions. + +A central goal of the author's has been to provide a +simple, consistent interface for similar classes of algorithms. For +example, all block cipher objects have the same methods and return +values, and support the same feedback modes; hash functions have a +different interface, but it too is consistent over all the +hash functions available. Individual modules also define variables to +help you write Python code that doesn't depend on the algorithms used; +for example, each block cipher module defines a variable that gives +the algorithm's block size. This is intended to make it easy to +replace old algorithms with newer, more secure ones. If you're given +a bit of portably-written Python code that uses the DES encryption +algorithm, you should be able to use IDEA instead by simply changing +\code{from Crypto.Cipher import DES} to \code{from Crypto.Cipher import +idea}, and changing all references to +\code{DES.new()} to \code{IDEA.new()}. It's also fairly simple to write +your own modules that mimic this interface, thus letting you use +combinations or permutations of algorithms. + +\index{C language} +\index{language, C} +\index{Intel} +Some modules are implemented in C for performance; others are written in +Python for ease of modification. Generally, low-level functions like +ciphers and hash functions are written in C, while less speed-critical +functions have been written in Python. This division may change in +future releases. When speeds are quoted in this document, they were +measured on a 266 MHz Pentium II running Linux. The exact speeds will obviously +vary with different machines and different compilers, but they provide a +basis for comparing algorithms. Currently the cryptographic +implementations are acceptably fast, but not spectacularly good. I +welcome any suggestions or patches for faster code. + +% You may be surprised that I'm distributing the code worldwide. Aren't +% North Americans prohibited from exporting cryptographic software? Well, +% yes and no. American citizens have to get permission from the US +% government before exporting \emph{any} such software, even if it's +% publicly available or originated outside the US; there are no exceptions +% whatsoever. + +% The laws in Canada are slightly less restrictive, though, and I'm a +% Canadian citizen. In particular, free software is specifically exempted +% from export restrictions, so I'm acting within the law. However, if +% you're a US citizen, you \emph{still} can't export these programs +% without government permission. + +\index{ITAR, regulations} +\index{regulations, ITAR} +If you live outside of Canada or the US, please do not attempt to +download it from a North American FTP site; you may get the site's +maintainer in trouble. Documentation is not covered by the ITAR +regulations, and can be freely sent anywhere in the world. + +\index{licensing terms} +I have placed the code under no restrictions; you can redistribute the +code freely or commercially, in its original form or with any +modifications you make, subject to whatever local laws may apply in your +jurisdiction. Note that you still have to come to some agreement with +the holders of any patented algorithms you're using. If you're +intensively using these modules, please tell me about it; there's little +incentive for me to work on this package if I don't know of anyone using +it. + +I also make no guarantees as to the usefulness, correctness, or legality +of these modules, nor does their inclusion constitute an endorsement of +their effectiveness. Many cryptographic algorithms are patented; +inclusion in this package does not necessarily mean you are allowed to +incorporate them in a product and sell it. Some of these algorithms may +have been cryptanalyzed, and may no longer be secure. While I will +include commentary on the relative security of the algorithms in the +sections entitled "Security Notes", there may be more recent analyses +I'm not aware of. (Or maybe I'm just clueless.) If you're implementing +an important system, don't just grab things out of a toolbox and put +them together; do some research first. On the other hand, if you're +just interested in keeping your co-workers or your relatives out of your +files, any of the components here could be used. + +This document is very much a work in progress. If you have any +questions, comments, complaints, or suggestions, please send them to me +at \email{akuchling@acm.org}. + +\subsection{Acknowledgements} +\index{Schneier, Bruce} +Much of the code that actually implements the various cryptographic +algorithms was not written by me. I'd like to thank all the people who +implemented them, and released their work under terms which allowed me +to use their code. The individuals are credited in the relevant +chapters of this documentation. Bruce Schneier's book \emph{Applied +Cryptography} was also very useful in writing this toolkit; I highly +recommend it if you're interested in learning more about cryptography. +Mr. Schneier also has a Web site at \url{http://www.counterpane.com}. + +Good luck with your cryptography hacking! + +A.M.K. + +\email{akuchling@acm.org} + +Washington, DC, USA + +January 1998 + +\section{Crypto.Hash: Hash Functions} + +\index{MD2 (hash function)} +\index{MD5 (hash function)} +\index{SHA (hash function)} +\index{HAVAL (hash function)} +Hash functions take arbitrary strings as input, and produce an output +of fixed size that is dependent on the input; it should never be +possible to derive the input data given only the hash function's +output. One simple hash function consists of simply adding together +all the bytes of the input, and taking the result modulo 256. For a +hash function to be cryptographically secure, it must be very +difficult to find two messages with the same hash value, or to find a +message with a given hash value. The simple additive hash function +fails this criterion miserably; the hash functions described below do +not. Examples of cryptographically secure hash functions include MD2, +MD5, SHA, and HAVAL. + +Hash functions can be used simply as a checksum, or, in association with a +public-key algorithm, can be used to implement digital signatures. + +The hashing algorithms currently implemented are listed in the following table: + +\begin{tableii}{c|l}{}{Hash function}{Digest length} +\lineii{HAVAL}{Variable size: 128, 160, 192, 224, or 256 bits} +\lineii{MD2}{128 bits} +\lineii{MD4}{128 bits} +\lineii{MD5}{128 bits} +\lineii{RIPEMD}{160 bits} +\lineii{SHA}{160 bits} +\end{tableii} + +All hashing modules share the same interface. After importing a given +hashing module, call the \code{new()} function to create a new hashing +object. (In older versions of the Python interpreter, the \code{md5()} +function was used to perform this task. If you're modifying an old script, you +should change any calls to \code{md5()} to use \code{new()} instead.) You +can now feed arbitrary strings into the object, and can ask for the +hash value at any time. The \code{new()} function can also be passed an +optional string parameter, which will be hashed immediately. + +Hash function modules define one variable: + +\begin{datadesc}{digestsize} +An integer value; the size of the digest +produced by the hashing objects. You could also obtain this value by +creating a sample object, and taking the length of the digest string +it returns, but using \code{digestsize} is faster. +\end{datadesc} + +The methods for hashing objects are always the following: + +\begin{funcdesc}{copy}{} +Return a separate copy of this hashing object. An \code{update} to this + copy won't affect the original object. +\end{funcdesc} + +\begin{funcdesc}{digest}{} +Return the hash value of this hashing object, as a string containing 8-bit data. The object is not +altered in any way by this function; you can continue updating the +object after calling this function. +\end{funcdesc} + +\begin{funcdesc}{digest}{} +Return the hash value of this hashing object, as a string containing the +digest data as hexadecimal digits. The resulting string will be twice +as long as that returned by \code{digest()}. The object is not altered +in any way by this function; you can continue updating the object after +calling this function. +\end{funcdesc} + +\begin{funcdesc}{update}{arg} +Update this hashing object with the string \var{arg}. +\end{funcdesc} + +\index{RSA Data Security, Inc.} +\index{MD5 (hash function)} +Here's an example, using RSA Data Security's MD5 algorithm: + +\begin{verbatim} +>>> from Crypto.Hash import MD5 +>>> m = MD5.new() +>>> m.update('abc') +>>> m.digest() +'\220\001P\230<\322O\260\326\226?@}(\341\177r' +\end{verbatim} + + +Or, more compactly: +\begin{verbatim} +>>> MD5.new('abc').digest() +'\220\001P\230<\322O\260\326\226?@}(\341\177r' +\end{verbatim} + +\subsection{Algorithm-specific Notes for Hash Functions} + +HAVAL provides a variable-size digest, and allows for a variable number +of rounds. It's believed that increasing the number of rounds increases +the security; at least, I don't know of any results to the contrary. +The \code{HAVAL.new()} accordingly has two keyword arguments, +\code{rounds} and \code{digestsize}. \code{rounds} can be 3, 4, or 5, +and has a default value of 5. \code{digestsize} can be 128, 160, 192, +224, or 256 bits, and has a default value of 256. + +\subsection{Security Notes} +Hashing algorithms are broken when it's easy to compute a +string that produces a given hash value, or to find two +messages that produce the same hash value. Consider an example where +Alice and Bob are using digital signatures to sign a contract. Alice +computes the hash value of the text of the contract and signs it. Bob +could then compute a different contract that has the same hash value, +and it would appear that Alice has signed that bogus contract; she'd +have no way to prove otherwise. Finding such a message by brute force +takes \code{pow(2, b-1)} operations, where the hash function produces +\emph{b}-bit hashes. + +If Bob can only find two messages with the same hash value but can't +choose the resulting hash value, he can look for two messages with +different meanings, such as "I will mow Bob's lawn for $10" and "I owe +Bob $1,000,000", and ask Alice to sign the first, innocuous contract. +This attack is easier for Bob, since finding two such messages by brute +force will take \code{pow(2, b/2)} operations on average. However, +Alice can protect herself by changing the protocol; she can simply +append a random string to the contract before hashing and signing it; +the random string can then be kept with the signature. + +None of the algorithms implemented here have been completely broken. +There are no attacks on MD2, but it's rather slow at 376 K/sec. MD4 is +faster at 11946 K/sec but there have been some partial attacks on it. MD4 +operates in three iterations of a basic mixing operation; two of the +three rounds have been cryptanalyzed, but the attack can't be extended +to the full algorithm. MD5 is a strengthened version of MD4 with four +rounds; an attack against one round has been found. XXX Dobbertin's +attack. Because MD5 is more +commonly used, the implementation is better optimized and thus faster on +x86 processors (27193 K/sec). MD4 may be faster than MD5 when other +processors and compilers are used. + +All the MD algorithms produce 128-bit hashes; SHA produces a larger 160-bit +hash, and there are no known attacks against it. The first version of +SHA had a weakness which was later corrected; the code used here +implements the second, corrected, version. It operates at 13157 K/sec. +RIPEMD also has a 160-bit output, and operates at 5869 K/sec. +HAVAL is a variable-size hash function; it can generate hash values that +are 128, 160, 192, 224, or 256 bits in size, and can use 3, 4, or 5 +rounds. 5-round HAVAL runs at 5371 K/sec. + +\subsection{Credits} +\index{Plumb, Colin} +\index{Kuchling, Andrew} +\index{Gutmann, Peter} +The MD2, MD4, and HAVAL implementations were written by A.M. Kuchling, +and the MD5 code was implemented by Colin Plumb. The SHA code was +originally written by Peter Gutmann. The RIPEMD code was written by +Antoon Bosselaers, and adapted for the toolkit by Hirendra Hindocha. + +\section{Crypto.Cipher: Encryption Algorithms} +Encryption algorithms transform their input data (called +\dfn{plaintext}) in some way that is +dependent on a variable \dfn{key}, producing \dfn{ciphertext}; +this transformation can easily be reversed, if (and, hopefully, only +if) one knows the key. The key can be varied by the user or +application, chosen from some very large space of possible keys. + +For a secure encryption algorithm, it should be very difficult to +determine the original plaintext without knowing the key; usually, no +clever attacks on the algorithm are known, so the only way of breaking +the algorithm is to try all possible keys. Since the number of possible +keys is usually of the order of 2 to the power of 56 or 128, this is not +a serious threat, although 2 to the power of 56 is now considered +insecure in the face of custom-built parallel computers and distributed +key guessing efforts. + +\index{feedback mode, ECB} +\dfn{Block ciphers} take multibyte inputs of a fixed size +(frequently 8 or 16 bytes long) and encrypt them. Block ciphers can +be operated in various modes. The simplest is Electronic Code Book +(or ECB) mode. In this mode, each block of plaintext is simply +encrypted to produce the ciphertext. This mode can be dangerous, +because many files will contain patterns greater than the block size; +for example, the comments in a C program may contain long strings of +asterisks intended to form a box. All these identical blocks will +encrypt to identical ciphertext; an adversary may be able to use this +structure to obtain some information about the text. + +\index{feedback mode, CBC} +\index{feedback mode, CFB} +To eliminate this weakness, there are various feedback modes, where +the plaintext is combined with the previous ciphertext before +encrypting; this eliminates any such structure. One mode is Cipher +Block Chaining (CBC mode); another is Cipher FeedBack (CFB +mode). CBC mode still encrypts in blocks, and thus is only +slightly slower than ECB mode. CFB mode encrypts on a byte-by-byte +basis, and is much slower than either of the other two modes. The +chaining feedback modes require an initialization value to start off +the encryption; this is a string of the same length as the ciphering +algorithm's block size, and is passed to the \code{new()} function. + +There is also a special PGP mode, which is a variant +of CFB used by the PGP program. While you can use it in non-PGP +programs, it's quite non-standard. + +The currently available block ciphers are listed in the following table, +and are available in the \code{Crypto.Cipher} package: + +\index{DES (block cipher)} +\index{DES3 (block cipher)} +\index{Triple DES (block cipher)} +\index{Diamond (block cipher)} +\index{RC5 (block cipher)} +\index{IDEA (block cipher)} +\index{Blowfish (block cipher)} +\index{ARC2 (block cipher)} +\index{CAST (block cipher)} + +\begin{tableii}{c|l}{}{Cipher}{Key Size/Block Size} +\lineii{ARC2}{Variable/8 bytes} +\lineii{Blowfish}{Variable/8 bytes} +\lineii{CAST}{Variable/8 bytes} +\lineii{DES}{8 bytes/8 bytes} +\lineii{DES3 (Triple DES)}{16 bytes/8 bytes} +\lineii{Diamond}{Variable/16 bytes} +\lineii{IDEA}{16 bytes/8 bytes} +\lineii{RC5}{Variable/8 bytes} +\end{tableii} + +\index{stream cipher} +In a strict formal sense, \dfn{stream ciphers} encrypt data bit-by-bit; +practically, stream ciphers work on a character-by-character basis. +Stream ciphers use exactly the +same interface as block ciphers, with a block length that will always +be 1; this is how block and stream ciphers can be distinguished. +The only feedback mode available for stream ciphers is ECB mode. + +The currently available stream ciphers are listed in the following table: + +\index{Sapphire (stream cipher)} +\index{ARC4 (stream cipher)} +\begin{tableii}{c|l}{}{Cipher}{Key Size} +\lineii{Cipher}{Key Size} +\lineii{ARC4}{Variable} +\lineii{Sapphire}{Variable} +\end{tableii} + +\index{RSA Data Security, Inc.} +\index{sci.crypt} +\index{RC4 (stream cipher)} +\index{ARC4 (stream cipher)} +ARC4 is short for `Alleged RC4'. The real RC4 algorithm is proprietary +to RSA Data Security Inc. In September of 1994, someone posted C code +to both the Cypherpunks mailing list and to the Usenet newsgroup +\code{sci.crypt}, claiming that it implemented the RC4 algorithm. This +posted code is what I'm calling Alleged RC4, or ARC4 for short. I don't +know if ARC4 is in fact RC4, but ARC4 has been subjected to scrutiny on +the Cypherpunks mailing list and elsewhere, and does not seem to be +easily breakable. The legal issues surrounding the use of ARC4 are +unclear, but be aware that it hasn't been subject to much scrutiny, and +may have some critical flaw that hasn't yet been discovered. The same +is true of ARC2, which was posted in January, 1996. + +An example usage of the DES module: +\begin{verbatim} +>>> from Crypto.Cipher import DES +>>> obj=DES.new('abcdefgh', DES.ECB) +>>> plain="Guido van Rossum is a space alien." +>>> len(plain) +34 +>>> obj.encrypt(plain) +Traceback (innermost last): + File "<stdin>", line 1, in ? +ValueError: Strings for DES must be a multiple of 8 in length +>>> ciph=obj.encrypt(plain+'XXXXXX') +>>> ciph +'\021,\343Nq\214DY\337T\342pA\372\255\311s\210\363,\300j\330\250\312\347\342I\3215w\03561\303dgb/\006' +>>> obj.decrypt(ciph) +'Guido van Rossum is a space alien.XXXXXX' +\end{verbatim} + + +All cipher algorithms share a common interface. After importing a +given module, there is exactly one function and two variables +available. + +\begin{funcdesc}{new}{key, mode\optional{, IV}} +Returns a ciphering object, using \var{key} and feedback mode +\var{mode}. If \var{mode} is CBC or CFB, \var{IV} must be provided, +and must be a string of the same length as the block size. Some +algorithms support additional keyword arguments to this function; see +the "Algorithm-specific Notes for Encryption Algorithms" section below for the details. +\end{funcdesc} + +\begin{datadesc}{blocksize} +An integer value; the size of the blocks encrypted by this module. +Strings passed to the \code{encrypt} and \code{decrypt} functions +must be a multiple of this length. For stream ciphers, +\code{blocksize} will be 1. +\end{datadesc} + +\begin{datadesc}{keysize} +An integer value; the size of the keys required by this module. If +\code{keysize} is zero, then the algorithm accepts arbitrary-length +keys. You cannot pass a key of length 0 (that is, the null string +\code{''} as such a variable-length key. +\end{datadesc} + +All cipher objects have at least three attributes: + +\begin{datadesc}{blocksize} +An integer value equal to the size of the blocks encrypted by this object. +Identical to the module variable of the same name. +\end{datadesc} + +\begin{datadesc}{IV} +Contains the initial value which will be used to start a cipher +feedback mode. After encrypting or decrypting a string, this value +will reflect the modified feedback text; it will always be one block +in length. It is read-only, and cannot be assigned a new value. +\end{datadesc} + +\begin{datadesc}{keysize} +An integer value equal to the size of the keys used by this object. If +\code{keysize} is zero, then the algorithm accepts arbitrary-length +keys. For algorithms that support variable length keys, this will be 0. +Identical to the module variable of the same name. +\end{datadesc} + +All ciphering objects have the following methods: + +\begin{funcdesc}{decrypt}{string} +Decrypts \var{string}, using the key-dependent data in the object, and +with the appropriate feedback mode. The string's length must be an exact +multiple of the algorithm's block size. Returns a string containing +the plaintext. +\end{funcdesc} + +\begin{funcdesc}{encrypt}{string} +Encrypts a non-null \var{string}, using the key-dependent data in the +object, and with the appropriate feedback mode. The string's length +must be an exact multiple of the algorithm's block size; for stream +ciphers, the string can be of any length. Returns a string containing +the ciphertext. +\end{funcdesc} + +\subsection{Algorithm-specific Notes for Encryption Algorithms} + +The Diamond block cipher allows you to select the number of rounds to +apply, ranging from 5 to 15 (inclusive.) This is set via the +\code{rounds} keyword argument to the \code{new()} function; the default +value is 8 rounds. + +RC5 has even more parameters; see Ronald Rivest's paper at \url{http://theory.lcs.mit.edu/~rivest/rc5rev.ps} +for the implementation details. The keyword parameters are: + +\begin{itemize} +\item \code{version}: +The version +of the RC5 algorithm to use; currently the only legal value is +\code{0x10} for RC5 1.0. +\item \code{wordsize}: +The word size to use; +16 or 32 are the only legal values. (A larger word size is better, so +usually 32 will be used. 16-bit RC5 is probably only of academic +interest.) +\item \code{rounds}: +The number of rounds to apply, the larger the more secure: this +can be any value from 0 to 255, so you will have to choose a value +balanced between speed and security. +\end{itemize} + +\subsection{Security Notes} +Encryption algorithms can be broken in several ways. If you have some +ciphertext and know (or can guess) the corresponding plaintext, you can +simply try every possible key in a \dfn{known-plaintext} attack. Or, it +might be possible to encrypt text of your choice using an unknown key; +for example, you might mail someone a message intending it to be +encrypted and forwarded to someone else. This is a +\dfn{chosen-plaintext} attack, which is particularly effective if it's +possible to choose plaintexts that reveal something about the key when +encrypted. + +DES (2458 K/sec) has a 56-bit key; this is starting to become too small +for safety. It has been estimated that it would only cost \$1,000,000 to +build a custom DES-cracking machine that could find a key in 3 hours. A +chosen-ciphertext attack using the technique of \dfn{linear +cryptanalysis} can break DES in \code{pow(2, 43)} steps. However, +unless you're encrypting data that you want to be safe from major +governments, DES will be fine. DES3 (509 K/sec) uses three DES +encryptions for greater security and a 112-bit or 168-bit key, but is +correspondingly slower. + +There are no publicly known attacks against IDEA (2005 K/sec), and +it's been around long enough to have been examined. There are no +known attacks against ARC2 (1644 K/sec), ARC4 (4630 K/sec), Blowfish +(5323 K/sec), CAST (1314 K/sec), Diamond (1670 K/sec), RC5 (1370 +K/sec), or Sapphire (3568 K/sec), but they're all relatively new +algorithms and there hasn't been time for much analysis to be +performed; use them for serious applications only after careful +research. Skipjack (864 K/sec) is relatively slow, and its 80-bit key size +is secure at the moment, but doesn't leave much of a margin of safety; +only use it if you believe that the NSA has mystical cipher design +abilities. + + +\subsection{Credits} +\index{Olson, Bryan} +\index{Schneier, Bruce} +\index{Young, Eric} +\index{Cyphers, Graven} +\index{Outerbridge, Richard} +\index{Brown, Lawrence} +\index{Kwan, Matthew} +\index{DES (block cipher)} +\index{IDEA (block cipher)} +\index{Blowfish (block cipher)} +The code for Blowfish was written by Bryan Olson, partially based on a +previous implementation by Bruce Schneier, who also invented the +algorithm; the Blowfish algorithm has been placed in the public domain +and can be used freely. (See \url{http://www.counterpane.com} for more +information about Blowfish.) The CAST implementation was written by +Wim Lewis. The DES implementation was written by Eric Young, and the +IDEA implementation by Colin Plumb. The RC5 implementation +was written by A.M. Kuchling. + +\index{sci.crypt} +\index{Johnson, Michael Paul} +\index{ARC4 (stream cipher)} +\index{Sapphire (stream cipher)} +The Alleged RC4 code was posted to the \code{sci.crypt} newsgroup by an +unknown party, and re-implemented by A.M. Kuchling. The Sapphire stream +cipher was developed by Michael P. Johnson, and is in the public domain; +the implementation used here was written by A.M. Kuchling and is based +on Johnson's code. + +\section{Crypto.Protocol: Various Protocols} + +\subsection{Crypto.Protocol.AllOrNothing} + +This module implements all-or-nothing package transformations. +An all-or-nothing package transformation is one in which some text is +transformed into message blocks, such that all blocks must be obtained before +the reverse transformation can be applied. Thus, if any blocks are corrupted +or lost, the original message cannot be reproduced. + +An all-or-nothing package transformation is not encryption, although a block +cipher algorithm is used. The encryption key is randomly generated and is +extractable from the message blocks. + +This class implements the All-Or-Nothing package transformation +algorithm described in Rivest: ``All-Or-Nothing Encryption and The +Package Transform.'' To appear in the Proceedings of the 1997 Fast +Software Encryption Conference. +http://theory.lcs.mit.edu/~rivest/fusion.ps + +\begin{classdesc}{AllOrNothing}{ciphermodule, mode=None, IV=None} +Class implementing the All-or-Nothing package transform. + +\var{ciphermodule} is a module implementing the cipher algorithm to +use. Optional arguments \var{mode} and \var{IV} are passed directly +through to the \var{ciphermodule}.\code{new()} method; they are the +feedback mode and initialization vector to use. All three arguments +must be the same for the object used to create the digest, and to +undigest'ify the message blocks. + +The module passed as \var{ciphermodule} must provide the +following interface: + +\var{ciphermodule}.\code{keysize}: +Attribute containing the cipher algorithm's key size in +bytes. If the cipher supports variable length keys, then +typically \code{ciphermodule.keysize} will be zero. In that case a +key size of 16 bytes will be used. + +\var{ciphermodule}.\code{blocksize}: +Attribute containing the cipher algorithm's input block size +in bytes. + +\var{ciphermodule}.\code{new}(\var{key}, \var{mode}, \var{IV}): + Function which returns a new instance of a cipher object, + initialized to \var{key}. The returned object must have an + \method{encrypt()} method that accepts a string of + \var{ciphermodule}.\code{blocksize} bytes and returns a string containing + the encrypted text. + +Note that the encryption key is randomly generated automatically +when needed. +\end{classdesc} + +The methods of the \class{AllorNothing} class are: + +\begin{methoddesc}{digest}{} +Perform the All-or-Nothing package transform on the current +string. Output is a list of message blocks describing the +transformed text, where each block is a string of bit length equal +to the cipher module's blocksize. +\end{methoddesc} + +\begin{methoddesc}{reset}{text = ""} +Reset the current string to be transformed to \var{text}. +\end{methoddesc} + +\begin{methoddesc}{undigest}{mblocks} +Perform the reverse package transformation on a list of message +blocks. Note that the cipher module used for both transformations +must be the same. \var{mblocks} is a list of strings of bit length +equal to \var{ciphermodule}'s blocksize. The output is a string object. +\end{methoddesc} + +\begin{methoddesc}{update}{text} +Concatenate \var{text} to the string that will be transformed. +\end{methoddesc} + +\subsection{Crypto.Protocol.Chaffing} + +Winnowing and chaffing is a technique for enhancing privacy without requiring +strong encryption. In short, the technique takes a set of authenticated +message blocks (the wheat) and adds a number of chaff blocks which have +randomly chosen data and MAC fields. This means that to an adversary, the +chaff blocks look as valid as the wheat blocks, and so the authentication +would have to be performed on every block. By tailoring the number of chaff +blocks added to the message, the sender can make breaking the message +computationally infeasible. There are many other interesting properties of +the winnow/chaff technique. + +For example, say Alice is sending a message to Bob. She packetizes the +message and performs an all-or-nothing transformation on the packets. Then +she authenticates each packet with a message authentication code (MAC). The +MAC is a hash of the data packet, and there is a secret key which she must +share with Bob (key distribution is an exercise left to the reader). She then +adds a serial number to each packet, and sends the packets to Bob. + +Bob receives the packets, and using the shared secret authentication key, +authenticates the MACs for each packet. Those packets that have bad MACs are +simply discarded. The remainder are sorted by serial number, and passed +through the reverse all-or-nothing transform. The transform means that an +eavesdropper (say Eve) must acquire all the packets before any of the data can +be read. If even one packet is missing, the data is useless. + +There's one twist: by adding chaff packets, Alice and Bob can make Eve's job +much harder, since Eve now has to break the shared secret key, or try every +combination of wheat and chaff packet to read any of the message. The cool +thing is that Bob doesn't need to add any additional code; the chaff packets +are already filtered out because their MACs don't match (in all likelihood -- +since the data and MACs for the chaff packets are randomly chosen it is +possible, but very unlikely that a chaff MAC will match the chaff data). And +Alice need not even be the party adding the chaff! She could be completely +unaware that a third party, say Charles, is adding chaff packets to her +messages as they are transmitted. + +For more information on winnowing and chaffing see this paper: + +XXX Rivest. + +\begin{classdesc}{Chaff}{factor=1.0, blocksper=1} +Class implementing the chaff adding algorithm. +\var{factor} is the number of message blocks + to add chaff to, expressed as a percentage between 0.0 and 1.0; the default value is 1.0. +\var{blocksper} is the number of chaff blocks to include for each block + being chaffed, and defaults to 1. The default settings +add one chaff block to every + message block. By changing the defaults, you can adjust how + computationally difficult it could be for an adversary to + brute-force crack the message. The difficulty is expressed as: + +\begin{verbatim} +pow(blocksper, int(factor * number-of-blocks)) +\end{verbatim} + +For ease of implementation, when \var{factor} < 1.0, only the first +\code{int(\var{factor}*number-of-blocks)} message blocks are chaffed. +\end{classdesc} + +\class{Chaff} instances have the following methods: + +\begin{methoddesc}{chaff}{blocks} +Add chaff to message blocks. \var{blocks} is a list of 3-tuples of the +form (\var{serial-number}, \var{data}, \var{MAC}). + +Chaff is created by choosing a random number of the same +byte-length as \var{data}, and another random number of the same +byte-length as \var{MAC}. The message block's serial number is placed +on the chaff block and all the packet's chaff blocks are randomly +interspersed with the single wheat block. This method then +returns a list of 3-tuples of the same form. Chaffed blocks will +contain multiple instances of 3-tuples with the same serial +number, but the only way to figure out which blocks are wheat and +which are chaff is to perform the MAC hash and compare values. +\end{methoddesc} + + Subclass methods: + +\begin{methoddesc}{__randnum}{size} +Returns a randomly generated number with a byte-length equal +to \var{size}. Subclasses can use this to implement better random +data and MAC generating algorithms. The default algorithm is +probably not very cryptographically secure. It is most +important that the chaff data does not contain any patterns +that can be used to discern it from wheat data without running +the MAC. +\end{methoddesc} + +\section{Crypto.PublicKey: Public Key Algorithms} +So far, the encryption algorithms described have all been \dfn{private +key} ciphers. That is, the same key is used for both encryption and +decryption, so all correspondents must know it. This poses a problem: +you may want encryption to communicate sensitive data over an insecure +channel, but how can you tell your correspondent what the key is? You +can't just e-mail it to her because the channel is insecure. One +solution is to arrange the key via some other way: over the phone or +by meeting in person. + +Another solution is to use \dfn{public key} cryptography. In a public +key system, there are two different keys: one for encryption and one for +decryption. The encryption key can be made public by listing it in a +directory or mailing it to your correspondent, while you keep the +decryption key secret. Your correspondent then sends you data encrypted +with your public key, and you use the private key to decrypt it. While +the two keys are related, it's very difficult to derive the private key +given only the public key; however, deriving the private key is always +possible given enough time and computing power. This makes it very +important to pick keys of the right size: large enough to be secure, but +small enough to be applied fairly quickly. + +Many public key algorithms can also be used to sign messages; simply +run the message to be signed through a decryption with your private +key key. Anyone receiving the message can encrypt it with your +publicly available key and read the message. Some algorithms do only +one thing, others can both encrypt and authenticate. + +The currently available public key algorithms are listed in the +following table: + +\begin{tableii}{c|l}{}{Algorithm}{Capabilities} +\lineii{RSA}{Encryption, authentication/signatures} +\lineii{ElGamal}{Encryption, authentication/signatures} +\lineii{DSA}{Authentication/signatures} +\lineii{qNEW}{Authentication/signatures} +\end{tableii} + +Many of these algorithms are patented. Before using any of them in a +commercial product, consult a patent attorney; you may have to arrange +a license with the patent holder. + +An example of using the RSA module to sign a message: +\begin{verbatim} +>>> from Crypto.Hash import MD5 +>>> from Crypto.PublicKey import RSA +>>> RSAkey=RSA.generate(384, randfunc) # This will take a while... +>>> hash=MD5.new(plaintext).digest() +>>> signature=RSAkey.sign(hash, "") +>>> signature # Print what an RSA sig looks like--you don't really care. +('\021\317\313\336\264\315' ...,) +>>> RSAkey.verify(hash, signature) # This sig will check out +1 +>>> RSAkey.verify(hash[:-1], signature)# This sig will fail +0 +\end{verbatim} + + +Public key modules make the following functions available: + +\begin{funcdesc}{construct}{tuple} +Constructs a key object from a tuple of data. This is +algorithm-specific; look at the source code for the details. (To be +documented later.) +\end{funcdesc} + +\begin{funcdesc}{generate}{size, randfunc, progress_func=\code{None}} +Generate a fresh public/private key pair. \var{size} is a +algorithm-dependent size parameter; the larger it is, the more +difficult it will be to break the key. Safe key sizes vary from +algorithm to algorithm; you'll have to research the question and +decide on a suitable key size for your application. \code{randfunc} +is a random number generation function; it should accept a single +integer \var{N} and return a string of random data \var{N} bytes long. +You should always use a cryptographically secure random number +generator, such as the one defined in the \code{randpool} module; +\emph{don't} just use the current time and the \code{whrandom} module. + +\var{progress_func} is an optional function that will be called with a short +string containing the key parameter currently being generated; it's +useful for interactive applications where a user is waiting for a key to +be generated. +\end{funcdesc} + +If you want to interface with some other program, you will have to know +the details of the algorithm being used; this isn't a big loss. If you +don't care about working with non-Python software, simply use the +\code{pickle} module when you need to write a key or a signature to a +file. It's portable across all the architectures that Python supports, +and it's simple to use. + +Public key objects always support the following methods. Some of them +may raise exceptions if their functionality is not supported by the +algorithm. + +\begin{funcdesc}{canencrypt}{} +Returns true if the algorithm is capable of encrypting and decrypting +data; returns false otherwise. To test if a given key object can sign +data, use \code{key.canencrypt() and key.hasprivate()}. +\end{funcdesc} + +\begin{funcdesc}{cansign}{} +Returns true if the algorithm is capable of signing data; returns false +otherwise. To test if a given key object can sign data, use +\code{key.cansign() and key.hasprivate()}. +\end{funcdesc} + +\begin{funcdesc}{decrypt}{tuple} +Decrypts \var{tuple} with the private key, returning another string. +This requires the private key to be present, and will raise an exception +if it isn't present. It will also raise an exception if \var{string} is +too long. +\end{funcdesc} + +\begin{funcdesc}{encrypt}{string, K} +Encrypts \var{string} with the private key, returning a tuple of +strings; the length of the tuple varies from algorithm to algorithm. +\var{K} should be a string of random data that is as long as +possible. Encryption does not require the private key to be present +inside the key object. It will raise an exception if \var{string} is +too long. For ElGamal objects, the value of \var{K} expressed as a +big-endian integer must be relatively prime to \code{self.p-1}; an +exception is raised if it is not. +\end{funcdesc} + +\begin{funcdesc}{hasprivate}{} +Returns true if the key object contains the private key data, which +will allow decrypting data and generating signatures. +Otherwise this returns false. +\end{funcdesc} + +\begin{funcdesc}{publickey}{} +Returns a new public key object that doesn't contain the private key +data. +\end{funcdesc} + +\begin{funcdesc}{sign}{string, K} +Sign \var{string}, returning a signature, which is just a tuple; in +theory the signature may be made up of any Python objects at all; in +practice they'll be either strings or numbers. \var{K} should be a +string of random data that is as long as possible. Different algorithms +will return tuples of different sizes. \code{sign()} raises an +exception if \var{string} is too long. For ElGamal objects, the value +of \var{K} expressed as a big-endian integer must be relatively prime to +\code{self.p-1}; an exception is raised if it is not. +\end{funcdesc} + +\begin{funcdesc}{size}{} +Returns the maximum size of a string that can be encrypted or signed, +measured in bits. String data is treated in big-endian format; the most +significant byte comes first. (This seems to be a \emph{de facto} standard +for cryptographical software.) If the size is not a multiple of 8, then +some of the high order bits of the first byte must be zero. Usually +it's simplest to just divide the size by 8 and round down. +\end{funcdesc} + +\begin{funcdesc}{verify}{string, signature} +Returns true if the signature is valid, and false otherwise. +\var{string} is not processed in any way; \code{verify} does +not run a hash function over the data, but you can easily do that yourself. +\end{funcdesc} + +\subsection{The ElGamal and DSA algorithms} +For RSA, the \var{K} parameters are unused; if you like, you can just +pass empty strings. The ElGamal and DSA algorithms require a real +\var{K} value for technical reasons; see Schneier's book for a detailed +explanation of the respective algorithms. This presents a possible +hazard that can +inadvertently reveal the private key. Without going into the +mathematical details, the danger is as follows. \var{K} is never derived +or needed by others; theoretically, it can be thrown away once the +encryption or signing operation is performed. However, revealing +\var{K} for a given message would enable others to derive the secret key +data; worse, reusing the same value of \var{K} for two different +messages would also enable someone to derive the secret key data. An +adversary could intercept and store every message, and then try deriving +the secret key from each pair of messages. + +This places implementors on the horns of a dilemma. On the one hand, +you want to store the \var{K} values to avoid reusing one; on the other +hand, storing them means they could fall into the hands of an adversary. +One can randomly generate \var{K} values of a suitable length such as +128 or 144 bits, and then trust that the random number generator +probably won't produce a duplicate anytime soon. This is an +implementation decision that depends on the desired level of security +and the expected usage lifetime of a private key. I cannot choose and +enforce one policy for this, so I've added the \var{K} parameter to the +\code{encrypt} and \code{sign} functions. You must choose \var{K} by +generating a string of random data; for ElGamal, when interpreted as a +big-endian number (with the most significant byte being the first byte +of the string), \var{K} must be relatively prime to \code{self.p-1}; any +size will do, but brute force searches would probably start with small +primes, so it's probably good to choose fairly large numbers. It might be +simplest to generate a prime number of a suitable length using the +\code{Crypto.Util.number} module. + +\subsection{Security Notes for Public-key Algorithms} +Any of these algorithms can be trivially broken; for example, RSA can be +broken by factoring the modulus \emph{n} into its two prime factors. +This is easily done by the following code: + +\begin{verbatim} +for i in range(2, n): + if (n%i)==0: print i, 'is a factor' ; break +\end{verbatim} + + +However, \emph{n} is usually a few hundred bits long, so this simple +program wouldn't find a solution before the universe comes to an end. +Smarter algorithms can factor numbers more quickly, but it's still +possible to choose keys so large that they can't be broken in a +reasonable amount of time. For ElGamal and DSA, discrete logarithms are +used instead of factoring, but the principle is the same. + +Safe key sizes depend on the current state of computer science and +technology. At the moment, one can roughly define three levels of +security: low-security commercial, high-security commercial, and +military-grade. For RSA, these three levels correspond roughly to 512, +768, and 1024 bit-keys. For ElGamal and DSA, the key sizes should be +somewhat larger for the same level of security, around 768, 1024, and +1536 bits. + +\section{Crypto.Util: Odds and Ends} +This chapter contains all the modules that don't fit into any of the +other chapters. + +\subsection{Crypto.Util.number} + +This module contains various functions of number-theoretic functions. + +\begin{funcdesc}{GCD}{x,y} +Return the greatest common divisor of \var{x} and \var{y}. +\end{funcdesc} + +\begin{funcdesc}{getPrime}{N, randfunc} +Return an \var{N}-bit random prime number, using random data obtained +from the function \var{randfunc}. \var{randfunc} must take a single +integer argument, and return a string of random data of the +corresponding length; the \code{getBytes()} method of a +\code{RandomPool} object will serve the purpose nicely, as will the +\code{read()} method of an opened file such as \file{/dev/random}. +\end{funcdesc} + +\begin{funcdesc}{getRandomNumber}{N, randfunc} +Return an \var{N}-bit random number, using random data obtained from the +function \var{randfunc}. As usual, \var{randfunc} must take a single +integer argument, and return a string of random data of the +corresponding length. +\end{funcdesc} + +\begin{funcdesc}{inverse}{u, v} +Return the inverse of \var{u} modulo \var{v}. +\end{funcdesc} + +\begin{funcdesc}{isPrime}{N} +Returns true if the number \var{N} is prime, as determined by a +Rabin-Miller test. +\end{funcdesc} + +\index{random numbers} +\subsection{Crypto.Util.randpool} +For cryptographic purposes, ordinary random number generators are +frequently insufficient, because if some of their output is known, it is +frequently possible to derive the generator's future (or past) output. +This is obviously a Bad Thing; given the generator's state at some point +in time, someone could try to derive any keys generated using it. The +solution is to use strong encryption or hashing algorithms to generate +successive data; this makes breaking the generator as difficult as +breaking the algorithms used. + +\index{entropy} +Understanding the concept of \dfn{entropy} is important for using the +random number generator properly. In the sense we'll be using it, +entropy measures the amount of randomness; the usual unit is in bits. +So, a single random bit has an entropy of 1 bit; a random byte has an +entropy of 8 bits. Now consider a one-byte field in a database containing a +person's sex, represented as a single character \samp{M} or \samp{F}. +What's the entropy of this field? Since there are only two possible +values, it's not 8 bits, but one; if you were trying to guess the value, +you wouldn't have to bother trying \samp{Q} or \samp{@}. + +Now imagine running that single byte field through a hash function that +produces 128 bits of output. Is the entropy of the resulting hash value +128 bits? No, it's still just 1 bit. The entropy is a measure of how many +possible states of the data exist. For English +text, the entropy of a five-character string is not 40 bits; it's +somewhat less, because not all combinations would be seen. \samp{Guido} +is a possible string, as is \samp{In th}; \samp{zJwvb} is not. + +The relevance to random number generation? We want enough bits of +entropy to avoid making an attack on our generator possible. An +example: One computer system had a mechanism which generated nonsense +passwords for its users. This is a good idea, since it would prevent +people from choosing their own name or some other easily guessed string. +Unfortunately, the random number generator used only had 65536 states, +which meant only 65536 different passwords would ever be generated, and +it was easily to compute all the possible passwords and try them. The +entropy of the random passwords was far too low. By the same token, if +you generate an RSA key with only 32 bits of entropy available, there +are only about 4.2 billion keys you could have generated, and an +adversary could compute them all to find your private key. See RFC 1750: +"Randomness Recommendations for Security" for an interesting discussion +of the issues related to random number generation. + +The \code{randpool} module implements a strong random number generator +in the \code{RandomPool} class. The internal state consists of a string +of random data, which is returned as callers request it. The class +keeps track of the number of bits of entropy left, and provides a function to +add new random data; this data can be obtained in various ways, such as +by using the variance in a user's keystroke timings. + +\begin{funcdesc}{RandomPool}{\optional{numbytes, cipher, hash} } +An object of the \code{RandomPool} class can be created without +parameters if desired. \var{numbytes} sets the number of bytes of +random data in the pool, and defaults to 160 (1280 bits). \var{hash} +can be a string containing the module name of the hash function to use +in stirring the random data, or a module object supporting the hashing +interface. The default action is to use SHA. + +The \var{cipher} argument is vestigial; it was removed from version +1.1 so RandomPool would work even in the limited exportable subset of +the code. It can have any value at all, since it's no longer used at +all. + +\end{funcdesc} + +\code{RandomPool} objects define the following variables and methods: + +\begin{funcdesc}{addEvent}{time\optional{, string}} +Adds an event to the random pool. \var{time} should be set to the +current system time, measured at the highest resolution available. +\var{string} can be a string of data that will be XORed into the pool, +and can be used to increase the entropy of the pool. For example, if +you're encrypting a document, you might use the hash value of the +document; an adversary presumably won't have the plaintext of the +document, and thus won't be able to use this information to break the +generator. +\end{funcdesc} + +The return value is the value of \code{self.entropy} after the data has +been added. The function works in the following manner: the time +between successive calls to the \code{addEvent} method is determined, +and the entropy of the data is guessed; the larger the time between +calls, the better. The system time is then read and added to the pool, +along with the \var{string} parameter, if present. The hope is that the +low-order bits of the time are effectively random. In an application, +it is recommended that \code{addEvent()} be called as frequently as +possible, with whatever random data can be found. + +\begin{datadesc}{bits} +A constant integer value containing the number of bits of data in +the pool, equal to the \code{bytes} variable multiplied by 8. +\end{datadesc} + +\begin{datadesc}{bytes} +A constant integer value containing the number of bytes of data in +the pool. +\end{datadesc} + +\begin{datadesc}{entropy} +An integer value containing the number of bits of entropy currently in +the pool. The value is incremented by the \code{addEvent()} method, +and decreased by the \code{getBytes} method. +\end{datadesc} + +\begin{funcdesc}{getBytes}{num} +Returns a string containing \var{num} bytes of random data, and +decrements the amount of entropy available. It is not an error to +reduce the entropy to zero, or to call this function when the entropy +is zero. This simply means that, in theory, enough random information has been +extracted to derive the state of the generator. It is the caller's +responsibility to monitor the amount of entropy remaining and decide +whether it is sufficent for secure operation. +\end{funcdesc} + +\begin{funcdesc}{stir}{} +Scrambles the random pool using the previously chosen encryption and +hash function. An adversary may attempt to learn or alter the state +of the pool in order to affect its future output; this function +destroys the existing state of the pool in a non-reversible way. It +is recommended that \code{stir()} be called before and after using +the \code{RandomPool} object. Even better, several calls to +\code{stir()} can be interleaved with calls to \code{addEvent()}. +\end{funcdesc} + +The \code{KeyboardRandomPool} class is a subclass of \code{RandomPool} +that adds the capability to save and load the pool from a disk file, and +provides a method to obtain random data from the keyboard. + +\begin{funcdesc}{KeyboardRandomPool}{\optional{filename, numbytes, cipher, hash}} +The path given in \var{filename} will be automatically opened, and an +existing random pool read; if no such file exists, the pool will be +initialized as usual. If omitted, the filename defaults to the empty +string, which will prevent it from being saved to a file. The other +arguments are identical to those for the \code{RandomPool} constructor. +\end{funcdesc} + +\begin{funcdesc}{randomize}{} +(Unix systems only) Obtain random data from the keyboard. This works +by prompting the +user to hit keys at random, and then using the keystroke timings (and +also the actual keys pressed) to add entropy to the pool. This works +similarly to PGP's random pool mechanism. +\end{funcdesc} + +\begin{funcdesc}{save}{} +Opens the file named by the \code{filename} attribute, and saves the +random data into the file using the \code{pickle} module. +\end{funcdesc} + + +\subsection{Crypto.Util.RFC1751} +The keys for private-key algorithms should be arbitrary binary data. +Many systems err by asking the user to enter a password, and then using +the password as the key. This limits the space of possible keys, as +each key byte is constrained within the range of possible ASCII +characters, 32-127, instead of the whole 0-255 range possible with ASCII. +Unfortunately, it's difficult for humans to remember 16 or 32 hex +digits. + +One solution is to request a lengthy passphrase from the user, and then +run it through a hash function such as SHA or MD5. Another solution is +discussed in RFC 1751, "A Convention for Human-Readable 128-bit Keys", +by Daniel L. McDonald. Binary keys are transformed into a list of short +English words that should be easier to remember. For example, the hex +key EB33F77EE73D4053 is transformed to "TIDE ITCH SLOW REIN RULE MOT". + +\begin{funcdesc}{Key2English}{key} +Accepts a string of arbitrary data \var{key}, and returns a string +containing uppercase English words separated by spaces. \var{key}'s +length must be a multiple of 8. +\end{funcdesc} + +\begin{funcdesc}{English2Key}{string} +Accepts \var{string} containing English words, and returns a string of +binary data representing the key. Words must be separated by +whitespace, and can be any mixture of uppercase and lowercase +characters. 6 words are required for 8 bytes of key data, so +the number of words in \var{string} must be a multiple of 6. +\end{funcdesc} + +\section{The Demonstration Programs} +The Python cryptography modules comes with various demonstration +programs, located in the \file{Demo/} directory. None of them is +particularly well-finished, or suitable for serious use. Rather, +they're intended to illustrate how the toolkit is used, and to provide +some interesting possible uses. Feel free to incorporate the code (or +modifications of it) into your own programs. + +\subsection{Demo 1: \file{cipher}} + +\index{crypt} +\index{cipher (demo program)} +\index{Enigma} +\index{Crypt Breaker's Workbench} +\file{cipher} encrypts and decrypts files. On most Unix systems, the +\file{crypt} program uses a variant of the Enigma cipher. This is not +secure, and there exists a freely available program called ``Crypt +Breaker's Workbench'' which helps in breaking the cipher if you have +some knowledge of the encrypted data. + +\file{cipher} is a more secure file encryption program. Simply list +the names of the files to be encrypted on the command line. +\file{cipher} will go through the list and encrypt or decrypt them; +\file{cipher} can recognize files it has previously encrypted. The +ciphertext of a file is placed in a file of the same name with +'\samp{.cip}' appended; the original file is not deleted, since I'm +not sure that all errors during operation are caught, and I don't want +people to accidentally erase important files. + +There are two command-line options: \code{-c} and \code{-k}. Both of +them require an argument. \code{-c \var{ciphername}} uses the +given encryption algorithm \var{ciphername}; for example, +\code{-c des} will use the DES algorithm. The name should be the same +as an available module name; thus it should be in lowercase letters. +The default cipher is IDEA. + +\index{Linux} +\index{cipher} +\code{-k \var{key}} can be used to set the encryption key to be +used. Note that on a multiuser Unix system, the \code{ps} command can +be used to view the arguments of commands executed by other users, so +this is insecure; if you're the only user (say, on your home computer +running Linux) you don't have to worry about this. If no key is set +on the command line, \file{cipher} will prompt the user to input a key +on standard input. + +\subsubsection{Technical Details} + +The encrypted file is not pure ciphertext. First comes a magic +string; this is currently the sequence \samp{ctx} and a byte +containing 1 (the version number of \file{cipher}). +This is followed by the null-terminated name of the encryption +algorithm, and the rest of the file contains the ciphertext. + +\index{feedback mode, CBC} +The plaintext is encrypted in CBC mode. The initial value for the +feedback is always set to a block filled with the letter 'A', and then +a block of random data is encrypted. This garbage block will be +discarded on decryption. Note that the random data is not generated +in a cryptographically secure way, and this may provide a tiny foothold for +an attacker. + +After the random block is generated, the magic string, length of the +original file, and original filename are all encrypted before the file +data is finally processed. Some extra characters of padding may be +added to obtain an integer number of blocks. This padding will also +be discarded on decryption. Note that the plaintext file will be +completely read into memory before encryption is performed; no +buffering is done. Therefore, don't encrypt 20-megabyte files unless +you're willing to face the consequences of a 20-megabyte process. + +Areas for improvements to \file{cipher} are: cryptographically secure +generation of random data +for padding, key entry, and buffering of file +input. + +\subsection{Demo 2: \file{secimp} and \file{sign}} + +\file{secimp} demonstrates an application of the Toolkit that may be +useful if Python is being used as an extension language for mail and Web +clients: secure importing of Python modules. To use it, run +\file{sign.py} in a directory with several compiled Python files +present. It will use the key in \file{testkey.py} to generate digital +signatures for the compiled Python code, and save both the signature and +the code in a file ending in \samp{.pys}. Then run \code{python -i +secimp.py}, and import a file by using \code{secimport}. + +For example, if \file{foo.pys} was constructed, do +\code{secimport('foo')}. The import should succeed. Now fire up Emacs +or some other editor, and change a string in the code in \file{foo.pys}; +you might try changing a letter in the name of a variable. When you run +\code{secimport('foo')}, it should raise an exception reporting the +failed signature. If you execute the statement \code{__import__ = +secimport}, the secure import will be used by default for all future +module imports. Alternatively, if you were creating a restricted +execution environment using \file{rexec.py}, you could place +\code{secimport()} in the restricted environment's namespace as the +default import function. + +\section{Extending the cryptography modules} +Preserving the a common interface for cryptographic routines is a +good idea. This chapter +explains how to interface your own routines to the Toolkit. + +The basic process is as follows: +\begin{enumerate} +\item Modify the default definition of a C structure to include +whatever instance data your algorithm requires. +\item Write 3 or 4 standard routines. Their names and parameters are +specified in the following subsections. +\item Modify \file{buildkit} to contain an entry for your new +algorithm. Then run \file{buildkit} to rebuild all the source files. +\item Send a copy of the code to me, if you like; code for new +algorithms will be gratefully accepted. +\end{enumerate} + +\subsection{Creating a Custom Object} +In the C code for the interpreter, Python objects are defined as a +structure. The default structure is the following: +\begin{verbatim} +typedef struct +{ + PCTObject_HEAD +} ALGobject; +\end{verbatim} + + +\code{PCTObject_HEAD} is a preprocessor macro which will contain various +internal variables used by the interpreter; it must always be the +first item in the structure definition, and must not be followed by a +semicolon. Following it, you can put whatever instance variables you +require. Data that does not depend on the instance or key, such as a +static lookup table, need not be encapsulated inside objects; instead, +it can be defined as a variable interior to the module. + +As an example, for IDEA encryption, a schedule of encryption and +decryption data has to be maintained, resulting in the following +definition: +\begin{verbatim} +typedef struct +{ + PCTObject_HEAD + int EK[6][9], DK[6][9]; +} IDEAobject; +\end{verbatim} + + +\subsection{Standard Routines} + +\index{buildkit} +The interface to Python is implemented in the files ending in +\samp{.in}, so \file{hash.in} contains the basic code for modules +containing hash functions, for example. \file{buildkit}, a Python +script, reads the configuration file and generates source code by +interweaving the interface files and the implementation file. + +\index{buildkit} +If your algorithm is called ALG, the implementation should be in the +file \file{ALG.c}. This is case-sensitive, as are the following function +names. + +\subsubsection{Hash functions} + +\begin{itemize} +\item \code{void \var{ALG}init(\var{ALG}object *self);} +\item \code{void \var{ALG}update(\var{ALG}object *self, char *buffer, int length);} +\item \code{PyObject *\var{ALG}digest(\var{ALG}object *self);} +\item \code{void \var{ALG}copy(\var{ALG}object *source, \var{ALG}object *dest);} +\end{itemize} + +\begin{funcdesc}{void ALGinit}{\rm ALGobject *\var{self}} +\index{ALGinit} +This function should initialize the hashing object, setting +state variables to their expected initial state. +\end{funcdesc} + +\begin{funcdesc}{void ALGupdate}{\rm ALGobject *\var{self}, +char *\var{buffer}, int \var{length}} +\index{ALGupdate} +This function should perform a hash on the region pointed to by +\var{buffer}, which will contain \var{length} bytes. The contents of +the object pointed to by \var{self} should be updated appropriately. +\end{funcdesc} + +\begin{funcdesc}{void ALGdigest}{\rm ALGobject *\var{self}} +This function returns a string containing the value of the hash +function. The object should not be changed in any way by this +function. Some hash functions require some computation to be +performed before returning a value; for example, the number of bytes +may be hashed into the final value. If this is the case for your hash +function, you must make a copy of the object's data, perform the final +computation on that copy, and return the result. +\end{funcdesc} + +Results are returned by calling a Python function, +\code{PyString_FromStringAndSize(char *\var{string}, int \var{length})}. This +function returns a string object which should be returned to the +caller. So, the last line of the \code{ALGdigest} +function might be: +\begin{verbatim} + return PyString_FromStringAndSize(digest, 16); +\end{verbatim} + +\index{ALGdigest} + +\begin{funcdesc}{void ALGcopy}{\rm ALGobject *\var{source}, ALGobject *\var{dest}} +Given the source and destination objects, the state variables of the +\var{source} object should be copied to the \var{dest} object; the +source object should not be altered in any way by the operation. +\index{ALGcopy} +\end{funcdesc} + +\subsubsection{Block ciphers} +\begin{itemize} +\item \code{void ALGinit(ALGobject *\var{self}, unsigned char *\var{key}, int \var{length});} +\item \code{PyObject *ALGencrypt(ALGobject *\var{self}, unsigned char *\var{block});} +\item \code{PyObject *ALGdecrypt(ALGobject *\var{self}, unsigned char *\var{block});} +\end{itemize} + +\begin{funcdesc}{void ALGinit}{\rm ALGobject *\var{self}, unsigned char *\var{key}, int \var{length}} +This function initializes a block cipher object to encrypt and decrypt +with \var{key}. If the cipher requires a fixed-length key, then the +buffer pointed to by \var{key} will always of that length, and the +value of \var{length} will be a random value that should be ignored. +If the algorithm accepts a variable-length key, then \var{length} will +be nonzero, and will contain the size of the key. +\index{ALGinit} +\end{funcdesc} + +\begin{funcdesc}{void ALGencrypt}{\rm ALGobject *\var{self}, unsigned char *\var{block}} +This function should encrypt the data pointed to by \var{block}, using +the key-dependent data contained in \var{self}. Only ECB mode needs +to be implemented; \code{block.in} takes care of the other +ciphering modes. +\index{ALGencrypt} +\end{funcdesc} + +\begin{funcdesc}{void ALGdecrypt}{\rm ALGobject *\var{self}, unsigned char *\var{block}} +This function should decrypt the data pointed to by \var{block}, using +the key-dependent data contained in \var{self}. +\index{ALGdecrypt} +\end{funcdesc} + +\subsection{Portability macros} + +Implementation code must be carefully written to produce the same +results with any machine or compiler, without having to set any +compile-time definitions. Code that is simply portable by nature is +preferable, but it is possible to detect features of the host machine +when new objects are created, and then execute special code to convert +data to a preferred form. + +While portability macros are written for speed, there's no need to +execute them on every encryption or updating operation. Instead, add +variables to your object to hold the values of the portability macros, +and execute the macros only once per object, in your +\code{ALGinit} function. Then the code can simply check the +results of the macros and act appropriately. + +Currently there is only one portability macro defined: + +\begin{funcdesc}{void TestEndianness}{variable} +Determines the endianness of the current machine, and sets +\var{variable} to a constant representing the value for this machine. +Possible constants are \code{PCT_BIG_ENDIAN} and \code{PCT_LITTLE_ENDIAN}; +they are defined along with the \code{TestEndianness} macro. +\end{funcdesc} + +\subsection{Informing the author} +Code for additional cryptographic algorithms can be mailed to me at +\email{akuchling@acm.org}. You can make things much easier for me by doing the +following: +\begin{itemize} +\item If you wrote the code, please release it into the public domain +or under the MIT X11 license. If you didn't write it, please tell me where +to find the original author or source code, so that I can check the +licensing conditions. +\item Include some test data. It is not sufficient to check that +encryption and decryption cancel out properly. An implementation +might work fine on a single platform, but two machines with +different endianness might produce different results. This would be +fatal for portability and interoperating programs. So, please include +test data; you can either send me patches to \file{test.py}, or simply +send me documents describing the data. +\end{itemize} + +\end{document} diff --git a/Doc/pycrypt.txt b/Doc/pycrypt.txt new file mode 100644 index 0000000..ed83488 --- /dev/null +++ b/Doc/pycrypt.txt @@ -0,0 +1,1400 @@ + + _________________________________________________________________ + + The Python Cryptography Modules + + A.M. Kuchling + + akuchling@acm.org + _________________________________________________________________ + + Abstract: + + This document describes a package containing various cryptographic + modules available for the Python programming language. It assumes you + have some basic knowledge about the Python language and about + cryptography in general. + +Contents + + * [1]1. Introduction + + [2]1.1 Design Goals + + [3]1.2 Acknowledgements + * [4]2. Crypto.Hash: Hash Functions + + [5]2.1 Algorithm-specific Notes for Hash Functions + + [6]2.2 Security Notes + + [7]2.3 Credits + * [8]3. Crypto.Cipher: Encryption Algorithms + + [9]3.1 Algorithm-specific Notes for Encryption Algorithms + + [10]3.2 Security Notes + + [11]3.3 Credits + * [12]4. Crypto.Protocol: Various Protocols + + [13]4.1 Crypto.Protocol.AllOrNothing + + [14]4.2 Crypto.Protocol.Chaffing + * [15]5. Crypto.PublicKey: Public Key Algorithms + + [16]5.1 The ElGamal and DSA algorithms + + [17]5.2 Security Notes for Public-key Algorithms + * [18]6. Crypto.Util: Odds and Ends + + [19]6.1 Crypto.Util.number + + [20]6.2 Crypto.Util.randpool + + [21]6.3 Crypto.Util.RFC1751 + * [22]7. The Demonstration Programs + + [23]7.1 Demo 1: "cipher" + + [24]7.2 Demo 2: "secimp" and "sign" + * [25]8. Extending the cryptography modules + + [26]8.1 Creating a Custom Object + + [27]8.2 Standard Routines + + [28]8.3 Portability macros + + [29]8.4 Informing the author + + 1. Introduction + +1.1 Design Goals + + The Python cryptography modules are intended to provide a reliable and + stable base for writing Python programs that require cryptographic + functions. + + A central goal of the author's has been to provide a simple, + consistent interface for similar classes of algorithms. For example, + all block cipher objects have the same methods and return values, and + support the same feedback modes; hash functions have a different + interface, but it too is consistent over all the hash functions + available. Individual modules also define variables to help you write + Python code that doesn't depend on the algorithms used; for example, + each block cipher module defines a variable that gives the algorithm's + block size. This is intended to make it easy to replace old algorithms + with newer, more secure ones. If you're given a bit of + portably-written Python code that uses the DES encryption algorithm, + you should be able to use IDEA instead by simply changing from + Crypto.Cipher import DES to from Crypto.Cipher import idea, and + changing all references to DES.new() to IDEA.new(). It's also fairly + simple to write your own modules that mimic this interface, thus + letting you use combinations or permutations of algorithms. + + Some modules are implemented in C for performance; others are written + in Python for ease of modification. Generally, low-level functions + like ciphers and hash functions are written in C, while less + speed-critical functions have been written in Python. This division + may change in future releases. When speeds are quoted in this + document, they were measured on a 266 MHz Pentium II running Linux. + The exact speeds will obviously vary with different machines and + different compilers, but they provide a basis for comparing + algorithms. Currently the cryptographic implementations are acceptably + fast, but not spectacularly good. I welcome any suggestions or patches + for faster code. + + If you live outside of Canada or the US, please do not attempt to + download it from a North American FTP site; you may get the site's + maintainer in trouble. Documentation is not covered by the ITAR + regulations, and can be freely sent anywhere in the world. + + I have placed the code under no restrictions; you can redistribute the + code freely or commercially, in its original form or with any + modifications you make, subject to whatever local laws may apply in + your jurisdiction. Note that you still have to come to some agreement + with the holders of any patented algorithms you're using. If you're + intensively using these modules, please tell me about it; there's + little incentive for me to work on this package if I don't know of + anyone using it. + + I also make no guarantees as to the usefulness, correctness, or + legality of these modules, nor does their inclusion constitute an + endorsement of their effectiveness. Many cryptographic algorithms are + patented; inclusion in this package does not necessarily mean you are + allowed to incorporate them in a product and sell it. Some of these + algorithms may have been cryptanalyzed, and may no longer be secure. + While I will include commentary on the relative security of the + algorithms in the sections entitled "Security Notes", there may be + more recent analyses I'm not aware of. (Or maybe I'm just clueless.) + If you're implementing an important system, don't just grab things out + of a toolbox and put them together; do some research first. On the + other hand, if you're just interested in keeping your co-workers or + your relatives out of your files, any of the components here could be + used. + + This document is very much a work in progress. If you have any + questions, comments, complaints, or suggestions, please send them to + me at akuchling@acm.org. + +1.2 Acknowledgements + + Much of the code that actually implements the various cryptographic + algorithms was not written by me. I'd like to thank all the people who + implemented them, and released their work under terms which allowed me + to use their code. The individuals are credited in the relevant + chapters of this documentation. Bruce Schneier's book Applied + Cryptography was also very useful in writing this toolkit; I highly + recommend it if you're interested in learning more about cryptography. + Mr. Schneier also has a Web site at [30]http://www.counterpane.com. + + Good luck with your cryptography hacking! + + A.M.K. + + akuchling@acm.org + + Washington, DC, USA + + January 1998 + + 2. Crypto.Hash: Hash Functions + + Hash functions take arbitrary strings as input, and produce an output + of fixed size that is dependent on the input; it should never be + possible to derive the input data given only the hash function's + output. One simple hash function consists of simply adding together + all the bytes of the input, and taking the result modulo 256. For a + hash function to be cryptographically secure, it must be very + difficult to find two messages with the same hash value, or to find a + message with a given hash value. The simple additive hash function + fails this criterion miserably; the hash functions described below do + not. Examples of cryptographically secure hash functions include MD2, + MD5, SHA, and HAVAL. + + Hash functions can be used simply as a checksum, or, in association + with a public-key algorithm, can be used to implement digital + signatures. The hashing algorithms currently implemented are listed in + the following table: + + Hash function Digest length + HAVAL Variable size: 128, 160, 192, 224, or 256 bits + MD2 128 bits + MD4 128 bits + MD5 128 bits + RIPEMD 160 bits + SHA 160 bits + + All hashing modules share the same interface. After importing a given + hashing module, call the new() function to create a new hashing + object. (In older versions of the Python interpreter, the md5() + function was used to perform this task. If you're modifying an old + script, you should change any calls to md5() to use new() instead.) + You can now feed arbitrary strings into the object, and can ask for + the hash value at any time. The new() function can also be passed an + optional string parameter, which will be hashed immediately. + + Hash function modules define one variable: + + digestsize + An integer value; the size of the digest produced by the + hashing objects. You could also obtain this value by creating a + sample object, and taking the length of the digest string it + returns, but using digestsize is faster. + + The methods for hashing objects are always the following: + + copy () + Return a separate copy of this hashing object. An update to + this copy won't affect the original object. + + digest () + Return the hash value of this hashing object, as a string + containing 8-bit data. The object is not altered in any way by + this function; you can continue updating the object after + calling this function. + + digest () + Return the hash value of this hashing object, as a string + containing the digest data as hexadecimal digits. The resulting + string will be twice as long as that returned by digest(). The + object is not altered in any way by this function; you can + continue updating the object after calling this function. + + update (arg) + Update this hashing object with the string arg. + + Here's an example, using RSA Data Security's MD5 algorithm: + + +>>> from Crypto.Hash import MD5 +>>> m = MD5.new() +>>> m.update('abc') +>>> m.digest() +'\220\001P\230<\322O\260\326\226?@}(\341\177r' + + Or, more compactly: + + +>>> MD5.new('abc').digest() +'\220\001P\230<\322O\260\326\226?@}(\341\177r' + +2.1 Algorithm-specific Notes for Hash Functions + + HAVAL provides a variable-size digest, and allows for a variable + number of rounds. It's believed that increasing the number of rounds + increases the security; at least, I don't know of any results to the + contrary. The HAVAL.new() accordingly has two keyword arguments, + rounds and digestsize. rounds can be 3, 4, or 5, and has a default + value of 5. digestsize can be 128, 160, 192, 224, or 256 bits, and has + a default value of 256. + +2.2 Security Notes + + Hashing algorithms are broken when it's easy to compute a string that + produces a given hash value, or to find two messages that produce the + same hash value. Consider an example where Alice and Bob are using + digital signatures to sign a contract. Alice computes the hash value + of the text of the contract and signs it. Bob could then compute a + different contract that has the same hash value, and it would appear + that Alice has signed that bogus contract; she'd have no way to prove + otherwise. Finding such a message by brute force takes pow(2, b-1) + operations, where the hash function produces b-bit hashes. + + If Bob can only find two messages with the same hash value but can't + choose the resulting hash value, he can look for two messages with + different meanings, such as "I will mow Bob's lawn for 10" and "I owe + Bob1,000,000", and ask Alice to sign the first, innocuous contract. + This attack is easier for Bob, since finding two such messages by + brute force will take pow(2, b/2) operations on average. However, + Alice can protect herself by changing the protocol; she can simply + append a random string to the contract before hashing and signing it; + the random string can then be kept with the signature. + + None of the algorithms implemented here have been completely broken. + There are no attacks on MD2, but it's rather slow at 376 K/sec. MD4 is + faster at 11946 K/sec but there have been some partial attacks on it. + MD4 operates in three iterations of a basic mixing operation; two of + the three rounds have been cryptanalyzed, but the attack can't be + extended to the full algorithm. MD5 is a strengthened version of MD4 + with four rounds; an attack against one round has been found. XXX + Dobbertin's attack. Because MD5 is more commonly used, the + implementation is better optimized and thus faster on x86 processors + (27193 K/sec). MD4 may be faster than MD5 when other processors and + compilers are used. + + All the MD algorithms produce 128-bit hashes; SHA produces a larger + 160-bit hash, and there are no known attacks against it. The first + version of SHA had a weakness which was later corrected; the code used + here implements the second, corrected, version. It operates at 13157 + K/sec. RIPEMD also has a 160-bit output, and operates at 5869 K/sec. + HAVAL is a variable-size hash function; it can generate hash values + that are 128, 160, 192, 224, or 256 bits in size, and can use 3, 4, or + 5 rounds. 5-round HAVAL runs at 5371 K/sec. + +2.3 Credits + + The MD2, MD4, and HAVAL implementations were written by A.M. Kuchling, + and the MD5 code was implemented by Colin Plumb. The SHA code was + originally written by Peter Gutmann. The RIPEMD code was written by + Antoon Bosselaers, and adapted for the toolkit by Hirendra Hindocha. + + 3. Crypto.Cipher: Encryption Algorithms + + Encryption algorithms transform their input data (called plaintext) in + some way that is dependent on a variable key, producing ciphertext; + this transformation can easily be reversed, if (and, hopefully, only + if) one knows the key. The key can be varied by the user or + application, chosen from some very large space of possible keys. + + For a secure encryption algorithm, it should be very difficult to + determine the original plaintext without knowing the key; usually, no + clever attacks on the algorithm are known, so the only way of breaking + the algorithm is to try all possible keys. Since the number of + possible keys is usually of the order of 2 to the power of 56 or 128, + this is not a serious threat, although 2 to the power of 56 is now + considered insecure in the face of custom-built parallel computers and + distributed key guessing efforts. + + Block ciphers take multibyte inputs of a fixed size (frequently 8 or + 16 bytes long) and encrypt them. Block ciphers can be operated in + various modes. The simplest is Electronic Code Book (or ECB) mode. In + this mode, each block of plaintext is simply encrypted to produce the + ciphertext. This mode can be dangerous, because many files will + contain patterns greater than the block size; for example, the + comments in a C program may contain long strings of asterisks intended + to form a box. All these identical blocks will encrypt to identical + ciphertext; an adversary may be able to use this structure to obtain + some information about the text. + + To eliminate this weakness, there are various feedback modes, where + the plaintext is combined with the previous ciphertext before + encrypting; this eliminates any such structure. One mode is Cipher + Block Chaining (CBC mode); another is Cipher FeedBack (CFB mode). CBC + mode still encrypts in blocks, and thus is only slightly slower than + ECB mode. CFB mode encrypts on a byte-by-byte basis, and is much + slower than either of the other two modes. The chaining feedback modes + require an initialization value to start off the encryption; this is a + string of the same length as the ciphering algorithm's block size, and + is passed to the new() function. + + There is also a special PGP mode, which is a variant of CFB used by + the PGP program. While you can use it in non-PGP programs, it's quite + non-standard. + + The currently available block ciphers are listed in the following + table, and are available in the Crypto.Cipher package: + + Cipher Key Size/Block Size + ARC2 Variable/8 bytes + Blowfish Variable/8 bytes + CAST Variable/8 bytes + DES 8 bytes/8 bytes + DES3 (Triple DES) 16 bytes/8 bytes + Diamond Variable/16 bytes + IDEA 16 bytes/8 bytes + RC5 Variable/8 bytes + + In a strict formal sense, stream ciphers encrypt data bit-by-bit; + practically, stream ciphers work on a character-by-character basis. + Stream ciphers use exactly the same interface as block ciphers, with a + block length that will always be 1; this is how block and stream + ciphers can be distinguished. The only feedback mode available for + stream ciphers is ECB mode. + + The currently available stream ciphers are listed in the following + table: + + Cipher Key Size + Cipher Key Size + ARC4 Variable + Sapphire Variable + + ARC4 is short for `Alleged RC4'. The real RC4 algorithm is proprietary + to RSA Data Security Inc. In September of 1994, someone posted C code + to both the Cypherpunks mailing list and to the Usenet newsgroup + sci.crypt, claiming that it implemented the RC4 algorithm. This posted + code is what I'm calling Alleged RC4, or ARC4 for short. I don't know + if ARC4 is in fact RC4, but ARC4 has been subjected to scrutiny on the + Cypherpunks mailing list and elsewhere, and does not seem to be easily + breakable. The legal issues surrounding the use of ARC4 are unclear, + but be aware that it hasn't been subject to much scrutiny, and may + have some critical flaw that hasn't yet been discovered. The same is + true of ARC2, which was posted in January, 1996. + + An example usage of the DES module: + + +>>> from Crypto.Cipher import DES +>>> obj=DES.new('abcdefgh', DES.ECB) +>>> plain="Guido van Rossum is a space alien." +>>> len(plain) +34 +>>> obj.encrypt(plain) +Traceback (innermost last): + File "<stdin>", line 1, in ? +ValueError: Strings for DES must be a multiple of 8 in length +>>> ciph=obj.encrypt(plain+'XXXXXX') +>>> ciph +'\021,\343Nq\214DY\337T\342pA\372\255\311s\210\363,\300j\330\250\312\347\342I\3 +215w\03561\303dgb/\006' +>>> obj.decrypt(ciph) +'Guido van Rossum is a space alien.XXXXXX' + + All cipher algorithms share a common interface. After importing a + given module, there is exactly one function and two variables + available. + + new (key, mode[, IV]) + Returns a ciphering object, using key and feedback mode mode. + If mode is CBC or CFB, IV must be provided, and must be a + string of the same length as the block size. Some algorithms + support additional keyword arguments to this function; see the + "Algorithm-specific Notes for Encryption Algorithms" section + below for the details. + + blocksize + An integer value; the size of the blocks encrypted by this + module. Strings passed to the encrypt and decrypt functions + must be a multiple of this length. For stream ciphers, + blocksize will be 1. + + keysize + An integer value; the size of the keys required by this module. + If keysize is zero, then the algorithm accepts arbitrary-length + keys. You cannot pass a key of length 0 (that is, the null + string '' as such a variable-length key. + + All cipher objects have at least three attributes: + + blocksize + An integer value equal to the size of the blocks encrypted by + this object. Identical to the module variable of the same name. + + IV + Contains the initial value which will be used to start a cipher + feedback mode. After encrypting or decrypting a string, this + value will reflect the modified feedback text; it will always + be one block in length. It is read-only, and cannot be assigned + a new value. + + keysize + An integer value equal to the size of the keys used by this + object. If keysize is zero, then the algorithm accepts + arbitrary-length keys. For algorithms that support variable + length keys, this will be 0. Identical to the module variable + of the same name. + + All ciphering objects have the following methods: + + decrypt (string) + Decrypts string, using the key-dependent data in the object, + and with the appropriate feedback mode. The string's length + must be an exact multiple of the algorithm's block size. + Returns a string containing the plaintext. + + encrypt (string) + Encrypts a non-null string, using the key-dependent data in the + object, and with the appropriate feedback mode. The string's + length must be an exact multiple of the algorithm's block size; + for stream ciphers, the string can be of any length. Returns a + string containing the ciphertext. + +3.1 Algorithm-specific Notes for Encryption Algorithms + + The Diamond block cipher allows you to select the number of rounds to + apply, ranging from 5 to 15 (inclusive.) This is set via the rounds + keyword argument to the new() function; the default value is 8 rounds. + + RC5 has even more parameters; see Ronald Rivest's paper at + [31]http://theory.lcs.mit.edu/~rivest/rc5rev.ps for the implementation + details. The keyword parameters are: + + * version: The version of the RC5 algorithm to use; currently the + only legal value is 0x10 for RC5 1.0. + * wordsize: The word size to use; 16 or 32 are the only legal + values. (A larger word size is better, so usually 32 will be used. + 16-bit RC5 is probably only of academic interest.) + * rounds: The number of rounds to apply, the larger the more secure: + this can be any value from 0 to 255, so you will have to choose a + value balanced between speed and security. + +3.2 Security Notes + + Encryption algorithms can be broken in several ways. If you have some + ciphertext and know (or can guess) the corresponding plaintext, you + can simply try every possible key in a known-plaintext attack. Or, it + might be possible to encrypt text of your choice using an unknown key; + for example, you might mail someone a message intending it to be + encrypted and forwarded to someone else. This is a chosen-plaintext + attack, which is particularly effective if it's possible to choose + plaintexts that reveal something about the key when encrypted. + + DES (2458 K/sec) has a 56-bit key; this is starting to become too + small for safety. It has been estimated that it would only cost + $1,000,000 to build a custom DES-cracking machine that could find a + key in 3 hours. A chosen-ciphertext attack using the technique of + linear cryptanalysis can break DES in pow(2, 43) steps. However, + unless you're encrypting data that you want to be safe from major + governments, DES will be fine. DES3 (509 K/sec) uses three DES + encryptions for greater security and a 112-bit or 168-bit key, but is + correspondingly slower. + + There are no publicly known attacks against IDEA (2005 K/sec), and + it's been around long enough to have been examined. There are no known + attacks against ARC2 (1644 K/sec), ARC4 (4630 K/sec), Blowfish (5323 + K/sec), CAST (1314 K/sec), Diamond (1670 K/sec), RC5 (1370 K/sec), or + Sapphire (3568 K/sec), but they're all relatively new algorithms and + there hasn't been time for much analysis to be performed; use them for + serious applications only after careful research. Skipjack (XXX) is + relatively slow, and its 80-bit key size is secure at the moment, but + doesn't leave much of a margin of safety; only use it if you believe + that the NSA has mystical cipher design abilities. + +3.3 Credits + + The code for Blowfish was written by Bryan Olson, partially based on a + previous implementation by Bruce Schneier, who also invented the + algorithm; the Blowfish algorithm has been placed in the public domain + and can be used freely. (See [32]http://www.counterpane.com for more + information about Blowfish.) The CAST implementation was written by + Wim Lewis. The DES implementation was written by Eric Young, and the + IDEA implementation by Colin Plumb. The RC5 implementation was written + by A.M. Kuchling. + + The Alleged RC4 code was posted to the sci.crypt newsgroup by an + unknown party, and re-implemented by A.M. Kuchling. The Sapphire + stream cipher was developed by Michael P. Johnson, and is in the + public domain; the implementation used here was written by A.M. + Kuchling and is based on Johnson's code. + + 4. Crypto.Protocol: Various Protocols + +4.1 Crypto.Protocol.AllOrNothing + + This module implements all-or-nothing package transformations. An + all-or-nothing package transformation is one in which some text is + transformed into message blocks, such that all blocks must be obtained + before the reverse transformation can be applied. Thus, if any blocks + are corrupted or lost, the original message cannot be reproduced. + + An all-or-nothing package transformation is not encryption, although a + block cipher algorithm is used. The encryption key is randomly + generated and is extractable from the message blocks. + + This class implements the All-Or-Nothing package transformation + algorithm described in Rivest: ``All-Or-Nothing Encryption and The + Package Transform.'' To appear in the Proceedings of the 1997 Fast + Software Encryption Conference. + http://theory.lcs.mit.edu/ rivest/fusion.ps + + AllOrNothing (ciphermodule, mode=None, IV=None) + Class implementing the All-or-Nothing package transform. + + ciphermodule is a module implementing the cipher algorithm to + use. Optional arguments mode and IV are passed directly through + to the ciphermodule.new() method; they are the feedback mode + and initialization vector to use. All three arguments must be + the same for the object used to create the digest, and to + undigest'ify the message blocks. + + The module passed as ciphermodule must provide the following + interface: + + ciphermodule.keysize: Attribute containing the cipher + algorithm's key size in bytes. If the cipher supports variable + length keys, then typically ciphermodule.keysize will be zero. + In that case a key size of 16 bytes will be used. + + ciphermodule.blocksize: Attribute containing the cipher + algorithm's input block size in bytes. + + ciphermodule.new(key, mode, IV): Function which returns a new + instance of a cipher object, initialized to key. The returned + object must have an encrypt() method that accepts a string of + ciphermodule.blocksize bytes and returns a string containing + the encrypted text. + + Note that the encryption key is randomly generated + automatically when needed. + + The methods of the AllorNothing class are: + + digest () + Perform the All-or-Nothing package transform on the current + string. Output is a list of message blocks describing the + transformed text, where each block is a string of bit length + equal to the cipher module's blocksize. + + reset (text = "") + Reset the current string to be transformed to text. + + undigest (mblocks) + Perform the reverse package transformation on a list of message + blocks. Note that the cipher module used for both + transformations must be the same. mblocks is a list of strings + of bit length equal to ciphermodule's blocksize. The output is + a string object. + + update (text) + Concatenate text to the string that will be transformed. + +4.2 Crypto.Protocol.Chaffing + + Winnowing and chaffing is a technique for enhancing privacy without + requiring strong encryption. In short, the technique takes a set of + authenticated message blocks (the wheat) and adds a number of chaff + blocks which have randomly chosen data and MAC fields. This means that + to an adversary, the chaff blocks look as valid as the wheat blocks, + and so the authentication would have to be performed on every block. + By tailoring the number of chaff blocks added to the message, the + sender can make breaking the message computationally infeasible. There + are many other interesting properties of the winnow/chaff technique. + + For example, say Alice is sending a message to Bob. She packetizes the + message and performs an all-or-nothing transformation on the packets. + Then she authenticates each packet with a message authentication code + (MAC). The MAC is a hash of the data packet, and there is a secret key + which she must share with Bob (key distribution is an exercise left to + the reader). She then adds a serial number to each packet, and sends + the packets to Bob. + + Bob receives the packets, and using the shared secret authentication + key, authenticates the MACs for each packet. Those packets that have + bad MACs are simply discarded. The remainder are sorted by serial + number, and passed through the reverse all-or-nothing transform. The + transform means that an eavesdropper (say Eve) must acquire all the + packets before any of the data can be read. If even one packet is + missing, the data is useless. + + There's one twist: by adding chaff packets, Alice and Bob can make + Eve's job much harder, since Eve now has to break the shared secret + key, or try every combination of wheat and chaff packet to read any of + the message. The cool thing is that Bob doesn't need to add any + additional code; the chaff packets are already filtered out because + their MACs don't match (in all likelihood - since the data and MACs + for the chaff packets are randomly chosen it is possible, but very + unlikely that a chaff MAC will match the chaff data). And Alice need + not even be the party adding the chaff! She could be completely + unaware that a third party, say Charles, is adding chaff packets to + her messages as they are transmitted. + + For more information on winnowing and chaffing see this paper: + + XXX Rivest. + + Chaff (factor=1.0, blocksper=1) + Class implementing the chaff adding algorithm. factor is the + number of message blocks to add chaff to, expressed as a + percentage between 0.0 and 1.0; the default value is 1.0. + blocksper is the number of chaff blocks to include for each + block being chaffed, and defaults to 1. The default settings + add one chaff block to every message block. By changing the + defaults, you can adjust how computationally difficult it could + be for an adversary to brute-force crack the message. The + difficulty is expressed as: + + +pow(blocksper, int(factor * number-of-blocks)) + + For ease of implementation, when factor < 1.0, only the first + int(factor*number-of-blocks) message blocks are chaffed. + + Chaff instances have the following methods: + + chaff (blocks) + Add chaff to message blocks. blocks is a list of 3-tuples of + the form (serial-number, data, MAC). + + Chaff is created by choosing a random number of the same + byte-length as data, and another random number of the same + byte-length as MAC. The message block's serial number is placed + on the chaff block and all the packet's chaff blocks are + randomly interspersed with the single wheat block. This method + then returns a list of 3-tuples of the same form. Chaffed + blocks will contain multiple instances of 3-tuples with the + same serial number, but the only way to figure out which blocks + are wheat and which are chaff is to perform the MAC hash and + compare values. + + Subclass methods: + + __randnum (size) + Returns a randomly generated number with a byte-length equal to + size. Subclasses can use this to implement better random data + and MAC generating algorithms. The default algorithm is + probably not very cryptographically secure. It is most + important that the chaff data does not contain any patterns + that can be used to discern it from wheat data without running + the MAC. + + 5. Crypto.PublicKey: Public Key Algorithms + + So far, the encryption algorithms described have all been private key + ciphers. That is, the same key is used for both encryption and + decryption, so all correspondents must know it. This poses a problem: + you may want encryption to communicate sensitive data over an insecure + channel, but how can you tell your correspondent what the key is? You + can't just e-mail it to her because the channel is insecure. One + solution is to arrange the key via some other way: over the phone or + by meeting in person. + + Another solution is to use public key cryptography. In a public key + system, there are two different keys: one for encryption and one for + decryption. The encryption key can be made public by listing it in a + directory or mailing it to your correspondent, while you keep the + decryption key secret. Your correspondent then sends you data + encrypted with your public key, and you use the private key to decrypt + it. While the two keys are related, it's very difficult to derive the + private key given only the public key; however, deriving the private + key is always possible given enough time and computing power. This + makes it very important to pick keys of the right size: large enough + to be secure, but small enough to be applied fairly quickly. + + Many public key algorithms can also be used to sign messages; simply + run the message to be signed through a decryption with your private + key key. Anyone receiving the message can encrypt it with your + publicly available key and read the message. Some algorithms do only + one thing, others can both encrypt and authenticate. + + The currently available public key algorithms are listed in the + following table: + + Algorithm Capabilities + RSA Encryption, authentication/signatures + ElGamal Encryption, authentication/signatures + DSA Authentication/signatures + qNEW Authentication/signatures + + Many of these algorithms are patented. Before using any of them in a + commercial product, consult a patent attorney; you may have to arrange + a license with the patent holder. + + An example of using the RSA module to sign a message: + + +>>> from Crypto.Hash import MD5 +>>> from Crypto.PublicKey import RSA +>>> RSAkey=RSA.generate(384, randfunc) # This will take a while... +>>> hash=MD5.new(plaintext).digest() +>>> signature=RSAkey.sign(hash, "") +>>> signature # Print what an RSA sig looks like--you don't really care. +('\021\317\313\336\264\315' ...,) +>>> RSAkey.verify(hash, signature) # This sig will check out +1 +>>> RSAkey.verify(hash[:-1], signature)# This sig will fail +0 + + Public key modules make the following functions available: + + construct (tuple) + Constructs a key object from a tuple of data. This is + algorithm-specific; look at the source code for the details. + (To be documented later.) + + generate (size, randfunc, progress_func=None) + Generate a fresh public/private key pair. size is a + algorithm-dependent size parameter; the larger it is, the more + difficult it will be to break the key. Safe key sizes vary from + algorithm to algorithm; you'll have to research the question + and decide on a suitable key size for your application. + randfunc is a random number generation function; it should + accept a single integer N and return a string of random data N + bytes long. You should always use a cryptographically secure + random number generator, such as the one defined in the + randpool module; don't just use the current time and the + whrandom module. + + progress_func is an optional function that will be called with + a short string containing the key parameter currently being + generated; it's useful for interactive applications where a + user is waiting for a key to be generated. + + If you want to interface with some other program, you will have to + know the details of the algorithm being used; this isn't a big loss. + If you don't care about working with non-Python software, simply use + the pickle module when you need to write a key or a signature to a + file. It's portable across all the architectures that Python supports, + and it's simple to use. + + Public key objects always support the following methods. Some of them + may raise exceptions if their functionality is not supported by the + algorithm. + + canencrypt () + Returns true if the algorithm is capable of encrypting and + decrypting data; returns false otherwise. To test if a given + key object can sign data, use key.canencrypt() and + key.hasprivate(). + + cansign () + Returns true if the algorithm is capable of signing data; + returns false otherwise. To test if a given key object can sign + data, use key.cansign() and key.hasprivate(). + + decrypt (tuple) + Decrypts tuple with the private key, returning another string. + This requires the private key to be present, and will raise an + exception if it isn't present. It will also raise an exception + if string is too long. + + encrypt (string, K) + Encrypts string with the private key, returning a tuple of + strings; the length of the tuple varies from algorithm to + algorithm. K should be a string of random data that is as long + as possible. Encryption does not require the private key to be + present inside the key object. It will raise an exception if + string is too long. For ElGamal objects, the value of K + expressed as a big-endian integer must be relatively prime to + self.p-1; an exception is raised if it is not. + + hasprivate () + Returns true if the key object contains the private key data, + which will allow decrypting data and generating signatures. + Otherwise this returns false. + + publickey () + Returns a new public key object that doesn't contain the + private key data. + + sign (string, K) + Sign string, returning a signature, which is just a tuple; in + theory the signature may be made up of any Python objects at + all; in practice they'll be either strings or numbers. K should + be a string of random data that is as long as possible. + Different algorithms will return tuples of different sizes. + sign() raises an exception if string is too long. For ElGamal + objects, the value of K expressed as a big-endian integer must + be relatively prime to self.p-1; an exception is raised if it + is not. + + size () + Returns the maximum size of a string that can be encrypted or + signed, measured in bits. String data is treated in big-endian + format; the most significant byte comes first. (This seems to + be a de facto standard for cryptographical software.) If the + size is not a multiple of 8, then some of the high order bits + of the first byte must be zero. Usually it's simplest to just + divide the size by 8 and round down. + + verify (string, signature) + Returns true if the signature is valid, and false otherwise. + string is not processed in any way; verify does not run a hash + function over the data, but you can easily do that yourself. + +5.1 The ElGamal and DSA algorithms + + For RSA, the K parameters are unused; if you like, you can just pass + empty strings. The ElGamal and DSA algorithms require a real K value + for technical reasons; see Schneier's book for a detailed explanation + of the respective algorithms. This presents a possible hazard that can + inadvertently reveal the private key. Without going into the + mathematical details, the danger is as follows. K is never derived or + needed by others; theoretically, it can be thrown away once the + encryption or signing operation is performed. However, revealing K for + a given message would enable others to derive the secret key data; + worse, reusing the same value of K for two different messages would + also enable someone to derive the secret key data. An adversary could + intercept and store every message, and then try deriving the secret + key from each pair of messages. + + This places implementors on the horns of a dilemma. On the one hand, + you want to store the K values to avoid reusing one; on the other + hand, storing them means they could fall into the hands of an + adversary. One can randomly generate K values of a suitable length + such as 128 or 144 bits, and then trust that the random number + generator probably won't produce a duplicate anytime soon. This is an + implementation decision that depends on the desired level of security + and the expected usage lifetime of a private key. I cannot choose and + enforce one policy for this, so I've added the K parameter to the + encrypt and sign functions. You must choose K by generating a string + of random data; for ElGamal, when interpreted as a big-endian number + (with the most significant byte being the first byte of the string), K + must be relatively prime to self.p-1; any size will do, but brute + force searches would probably start with small primes, so it's + probably good to choose fairly large numbers. It might be simplest to + generate a prime number of a suitable length using the + Crypto.Util.number module. + +5.2 Security Notes for Public-key Algorithms + + Any of these algorithms can be trivially broken; for example, RSA can + be broken by factoring the modulus n into its two prime factors. This + is easily done by the following code: + + +for i in range(2, n): + if (n%i)==0: print i, 'is a factor' ; break + + However, n is usually a few hundred bits long, so this simple program + wouldn't find a solution before the universe comes to an end. Smarter + algorithms can factor numbers more quickly, but it's still possible to + choose keys so large that they can't be broken in a reasonable amount + of time. For ElGamal and DSA, discrete logarithms are used instead of + factoring, but the principle is the same. + + Safe key sizes depend on the current state of computer science and + technology. At the moment, one can roughly define three levels of + security: low-security commercial, high-security commercial, and + military-grade. For RSA, these three levels correspond roughly to 512, + 768, and 1024 bit-keys. For ElGamal and DSA, the key sizes should be + somewhat larger for the same level of security, around 768, 1024, and + 1536 bits. + + 6. Crypto.Util: Odds and Ends + + This chapter contains all the modules that don't fit into any of the + other chapters. + +6.1 Crypto.Util.number + + This module contains various functions of number-theoretic functions. + + GCD (x,y) + Return the greatest common divisor of x and y. + + getPrime (N, randfunc) + Return an N-bit random prime number, using random data obtained + from the function randfunc. randfunc must take a single integer + argument, and return a string of random data of the + corresponding length; the getBytes() method of a RandomPool + object will serve the purpose nicely, as will the read() method + of an opened file such as "/dev/random". + + getRandomNumber (N, randfunc) + Return an N-bit random number, using random data obtained from + the function randfunc. As usual, randfunc must take a single + integer argument, and return a string of random data of the + corresponding length. + + inverse (u, v) + Return the inverse of u modulo v. + + isPrime (N) + Returns true if the number N is prime, as determined by a + Rabin-Miller test. + +6.2 Crypto.Util.randpool + + For cryptographic purposes, ordinary random number generators are + frequently insufficient, because if some of their output is known, it + is frequently possible to derive the generator's future (or past) + output. This is obviously a Bad Thing; given the generator's state at + some point in time, someone could try to derive any keys generated + using it. The solution is to use strong encryption or hashing + algorithms to generate successive data; this makes breaking the + generator as difficult as breaking the algorithms used. + + Understanding the concept of entropy is important for using the random + number generator properly. In the sense we'll be using it, entropy + measures the amount of randomness; the usual unit is in bits. So, a + single random bit has an entropy of 1 bit; a random byte has an + entropy of 8 bits. Now consider a one-byte field in a database + containing a person's sex, represented as a single character "M" or + "F". What's the entropy of this field? Since there are only two + possible values, it's not 8 bits, but two; if you were trying to guess + the value, you wouldn't have to bother trying "Q" or "@". + + Now imagine running that single byte field through a hash function + that produces 128 bits of output. Is the entropy of the resulting hash + value 128 bits? No, it's still just 1 bit. The entropy is a measure of + how many possible states of the data exist. For English text, the + entropy of a five-character string is not 40 bits; it's somewhat less, + because not all combinations would be seen. "Guido" is a possible + string, as is "In th"; "zJwvb" is not. + + The relevance to random number generation? We want enough bits of + entropy to avoid making an attack on our generator possible. An + example: One computer system had a mechanism which generated nonsense + passwords for its users. This is a good idea, since it would prevent + people from choosing their own name or some other easily guessed + string. Unfortunately, the random number generator used only had 65536 + states, which meant only 65536 different passwords would ever be + generated, and it was easily to compute all the possible passwords and + try them. The entropy of the random passwords was far too low. By the + same token, if you generate an RSA key with only 32 bits of entropy + available, there are only about 4.2 billion keys you could have + generated, and an adversary could compute them all to find your + private key. See RFC 1750: "Randomness Recommendations for Security" + for an interesting discussion of the issues related to random number + generation. + + The randpool module implements a strong random number generator in the + RandomPool class. The internal state consists of a string of random + data, which is returned as callers request it. The class keeps track + of the number of bits of entropy left, and provides a function to add + new random data; this data can be obtained in various ways, such as by + using the variance in a user's keystroke timings. + + RandomPool ([numbytes, cipher, hash]) + An object of the RandomPool class can be created without + parameters if desired. numbytes sets the number of bytes of + random data in the pool, and defaults to 160 (1280 bits). hash + can be a string containing the module name of the hash function + to use in stirring the random data, or a module object + supporting the hashing interface. The default action is to use + SHA. + + The cipher argument is vestigial; it was removed from version + 1.1 so RandomPool would work even in the limited exportable + subset of the code. It can have any value at all, since it's no + longer used at all. + + RandomPool objects define the following variables and methods: + + addEvent (time[, string]) + Adds an event to the random pool. time should be set to the + current system time, measured at the highest resolution + available. string can be a string of data that will be XORed + into the pool, and can be used to increase the entropy of the + pool. For example, if you're encrypting a document, you might + use the hash value of the document; an adversary presumably + won't have the plaintext of the document, and thus won't be + able to use this information to break the generator. + + The return value is the value of self.entropy after the data has been + added. The function works in the following manner: the time between + successive calls to the addEvent method is determined, and the entropy + of the data is guessed; the larger the time between calls, the better. + The system time is then read and added to the pool, along with the + string parameter, if present. The hope is that the low-order bits of + the time are effectively random. In an application, it is recommended + that addEvent() be called as frequently as possible, with whatever + random data can be found. + + bits + A constant integer value containing the number of bits of data + in the pool, equal to the bytes variable multiplied by 8. + + bytes + A constant integer value containing the number of bytes of data + in the pool. + + entropy + An integer value containing the number of bits of entropy + currently in the pool. The value is incremented by the + addEvent() method, and decreased by the getBytes method. + + getBytes (num) + Returns a string containing num bytes of random data, and + decrements the amount of entropy available. It is not an error + to reduce the entropy to zero, or to call this function when + the entropy is zero. This simply means that, in theory, enough + random information has been extracted to derive the state of + the generator. It is the caller's responsibility to monitor the + amount of entropy remaining and decide whether it is sufficent + for secure operation. + + stir () + Scrambles the random pool using the previously chosen + encryption and hash function. An adversary may attempt to learn + or alter the state of the pool in order to affect its future + output; this function destroys the existing state of the pool + in a non-reversible way. It is recommended that stir() be + called before and after using the RandomPool object. Even + better, several calls to stir() can be interleaved with calls + to addEvent(). + + The KeyboardRandomPool class is a subclass of RandomPool that adds the + capability to save and load the pool from a disk file, and provides a + method to obtain random data from the keyboard. + + KeyboardRandomPool ([filename, numbytes, cipher, hash]) + The path given in filename will be automatically opened, and an + existing random pool read; if no such file exists, the pool + will be initialized as usual. If omitted, the filename defaults + to the empty string, which will prevent it from being saved to + a file. The other arguments are identical to those for the + RandomPool constructor. + + randomize () + (Unix systems only) Obtain random data from the keyboard. This + works by prompting the user to hit keys at random, and then + using the keystroke timings (and also the actual keys pressed) + to add entropy to the pool. This works similarly to PGP's + random pool mechanism. + + save () + Opens the file named by the filename attribute, and saves the + random data into the file using the pickle module. + +6.3 Crypto.Util.RFC1751 + + The keys for private-key algorithms should be arbitrary binary data. + Many systems err by asking the user to enter a password, and then + using the password as the key. This limits the space of possible keys, + as each key byte is constrained within the range of possible ASCII + characters, 32-127, instead of the whole 0-255 range possible with + ASCII. Unfortunately, it's difficult for humans to remember 16 or 32 + hex digits. + + One solution is to request a lengthy passphrase from the user, and + then run it through a hash function such as SHA or MD5. Another + solution is discussed in RFC 1751, "A Convention for Human-Readable + 128-bit Keys", by Daniel L. McDonald. Binary keys are transformed into + a list of short English words that should be easier to remember. For + example, the hex key EB33F77EE73D4053 is transformed to "TIDE ITCH + SLOW REIN RULE MOT". + + Key2English (key) + Accepts a string of arbitrary data key, and returns a string + containing uppercase English words separated by spaces. key's + length must be a multiple of 8. + + English2Key (string) + Accepts string containing English words, and returns a string + of binary data representing the key. Words must be separated by + whitespace, and can be any mixture of uppercase and lowercase + characters. 6 words are required for 8 bytes of key data, so + the number of words in string must be a multiple of 6. + + 7. The Demonstration Programs + + The Python cryptography modules comes with various demonstration + programs, located in the "Demo/" directory. None of them is + particularly well-finished, or suitable for serious use. Rather, + they're intended to illustrate how the toolkit is used, and to provide + some interesting possible uses. Feel free to incorporate the code (or + modifications of it) into your own programs. + +7.1 Demo 1: "cipher" + + "cipher" encrypts and decrypts files. On most Unix systems, the + "crypt" program uses a variant of the Enigma cipher. This is not + secure, and there exists a freely available program called ``Crypt + Breaker's Workbench'' which helps in breaking the cipher if you have + some knowledge of the encrypted data. + + "cipher" is a more secure file encryption program. Simply list the + names of the files to be encrypted on the command line. "cipher" will + go through the list and encrypt or decrypt them; "cipher" can + recognize files it has previously encrypted. The ciphertext of a file + is placed in a file of the same name with '".cip"' appended; the + original file is not deleted, since I'm not sure that all errors + during operation are caught, and I don't want people to accidentally + erase important files. + + There are two command-line options: -c and -k. Both of them require an + argument. -c ciphername uses the given encryption algorithm + ciphername; for example, -c des will use the DES algorithm. The name + should be the same as an available module name; thus it should be in + lowercase letters. The default cipher is IDEA. + + -k key can be used to set the encryption key to be used. Note that on + a multiuser Unix system, the ps command can be used to view the + arguments of commands executed by other users, so this is insecure; if + you're the only user (say, on your home computer running Linux) you + don't have to worry about this. If no key is set on the command line, + "cipher" will prompt the user to input a key on standard input. + + 7.1.1 Technical Details + + The encrypted file is not pure ciphertext. First comes a magic string; + this is currently the sequence "ctx" and a byte containing 1 (the + version number of "cipher"). This is followed by the null-terminated + name of the encryption algorithm, and the rest of the file contains + the ciphertext. + + The plaintext is encrypted in CBC mode. The initial value for the + feedback is always set to a block filled with the letter 'A', and then + a block of random data is encrypted. This garbage block will be + discarded on decryption. Note that the random data is not generated in + a cryptographically secure way, and this may provide a tiny foothold + for an attacker. + + After the random block is generated, the magic string, length of the + original file, and original filename are all encrypted before the file + data is finally processed. Some extra characters of padding may be + added to obtain an integer number of blocks. This padding will also be + discarded on decryption. Note that the plaintext file will be + completely read into memory before encryption is performed; no + buffering is done. Therefore, don't encrypt 20-megabyte files unless + you're willing to face the consequences of a 20-megabyte process. + + Areas for improvements to "cipher" are: cryptographically secure + generation of random data for padding, key entry, and buffering of + file input. + +7.2 Demo 2: "secimp" and "sign" + + "secimp" demonstrates an application of the Toolkit that may be useful + if Python is being used as an extension language for mail and Web + clients: secure importing of Python modules. To use it, run "sign.py" + in a directory with several compiled Python files present. It will use + the key in "testkey.py" to generate digital signatures for the + compiled Python code, and save both the signature and the code in a + file ending in ".pys". Then run python -i secimp.py, and import a file + by using secimport. + + For example, if "foo.pys" was constructed, do secimport('foo'). The + import should succeed. Now fire up Emacs or some other editor, and + change a string in the code in "foo.pys"; you might try changing a + letter in the name of a variable. When you run secimport('foo'), it + should raise an exception reporting the failed signature. If you + execute the statement __import__ = secimport, the secure import will + be used by default for all future module imports. Alternatively, if + you were creating a restricted execution environment using "rexec.py", + you could place secimport() in the restricted environment's namespace + as the default import function. + + 8. Extending the cryptography modules + + Preserving the a common interface for cryptographic routines is a good + idea. This chapter explains how to interface your own routines to the + Toolkit. + + The basic process is as follows: + 1. + Modify the default definition of a C structure to include + whatever instance data your algorithm requires. + 2. + Write 3 or 4 standard routines. Their names and parameters are + specified in the following subsections. + 3. + Modify "buildkit" to contain an entry for your new algorithm. + Then run "buildkit" to rebuild all the source files. + 4. + Send a copy of the code to me, if you like; code for new + algorithms will be gratefully accepted. + +8.1 Creating a Custom Object + + In the C code for the interpreter, Python objects are defined as a + structure. The default structure is the following: + + +typedef struct +{ + PCTObject_HEAD +} ALGobject; + + PCTObject_HEAD is a preprocessor macro which will contain various + internal variables used by the interpreter; it must always be the + first item in the structure definition, and must not be followed by a + semicolon. Following it, you can put whatever instance variables you + require. Data that does not depend on the instance or key, such as a + static lookup table, need not be encapsulated inside objects; instead, + it can be defined as a variable interior to the module. + + As an example, for IDEA encryption, a schedule of encryption and + decryption data has to be maintained, resulting in the following + definition: + + +typedef struct +{ + PCTObject_HEAD + int EK[6][9], DK[6][9]; +} IDEAobject; + +8.2 Standard Routines + + The interface to Python is implemented in the files ending in ".in", + so "hash.in" contains the basic code for modules containing hash + functions, for example. "buildkit", a Python script, reads the + configuration file and generates source code by interweaving the + interface files and the implementation file. + + If your algorithm is called ALG, the implementation should be in the + file "ALG.c". This is case-sensitive, as are the following function + names. + + 8.2.1 Hash functions + + * void ALGinit(ALGobject *self); + * void ALGupdate(ALGobject *self, char *buffer, int length); + * PyObject *ALGdigest(ALGobject *self); + * void ALGcopy(ALGobject *source, ALGobject *dest); + + void ALGinit ( ALGobject *self) + This function should initialize the hashing object, setting + state variables to their expected initial state. + + void ALGupdate ( ALGobject *self, char *buffer, int length) + This function should perform a hash on the region pointed to by + buffer, which will contain length bytes. The contents of the + object pointed to by self should be updated appropriately. + + void ALGdigest ( ALGobject *self) + This function returns a string containing the value of the hash + function. The object should not be changed in any way by this + function. Some hash functions require some computation to be + performed before returning a value; for example, the number of + bytes may be hashed into the final value. If this is the case + for your hash function, you must make a copy of the object's + data, perform the final computation on that copy, and return + the result. + + Results are returned by calling a Python function, + PyString_FromStringAndSize(char *string, int length). This function + returns a string object which should be returned to the caller. So, + the last line of the ALGdigest function might be: + + + return PyString_FromStringAndSize(digest, 16); + + void ALGcopy ( ALGobject *source, ALGobject *dest) + Given the source and destination objects, the state variables + of the source object should be copied to the dest object; the + source object should not be altered in any way by the + operation. + + 8.2.2 Block ciphers + + * void ALGinit(ALGobject *self, unsigned char *key, int length); + * PyObject *ALGencrypt(ALGobject *self, unsigned char *block); + * PyObject *ALGdecrypt(ALGobject *self, unsigned char *block); + + void ALGinit ( ALGobject *self, unsigned char *key, int length) + This function initializes a block cipher object to encrypt and + decrypt with key. If the cipher requires a fixed-length key, + then the buffer pointed to by key will always of that length, + and the value of length will be a random value that should be + ignored. If the algorithm accepts a variable-length key, then + length will be nonzero, and will contain the size of the key. + + void ALGencrypt ( ALGobject *self, unsigned char *block) + This function should encrypt the data pointed to by block, + using the key-dependent data contained in self. Only ECB mode + needs to be implemented; block.in takes care of the other + ciphering modes. + + void ALGdecrypt ( ALGobject *self, unsigned char *block) + This function should decrypt the data pointed to by block, + using the key-dependent data contained in self. + +8.3 Portability macros + + Implementation code must be carefully written to produce the same + results with any machine or compiler, without having to set any + compile-time definitions. Code that is simply portable by nature is + preferable, but it is possible to detect features of the host machine + when new objects are created, and then execute special code to convert + data to a preferred form. + + While portability macros are written for speed, there's no need to + execute them on every encryption or updating operation. Instead, add + variables to your object to hold the values of the portability macros, + and execute the macros only once per object, in your ALGinit function. + Then the code can simply check the results of the macros and act + appropriately. + + Currently there is only one portability macro defined: + + void TestEndianness (variable) + Determines the endianness of the current machine, and sets + variable to a constant representing the value for this machine. + Possible constants are PCT_BIG_ENDIAN and PCT_LITTLE_ENDIAN; + they are defined along with the TestEndianness macro. + +8.4 Informing the author + + Code for additional cryptographic algorithms can be mailed to me at + akuchling@acm.org. You can make things much easier for me by doing the + following: + * If you wrote the code, please release it into the public domain or + under the MIT X11 license. If you didn't write it, please tell me + where to find the original author or source code, so that I can + check the licensing conditions. + * Include some test data. It is not sufficient to check that + encryption and decryption cancel out properly. An implementation + might work fine on a single platform, but two machines with + different endianness might produce different results. This would + be fatal for portability and interoperating programs. So, please + include test data; you can either send me patches to "test.py", or + simply send me documents describing the data. + + About this document ... + + The Python Cryptography Modules + + This document was generated using the [33]LaTeX2HTML translator + Version 98.2beta (June 26th, 1998) + + Copyright © 1993, 1994, 1995, 1996, 1997, [34]Nikos Drakos, Computer + Based Learning Unit, University of Leeds. + + The command line arguments were: + latex2html -init_file + /home/akuchlin/src/Python-1.5/Doc/perl/l2hinit.perl -link 3 -split 1 + -dir pycrypt ./pycrypt.tex. + + The translation was initiated by on 1998-07-13 + _________________________________________________________________ + +References + + 1. file://localhost/tmp/pycrypt.html + 2. file://localhost/tmp/pycrypt.html#SECTION000210000000000000000 + 3. file://localhost/tmp/pycrypt.html#SECTION000220000000000000000 + 4. file://localhost/tmp/pycrypt.html#SECTION000300000000000000000 + 5. file://localhost/tmp/pycrypt.html#SECTION000310000000000000000 + 6. file://localhost/tmp/pycrypt.html#SECTION000320000000000000000 + 7. file://localhost/tmp/pycrypt.html#SECTION000330000000000000000 + 8. file://localhost/tmp/pycrypt.html#SECTION000400000000000000000 + 9. file://localhost/tmp/pycrypt.html#SECTION000410000000000000000 + 10. file://localhost/tmp/pycrypt.html#SECTION000420000000000000000 + 11. file://localhost/tmp/pycrypt.html#SECTION000430000000000000000 + 12. file://localhost/tmp/pycrypt.html#SECTION000500000000000000000 + 13. file://localhost/tmp/pycrypt.html#SECTION000510000000000000000 + 14. file://localhost/tmp/pycrypt.html#SECTION000520000000000000000 + 15. file://localhost/tmp/pycrypt.html#SECTION000600000000000000000 + 16. file://localhost/tmp/pycrypt.html#SECTION000610000000000000000 + 17. file://localhost/tmp/pycrypt.html#SECTION000620000000000000000 + 18. file://localhost/tmp/pycrypt.html#SECTION000700000000000000000 + 19. file://localhost/tmp/pycrypt.html#SECTION000710000000000000000 + 20. file://localhost/tmp/pycrypt.html#SECTION000720000000000000000 + 21. file://localhost/tmp/pycrypt.html#SECTION000730000000000000000 + 22. file://localhost/tmp/pycrypt.html#SECTION000800000000000000000 + 23. file://localhost/tmp/pycrypt.html#SECTION000810000000000000000 + 24. file://localhost/tmp/pycrypt.html#SECTION000820000000000000000 + 25. file://localhost/tmp/pycrypt.html#SECTION000900000000000000000 + 26. file://localhost/tmp/pycrypt.html#SECTION000910000000000000000 + 27. file://localhost/tmp/pycrypt.html#SECTION000920000000000000000 + 28. file://localhost/tmp/pycrypt.html#SECTION000930000000000000000 + 29. file://localhost/tmp/pycrypt.html#SECTION000940000000000000000 + 30. http://www.counterpane.com/ + 31. http://theory.lcs.mit.edu/~rivest/rc5rev.ps + 32. http://www.counterpane.com/ + 33. http://www-dsed.llnl.gov/files/programs/unix/latex2html/manual/ + 34. http://cbl.leeds.ac.uk/nikos/personal.html diff --git a/Hash/HAVAL.c b/Hash/HAVAL.c new file mode 100644 index 0000000..612937d --- /dev/null +++ b/Hash/HAVAL.c @@ -0,0 +1,398 @@ + +/* + * haval.c : Implementation code for the HAVAL hash function + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +#define VERSION 1 /* Version of HAVAL algorithm */ + +static int Endianness=-1; + +typedef unsigned int U32; +typedef unsigned char U8; + +#define rotate(x,n) (((x) >> (n)) | ((x) << (32-(n)))) +#define f1(x6,x5,x4,x3,x2,x1,x0) ((x1&x4)^(x2&x5)^(x3&x6)^(x0&x1)^(x0)) +#define f2(x6,x5,x4,x3,x2,x1,x0) (((x4&x5)|x2) ^ (x0|x2) ^ x2&(x1&(~x3)^x6)\ + ^ x3&x5 ^ x1&x4) +#define f3(x6,x5,x4,x3,x2,x1,x0) ((x1&x2&x3) ^ (x1&x4) ^ (x2&x5) ^ (x3&x6) \ + ^ (x0&x3) ^ (x0)) +#define f4(x6,x5,x4,x3,x2,x1,x0) ((((~x2&x5)^(x3|x6)^x1^x0)&x4) ^ ((x1&x2^x5^x6)&x3) ^ (x2&x6) ^ x0) +#define f5(x6,x5,x4,x3,x2,x1,x0) ((((x0&x2&x3)^x4)&x1) ^ ((x0^x2)&x5) ^ (x3&x6) ^ x0) + +static int W2[32]={5,14,26,18,11,28,7,16,0,23,20,22,1,10,4,8, + 30,3,21,9,17,24,29,6,19,12,15,13,2,25,31,27}; +static int W3[32]={19,9,4,20,28,17,8,22,29,14,25,12,24,30,16,26, + 31,15,7,3,1,0,18,27,13,6,21,10,23,11,5,2}; +static int W4[32]={24,4,0,14,2,7,28,23,26,6,30,20,18,25,19,3, + 22,11,31,21,8,27,12,9,1,29,5,15,17,10,16,13}; +static int W5[32]={27,3,21,26,17,11,20,29,19,0,12,7,13,8,31,10, + 5,9,14,30,18,6,28,24,2,23,16,22,4,1,25,15}; +static U32 K2[32]={0x452821E6U, 0x38D01377U, 0xBE5466CFU, 0x34E90C6CU, 0xC0AC29B7U, + 0xC97C50DDU, 0x3F84D5B5U, 0xB5470917U, 0x9216D5D9U, 0x8979FB1BU, + 0xD1310BA6U, 0x98DFB5ACU, 0x2FFD72DBU, 0xD01ADFB7U, 0xB8E1AFEDU, + 0x6A267E96U, 0xBA7C9045U, 0xF12C7F99U, 0x24A19947U, 0xB3916CF7U, + 0x0801F2E2U, 0x858EFC16U, 0x636920D8U, 0x71574E69U, 0xA458FEA3U, + 0xF4933D7EU, 0x0D95748FU, 0x728EB658U, 0x718BCD58U, 0x82154AEEU, + 0x7B54A41DU, 0xC25A59B5}; +static U32 K3[32]={0x9C30D539U, 0x2AF26013U, 0xC5D1B023U, 0x286085F0U, 0xCA417918U, + 0xB8DB38EFU, 0x8E79DCB0U, 0x603A180EU, 0x6C9E0E8BU, 0xB01E8A3EU, + 0xD71577C1U, 0xBD314B27U, 0x78AF2FDAU, 0x55605C60U, 0xE65525F3U, + 0xAA55AB94U, 0x57489862U, 0x63E81440U, 0x55CA396AU, 0x2AAB10B6U, + 0xB4CC5C34U, 0x1141E8CEU, 0xA15486AFU, 0x7C72E993U, 0xB3EE1411U, + 0x636FBC2AU, 0x2BA9C55DU, 0x741831F6U, 0xCE5C3E16U, 0x9B87931EU, + 0xAFD6BA33U, 0x6C24CF5CU}; +static U32 K4[32]={0x7A325381U, 0x28958677U, 0x3B8F4898U, 0x6B4BB9AFU, 0xC4BFE81BU, 0x66282193U, 0x61D809CCU, 0xFB21A991U, 0x487CAC60U, 0x5DEC8032U, 0xEF845D5DU, 0xE98575B1U, 0xDC262302U, 0xEB651B88U, 0x23893E81U, 0xD396ACC5U, 0x0F6D6FF3U, 0x83F44239U, 0x2E0B4482U, 0xA4842004U, 0x69C8F04AU, 0x9E1F9B5EU, 0x21C66842U, 0xF6E96C9AU, 0x670C9C61U, 0xABD388F0U, 0x6A51A0D2U, 0xD8542F68U, 0x960FA728U, 0xAB5133A3U, 0x6EEF0B6CU, 0x137A3BE4U}; +static U32 K5[32]={0xBA3BF050U, 0x7EFB2A98U, 0xA1F1651DU, 0x39AF0176U, 0x66CA593EU, 0x82430E88U, 0x8CEE8619U, 0x456F9FB4U, 0x7D84A5C3U, 0x3B8B5EBEU, 0xE06F75D8U, 0x85C12073U, 0x401A449FU, 0x56C16AA6U, 0x4ED3AA62U, 0x363F7706U, 0x1BFEDF72U, 0x429B023DU, 0x37D0D724U, 0xD00A1248U, 0xDB0FEAD3U, 0x49F1C09BU, 0x075372C9U, 0x80991B7BU, 0x25D479D8U, 0xF6E8DEF7U, 0xE3FE501AU, 0xB6794C3BU, 0x976CE0BDU, 0x04C006BAU, 0xC1A94FB6U, 0x409F60C4U}; + +enum ePASS {P3=3, P4=4, P5=5}; +enum eFPTLEN {L128=128, L160=160, L192=192, L224=224, L256=256}; + +typedef struct { + PCTObject_HEAD + U32 D[8], msglen1, msglen2; + U8 buf[128]; + int buflen; + enum eFPTLEN digestsize; + enum ePASS rounds; +} HAVALobject; + + +static void HV_Hash(s, D, buf) + HAVALobject *s; + U32 *D; + U8 *buf; +{ + U32 temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; + U32 w[32]; + int i, j; + + for(i=0, j=0; i<32; i++,j+=4) + w[i]=buf[j]+(buf[j+1]<<8)+(buf[j+2]<<16)+(buf[j+3]<<24); + + /* e1 = H1(D, B) */ + + temp0=D[0]; temp1=D[1]; temp2=D[2]; temp3=D[3]; temp4=D[4]; temp5=D[5]; temp6=D[6]; temp7=D[7]; +#ifdef DEBUG + printf("start: %08x %08x %08x %08x %08x %08x %08x %08x\n", + temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7); +#endif + for(i=0; i<32; i++) + { + U32 P = 0, R; + + switch(s->rounds) + { + case(P3): + P=f1(temp1, temp0, temp3, temp5, temp6, temp2, temp4); + break; + case(P4): + P=f1(temp2, temp6, temp1, temp4, temp5, temp3, temp0); + break; + case(P5): + P=f1(temp3, temp4, temp1, temp0, temp5, temp2, temp6); + break; + } + R=rotate(P,7) + rotate(temp7,11) + w[i]; + temp7=temp6; temp6=temp5; temp5=temp4; temp4=temp3; + temp3=temp2; temp2=temp1; temp1=temp0; temp0=R; + } + +#ifdef DEBUG + printf(" h1: %08x %08x %08x %08x %08x %08x %08x %08x\n", + temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7); +#endif + /* e2 = H2(e1, B) */ + + for(i=0; i<32; i++) + { + U32 P,R; + switch(s->rounds) + { + case(P3): + P=f2(temp4, temp2, temp1, temp0, temp5, temp3, temp6); + break; + case(P4): + P=f2(temp3, temp5, temp2, temp0, temp1, temp6, temp4); + break; + case(P5): + P=f2(temp6, temp2, temp1, temp0, temp3, temp4, temp5); + break; + } + R=rotate(P, 7) + rotate(temp7, 11) + K2[i]+ w[W2[i]]; + temp7=temp6; temp6=temp5; temp5=temp4; temp4=temp3; + temp3=temp2; temp2=temp1; temp1=temp0; temp0=R; + } +#ifdef DEBUG + printf(" h2: %08x %08x %08x %08x %08x %08x %08x %08x\n", + temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7); +#endif + + /* e3 = H3(e2, B) */ + + for(i=0; i<32; i++) + { + U32 P,R; + switch(s->rounds) + { + case(P3): + P=f3(temp6, temp1, temp2, temp3, temp4, temp5, temp0); + break; + case(P4): + P=f3(temp1, temp4, temp3, temp6, temp0, temp2, temp5); + break; + case(P5): + P=f3(temp2, temp6, temp0, temp4, temp3, temp1, temp5); + break; + } + R=rotate(P, 7) + rotate(temp7, 11) + K3[i]+ w[W3[i]]; + temp7=temp6; temp6=temp5; temp5=temp4; temp4=temp3; + temp3=temp2; temp2=temp1; temp1=temp0; temp0=R; + } + +#ifdef DEBUG + printf(" h3: %08x %08x %08x %08x %08x %08x %08x %08x\n", + temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7); +#endif + + if (s->rounds==P3) /* Final output */ + { + D[0]+=temp0; D[1]+=temp1; D[2]+=temp2; D[3]+=temp3; + D[4]+=temp4; D[5]+=temp5; D[6]+=temp6; D[7]+=temp7; + return; + } + + /* e4 = H4(e3, B) */ + for(i=0; i<32; i++) + { + U32 P,R; + switch(s->rounds) + { + case(P4): + P=f4(temp6, temp4, temp0, temp5, temp2, temp1, temp3); + break; + case(P5): + P=f4(temp1, temp5, temp3, temp2, temp0, temp4, temp6); + break; + } + R=rotate(P, 7) + rotate(temp7, 11) + K4[i]+ w[W4[i]]; + temp7=temp6; temp6=temp5; temp5=temp4; temp4=temp3; + temp3=temp2; temp2=temp1; temp1=temp0; temp0=R; + } + +#ifdef DEBUG + printf(" h4: %08x %08x %08x %08x %08x %08x %08x %08x\n", + temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7); +#endif + if (s->rounds==P4) /* Final output */ + { + D[0]+=temp0; D[1]+=temp1; D[2]+=temp2; D[3]+=temp3; + D[4]+=temp4; D[5]+=temp5; D[6]+=temp6; D[7]+=temp7; + return; + } + + /* e5 = H5(e4, B) */ + + for(i=0; i<32; i++) + { + U32 P,R; + P=f5(temp2, temp5, temp0, temp6, temp4, temp3, temp1); + R=rotate(P, 7) + rotate(temp7, 11) + K5[i]+ w[W5[i]]; + temp7=temp6; temp6=temp5; temp5=temp4; temp4=temp3; + temp3=temp2; temp2=temp1; temp1=temp0; temp0=R; + } +#ifdef DEBUG + printf(" h5: %08x %08x %08x %08x %08x %08x %08x %08x\n", + temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7); +#endif + D[0]+=temp0; D[1]+=temp1; D[2]+=temp2; D[3]+=temp3; + D[4]+=temp4; D[5]+=temp5; D[6]+=temp6; D[7]+=temp7; + return; +} + +static void HAVALinit (s) + HAVALobject *s; /* context */ +{ + if (Endianness==-1) TestEndianness(Endianness); + if (s->rounds<P3 || P5<s->rounds) + { + PyErr_SetString(PyExc_ValueError, + "HAVAL: rounds must be 3, 4, or 5"); + return; + } + if (s->digestsize!=L128 && s->digestsize!=L160 && s->digestsize!=L192 && + s->digestsize!=L224 && s->digestsize!=L256) + { + PyErr_SetString(PyExc_ValueError, + "HAVAL: digest size must be 128, 160, 192, 224, or 256"); + return; + } + s->buflen=0; + s->msglen1=s->msglen2=0; + s->D[0]=0x243f6a88U; + s->D[1]=0x85a308d3U; + s->D[2]=0x13198a2eU; + s->D[3]=0x03707344U; + s->D[4]=0xa4093822U; + s->D[5]=0x299f31d0U; + s->D[6]=0x082efa98U; + s->D[7]=0xec4e6c89U; +} + +static void HAVALupdate (s, buf, len) +HAVALobject *s; +U8 *buf; /* input block */ +unsigned int len; /* length of input block */ +{ + int temp; + + temp=s->msglen2; + s->msglen2+=len*8; + if (s->msglen2<temp) /* Did an overflow occur on the 32-bit msg length? */ + { + s->msglen1++; + } + while (len>0) + { + temp = ((128-s->buflen)>len) ? len : 128-s->buflen; + memcpy(s->buf+s->buflen, buf, temp); + len-=temp; + buf+=temp; + s->buflen+=temp; + if (s->buflen==128) + { + HV_Hash(s, s->D, s->buf); s->buflen=0; + } + } +} + +static PyObject *HAVALdigest (s) + HAVALobject *s; +{ + int i, j; + U32 value, D[8]; + U8 buf[128]; + + for(i=0; i<8; i++) D[i]=s->D[i]; + memcpy(buf, s->buf, s->buflen); + i=s->buflen; + buf[i++]=128; + if (i>944/8) + { + memset(buf+i, 0, 128-i); + HV_Hash(s, D, buf); + i=0; + } + memset(buf+i, 0, (944/8)-i); + i=944/8; + value=VERSION | (s->rounds<<3) | (s->digestsize<<6); + buf[i++]=value & 0xff; + buf[i++]=(value >> 8) & 0xff; + + /* Append the message length */ + buf[i++]=(s->msglen2 ) & 0xff; + buf[i++]=(s->msglen2 >> 8 ) & 0xff; + buf[i++]=(s->msglen2 >> 16 ) & 0xff; + buf[i++]=(s->msglen2 >> 24 ) & 0xff; + + buf[i++]=(s->msglen1 ) & 0xff; + buf[i++]=(s->msglen1 >> 8 ) & 0xff; + buf[i++]=(s->msglen1 >> 16 ) & 0xff; + buf[i++]=(s->msglen1 >> 24 ) & 0xff; + + HV_Hash(s, D, buf); + + /* Tailor the output to the appropriate length */ +/* 256-byte output + D[0]=0xac869783U; D[1]=0x6aee894fU; D[2]=0x65207fe8U; D[3]=0x971068acU; + D[4]=0x996a27d7U; D[5]=0x72e9b703U; D[6]=0x6d5692e9U; D[7]=0xc8ee30dU; */ +/* 128-byte output + D[0]=0xd7b8185dU; D[1]=0x59d1f164U; D[2]=0x87bba84dU; D[3]=0x4b7f55dcU; + D[4]=0xbcc5be6bU; D[5]=0xbbc32906U; D[6]=0x9dabd130U; D[7]=0x3bde9293U; */ + + if (s->digestsize==128) + { + D[3] += (D[7] & 0xff000000U) | (D[6] & 0x00ff0000U) | + (D[5] & 0x0000ff00U) | (D[4] & 0x000000ffU); + D[2] += (( + (D[7] & 0x00ff0000U) | (D[6] & 0x0000ff00U) | + (D[5] & 0x000000ffU) + )<<8) | ((D[4] & 0xff000000U)>>24); + D[1] += (( + (D[7] & 0x0000ff00U) | (D[6] & 0x000000ffU) + )<<16) | + (( + (D[5] & 0xff000000U) | (D[4] & 0x00ff0000U) + )>>16); + D[0] += + (( + D[7] & 0x000000ffU + )<<24) | + (( + (D[6] & 0xff000000U) | (D[5] & 0x00ff0000U) | + (D[4] & 0x0000ff00U) + )>>8); + } + if (s->digestsize==L160) + { + D[4] += ((D[7] & 0xfe000000U) | (D[6] & 0x01f80000U) | + (D[5] & 0x0007f000U)) >>12; + D[3] += ((D[7] & 0x01f80000U) | (D[6] & 0x0007f000U) | + (D[5] & 0x00000fc0U)) >> 6; + D[2] += ((D[7] & 0x0007f000U) | (D[6] & 0x00000fc0U) | + (D[5] & 0x0000003fU)) ; + D[1] += (((D[7] & 0x00000fc0U) | (D[6] & 0x0000003fU)) <<7) | + ((D[5] & 0xfe000000U)>>25); + D[0] += (((D[6] & 0xfe000000U) | (D[5] & 0x01f80000U)) >>19) | + ((D[7] & 0x0000003fU)<<13); + } + if (s->digestsize==L192) + { + D[5] += ((D[7] & 0xfc000000U) | (D[6] & 0x03e00000U)) >> 21; + D[4] += ((D[7] & 0x03e00000U) | (D[6] & 0x001f0000U)) >> 16; + D[3] += ((D[7] & 0x001f0000U) | (D[6] & 0x0000fc00U)) >> 10; + D[2] += ((D[7] & 0x0000fc00U) | (D[6] & 0x000003e0U)) >> 5; + D[1] += ((D[7] & 0x000003e0U) | (D[6] & 0x0000001fU)) ; + D[0] += ((D[7] & 0x0000001fU)<<6) | ((D[6] & 0xfc000000U) >>26); + } + if (s->digestsize==L224) + { + D[6] += (D[7] & 0x0000000fU); + D[5] += (D[7] & 0x000001f0U) >> 4; + D[4] += (D[7] & 0x00001e00U) >> 9; + D[3] += (D[7] & 0x0003e000U) >> 13; + D[2] += (D[7] & 0x003c0000U) >> 18; + D[1] += (D[7] & 0x07c00000U) >> 22; + D[0] += (D[7] & 0xf8000000U) >> 27; + } + for(i=0,j=0; i<s->digestsize/32; i++) + { + buf[j++]=D[i] & 0xff; D[i] >>= 8; + buf[j++]=D[i] & 0xff; D[i] >>= 8; + buf[j++]=D[i] & 0xff; D[i] >>= 8; + buf[j++]=D[i] & 0xff; + } + return PyString_FromStringAndSize((unsigned char *)buf, s->digestsize/8); +} + + +static void +HAVALcopy(src, dest) + HAVALobject *src, *dest; +{ + int i; + memcpy(dest->buf, src->buf, src->buflen); + dest->buflen =src->buflen; + dest->msglen1=src->msglen1; + dest->msglen2=src->msglen2; + dest->rounds =src->rounds; + dest->digestsize =src->digestsize; + + for(i=0; i<8; i++) dest->D[i]=src->D[i]; +} diff --git a/Hash/HMAC.py b/Hash/HMAC.py new file mode 100644 index 0000000..f1eb8bd --- /dev/null +++ b/Hash/HMAC.py @@ -0,0 +1,141 @@ +"""This file implements message authentication (HMAC). + +A MAC is a message authentication code, which is essentially a fancy hash of +some input text. The text is sent along with the MAC and the receiver can +verify the text by computing their own MAC and comparing results. The sender +and receiver share a secret key which is used by HMAC. + +HMAC is defined in the informational RFC 2104, which describes a procedure for +authenticating messages using pluggable hash functions. The user of HMAC must +choose which hash function to use (e.g. SHA-1 or MD5) and this is designated +by HMAC-SHA or HMAC-MD5. See the RFC for details. + +This package can be integrated with Andrew Kuchling's Python Cryptography +Toolkit, http://starship.skyport.net/crew/amk/maintained/crypto.html + +""" + +from Crypto.Util.number import bytestolong, longtobytes + + + +class HMAC: + """Class implementing HMAC as defined in RFC 2104. + + Public Methods: + + __init__(hashmodule) + Constructor for the class. hashmodule is a module implementing + the hashing algorithm to use. In essence it must provide the + following interface: + + hashmodule.digestsize + The length of the hash's output in bytes + + hashmodule.new(key) + Function which returns a new instance of a hash object, + initialized to key. The returned object must have a digest() + method that returns a string of size hashmodule.digestsize, + and an update() method that accepts strings to add to the + digest. + + hash(key, blocks): + Produce the HMAC hashes for the given blocks. Key is the shared + secret authentication key, as a string. For best results RFC 2104 + recommends that the length of key should be at least as large as + the underlying hash's output block size, but this is not + enforced. + + If the key length is greater than the hash algorithm's basic + compression function's block size (typically 64 bytes), then it is + hashed to get the used key value. If it is less than this block + size, it is padded by appending enough zero bytes to the key. + + blocks is a list of strings to generate message authentication + codes for. Output is a list of strings containing the MACs. + + """ + def __init__(self, hashmodule): + self.__hashmodule = hashmodule + + __IPAD = 0x36 + __OPAD = 0x5c + + def hash(self, key, blocks): + # L is the byte length of hash outputs. + # B is the byte length of hash algorithm's basic compression + # function's block size (64 for most hashes) + # + # Sanitize the key. RFC 2104 recommends key length be at least L and + # if it is longer than B, it should be hashed and the resulting L + # bytes will be used as the key + # + L = self.__hashmodule.digestsize + B = 64 # can't get from module + keylen = len(key) + if keylen > B: + key = self.__hashmodule.new(key).digest() + keylen = len(key) + assert keylen == L + elif keylen < B: + # append enough zeros to get it to length B + key = key + '\000' * (B - keylen) + keylen = len(key) + # + # Precompute the inner and outer intermediate values + kipad = bytestolong(key) ^ bytestolong(chr(self.__IPAD) * keylen) + kopad = bytestolong(key) ^ bytestolong(chr(self.__OPAD) * keylen) + kipad = longtobytes(kipad) + kopad = longtobytes(kopad) + # + # perform the inner hashes + inners = [] + for text in blocks: + hash = self.__hashmodule.new(kipad) + hash.update(text) + inners.append(hash.digest()) + # + # preform the outer hashes + outers = [] + for inner in inners: + hash = self.__hashmodule.new(kopad) + hash.update(inner) + outers.append(hash.digest()) + return outers + + + +if __name__ == '__main__': + from types import StringType + + # Test data taken from RFC 2104 + testdata = [ + # (key, data, digest) + (0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0bL, + 'Hi There', + 0x9294727a3638bb1c13f48ef8158bfc9dL), + ("Jefe", + "what do ya want for nothing?", + 0x750c783e6ab0b503eaa86e310a5db738L), + (0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL, + 0xDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDL, + 0x56be34521d144c88dbb8c733f0e8b3f6L), + ] + + # RFC 2104 uses MD5 + from Crypto.Hash import MD5 + for key, data, digest in testdata: + if type(key) <> StringType: + key = longtobytes(key) + if type(data) <> StringType: + data = longtobytes(data) + + h = HMAC(MD5) + d = h.hash(key, [data]) + d = bytestolong(d[0]) + if d == digest: + print 'They match!' + else: + print 'They differ...' + print ' expected:', hex(digest) + print ' got:', hex(d) diff --git a/Hash/MD2.c b/Hash/MD2.c new file mode 100644 index 0000000..66f912f --- /dev/null +++ b/Hash/MD2.c @@ -0,0 +1,120 @@ + +/* + * md2.c : MD2 hash algorithm. + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + + +#include <string.h> + +typedef unsigned char U8; +typedef unsigned int U32; + +typedef struct { + PCTObject_HEAD + U8 C[16], X[48]; + int count; + U8 buf[16]; +} MD2object; + +static void MD2init (ptr) + MD2object *ptr; +{ + memset(ptr->X, 0, 48); + memset(ptr->C, 0, 16); + ptr->count=0; +} + +static U8 S[256] = { + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, + 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, + 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, + 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, + 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, + 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, + 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, + 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, + 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, + 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, + 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, + 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, + 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, + 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, + 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, + 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, + 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, + 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 +}; + +static void +MD2copy(src, dest) + MD2object *src, *dest; +{ + dest->count=src->count; + memcpy(dest->buf, src->buf, dest->count); + memcpy(dest->X, src->X, 48); + memcpy(dest->C, src->C, 16); +} + + +static void MD2update (self, buf, len) +MD2object *self; +const U8 *buf; +U32 len; +{ + U32 L; + while (len) + { + L=(16-self->count) < len ? (16-self->count) : len; + memcpy(self->buf+self->count, buf, L); + self->count+=L; + buf+=L; + len-=L; + if (self->count==16) + { + U8 t; + int i,j; + + self->count=0; + memcpy(self->X+16, self->buf, 16); + t=self->C[15]; + for(i=0; i<16; i++) + { + self->X[32+i]=self->X[16+i]^self->X[i]; + t=self->C[i]^=S[self->buf[i]^t]; + } + + t=0; + for(i=0; i<18; i++) + { + for(j=0; j<48; j++) + t=self->X[j]^=S[t]; + t=(t+i) & 0xFF; + } + } + } +} + +static PyObject * +MD2digest (self) + const MD2object *self; +{ + U8 padding[16]; + U32 padlen; + MD2object temp; + int i; + + memcpy(&temp, self, sizeof(MD2object)); + padlen= 16-self->count; + for(i=0; i<padlen; i++) padding[i]=padlen; + MD2update(&temp, padding, padlen); + MD2update(&temp, temp.C, 16); + return PyString_FromStringAndSize(temp.X, 16); +} + diff --git a/Hash/MD4.c b/Hash/MD4.c new file mode 100644 index 0000000..f79fc33 --- /dev/null +++ b/Hash/MD4.c @@ -0,0 +1,203 @@ + +/* + * md4.c : MD4 hash algorithm. + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + + +#include <string.h> + +typedef unsigned int U32; +typedef unsigned char U8; +#define U32_MAX (U32)4294967295 + +typedef struct { + PCTObject_HEAD + U32 A,B,C,D, count; + U32 len1, len2; + U8 buf[64]; +} MD4object; + +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROL(x, n) (((x) << n) | ((x) >> (32-n) )) + +static void MD4init (ptr) + MD4object *ptr; +{ + ptr->A=(U32)0x67452301; + ptr->B=(U32)0xefcdab89; + ptr->C=(U32)0x98badcfe; + ptr->D=(U32)0x10325476; + ptr->count=ptr->len1=ptr->len2=0; +} + +static void +MD4copy(src, dest) + MD4object *src, *dest; +{ + dest->len1=src->len1; + dest->len2=src->len2; + dest->A=src->A; + dest->B=src->B; + dest->C=src->C; + dest->D=src->D; + dest->count=src->count; + memcpy(dest->buf, src->buf, dest->count); +} + +static void MD4update (self, buf, len) +MD4object *self; +const U8 *buf; +U32 len; +{ + U32 L; + + if ((self->len1+(len<<3))<self->len1) + { + self->len2++; + } + self->len1+=len<< 3; + self->len2+=len>>29; + while (len>0) + { + L=(64-self->count) < len ? (64-self->count) : len; + memcpy(self->buf+self->count, buf, L); + self->count+=L; + buf+=L; + len-=L; + if (self->count==64) + { + U32 X[16], A, B, C, D; + int i,j; + self->count=0; + for(i=j=0; j<16; i+=4, j++) + X[j]=((U32)self->buf[i] + ((U32)self->buf[i+1]<<8) + + ((U32)self->buf[i+2]<<16) + ((U32)self->buf[i+3]<<24)); + + + A=self->A; B=self->B; C=self->C; D=self->D; + +#define function(a,b,c,d,k,s) a=ROL(a+F(b,c,d)+X[k],s); + function(A,B,C,D, 0, 3); + function(D,A,B,C, 1, 7); + function(C,D,A,B, 2,11); + function(B,C,D,A, 3,19); + function(A,B,C,D, 4, 3); + function(D,A,B,C, 5, 7); + function(C,D,A,B, 6,11); + function(B,C,D,A, 7,19); + function(A,B,C,D, 8, 3); + function(D,A,B,C, 9, 7); + function(C,D,A,B,10,11); + function(B,C,D,A,11,19); + function(A,B,C,D,12, 3); + function(D,A,B,C,13, 7); + function(C,D,A,B,14,11); + function(B,C,D,A,15,19); + +#undef function +#define function(a,b,c,d,k,s) a=ROL(a+G(b,c,d)+X[k]+(U32)0x5a827999,s); + function(A,B,C,D, 0, 3); + function(D,A,B,C, 4, 5); + function(C,D,A,B, 8, 9); + function(B,C,D,A,12,13); + function(A,B,C,D, 1, 3); + function(D,A,B,C, 5, 5); + function(C,D,A,B, 9, 9); + function(B,C,D,A,13,13); + function(A,B,C,D, 2, 3); + function(D,A,B,C, 6, 5); + function(C,D,A,B,10, 9); + function(B,C,D,A,14,13); + function(A,B,C,D, 3, 3); + function(D,A,B,C, 7, 5); + function(C,D,A,B,11, 9); + function(B,C,D,A,15,13); + +#undef function +#define function(a,b,c,d,k,s) a=ROL(a+H(b,c,d)+X[k]+(U32)0x6ed9eba1,s); + function(A,B,C,D, 0, 3); + function(D,A,B,C, 8, 9); + function(C,D,A,B, 4,11); + function(B,C,D,A,12,15); + function(A,B,C,D, 2, 3); + function(D,A,B,C,10, 9); + function(C,D,A,B, 6,11); + function(B,C,D,A,14,15); + function(A,B,C,D, 1, 3); + function(D,A,B,C, 9, 9); + function(C,D,A,B, 5,11); + function(B,C,D,A,13,15); + function(A,B,C,D, 3, 3); + function(D,A,B,C,11, 9); + function(C,D,A,B, 7,11); + function(B,C,D,A,15,15); + + self->A+=A; self->B+=B; self->C+=C; self->D+=D; + } + } +} + +static PyObject * +MD4digest (self) + const MD4object *self; +{ + U8 digest[16]; + static U8 s[8]; + U32 padlen, oldlen1, oldlen2; + MD4object temp; + static U8 padding[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + memcpy(&temp, self, sizeof(MD4object)); + oldlen1=temp.len1; oldlen2=temp.len2; /* Save current length */ + padlen= (56<=self->count) ? 56-self->count+64: 56-self->count; + MD4update(&temp, padding, padlen); + s[0]= oldlen1 & 255; + s[1]=(oldlen1 >> 8) & 255; + s[2]=(oldlen1 >> 16) & 255; + s[3]=(oldlen1 >> 24) & 255; + s[4]= oldlen2 & 255; + s[5]=(oldlen2 >> 8) & 255; + s[6]=(oldlen2 >> 16) & 255; + s[7]=(oldlen2 >> 24) & 255; + MD4update(&temp, s, 8); + + digest[ 0]= temp.A & 255; + digest[ 1]=(temp.A >> 8) & 255; + digest[ 2]=(temp.A >> 16) & 255; + digest[ 3]=(temp.A >> 24) & 255; + digest[ 4]= temp.B & 255; + digest[ 5]=(temp.B >> 8) & 255; + digest[ 6]=(temp.B >> 16) & 255; + digest[ 7]=(temp.B >> 24) & 255; + digest[ 8]= temp.C & 255; + digest[ 9]=(temp.C >> 8) & 255; + digest[10]=(temp.C >> 16) & 255; + digest[11]=(temp.C >> 24) & 255; + digest[12]= temp.D & 255; + digest[13]=(temp.D >> 8) & 255; + digest[14]=(temp.D >> 16) & 255; + digest[15]=(temp.D >> 24) & 255; + + return PyString_FromStringAndSize(digest, 16); +} + diff --git a/Hash/MD5.c b/Hash/MD5.c new file mode 100644 index 0000000..ec5e030 --- /dev/null +++ b/Hash/MD5.c @@ -0,0 +1,289 @@ + +/* + * md5.c : Implementation of the MD5 hash function + * + * Part of the Python Cryptography Toolkit, version 1.1 + * Colin Plumb's original code modified by A.M. Kuchling + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ +#if HAVE_PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + +typedef unsigned int uint32; + +typedef struct { + PCTObject_HEAD + uint32 buf[4]; + uint32 bits[2]; + int Endianness; + unsigned char in[64]; +} MD5object; + +static void MD5Transform PROTO_LIST((uint32 buf[4], uint32 const in[16])); +static void MD5init PROTO_LIST((MD5object *)); +static void MD5update PROTO_LIST((MD5object *, unsigned char *, unsigned int)); +static PyObject *MD5digest PROTO_LIST((MD5object *)); + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + */ +#include <string.h> /* for memcpy() */ + +#define byteReverse(ptr, field, num) if (ptr->Endianness==PCT_BIG_ENDIAN)\ +{uint32 t, longs=num; unsigned char *buf=(unsigned char*)&(ptr->field);\ + do {\ + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |\ + ((unsigned) buf[1] << 8 | buf[0]);\ + *(uint32 *) buf = t;\ + buf += 4;\ + } while (--longs);\ +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void MD5init(ptr) + MD5object *ptr; +{ + ptr->buf[0] = 0x67452301; + ptr->buf[1] = 0xefcdab89; + ptr->buf[2] = 0x98badcfe; + ptr->buf[3] = 0x10325476; + + ptr->bits[0] = 0; + ptr->bits[1] = 0; + TestEndianness(ptr->Endianness) +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void MD5update(ptr, buf, len) + MD5object *ptr; + unsigned char *buf; + unsigned int len; +{ + uint32 t; + + /* Update bitcount */ + + t = ptr->bits[0]; + if ((ptr->bits[0] = t + ((uint32) len << 3)) < t) + ptr->bits[1]++; /* Carry from low to high */ + ptr->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ptr->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ptr, in, 16); + MD5Transform(ptr->buf, (uint32 *) ptr->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ptr->in, buf, 64); + byteReverse(ptr, in, 16); + MD5Transform(ptr->buf, (uint32 *) ptr->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ptr->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ + +static PyObject * +MD5digest(ptr) + MD5object *ptr; +{ + unsigned count; + unsigned char *p; + MD5object temp; + + memcpy(&temp, ptr, sizeof(MD5object)); + /* Compute number of bytes mod 64 */ + count = (temp.bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = temp.in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse((&temp), in, 16); + MD5Transform(temp.buf, (uint32 *) temp.in); + + /* Now fill the next block with 56 bytes */ + memset(temp.in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse((&temp), in, 14); + + /* Append length in bits and transform */ + ((uint32 *) temp.in)[14] = temp.bits[0]; + ((uint32 *) temp.in)[15] = temp.bits[1]; + + MD5Transform(temp.buf, (uint32 *) temp.in); + byteReverse((&temp), buf, 4); + + return PyString_FromStringAndSize((unsigned char *)temp.buf, 16); +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(buf, in) + uint32 buf[4]; + const uint32 in[16]; +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +static void +MD5copy(src, dest) + MD5object *src, *dest; +{ + dest->Endianness = src->Endianness; + memcpy(dest->in, src->in, 64); + memcpy(&(dest->bits), &(src->bits), 2*sizeof(uint32)); + memcpy(&(dest->buf), &(src->buf), 4*sizeof(uint32)); +} + + + + diff --git a/Hash/RIPEMD.c b/Hash/RIPEMD.c new file mode 100644 index 0000000..5fc3dde --- /dev/null +++ b/Hash/RIPEMD.c @@ -0,0 +1,519 @@ +/* header files */ + +/********************************************************************\ + * FILE: rmd160.c + * CONTENTS: A sample C-implementation of the RIPEMD-160 hash-function. + * TARGET: any computer with an ANSI C compiler + * AUTHOR: Antoon Bosselaers, Dept. Electrical Eng.-ESAT/COSIC + * DATE: 1 March 1996 VERSION: 1.0 + ********************************************************************** + * Copyright (c) Katholieke Universiteit Leuven 1996, All Rights Reserved + * The Katholieke Universiteit Leuven makes no representations concerning + * either the merchantability of this software or the suitability of this + * software for any particular purpose. It is provided "as is" without + * express or implied warranty of any kind. These notices must be retained + * in any copies of any part of this documentation and/or software. +\********************************************************************/ + +#include <stdio.h> +#include <string.h> +/********************************************************************/ +/* Macro definitions */ + +/* ROL(x, n) cyclically rotates x over n bits to the left + x must be of an unsigned 32 bits type and 0 <= n < 32. +*/ +#define ROL(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* The five basic RIPEMD-160 functions F1(), F2(), F3(), F4(), and F5() +*/ +#define F1(x, y, z) ((x) ^ (y) ^ (z)) +#define F2(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define F3(x, y, z) (((x) | ~(y)) ^ (z)) +#define F4(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define F5(x, y, z) ((x) ^ ((y) | ~(z))) + +/* The ten basic RIPEMD-160 transformations FF1() through FFF5() +*/ +#define FF1(a, b, c, d, e, x, s) {\ + (a) += F1((b), (c), (d)) + (x);\ + (a) = ROL((a), (s)) + (e);\ + (c) = ROL((c), 10);\ + } +#define FF2(a, b, c, d, e, x, s) {\ + (a) += F2((b), (c), (d)) + (x) + 0x5a827999UL;\ + (a) = ROL((a), (s)) + (e);\ + (c) = ROL((c), 10);\ + } +#define FF3(a, b, c, d, e, x, s) {\ + (a) += F3((b), (c), (d)) + (x) + 0x6ed9eba1UL;\ + (a) = ROL((a), (s)) + (e);\ + (c) = ROL((c), 10);\ + } +#define FF4(a, b, c, d, e, x, s) {\ + (a) += F4((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\ + (a) = ROL((a), (s)) + (e);\ + (c) = ROL((c), 10);\ + } +#define FF5(a, b, c, d, e, x, s) {\ + (a) += F5((b), (c), (d)) + (x) + 0xa953fd4eUL;\ + (a) = ROL((a), (s)) + (e);\ + (c) = ROL((c), 10);\ + } +#define FFF1(a, b, c, d, e, x, s) {\ + (a) += F1((b), (c), (d)) + (x);\ + (a) = ROL((a), (s)) + (e);\ + (c) = ROL((c), 10);\ + } +#define FFF2(a, b, c, d, e, x, s) {\ + (a) += F2((b), (c), (d)) + (x) + 0x7a6d76e9UL;\ + (a) = ROL((a), (s)) + (e);\ + (c) = ROL((c), 10);\ + } +#define FFF3(a, b, c, d, e, x, s) {\ + (a) += F3((b), (c), (d)) + (x) + 0x6d703ef3UL;\ + (a) = ROL((a), (s)) + (e);\ + (c) = ROL((c), 10);\ + } +#define FFF4(a, b, c, d, e, x, s) {\ + (a) += F4((b), (c), (d)) + (x) + 0x5c4dd124UL;\ + (a) = ROL((a), (s)) + (e);\ + (c) = ROL((c), 10);\ + } +#define FFF5(a, b, c, d, e, x, s) {\ + (a) += F5((b), (c), (d)) + (x) + 0x50a28be6UL;\ + (a) = ROL((a), (s)) + (e);\ + (c) = ROL((c), 10);\ + } + +typedef unsigned char byte; /* unsigned 8-bit integer */ +#ifdef __alpha__ +typedef unsigned int word; /* unsigned 32-bit integer */ +typedef unsigned int LONG; +#else +typedef unsigned long word; /* unsigned 32-bit integer */ +typedef unsigned long LONG; +#endif +typedef unsigned char BYTE; +typedef unsigned int WORD; +#define RMD_DATASIZE 64 +#define RMD_DIGESTSIZE 20 +#define RMDsize 160 +/* collect four bytes into one word using a Little-endian convention */ +#define BYTES_TO_DWORD(strptr) \ + (((word) *((strptr)+3) << 24) | \ + ((word) *((strptr)+2) << 16) | \ + ((word) *((strptr)+1) << 8) | \ + ((word) *(strptr))) +typedef struct { + PCTObject_HEAD + word digest[ 5 ]; /* Message digest */ + word countLo, countHi; /* 64-bit bit count */ + word data[ 16 ]; /* data buffer*/ + int nbytes; + int Endianness; + } RIPEMDobject; + +/* Function Prototype */ + +static void RIPEMDinit(RIPEMDobject *rmdInfo); +static PyObject *RIPEMDdigest(RIPEMDobject *self); +static void RIPEMDcopy(RIPEMDobject *src,RIPEMDobject *dest); +static void RIPEMDupdate(RIPEMDobject *self,char *buffer, int length); +static void MDinit(word *MDbuf); +static void MDcompress(word *MDbuf, word *X); +static void MDfinish(RIPEMDobject *self); +/********************************************************************/ +static void RIPEMDinit(RIPEMDobject *rmdInfo) +/* Initialization of the 5-word MDbuf array to the magic + initialization constants + */ +{ + TestEndianness(rmdInfo->Endianness) + MDinit(rmdInfo->digest); + rmdInfo->countLo = rmdInfo->countHi =rmdInfo->nbytes = 0; +} + +#if 0 +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. */ + +static void longReverse( buffer, byteCount, Endianness ) + LONG *buffer; + int byteCount, Endianness; + { + LONG value; + + if (Endianness==BIG_ENDIAN) return; + byteCount /= sizeof( LONG ); + while( byteCount-- ) + { + value = *buffer; + value = ( ( value & 0xFF00FF00L ) >> 8 ) | \ + ( ( value & 0x00FF00FFL ) << 8 ); + *buffer++ = ( value << 16 ) | ( value >> 16 ); + } + } +#endif + +static void RIPEMDupdate(RIPEMDobject *shsInfo,char *buffer, int count) +{ + LONG tmp; + int dataCount; + BYTE *p; + + /* Update bitcount */ + tmp = shsInfo->countLo; + if ( ( shsInfo->countLo = tmp + ( ( LONG ) count << 3 ) ) < tmp ) + shsInfo->countHi++; /* Carry from low to high */ + shsInfo->countHi += count >> 29; + + /* Get count of bytes already in data */ + dataCount = ( int ) ( tmp >> 3 ) & 0x3F; + + /* Handle any leading odd-sized chunks */ + if( dataCount ) + { + p = ( BYTE * ) shsInfo->data + dataCount; + + dataCount = RMD_DATASIZE - dataCount; + if( count < dataCount ) + { + memcpy( p, buffer, count ); + return; + } + memcpy( p, buffer, dataCount ); + /*longReverse( shsInfo->data, RMD_DATASIZE, shsInfo->Endianness);*/ + MDcompress(shsInfo->digest,shsInfo->data); + buffer += dataCount; + count -= dataCount; + } + + /* Process data in RMD_DATASIZE chunks */ + while( count >= RMD_DATASIZE ) + { + memcpy( shsInfo->data, buffer, RMD_DATASIZE ); +/* longReverse( shsInfo->data, RMD_DATASIZE, shsInfo->Endianness );*/ + MDcompress(shsInfo->digest,shsInfo->data); + buffer += RMD_DATASIZE; + count -= RMD_DATASIZE; + } + + /* Handle any remaining bytes of data. */ + memcpy( shsInfo->data, buffer, count ); +} + +static PyObject *RIPEMDdigest(RIPEMDobject *self) +{ + RIPEMDobject temp; + int i; + byte hashcode[RMDsize/8]; /* final hash-value */ + + temp.Endianness=self->Endianness; + temp.countLo=self->countLo; + temp.countHi=self->countHi; + for(i=0; i<5; i++) temp.digest[i]=self->digest[i]; + for(i=0; i<16; i++) temp.data[i]=self->data[i]; + + MDfinish(&temp); + /* Convert word to a string of bytes using a Little-endian convention */ + for (i=0; i<RMDsize/8; i+=4) { + hashcode[i] = temp.digest[i>>2]; + hashcode[i+1] = (temp.digest[i>>2] >> 8); + hashcode[i+2] = (temp.digest[i>>2] >> 16); + hashcode[i+3] = (temp.digest[i>>2] >> 24); + } + return PyString_FromStringAndSize(hashcode, RMD_DIGESTSIZE); +} + +static void RIPEMDcopy(RIPEMDobject *src,RIPEMDobject *dest) +{ + int i; + + dest->Endianness=src->Endianness; + dest->countLo=src->countLo; + dest->countHi=src->countHi; + for(i=0; i<5; i++) dest->digest[i]=src->digest[i]; + for(i=0; i<16; i++) dest->data[i]=src->data[i]; +} +/********************************************************************/ +static void MDinit(word *MDbuf) +/* Initialization of the 5-word MDbuf array to the magic + initialization constants + */ +{ + MDbuf[0] = 0x67452301UL; + MDbuf[1] = 0xefcdab89UL; + MDbuf[2] = 0x98badcfeUL; + MDbuf[3] = 0x10325476UL; + MDbuf[4] = 0xc3d2e1f0UL; +} + +/********************************************************************/ +static void MDcompress(word *MDbuf, word *X) +/* The compression function is called for every complete 64-byte + message block. The 5-word internal state MDbuf is updated using + message words X[0] through X[15]. The conversion from a string + of 64 bytes to an array of 16 words using a Little-endian + convention is the responsibility of the calling function. +*/ +{ + /* make two copies of the old state */ + word aa = MDbuf[0], bb = MDbuf[1], cc = MDbuf[2], + dd = MDbuf[3], ee = MDbuf[4]; + word aaa = MDbuf[0], bbb = MDbuf[1], ccc = MDbuf[2], + ddd = MDbuf[3], eee = MDbuf[4]; + +/* {int i; + printf("\nWords: "); + for(i=0; i<16; i++) printf("%x ", X[i]); + printf("\n");} + printf("before compress: %x %x %x %x %x\n", + MDbuf[0], MDbuf[1], MDbuf[2], MDbuf[3], MDbuf[4]); + for(i=0;i<16;i++){printf("%08x ", X[i]);}printf("\n");*/ + + /* round 1 */ + FF1(aa, bb, cc, dd, ee, X[ 0], 11); + FF1(ee, aa, bb, cc, dd, X[ 1], 14); + FF1(dd, ee, aa, bb, cc, X[ 2], 15); + FF1(cc, dd, ee, aa, bb, X[ 3], 12); + FF1(bb, cc, dd, ee, aa, X[ 4], 5); + FF1(aa, bb, cc, dd, ee, X[ 5], 8); + FF1(ee, aa, bb, cc, dd, X[ 6], 7); + FF1(dd, ee, aa, bb, cc, X[ 7], 9); + FF1(cc, dd, ee, aa, bb, X[ 8], 11); + FF1(bb, cc, dd, ee, aa, X[ 9], 13); + FF1(aa, bb, cc, dd, ee, X[10], 14); + FF1(ee, aa, bb, cc, dd, X[11], 15); + FF1(dd, ee, aa, bb, cc, X[12], 6); + FF1(cc, dd, ee, aa, bb, X[13], 7); + FF1(bb, cc, dd, ee, aa, X[14], 9); + FF1(aa, bb, cc, dd, ee, X[15], 8); + + /* round 2 */ + FF2(ee, aa, bb, cc, dd, X[ 7], 7); + FF2(dd, ee, aa, bb, cc, X[ 4], 6); + FF2(cc, dd, ee, aa, bb, X[13], 8); + FF2(bb, cc, dd, ee, aa, X[ 1], 13); + FF2(aa, bb, cc, dd, ee, X[10], 11); + FF2(ee, aa, bb, cc, dd, X[ 6], 9); + FF2(dd, ee, aa, bb, cc, X[15], 7); + FF2(cc, dd, ee, aa, bb, X[ 3], 15); + FF2(bb, cc, dd, ee, aa, X[12], 7); + FF2(aa, bb, cc, dd, ee, X[ 0], 12); + FF2(ee, aa, bb, cc, dd, X[ 9], 15); + FF2(dd, ee, aa, bb, cc, X[ 5], 9); + FF2(cc, dd, ee, aa, bb, X[ 2], 11); + FF2(bb, cc, dd, ee, aa, X[14], 7); + FF2(aa, bb, cc, dd, ee, X[11], 13); + FF2(ee, aa, bb, cc, dd, X[ 8], 12); + + /* round 3 */ + FF3(dd, ee, aa, bb, cc, X[ 3], 11); + FF3(cc, dd, ee, aa, bb, X[10], 13); + FF3(bb, cc, dd, ee, aa, X[14], 6); + FF3(aa, bb, cc, dd, ee, X[ 4], 7); + FF3(ee, aa, bb, cc, dd, X[ 9], 14); + FF3(dd, ee, aa, bb, cc, X[15], 9); + FF3(cc, dd, ee, aa, bb, X[ 8], 13); + FF3(bb, cc, dd, ee, aa, X[ 1], 15); + FF3(aa, bb, cc, dd, ee, X[ 2], 14); + FF3(ee, aa, bb, cc, dd, X[ 7], 8); + FF3(dd, ee, aa, bb, cc, X[ 0], 13); + FF3(cc, dd, ee, aa, bb, X[ 6], 6); + FF3(bb, cc, dd, ee, aa, X[13], 5); + FF3(aa, bb, cc, dd, ee, X[11], 12); + FF3(ee, aa, bb, cc, dd, X[ 5], 7); + FF3(dd, ee, aa, bb, cc, X[12], 5); + + /* round 4 */ + FF4(cc, dd, ee, aa, bb, X[ 1], 11); + FF4(bb, cc, dd, ee, aa, X[ 9], 12); + FF4(aa, bb, cc, dd, ee, X[11], 14); + FF4(ee, aa, bb, cc, dd, X[10], 15); + FF4(dd, ee, aa, bb, cc, X[ 0], 14); + FF4(cc, dd, ee, aa, bb, X[ 8], 15); + FF4(bb, cc, dd, ee, aa, X[12], 9); + FF4(aa, bb, cc, dd, ee, X[ 4], 8); + FF4(ee, aa, bb, cc, dd, X[13], 9); + FF4(dd, ee, aa, bb, cc, X[ 3], 14); + FF4(cc, dd, ee, aa, bb, X[ 7], 5); + FF4(bb, cc, dd, ee, aa, X[15], 6); + FF4(aa, bb, cc, dd, ee, X[14], 8); + FF4(ee, aa, bb, cc, dd, X[ 5], 6); + FF4(dd, ee, aa, bb, cc, X[ 6], 5); + FF4(cc, dd, ee, aa, bb, X[ 2], 12); + + /* round 5 */ + FF5(bb, cc, dd, ee, aa, X[ 4], 9); + FF5(aa, bb, cc, dd, ee, X[ 0], 15); + FF5(ee, aa, bb, cc, dd, X[ 5], 5); + FF5(dd, ee, aa, bb, cc, X[ 9], 11); + FF5(cc, dd, ee, aa, bb, X[ 7], 6); + FF5(bb, cc, dd, ee, aa, X[12], 8); + FF5(aa, bb, cc, dd, ee, X[ 2], 13); + FF5(ee, aa, bb, cc, dd, X[10], 12); + FF5(dd, ee, aa, bb, cc, X[14], 5); + FF5(cc, dd, ee, aa, bb, X[ 1], 12); + FF5(bb, cc, dd, ee, aa, X[ 3], 13); + FF5(aa, bb, cc, dd, ee, X[ 8], 14); + FF5(ee, aa, bb, cc, dd, X[11], 11); + FF5(dd, ee, aa, bb, cc, X[ 6], 8); + FF5(cc, dd, ee, aa, bb, X[15], 5); + FF5(bb, cc, dd, ee, aa, X[13], 6); + + /* parallel round 1 */ + FFF5(aaa, bbb, ccc, ddd, eee, X[ 5], 8); + FFF5(eee, aaa, bbb, ccc, ddd, X[14], 9); + FFF5(ddd, eee, aaa, bbb, ccc, X[ 7], 9); + FFF5(ccc, ddd, eee, aaa, bbb, X[ 0], 11); + FFF5(bbb, ccc, ddd, eee, aaa, X[ 9], 13); + FFF5(aaa, bbb, ccc, ddd, eee, X[ 2], 15); + FFF5(eee, aaa, bbb, ccc, ddd, X[11], 15); + FFF5(ddd, eee, aaa, bbb, ccc, X[ 4], 5); + FFF5(ccc, ddd, eee, aaa, bbb, X[13], 7); + FFF5(bbb, ccc, ddd, eee, aaa, X[ 6], 7); + FFF5(aaa, bbb, ccc, ddd, eee, X[15], 8); + FFF5(eee, aaa, bbb, ccc, ddd, X[ 8], 11); + FFF5(ddd, eee, aaa, bbb, ccc, X[ 1], 14); + FFF5(ccc, ddd, eee, aaa, bbb, X[10], 14); + FFF5(bbb, ccc, ddd, eee, aaa, X[ 3], 12); + FFF5(aaa, bbb, ccc, ddd, eee, X[12], 6); + + /* parallel round 2 */ + FFF4(eee, aaa, bbb, ccc, ddd, X[ 6], 9); + FFF4(ddd, eee, aaa, bbb, ccc, X[11], 13); + FFF4(ccc, ddd, eee, aaa, bbb, X[ 3], 15); + FFF4(bbb, ccc, ddd, eee, aaa, X[ 7], 7); + FFF4(aaa, bbb, ccc, ddd, eee, X[ 0], 12); + FFF4(eee, aaa, bbb, ccc, ddd, X[13], 8); + FFF4(ddd, eee, aaa, bbb, ccc, X[ 5], 9); + FFF4(ccc, ddd, eee, aaa, bbb, X[10], 11); + FFF4(bbb, ccc, ddd, eee, aaa, X[14], 7); + FFF4(aaa, bbb, ccc, ddd, eee, X[15], 7); + FFF4(eee, aaa, bbb, ccc, ddd, X[ 8], 12); + FFF4(ddd, eee, aaa, bbb, ccc, X[12], 7); + FFF4(ccc, ddd, eee, aaa, bbb, X[ 4], 6); + FFF4(bbb, ccc, ddd, eee, aaa, X[ 9], 15); + FFF4(aaa, bbb, ccc, ddd, eee, X[ 1], 13); + FFF4(eee, aaa, bbb, ccc, ddd, X[ 2], 11); + + /* parallel round 3 */ + FFF3(ddd, eee, aaa, bbb, ccc, X[15], 9); + FFF3(ccc, ddd, eee, aaa, bbb, X[ 5], 7); + FFF3(bbb, ccc, ddd, eee, aaa, X[ 1], 15); + FFF3(aaa, bbb, ccc, ddd, eee, X[ 3], 11); + FFF3(eee, aaa, bbb, ccc, ddd, X[ 7], 8); + FFF3(ddd, eee, aaa, bbb, ccc, X[14], 6); + FFF3(ccc, ddd, eee, aaa, bbb, X[ 6], 6); + FFF3(bbb, ccc, ddd, eee, aaa, X[ 9], 14); + FFF3(aaa, bbb, ccc, ddd, eee, X[11], 12); + FFF3(eee, aaa, bbb, ccc, ddd, X[ 8], 13); + FFF3(ddd, eee, aaa, bbb, ccc, X[12], 5); + FFF3(ccc, ddd, eee, aaa, bbb, X[ 2], 14); + FFF3(bbb, ccc, ddd, eee, aaa, X[10], 13); + FFF3(aaa, bbb, ccc, ddd, eee, X[ 0], 13); + FFF3(eee, aaa, bbb, ccc, ddd, X[ 4], 7); + FFF3(ddd, eee, aaa, bbb, ccc, X[13], 5); + + /* parallel round 4 */ + FFF2(ccc, ddd, eee, aaa, bbb, X[ 8], 15); + FFF2(bbb, ccc, ddd, eee, aaa, X[ 6], 5); + FFF2(aaa, bbb, ccc, ddd, eee, X[ 4], 8); + FFF2(eee, aaa, bbb, ccc, ddd, X[ 1], 11); + FFF2(ddd, eee, aaa, bbb, ccc, X[ 3], 14); + FFF2(ccc, ddd, eee, aaa, bbb, X[11], 14); + FFF2(bbb, ccc, ddd, eee, aaa, X[15], 6); + FFF2(aaa, bbb, ccc, ddd, eee, X[ 0], 14); + FFF2(eee, aaa, bbb, ccc, ddd, X[ 5], 6); + FFF2(ddd, eee, aaa, bbb, ccc, X[12], 9); + FFF2(ccc, ddd, eee, aaa, bbb, X[ 2], 12); + FFF2(bbb, ccc, ddd, eee, aaa, X[13], 9); + FFF2(aaa, bbb, ccc, ddd, eee, X[ 9], 12); + FFF2(eee, aaa, bbb, ccc, ddd, X[ 7], 5); + FFF2(ddd, eee, aaa, bbb, ccc, X[10], 15); + FFF2(ccc, ddd, eee, aaa, bbb, X[14], 8); + + /* parallel round 5 */ + FFF1(bbb, ccc, ddd, eee, aaa, X[12] , 8); + FFF1(aaa, bbb, ccc, ddd, eee, X[15] , 5); + FFF1(eee, aaa, bbb, ccc, ddd, X[10] , 12); + FFF1(ddd, eee, aaa, bbb, ccc, X[ 4] , 9); + FFF1(ccc, ddd, eee, aaa, bbb, X[ 1] , 12); + FFF1(bbb, ccc, ddd, eee, aaa, X[ 5] , 5); + FFF1(aaa, bbb, ccc, ddd, eee, X[ 8] , 14); + FFF1(eee, aaa, bbb, ccc, ddd, X[ 7] , 6); + FFF1(ddd, eee, aaa, bbb, ccc, X[ 6] , 8); + FFF1(ccc, ddd, eee, aaa, bbb, X[ 2] , 13); + FFF1(bbb, ccc, ddd, eee, aaa, X[13] , 6); + FFF1(aaa, bbb, ccc, ddd, eee, X[14] , 5); + FFF1(eee, aaa, bbb, ccc, ddd, X[ 0] , 15); + FFF1(ddd, eee, aaa, bbb, ccc, X[ 3] , 13); + FFF1(ccc, ddd, eee, aaa, bbb, X[ 9] , 11); + FFF1(bbb, ccc, ddd, eee, aaa, X[11] , 11); + + /* combine results into new state */ + ddd += cc + MDbuf[1]; + MDbuf[1] = MDbuf[2] + dd + eee; + MDbuf[2] = MDbuf[3] + ee + aaa; + MDbuf[3] = MDbuf[4] + aa + bbb; + MDbuf[4] = MDbuf[0] + bb + ccc; + MDbuf[0] = ddd; +/* printf("after compress: %x %x %x %x %x\n", + MDbuf[0], MDbuf[1], MDbuf[2], MDbuf[3], MDbuf[4]); */ +} + +/********************************************************************/ +static void MDfinish( RIPEMDobject *shsInfo) +/* The final value of the 5-word MDbuf array is calculated. + lswlen and mswlen contain, respectively, the least and most significant + 32 bits of the message bit length mod 2^64, and string is an incomplete + block containing the (lswlen mod 512) remaining message bits. + (In case the message is already a multiple of 512 bits, string + is not used.) The conversion of the 5-word final state MDbuf to + the 20-byte hash result using a Little-endian convention is the + responsibility of the calling function. +*/ +{ + word *MDbuf = shsInfo->digest; + byte *string = (byte *)shsInfo->data; + word lswlen = shsInfo->countLo ; + word mswlen = shsInfo->countHi ; +/* word lswlen = shsInfo->countLo << 3;*/ +/* word mswlen = (shsInfo->countLo >>29)|(shsInfo->countHi <<3);*/ + + size_t i, length; + byte mask; + word X[16]; + + /* clear 16-word message block */ + memset(X, 0, 16*sizeof(word)); + + /* copy (lswlen mod 512) bits from string into X */ + length = ((lswlen&511)+7)/8; /* number of bytes */ + mask = (lswlen&7) ? ((byte)1 << (lswlen&7)) - 1 : 0xff; + for (i=0; i<length; i++) { + /* byte i goes into word X[i div 4] at bit position 8*(i mod 4) */ + if (i == length-1) + X[i>>2] ^= (word) (*string&mask) << (8*(i&3)); + else + X[i>>2] ^= (word) *string++ << (8*(i&3)); + } + + /* append a single 1 */ + X[(lswlen>>5)&15] ^= (word)1 << (8*((lswlen>>3)&3)+7-(lswlen&7)); + + if ((lswlen & 511) > 447) { + /* length doesn't fit in this block anymore. + Compress, and put length in the next block */ + /* longReverse( X, RMD_DATASIZE, shsInfo->Endianness);*/ + MDcompress(MDbuf, X); + memset(X, 0, 16*sizeof(word)); + } + /* append length in bits*/ + X[14] = lswlen; + X[15] = mswlen; + MDcompress(MDbuf, X); +} + + diff --git a/Hash/SHA.c b/Hash/SHA.c new file mode 100644 index 0000000..749a6c2 --- /dev/null +++ b/Hash/SHA.c @@ -0,0 +1,436 @@ +/* + * sha.c : Implementation of the Secure Hash Algorithm + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +/* SHA: NIST's Secure Hash Algorithm */ + +/* Based on SHA code originally posted to sci.crypt by Peter Gutmann + in message <30ajo5$oe8@ccu2.auckland.ac.nz>. + Modified to test for endianness on creation of SHA objects by AMK. + Also, the original specification of SHA was found to have a weakness + by NSA/NIST. This code implements the fixed version of SHA. +*/ + +/* Here's the first paragraph of Peter Gutmann's posting: + +The following is my SHA (FIPS 180) code updated to allow use of the "fixed" +SHA, thanks to Jim Gillogly and an anonymous contributor for the information on +what's changed in the new version. The fix is a simple change which involves +adding a single rotate in the initial expansion function. It is unknown +whether this is an optimal solution to the problem which was discovered in the +SHA or whether it's simply a bandaid which fixes the problem with a minimum of +effort (for example the reengineering of a great many Capstone chips). +*/ + +/* Some useful types */ + +typedef unsigned char BYTE; +#ifndef __alpha__ +typedef unsigned long LONG; +#else +typedef unsigned int LONG; +#endif +/* The SHS block size and message digest sizes, in bytes */ + +#define SHS_DATASIZE 64 +#define SHS_DIGESTSIZE 20 + +/* The structure for storing SHS info */ + +typedef struct { + PCTObject_HEAD + LONG digest[ 5 ]; /* Message digest */ + LONG countLo, countHi; /* 64-bit bit count */ + LONG data[ 16 ]; /* SHS data buffer */ + int Endianness; + } SHAobject; + +/* Message digest functions */ + +static void SHAinit(); +static void SHAupdate(); +static PyObject *SHAdigest(); +static void SHAFinal(); +static void SHAcopy(); + +static void SHAcopy(src, dest) + SHAobject *src, *dest; +{ + int i; + + dest->Endianness=src->Endianness; + dest->countLo=src->countLo; + dest->countHi=src->countHi; + for(i=0; i<5; i++) dest->digest[i]= src->digest[i]; + for(i=0; i<16; i++) dest->data[i] = src->data[i]; +} + + +/* The SHS f()-functions. The f1 and f3 functions can be optimized to + save one boolean operation each - thanks to Rich Schroeppel, + rcs@cs.arizona.edu for discovering this */ + +/*#define f1(x,y,z) ( ( x & y ) | ( ~x & z ) ) // Rounds 0-19 */ +#define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */ +#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */ +/*#define f3(x,y,z) ( ( x & y ) | ( x & z ) | ( y & z ) ) // Rounds 40-59 */ +#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */ +#define f4(x,y,z) ( x ^ y ^ z ) /* Rounds 60-79 */ + +/* The SHS Mysterious Constants */ + +#define K1 0x5A827999L /* Rounds 0-19 */ +#define K2 0x6ED9EBA1L /* Rounds 20-39 */ +#define K3 0x8F1BBCDCL /* Rounds 40-59 */ +#define K4 0xCA62C1D6L /* Rounds 60-79 */ + +/* SHS initial values */ + +#define h0init 0x67452301L +#define h1init 0xEFCDAB89L +#define h2init 0x98BADCFEL +#define h3init 0x10325476L +#define h4init 0xC3D2E1F0L + +/* Note that it may be necessary to add parentheses to these macros if they + are to be called with expressions as arguments */ +/* 32-bit rotate left - kludged with shifts */ + +#define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) ) + +/* The initial expanding function. The hash function is defined over an + 80-word expanded input array W, where the first 16 are copies of the input + data, and the remaining 64 are defined by + + W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ] + + This implementation generates these values on the fly in a circular + buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this + optimization. + + The updated SHS changes the expanding function by adding a rotate of 1 + bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor + for this information */ + +#define expand(W,i) ( W[ i & 15 ] = ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ + W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) + + +/* The prototype SHS sub-round. The fundamental sub-round is: + + a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data; + b' = a; + c' = ROTL( 30, b ); + d' = c; + e' = d; + + but this is implemented by unrolling the loop 5 times and renaming the + variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration. + This code is then replicated 20 times for each of the 4 functions, using + the next 20 values from the W[] array each time */ + +#define subRound(a, b, c, d, e, f, k, data) \ + ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) ) + +/* Initialize the SHS values */ + +static void SHAinit( shsInfo ) + SHAobject *shsInfo; + { + TestEndianness(shsInfo->Endianness) + /* Set the h-vars to their initial values */ + shsInfo->digest[ 0 ] = h0init; + shsInfo->digest[ 1 ] = h1init; + shsInfo->digest[ 2 ] = h2init; + shsInfo->digest[ 3 ] = h3init; + shsInfo->digest[ 4 ] = h4init; + + /* Initialise bit count */ + shsInfo->countLo = shsInfo->countHi = 0; + } + + +/* Perform the SHS transformation. Note that this code, like MD5, seems to + break some optimizing compilers due to the complexity of the expressions + and the size of the basic block. It may be necessary to split it into + sections, e.g. based on the four subrounds + + Note that this corrupts the shsInfo->data area */ + +static void SHSTransform( digest, data ) + LONG *digest, *data ; + { + LONG A, B, C, D, E; /* Local vars */ + LONG eData[ 16 ]; /* Expanded data */ + + /* Set up first buffer and local data buffer */ + A = digest[ 0 ]; + B = digest[ 1 ]; + C = digest[ 2 ]; + D = digest[ 3 ]; + E = digest[ 4 ]; + memcpy( eData, data, SHS_DATASIZE ); + +#ifdef DEBUG + printf("start: %08x %08x %08x %08x %08x\n", A, B, C, D, E); +#endif + /* Heavy mangling, in 4 sub-rounds of 20 interations each. */ + subRound( A, B, C, D, E, f1, K1, eData[ 0 ] ); + subRound( E, A, B, C, D, f1, K1, eData[ 1 ] ); + subRound( D, E, A, B, C, f1, K1, eData[ 2 ] ); + subRound( C, D, E, A, B, f1, K1, eData[ 3 ] ); + subRound( B, C, D, E, A, f1, K1, eData[ 4 ] ); + subRound( A, B, C, D, E, f1, K1, eData[ 5 ] ); + subRound( E, A, B, C, D, f1, K1, eData[ 6 ] ); + subRound( D, E, A, B, C, f1, K1, eData[ 7 ] ); + subRound( C, D, E, A, B, f1, K1, eData[ 8 ] ); + subRound( B, C, D, E, A, f1, K1, eData[ 9 ] ); + subRound( A, B, C, D, E, f1, K1, eData[ 10 ] ); + subRound( E, A, B, C, D, f1, K1, eData[ 11 ] ); + subRound( D, E, A, B, C, f1, K1, eData[ 12 ] ); + subRound( C, D, E, A, B, f1, K1, eData[ 13 ] ); + subRound( B, C, D, E, A, f1, K1, eData[ 14 ] ); + subRound( A, B, C, D, E, f1, K1, eData[ 15 ] ); + subRound( E, A, B, C, D, f1, K1, expand( eData, 16 ) ); + subRound( D, E, A, B, C, f1, K1, expand( eData, 17 ) ); + subRound( C, D, E, A, B, f1, K1, expand( eData, 18 ) ); + subRound( B, C, D, E, A, f1, K1, expand( eData, 19 ) ); + +#ifdef DEBUG + printf("2 : %08x %08x %08x %08x %08x\n", A, B, C, D, E); +#endif + + subRound( A, B, C, D, E, f2, K2, expand( eData, 20 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 21 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 22 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 23 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 24 ) ); + subRound( A, B, C, D, E, f2, K2, expand( eData, 25 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 26 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 27 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 28 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 29 ) ); + subRound( A, B, C, D, E, f2, K2, expand( eData, 30 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 31 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 32 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 33 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 34 ) ); + subRound( A, B, C, D, E, f2, K2, expand( eData, 35 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 36 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 37 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 38 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 39 ) ); + +#ifdef DEBUG + printf("3 : %08x %08x %08x %08x %08x\n", A, B, C, D, E); +#endif + + subRound( A, B, C, D, E, f3, K3, expand( eData, 40 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 41 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 42 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 43 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 44 ) ); + subRound( A, B, C, D, E, f3, K3, expand( eData, 45 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 46 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 47 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 48 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 49 ) ); + subRound( A, B, C, D, E, f3, K3, expand( eData, 50 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 51 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 52 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 53 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 54 ) ); + subRound( A, B, C, D, E, f3, K3, expand( eData, 55 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 56 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 57 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 58 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 59 ) ); + +#ifdef DEBUG + printf("4 : %08x %08x %08x %08x %08x\n", A, B, C, D, E); +#endif + + subRound( A, B, C, D, E, f4, K4, expand( eData, 60 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 61 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 62 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 63 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 64 ) ); + subRound( A, B, C, D, E, f4, K4, expand( eData, 65 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 66 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 67 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 68 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 69 ) ); + subRound( A, B, C, D, E, f4, K4, expand( eData, 70 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 71 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 72 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 73 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 74 ) ); + subRound( A, B, C, D, E, f4, K4, expand( eData, 75 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 76 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 77 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 78 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 79 ) ); + +#ifdef DEBUG + printf("5 : %08x %08x %08x %08x %08x\n", A, B, C, D, E); +#endif + + /* Build message digest */ + digest[ 0 ] += A; + digest[ 1 ] += B; + digest[ 2 ] += C; + digest[ 3 ] += D; + digest[ 4 ] += E; + } + +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. */ + +static void longReverse( buffer, byteCount, Endianness ) + LONG *buffer; + int byteCount, Endianness; + { + LONG value; + + if (Endianness==PCT_BIG_ENDIAN) return; + byteCount /= sizeof( LONG ); + while( byteCount-- ) + { + value = *buffer; + value = ( ( value & 0xFF00FF00L ) >> 8 ) | \ + ( ( value & 0x00FF00FFL ) << 8 ); + *buffer++ = ( value << 16 ) | ( value >> 16 ); + } + } + +/* Update SHS for a block of data */ + +static void SHAupdate( shsInfo, buffer, count ) + SHAobject *shsInfo; + BYTE *buffer; + int count; + { + LONG tmp; + int dataCount; + + /* Update bitcount */ + tmp = shsInfo->countLo; + if ( ( shsInfo->countLo = tmp + ( ( LONG ) count << 3 ) ) < tmp ) + shsInfo->countHi++; /* Carry from low to high */ + shsInfo->countHi += count >> 29; + + /* Get count of bytes already in data */ + dataCount = ( int ) ( tmp >> 3 ) & 0x3F; + + /* Handle any leading odd-sized chunks */ + if( dataCount ) + { + BYTE *p = ( BYTE * ) shsInfo->data + dataCount; + + dataCount = SHS_DATASIZE - dataCount; + if( count < dataCount ) + { + memcpy( p, buffer, count ); + return; + } + memcpy( p, buffer, dataCount ); + longReverse( shsInfo->data, SHS_DATASIZE, shsInfo->Endianness); + SHSTransform( shsInfo->digest, shsInfo->data ); + buffer += dataCount; + count -= dataCount; + } + + /* Process data in SHS_DATASIZE chunks */ + while( count >= SHS_DATASIZE ) + { + memcpy( shsInfo->data, buffer, SHS_DATASIZE ); + longReverse( shsInfo->data, SHS_DATASIZE, shsInfo->Endianness ); + SHSTransform( shsInfo->digest, shsInfo->data ); + buffer += SHS_DATASIZE; + count -= SHS_DATASIZE; + } + + /* Handle any remaining bytes of data. */ + memcpy( shsInfo->data, buffer, count ); + } + +/* Final wrapup - pad to SHS_DATASIZE-byte boundary with the bit pattern + 1 0* (64-bit count of bits processed, MSB-first) */ + +static void SHAFinal( shsInfo ) + SHAobject *shsInfo; + { + int count; + BYTE *dataPtr; + + /* Compute number of bytes mod 64 */ + count = ( int ) shsInfo->countLo; + count = ( count >> 3 ) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + dataPtr = ( BYTE * ) shsInfo->data + count; + *dataPtr++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = SHS_DATASIZE - 1 - count; + + /* Pad out to 56 mod 64 */ + if( count < 8 ) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset( dataPtr, 0, count ); + longReverse( shsInfo->data, SHS_DATASIZE, shsInfo->Endianness ); + SHSTransform( shsInfo->digest, shsInfo->data ); + + /* Now fill the next block with 56 bytes */ + memset( shsInfo->data, 0, SHS_DATASIZE - 8 ); + } + else + /* Pad block to 56 bytes */ + memset( dataPtr, 0, count - 8 ); + + /* Append length in bits and transform */ + shsInfo->data[ 14 ] = shsInfo->countHi; + shsInfo->data[ 15 ] = shsInfo->countLo; + + longReverse( shsInfo->data, SHS_DATASIZE - 8, shsInfo->Endianness ); + SHSTransform( shsInfo->digest, shsInfo->data ); +} + +static PyObject *SHAdigest(shsInfo) + SHAobject *shsInfo; +{ + SHAobject temp; + int i, j; + unsigned char digest[SHS_DIGESTSIZE]; + + temp.Endianness=shsInfo->Endianness; + temp.countLo=shsInfo->countLo; + temp.countHi=shsInfo->countHi; + for(i=0; i<5; i++) temp.digest[i]=shsInfo->digest[i]; + for(i=0; i<16; i++) temp.data[i]=shsInfo->data[i]; + + SHAFinal(&temp); + for(i=j=0; i<SHS_DIGESTSIZE; i+=4, j++) + { + digest[i+3]=(temp.digest[j] ) & 255; + digest[i+2]=(temp.digest[j] >> 8) & 255; + digest[i+1]=(temp.digest[j] >> 16) & 255; + digest[i ]=(temp.digest[j] >> 24) & 255; + } + return PyString_FromStringAndSize(digest, SHS_DIGESTSIZE); +} + + + + + + diff --git a/Hash/__init__.py b/Hash/__init__.py new file mode 100644 index 0000000..bf32a43 --- /dev/null +++ b/Hash/__init__.py @@ -0,0 +1,3 @@ + +__all__ = ['MD2', 'MD4', 'MD5', 'SHA', 'HAVAL', 'HMAC'] + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3861bac --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ + +PYTHON = /usr/bin/env python +CRYPTO_DIR = Crypto-1.1a2 + +all : source + -ln -f Makefile.pre.in src/Makefile.pre.in + # If the block/ directory is present, this must be the full + # kit, not the suitable-for-export subset + if [ -d block ] ; then \ + ln -f Setup.in src/Setup.in; \ + else \ + ln -f Setup.in-export src/Setup.in; \ + fi + (cd src ; make VERSION=1.5 -f Makefile.pre.in boot ; make) +# mv src/python . + +source : + $(PYTHON) buildkit + +clean : + -rm -f `find . -name '*~'` python + -rm -f `find . -name '*.pyc'` + -rm -f Demo/secimp/*.pys src/* Cipher/*.so Cipher/*.sl Hash/*.so Hash/*.sl + -rm -f sedscript config.c + -(cd Doc ; rm -f *.ps *.log *.aux *.ilg *.toc) + -rm -f err out + +install: + cd src ; make install + if [ -d block ] ; then \ + cd src ; make cipherinstall; \ + fi + +test: + cd src ; make links + python test.py --quiet + +distrib : clean + rm -f src/* +# python buildkit +# cd Doc ; rm -f html/* ; texi2html pycrypt.texi html ; makeinfo pycrypt.texi + cd .. ; tar -cvf /scratch/pycrypt-export.tar -X $(CRYPTO_DIR)/not-for-export -X $(CRYPTO_DIR)/excludefiles $(CRYPTO_DIR) + cd .. ; tar -cvf /scratch/pycrypt-US.tar `cat $(CRYPTO_DIR)/not-for-export` -X $(CRYPTO_DIR)/excludefiles + cd Doc ; rm -f *.?? *.log *.aux *.ilg + +links: + cd src ; make links diff --git a/Makefile.pre.in b/Makefile.pre.in new file mode 100644 index 0000000..01dfa40 --- /dev/null +++ b/Makefile.pre.in @@ -0,0 +1,369 @@ +# Universal Unix Makefile for Python extensions +# ============================================= + +# Short Instructions +# ------------------ + +# 1. Build and install Python (1.5 or newer). +# 2. "make -f Makefile.pre.in boot" +# 3. "make" +# You should now have a shared library. + +# Long Instructions +# ----------------- + +# Build *and install* the basic Python 1.5 distribution. See the +# Python README for instructions. (This version of Makefile.pre.in +# only withs with Python 1.5, alpha 3 or newer.) + +# Create a file Setup.in for your extension. This file follows the +# format of the Modules/Setup.in file; see the instructions there. +# For a simple module called "spam" on file "spammodule.c", it can +# contain a single line: +# spam spammodule.c +# You can build as many modules as you want in the same directory -- +# just have a separate line for each of them in the Setup.in file. + +# If you want to build your extension as a shared library, insert a +# line containing just the string +# *shared* +# at the top of your Setup.in file. + +# Note that the build process copies Setup.in to Setup, and then works +# with Setup. It doesn't overwrite Setup when Setup.in is changed, so +# while you're in the process of debugging your Setup.in file, you may +# want to edit Setup instead, and copy it back to Setup.in later. +# (All this is done so you can distribute your extension easily and +# someone else can select the modules they actually want to build by +# commenting out lines in the Setup file, without editing the +# original. Editing Setup is also used to specify nonstandard +# locations for include or library files.) + +# Copy this file (Misc/Makefile.pre.in) to the directory containing +# your extension. + +# Run "make -f Makefile.pre.in boot". This creates Makefile +# (producing Makefile.pre and sedscript as intermediate files) and +# config.c, incorporating the values for sys.prefix, sys.exec_prefix +# and sys.version from the installed Python binary. For this to work, +# the python binary must be on your path. If this fails, try +# make -f Makefile.pre.in Makefile VERSION=1.5 installdir=<prefix> +# where <prefix> is the prefix used to install Python for installdir +# (and possibly similar for exec_installdir=<exec_prefix>). + +# Note: "make boot" implies "make clobber" -- it assumes that when you +# bootstrap you may have changed platforms so it removes all previous +# output files. + +# If you are building your extension as a shared library (your +# Setup.in file starts with *shared*), run "make" or "make sharedmods" +# to build the shared library files. If you are building a statically +# linked Python binary (the only solution of your platform doesn't +# support shared libraries, and sometimes handy if you want to +# distribute or install the resulting Python binary), run "make +# python". + +# Note: Each time you edit Makefile.pre.in or Setup, you must run +# "make Makefile" before running "make". + +# Hint: if you want to use VPATH, you can start in an empty +# subdirectory and say (e.g.): +# make -f ../Makefile.pre.in boot srcdir=.. VPATH=.. + + +# === Bootstrap variables (edited through "make boot") === + +# The prefix used by "make inclinstall libainstall" of core python +installdir= /usr/local + +# The exec_prefix used by the same +exec_installdir=$(installdir) + +# Source directory and VPATH in case you want to use VPATH. +# (You will have to edit these two lines yourself -- there is no +# automatic support as the Makefile is not generated by +# config.status.) +srcdir= . +VPATH= . + +# === Variables that you may want to customize (rarely) === + +# (Static) build target +TARGET= python + +# Installed python binary (used only by boot target) +PYTHON= python + +# Add more -I and -D options here +CFLAGS= $(OPT) -I$(INCLUDEPY) -I$(EXECINCLUDEPY) $(DEFS) + +# These two variables can be set in Setup to merge extensions. +# See example[23]. +BASELIB= +BASESETUP= + +# === Variables set by makesetup === + +MODOBJS= _MODOBJS_ +MODLIBS= _MODLIBS_ + +# === Definitions added by makesetup === + +# === Variables from configure (through sedscript) === + +VERSION= @VERSION@ +CC= @CC@ +LINKCC= @LINKCC@ +SGI_ABI= @SGI_ABI@ +OPT= @OPT@ +LDFLAGS= @LDFLAGS@ +LDLAST= @LDLAST@ +DEFS= @DEFS@ +LIBS= @LIBS@ +LIBM= @LIBM@ +LIBC= @LIBC@ +RANLIB= @RANLIB@ +MACHDEP= @MACHDEP@ +SO= @SO@ +LDSHARED= @LDSHARED@ +CCSHARED= @CCSHARED@ +LINKFORSHARED= @LINKFORSHARED@ +#@SET_CCC@ + +# Install prefix for architecture-independent files +prefix= /usr/local + +# Install prefix for architecture-dependent files +exec_prefix= $(prefix) + +# === Fixed definitions === + +# Shell used by make (some versions default to the login shell, which is bad) +SHELL= /bin/sh + +# Expanded directories +BINDIR= $(exec_installdir)/bin +LIBDIR= $(exec_prefix)/lib +MANDIR= $(installdir)/man +INCLUDEDIR= $(installdir)/include +SCRIPTDIR= $(prefix)/lib + +# Detailed destination directories +BINLIBDEST= $(LIBDIR)/python$(VERSION) +LIBDEST= $(SCRIPTDIR)/python$(VERSION) +INCLUDEPY= $(INCLUDEDIR)/python$(VERSION) +EXECINCLUDEPY= $(exec_installdir)/include/python$(VERSION) +LIBP= $(exec_installdir)/lib/python$(VERSION) +DESTSHARED= $(BINLIBDEST)/site-packages + +LIBPL= $(LIBP)/config + +PYTHONLIBS= $(LIBPL)/libpython$(VERSION).a + +MAKESETUP= $(LIBPL)/makesetup +MAKEFILE= $(LIBPL)/Makefile +CONFIGC= $(LIBPL)/config.c +CONFIGCIN= $(LIBPL)/config.c.in +SETUP= $(LIBPL)/Setup + +SYSLIBS= $(LIBM) $(LIBC) + +ADDOBJS= $(LIBPL)/python.o config.o + +# Portable install script (configure doesn't always guess right) +INSTALL= $(LIBPL)/install-sh -c +# Shared libraries must be installed with executable mode on some systems; +# rather than figuring out exactly which, we always give them executable mode. +# Also, making them read-only seems to be a good idea... +INSTALL_SHARED= ${INSTALL} -m 555 +INSTALL_DATA= ${INSTALL} -m 644 + +# === Fixed rules === + +# Default target. This builds shared libraries only +default: sharedmods + +# Build everything +all: static sharedmods + +# Build shared libraries from our extension modules +sharedmods: $(SHAREDMODS) + +# Build a static Python binary containing our extension modules +static: $(TARGET) +$(TARGET): $(ADDOBJS) lib.a $(PYTHONLIBS) $(BASELIB) + $(LINKCC) $(LDFLAGS) $(LINKFORSHARED) \ + $(ADDOBJS) lib.a $(PYTHONLIBS) \ + $(LINKPATH) $(BASELIB) $(MODLIBS) $(LIBS) $(SYSLIBS) \ + -o $(TARGET) $(LDLAST) + +# Build the library containing our extension modules +lib.a: $(MODOBJS) + -rm -f lib.a + ar cr lib.a $(MODOBJS) + -$(RANLIB) lib.a + +# This runs makesetup *twice* to use the BASESETUP definition from Setup +config.c Makefile: Makefile.pre Setup $(BASESETUP) $(MAKESETUP) + $(MAKESETUP) \ + -m Makefile.pre -c $(CONFIGCIN) Setup -n $(BASESETUP) $(SETUP) + $(MAKE) -f Makefile do-it-again + +# Internal target to run makesetup for the second time +do-it-again: + $(MAKESETUP) \ + -m Makefile.pre -c $(CONFIGCIN) Setup -n $(BASESETUP) $(SETUP) + +# Make config.o from the config.c created by makesetup +config.o: config.c + $(CC) $(CFLAGS) -c config.c + +# Setup is copied from Setup.in *only* if it doesn't yet exist +Setup: + cp $(srcdir)/Setup.in Setup + +# Make the intermediate Makefile.pre from Makefile.pre.in +Makefile.pre: Makefile.pre.in sedscript + sed -f sedscript $(srcdir)/Makefile.pre.in >Makefile.pre + +# Shortcuts to make the sed arguments on one line +P=prefix +E=exec_prefix +H=Generated automatically from Makefile.pre.in by sedscript. +L=LINKFORSHARED + +# Make the sed script used to create Makefile.pre from Makefile.pre.in +sedscript: $(MAKEFILE) + sed -n \ + -e '1s/.*/1i\\/p' \ + -e '2s%.*%# $H%p' \ + -e '/^VERSION=/s/^VERSION=[ ]*\(.*\)/s%@VERSION[@]%\1%/p' \ + -e '/^CC=/s/^CC=[ ]*\(.*\)/s%@CC[@]%\1%/p' \ + -e '/^CCC=/s/^CCC=[ ]*\(.*\)/s%#@SET_CCC[@]%CCC=\1%/p' \ + -e '/^LINKCC=/s/^LINKCC=[ ]*\(.*\)/s%@LINKCC[@]%\1%/p' \ + -e '/^OPT=/s/^OPT=[ ]*\(.*\)/s%@OPT[@]%\1%/p' \ + -e '/^LDFLAGS=/s/^LDFLAGS=[ ]*\(.*\)/s%@LDFLAGS[@]%\1%/p' \ + -e '/^DEFS=/s/^DEFS=[ ]*\(.*\)/s%@DEFS[@]%\1%/p' \ + -e '/^LIBS=/s/^LIBS=[ ]*\(.*\)/s%@LIBS[@]%\1%/p' \ + -e '/^LIBM=/s/^LIBM=[ ]*\(.*\)/s%@LIBM[@]%\1%/p' \ + -e '/^LIBC=/s/^LIBC=[ ]*\(.*\)/s%@LIBC[@]%\1%/p' \ + -e '/^RANLIB=/s/^RANLIB=[ ]*\(.*\)/s%@RANLIB[@]%\1%/p' \ + -e '/^MACHDEP=/s/^MACHDEP=[ ]*\(.*\)/s%@MACHDEP[@]%\1%/p' \ + -e '/^SO=/s/^SO=[ ]*\(.*\)/s%@SO[@]%\1%/p' \ + -e '/^LDSHARED=/s/^LDSHARED=[ ]*\(.*\)/s%@LDSHARED[@]%\1%/p' \ + -e '/^CCSHARED=/s/^CCSHARED=[ ]*\(.*\)/s%@CCSHARED[@]%\1%/p' \ + -e '/^LDLAST=/s/^LDLAST=[ ]*\(.*\)/s%@LDLAST[@]%\1%/p' \ + -e '/^$L=/s/^$L=[ ]*\(.*\)/s%@$L[@]%\1%/p' \ + -e '/^$P=/s/^$P=\(.*\)/s%^$P=.*%$P=\1%/p' \ + -e '/^$E=/s/^$E=\(.*\)/s%^$E=.*%$E=\1%/p' \ + $(MAKEFILE) >sedscript + echo "/^#@SET_CCC@/d" >>sedscript + echo "/^installdir=/s%=.*%= $(installdir)%" >>sedscript + echo "/^exec_installdir=/s%=.*%=$(exec_installdir)%" >>sedscript + echo "/^srcdir=/s%=.*%= $(srcdir)%" >>sedscript + echo "/^VPATH=/s%=.*%= $(VPATH)%" >>sedscript + echo "/^LINKPATH=/s%=.*%= $(LINKPATH)%" >>sedscript + echo "/^BASELIB=/s%=.*%= $(BASELIB)%" >>sedscript + echo "/^BASESETUP=/s%=.*%= $(BASESETUP)%" >>sedscript + +# Bootstrap target +boot: clobber + VERSION=`$(PYTHON) -c "import sys; print sys.version[:3]"`; \ + installdir=`$(PYTHON) -c "import sys; print sys.prefix"`; \ + exec_installdir=`$(PYTHON) -c "import sys; print sys.exec_prefix"`; \ + $(MAKE) -f $(srcdir)/Makefile.pre.in VPATH=$(VPATH) srcdir=$(srcdir) \ + VERSION=$$VERSION \ + installdir=$$installdir \ + exec_installdir=$$exec_installdir \ + Makefile + +# Handy target to remove intermediate files and backups +clean: + -rm -f *.o *~ + +# Handy target to remove everything that is easily regenerated +clobber: clean + -rm -f *.a tags TAGS config.c Makefile.pre $(TARGET) sedscript + -rm -f *.so *.sl so_locations + + +# Handy target to remove everything you don't want to distribute +distclean: clobber + -rm -f Makefile Setup + +CRYPTO_DIR= $(LIBDEST)/site-packages/Crypto +HASHMODS= MD2 MD4 MD5 SHA HAVAL RIPEMD +CIPHERMODS= ARC2 ARC4 Blowfish CAST DES DES3 Diamond IDEA RC5 Sapphire \ + Skipjack XOR + +links: $(SHAREDMODS) + # Make links in the Hash directory + -for i in $(HASHMODS) ; do \ + ln -f $${i}module$(SO) ../Hash/$${i}module$(SO) ; \ + done + # Make links in the Cipher directory + -for i in $(CIPHERMODS) ; do \ + ln -f $${i}module$(SO) ../Cipher/$${i}module$(SO) ; \ + done + +install: $(SHAREDMODS) + # Create package directories + @for i in $(CRYPTO_DIR) $(CRYPTO_DIR)/Cipher \ + $(CRYPTO_DIR)/PublicKey $(CRYPTO_DIR)/Hash \ + $(CRYPTO_DIR)/Protocol $(CRYPTO_DIR)/Util \ + $(CRYPTO_DIR)/Protocol/Winnow ; \ + do \ + if test ! -d $$i; then \ + echo "Creating directory $$i"; \ + mkdir $$i; \ + chmod 755 $$i; \ + else true; \ + fi; \ + done + + $(INSTALL_DATA) ../_checkversion.py $(CRYPTO_DIR) + + # Copy the __init__.py files + -for i in "" Cipher Hash Protocol; do \ + $(INSTALL_DATA) ../$$i/__init__.py $(CRYPTO_DIR)/$$i/__init__.py ; \ + done + + # Copy things into the Hash directory + -for i in $(HASHMODS) ; do \ + $(INSTALL_SHARED) $${i}module$(SO) $(CRYPTO_DIR)/Hash/ ; \ + done + + # Copy *.py files into the Hash directory + -for i in ../Hash/*.py ; do \ + base=`basename $$i`; \ + $(INSTALL_DATA) $$i $(CRYPTO_DIR)/Hash/$$base ; \ + done + # Copy things into the PublicKey directory + -for i in ../PublicKey/*.py ; do \ + base=`basename $$i`; \ + $(INSTALL_DATA) $$i $(CRYPTO_DIR)/PublicKey/$$base ; \ + done + # Copy things into the Util directory + -for i in ../Util/*.py ; do \ + base=`basename $$i`; \ + $(INSTALL_DATA) $$i $(CRYPTO_DIR)/Util/$$base ; \ + done + # Copy things into the Winnow directory + -for i in ../Protocol/Winnow/*.py ; do \ + base=`basename $$i`; \ + $(INSTALL_DATA) $$i $(CRYPTO_DIR)/Protocol/Winnow/$$base ; \ + done + python -c 'from compileall import *;compile_dir("$(CRYPTO_DIR)")' + python -O -c 'from compileall import *;compile_dir("$(CRYPTO_DIR)")' + +cipherinstall: + # Copy things into the Cipher directory + for i in $(CIPHERMODS) ; do \ + $(INSTALL_SHARED) $${i}module$(SO) $(CRYPTO_DIR)/Cipher/ ; \ + done + + + + + + diff --git a/Protocol/AllOrNothing.py b/Protocol/AllOrNothing.py new file mode 100644 index 0000000..aae7dba --- /dev/null +++ b/Protocol/AllOrNothing.py @@ -0,0 +1,306 @@ +"""This file implements all-or-nothing package transformations. + +An all-or-nothing package transformation is one in which some text is +transformed into message blocks, such that all blocks must be obtained before +the reverse transformation can be applied. Thus, if any blocks are corrupted +or lost, the original message cannot be reproduced. + +An all-or-nothing package transformation is not encryption, although a block +cipher algorithm is used. The encryption key is randomly generated and is +extractable from the message blocks. + +This class implements the All-Or-Nothing package transformation algorithm +described in: + +Rivest. All-Or-Nothing Encryption and The Package Transform. To appear in +the Proceedings of the 1997 Fast Software Encryption Conference. +http://theory.lcs.mit.edu/~rivest/fusion.ps + +""" + +import operator +import string +from Crypto.Util.number import bytestolong, longtobytes + + + +class AllOrNothing: + """Class implementing the All-or-Nothing package transform. + + Public Methods: + + __init__(ciphermodule, mode=None, IV=None): + Constructor for the class. ciphermodule is a module implementing + the cipher algorithm to use. In essence it must provide the + following interface: + + ciphermodule.keysize + Attribute containing the cipher algorithm's key size in + bytes. If the cipher supports variable length keys, then + typically ciphermodule.keysize will be zero. In that case a + key size of 16 bytes will be used. + + ciphermodule.blocksize + Attribute containing the cipher algorithm's input block size + in bytes + + ciphermodule.new(key, mode, IV) + Function which returns a new instance of a cipher object, + initialized to key. The returned object must have an + encrypt() method that accepts a string of + ciphermodule.blocksize bytes and returns a string containing + the encrypted text. + + Note that the encryption key is randomly generated automatically + when needed. Optional arguments mode and IV are passed directly + through to the ciphermodule.new() method; they are the feedback + mode and initialization vector to use. All three arguments must + be the same for the object used to create the digest, and to + undigest'ify the message blocks. + + update(text): + Concatenate text to the string that will be transformed. + + reset(text=''): + Reset the current string to be transformed to text. + + digest(): + Perform the All-or-Nothing package transform on the current + string. Output is a list of message blocks describing the + transformed text, where each block is a string of bit length equal + to the ciphermodule's blocksize. + + undigest(mblocks): + Perform the reverse package transformation on a list of message + blocks. Note that the ciphermodule used for both transformations + must be the same. mblocks is a list of strings of bit length + equal to the ciphermodule's blocksize. Output is a string object. + + Subclass methods: + + _inventkey(keysize): + Returns a randomly generated key. Subclasses can use this to + implement better random key generating algorithms. The default + algorithm is probably not very cryptographically secure. + + """ + def __init__(self, ciphermodule, mode=None, IV=None): + self.__ciphermodule = ciphermodule + self.__mode = mode + self.__IV = IV + self.__text = '' + self.__keysize = ciphermodule.keysize + if self.__keysize == 0: + self.__keysize = 16 + + def update(self, text): + self.__text = self.__text + text + + def reset(self, text=''): + self.__text = text + + __K0digit = chr(0x69) + + def digest(self): + text = self.__text + # generate a random session key and K0, the key used to encrypt the + # hash blocks. Rivest calls this a fixed, publically-known encryption + # key, but says nothing about the security implications of this key or + # how to choose it. + key = self._inventkey(self.__keysize) + K0 = self.__K0digit * self.__keysize + # we need to cipher objects here, one that is used to encrypt the + # message blocks and one that is used to encrypt the hashes. The + # former uses the randomly generated key, while the latter uses the + # well-known key. + mcipher = self.__newcipher(key) + hcipher = self.__newcipher(K0) + # Pad the text so that it's length is a multiple of the cipher's + # blocksize. Pad with trailing spaces, which will be eliminated in + # the undigest() step. + blocksize = self.__ciphermodule.blocksize + padbytes = blocksize - (len(text) % blocksize) + text = text + ' ' * padbytes + # Run through the algorithm: + # s: number of message blocks (size of text / blocksize) + # input sequence: m1, m2, ... ms + # random key K' (`key' in the code) + # Compute output sequence: m'1, m'2, ... m's' for s' = s + 1 + # Let m'i = mi ^ E(K', i) for i = 1, 2, 3, ..., s + # Let m's' = K' ^ h1 ^ h2 ^ ... hs + # where hi = E(K0, m'i ^ i) for i = 1, 2, ... s + # + # The one complication I add is that the last message block is hard + # coded to the number of padbytes added, so that these can be stripped + # during the undigest() step + s = len(text) / blocksize + blocks = [] + hashes = [] + for i in range(1, s+1): + start = (i-1) * blocksize + end = start + blocksize + mi = text[start:end] + assert len(mi) == blocksize + cipherblock = mcipher.encrypt(longtobytes(i, blocksize)) + mticki = bytestolong(mi) ^ bytestolong(cipherblock) + blocks.append(mticki) + # calculate the hash block for this block + hi = hcipher.encrypt(longtobytes(mticki ^ i, blocksize)) + hashes.append(bytestolong(hi)) + # Add the padbytes length as a message block + i = i + 1 + cipherblock = mcipher.encrypt(longtobytes(i, blocksize)) + mticki = padbytes ^ bytestolong(cipherblock) + blocks.append(mticki) + # calculate this block's hash + hi = hcipher.encrypt(longtobytes(mticki ^ i, blocksize)) + hashes.append(bytestolong(hi)) + # Now calculate the last message block of the sequence 1..s'. This + # will contain the random session key XOR'd with all the hash blocks, + # so that for undigest(), once all the hash blocks are calculated, the + # session key can be trivially extracted. Calculating all the hash + # blocks requires that all the message blocks be received, thus the + # All-or-Nothing algorithm succeeds. + mtick_stick = bytestolong(key) ^ reduce(operator.xor, hashes) + blocks.append(mtick_stick) + # we convert the blocks to strings since in Python, byte sequences are + # always represented as strings. This is more consistent with the + # model that encryption and hash algorithm always operates on strings. + return map(longtobytes, blocks) + + def undigest(self, blocks): + # better have at least 2 blocks, for the padbytes package and the hash + # block accumulator + if len(blocks) < 2: + raise ValueError, "List must be at least length 2." + # blocks is a list of strings. We need to deal with them as long + # integers + blocks = map(bytestolong, blocks) + # Calculate the well-known key, to which the hash blocks are + # encrypted, and create the hash cipher. + K0 = self.__K0digit * self.__keysize + hcipher = self.__newcipher(K0) + # Since we have all the blocks (or this method would have been called + # prematurely), we can calcualte all the hash blocks. + hashes = [] + for i in range(1, len(blocks)): + mticki = blocks[i-1] ^ i + hi = hcipher.encrypt(longtobytes(mticki)) + hashes.append(bytestolong(hi)) + # now we can calculate K' (key). remember the last block contains + # m's' which we don't include here + key = blocks[-1] ^ reduce(operator.xor, hashes) + # and now we can create the cipher object + mcipher = self.__newcipher(longtobytes(key)) + blocksize = self.__ciphermodule.blocksize + # And we can now decode the original message blocks + parts = [] + for i in range(1, len(blocks)): + cipherblock = mcipher.encrypt(longtobytes(i, blocksize)) + mi = blocks[i-1] ^ bytestolong(cipherblock) + parts.append(mi) + # The last message block contains the number of pad bytes appended to + # the original text string, such that its length was an even multiple + # of the cipher's blocksize. This number should be small enough that + # the conversion from long integer to integer should never overflow + padbytes = int(parts[-1]) + text = string.join(map(longtobytes, parts[:-1]), '') + return text[:-padbytes] + + def _inventkey(self, keysize): + # TBD: Not a very secure algorithm. Eventually, I'd like to use JHy's + # kernelrand module + import time + from Crypto.Util import randpool + # TBD: keysize * 2 to work around possible bug in RandomPool? + pool = randpool.RandomPool(keysize * 2) + while keysize > pool.addEvent(time.time()) / 8: + pass + # we now have enough entropy in the pool to get a keysize'd key + return pool.getBytes(keysize) + + def __newcipher(self, key): + if self.__mode is None and self.__IV is None: + return self.__ciphermodule.new(key) + elif self.__IV is None: + return self.__ciphermodule.new(key, self.__mode) + else: + return self.__ciphermodule.new(key, self.__mode, self.__IV) + + + +if __name__ == '__main__': + import sys + import getopt + import base64 + + usagemsg = '''\ +Test module usage: %(program)s [-c cipher] [-l] [-h] + +Where: + --cipher module + -c module + Cipher module to use. Default: %(ciphermodule)s + + --aslong + -l + Print the encoded message blocks as long integers instead of base64 + encoded strings + + --help + -h + Print this help message +''' + + ciphermodule = 'XOR' + aslong = 0 + + def usage(code, msg=None): + if msg: + print msg + print usagemsg % {'program': sys.argv[0], + 'ciphermodule': ciphermodule} + sys.exit(code) + + try: + opts, args = getopt.getopt(sys.argv[1:], + 'c:l', ['cipher=', 'aslong']) + except getopt.error, msg: + usage(1, msg) + + if args: + usage(1, 'Too many arguments') + + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + elif opt in ('-c', '--cipher'): + ciphermodule = arg + elif opt in ('-l', '--aslong'): + aslong = 1 + + # ugly hack to force __import__ to give us the end-path module + module = __import__('Crypto.Cipher.'+ciphermodule, None, None, ['new']) + + a = AllOrNothing(module) + print 'Original text:\n==========' + print __doc__ + print '==========' + a.update(__doc__) + msgblocks = a.digest() + print 'message blocks:' + for i, blk in map(None, range(len(msgblocks)), msgblocks): + # base64 adds a trailing newline + print ' %3d' % i, + if aslong: + print bytestolong(blk) + else: + print base64.encodestring(blk)[:-1] + # + # get a new undigest-only object so there's no leakage + b = AllOrNothing(module) + text = b.undigest(msgblocks) + if text == __doc__: + print 'They match!' + else: + print 'They differ!' diff --git a/Protocol/Chaffing.py b/Protocol/Chaffing.py new file mode 100644 index 0000000..de14925 --- /dev/null +++ b/Protocol/Chaffing.py @@ -0,0 +1,220 @@ +"""This file implements the chaffing algorithm. + +Winnowing and chaffing is a technique for enhancing privacy without requiring +strong encryption. In short, the technique takes a set of authenticated +message blocks (the wheat) and adds a number of chaff blocks which have +randomly chosen data and MAC fields. This means that to an adversary, the +chaff blocks look as valid as the wheat blocks, and so the authentication +would have to be performed on every block. By tailoring the number of chaff +blocks added to the message, the sender can make breaking the message +computationally infeasible. There are many other interesting properties of +the winnow/chaff technique. + +For example, say Alice is sending a message to Bob. She packetizes the +message and performs an all-or-nothing transformation on the packets. Then +she authenticates each packet with a message authentication code (MAC). The +MAC is a hash of the data packet, and there is a secret key which she must +share with Bob (key distribution is an exercise left to the reader). She then +adds a serial number to each packet, and sends the packets to Bob. + +Bob receives the packets, and using the shared secret authentication key, +authenticates the MACs for each packet. Those packets that have bad MACs are +simply discarded. The remainder are sorted by serial number, and passed +through the reverse all-or-nothing transform. The transform means that an +eavesdropper (say Eve) must acquire all the packets before any of the data can +be read. If even one packet is missing, the data is useless. + +There's one twist: by adding chaff packets, Alice and Bob can make Eve's job +much harder, since Eve now has to break the shared secret key, or try every +combination of wheat and chaff packet to read any of the message. The cool +thing is that Bob doesn't need to add any additional code; the chaff packets +are already filtered out because their MACs don't match (in all likelihood -- +since the data and MACs for the chaff packets are randomly chosen it is +possible, but very unlikely that a chaff MAC will match the chaff data). And +Alice need not even be the party adding the chaff! She could be completely +unaware that a third party, say Charles, is adding chaff packets to her +messages as they are transmitted. + +For more information on winnowing and chaffing see this paper: + +XXX Rivest. + +""" + +from Crypto.Util.number import longtobytes, bytestolong + +class Chaff: + """Class implementing the chaff adding algorithm. + + Public Methods: + + __init__(factor=1.0, blocksper=1): + Constructor for the class. factor is the number of message blocks + to add chaff to, expressed as a percentage between 0.0 and 1.0. + blocksper is the number of chaff blocks to include for each block + being chaffed. Thus the defaults add one chaff block to every + message block. By changing the defaults, you can adjust how + computationally difficult it could be for an adversary to + brute-force crack the message. The difficulty is expressed as: + + pow(blocksper, int(factor * number-of-blocks)) + + For ease of implementation, when factor < 1.0, only the first + int(factor*number-of-blocks) message blocks are chaffed. + + chaff(blocks): + Add chaff to message blocks. blocks is a list of 3-tuples of the + form: + + (serial-number, data, MAC) + + Chaff is created by choosing a random number of the same + byte-length as data, and another random number of the same + byte-length as MAC. The message block's serial number is placed + on the chaff block and all the packet's chaff blocks are randomly + interspersed with the single wheat block. This method then + returns a list of 3-tuples of the same form. Chaffed blocks will + contain multiple instances of 3-tuples with the same serial + number, but the only way to figure out which blocks are wheat and + which are chaff is to perform the MAC hash and compare values. + + Subclass methods: + + __randnum(size): + Returns a randomly generated number with a byte-length equal + to size. Subclasses can use this to implement better random + data and MAC generating algorithms. The default algorithm is + probably not very cryptographically secure. It is most + important that the chaff data does not contain any patterns + that can be used to discern it from wheat data without running + the MAC. + + """ + def __init__(self, factor=1.0, blocksper=1): + self.__factor = factor + self.__blocksper = blocksper + + def chaff(self, blocks): + chaffedblocks = [] + # count is the number of blocks to add chaff to. blocksper is the + # number of chaff blocks to add per message block that is being + # chaffed. + count = len(blocks) * self.__factor + blocksper = range(self.__blocksper) + for i, wheat in map(None, range(len(blocks)), blocks): + # it shouldn't matter which of the n blocks we add chaff to so for + # ease of implementation, we'll just add them to the first count + # blocks + if i < count: + serial, data, mac = wheat + datasize = len(data) + macsize = len(mac) + addwheat = 1 + # add chaff to this block + for j in blocksper: + import sys + chaffdata = self._randnum(datasize) + chaffmac = self._randnum(macsize) + chaff = (serial, chaffdata, chaffmac) + # mix up the order, if the 5th bit is on then put the + # wheat on the list + if addwheat and bytestolong(self._randnum(16)) & 0x40: + chaffedblocks.append(wheat) + addwheat = 0 + chaffedblocks.append(chaff) + if addwheat: + chaffedblocks.append(wheat) + else: + # just add the wheat + chaffedblocks.append(wheat) + return chaffedblocks + + def _randnum(self, size): + # TBD: Not a very secure algorithm. + # TBD: size * 2 to work around possible bug in RandomPool + from Crypto.Util import randpool + import time + pool = randpool.RandomPool(size * 2) + while size > pool.addEvent(time.time()) / 8: + pass + # we now have enough entropy in the pool to get size bytes of random + # data... well, probably + return pool.getBytes(size) + + + +if __name__ == '__main__': + text = """\ +When in the Course of human events, it becomes necessary for one people to +dissolve the political bands which have connected them with another, and to +assume among the powers of the earth, the separate and equal station to which +the Laws of Nature and of Nature's God entitle them, a decent respect to the +opinions of mankind requires that they should declare the causes which impel +them to the separation. + +We hold these truths to be self-evident, that all men are created equal, that +they are endowed by their Creator with certain unalienable Rights, that among +these are Life, Liberty, and the pursuit of Happiness. That to secure these +rights, Governments are instituted among Men, deriving their just powers from +the consent of the governed. That whenever any Form of Government becomes +destructive of these ends, it is the Right of the People to alter or to +abolish it, and to institute new Government, laying its foundation on such +principles and organizing its powers in such form, as to them shall seem most +likely to effect their Safety and Happiness. +""" + print 'Original text:\n==========' + print text + print '==========' + + # first transform the text into packets + blocks = [] ; size = 40 + for i in range(0, len(text), size): + blocks.append( text[i:i+size] ) + + # now get MACs for all the text blocks. The key is obvious... + print 'Calculating MACs...' + from Crypto.Hash import SHA + from Crypto.Hash.HMAC import HMAC + h = HMAC(SHA) + macs = h.hash('Jefferson', blocks) + + assert len(blocks) == len(macs) + + # put these into a form acceptable as input to the chaffing procedure + source = [] + m = map(None, range(len(blocks)), blocks, macs) + for i, data, mac in m: + source.append((i, data, mac)) + + # now chaff these + print 'Adding chaff...' + c = Chaff(factor=0.5, blocksper=2) + chaffed = c.chaff(source) + + from base64 import encodestring + + # print the chaffed message blocks. meanwhile, separate the wheat from + # the chaff + + wheat = [] + print 'chaffed message blocks:' + for i, data, mac in chaffed: + # do the authentication + pmac = h.hash('Jefferson', [data])[0] + if pmac == mac: + tag = '-->' + wheat.append(data) + else: + tag = ' ' + # base64 adds a trailing newline + print tag, '%3d' % i, \ + repr(data), encodestring(mac)[:-1] + + # now decode the message packets and check it against the original text + print 'Undigesting wheat...' + import string + newtext = string.join(wheat, "") + if newtext == text: + print 'They match!' + else: + print 'They differ!' diff --git a/Protocol/PGP/Info/draft-ietf-openpgp-formats b/Protocol/PGP/Info/draft-ietf-openpgp-formats new file mode 100644 index 0000000..6c1406a --- /dev/null +++ b/Protocol/PGP/Info/draft-ietf-openpgp-formats @@ -0,0 +1,2356 @@ +Network Working Group Jon Callas +Category: INTERNET-DRAFT Pretty Good Privacy +draft-ietf-openpgp-formats-00.txt Lutz Donnerhacke +Expires May 1998 IN-Root-CA Individual Network e.V. +November 1997 Hal Finney + Pretty Good Privacy + Rodney Thayer + Sable Technology + + OP Formats - OpenPGP Message Format + draft-ietf-openpgp-formats-00.txt + +Copyright 1997 by The Internet Society. All Rights Reserved. + +Status of this Memo + +This document is an Internet-Draft. Internet-Drafts are working +documents of the Internet Engineering Task Force (IETF), its areas, and +its working groups. Note that other groups may also distribute working +documents as Internet-Drafts. + +Internet-Drafts are draft documents valid for a maximum of six months +and may be updated, replaced, or obsoleted by other documents at any +time. It is inappropriate to use Internet-Drafts as reference material +or to cite them other than as "work in progress." + +To learn the current status of any Internet-Draft, please check the +"1id-abstracts.txt" listing contained in the Internet-Drafts Shadow +Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe), +munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), or +ftp.isi.edu (US West Coast). + +Abstract + +This document is maintained in order to publish all necessary +information needed to develop interoperable applications based on the +OP format. It is not a step-by-step cookbook for writing an +application, it describes only the format and methods needed to read, +check, generate and write conforming packets crossing any network. It +does not deal with storing and implementation questions albeit it is +necessary to avoid security flaws. + +OP (Open-PGP) software uses a combination of strong public-key and +conventional cryptography to provide security services for electronic +communications and data storage. These services include +confidentiality, key management, authentication and digital signatures. +This document specifies the message formats used in OP. + + + + + + + +Callas, et. al. Expires May 1998 [Page 1] +Internet Draft OpenPGP Message Format Nov 1998 + + +Table of Contents + +1. Introduction +1.1 Terms +2. General functions +2.1 Confidentiality +2.2 Digital signature +2.3 Compression +2.4 Conversion to Radix-64 +2.4.1 Forming ASCII Armor +2.4.2 Encoding Binary in Radix-64 +2.4.3 Decoding Radix-64 +2.4.4 Examples of Radix-64 +2.5 Example of an ASCII Armored Message +2.6 Cleartext signature framework +3.0 Data Element Formats +3.1 Scalar numbers +3.2 Multi-Precision Integers +3.3 Counted Strings +3.4 Time fields +3.5 String-to-key (S2K) specifiers +3.5.1 String-to-key (S2k) specifier types +3.5.1.1 Simple S2K +3.5.1.2 Salted S2K +3.5.1.3 Iterated and Salted S2K +3.5.2 String-to-key usage +3.5.2.1 Secret key encryption +3.5.2.2 Conventional message encryption +3.5.3 String-to-key algorithms +3.5.3.1 Simple S2K algorithm +3.5.3.2 Salted S2K algorithm +3.5.3.3 Iterated-Salted S2K algorithm +4.0 Packet Syntax +4.1 Overview +4.2 Packet Headers +4.3 Packet Tags +5.0 Packet Types +5.1 Encrypted Session Key Packets (Tag 1) +5.2 Signature Packet (Tag 2) +5.2.1 Version 3 Signature Packet Format +5.2.2 Version 4 Signature Packet Format +5.2.2.1 Signature Subpacket Specification +5.2.2.2 Signature Subpacket Types +5.2.3 Signature Types +5.3 Conventional Encrypted Session-Key Packets (Tag 3) +5.4 One-Pass Signature Packets (Tag 4) +5.5 Key Material Packet +5.5.1 Key Packet Variants +5.5.1.1 Public Key Packet (Tag 6) +5.5.1.2 Public Subkey Packet (Tag 14) +5.5.1.3 Secret Key Packet (Tag 5) +5.5.1.4 Secret Subkey Packet (Tag 7) +5.5.2 Public Key Packet Formats + + +Callas, et. al. Expires May 1998 [Page 2] +Internet Draft OpenPGP Message Format Nov 1998 + + +5.5.3 Secret Key Packet Formats +5.6 Compressed Data Packet (Tag 8) +5.7 Symmetrically Encrypted Data Packet (Tag 9) +5.8 Marker Packet (Obsolete Literal Packet) (Tag 10) +5.9 Literal Data Packet (Tag 11) +5.10 Trust Packet (Tag 12) +5.11 User ID Packet (Tag 13) +5.12 Comment Packet (Tag 16) +6. Constants +6.1 Public Key Algorithms +6.2 Symmetric Key Algorithms +6.3 Compression Algorithms +6.4 Hash Algorithms +7. Packet Composition +7.1 Transferable Public Keys +7.2 OP Messages +8. Enhanced Key Formats +8.1 Key Structures +8.4 V4 Key IDs and Fingerprints +9. Security Considerations +10. Authors and Working Group Chair +11. References +12. Full Copyright Statement + +1. Introduction + +This document provides information on the message-exchange packet +formats used by OP to provide encryption, decryption, signing, key +management and functions. It builds on the foundation provided RFC +1991 "PGP Message Exchange Formats" [1]. + +1.1 Terms + +OP - OpenPGP. This is a definition for security software that uses PGP +5.x as a basis. + +PGP - Pretty Good Privacy. PGP is a family of software systems +developed by Philip R. Zimmermann from which OP is based. + +PGP 2.6.x - This version of PGP has many variants, hence the term PGP +2.6.x. It used only RSA and IDEA for its cryptography. + +PGP 5.x - This version of PGP is formerly known as "PGP 3" in the +community and also in the predecessor of this document, RFC1991. It +has new formats and corrects a number of problems in the PGP 2.6.x. It +is referred to here as PGP 5.x because that software was the first +release of the "PGP 3" code base. + +"PGP", "Pretty Good", and "Pretty Good Privacy" are trademarks of +Pretty Good Privacy, Inc. + + +2. General functions + + +Callas, et. al. Expires May 1998 [Page 3] +Internet Draft OpenPGP Message Format Nov 1998 + + + +OP provides data integrity services for messages and data files by +using these core technologies: + +-digital signature -encryption -compression -radix-64 conversion + +In addition, OP provides key management and certificate services. + +2.1 Confidentiality via Encryption + +OP offers two encryption options to provide confidentiality: +conventional (symmetric-key) encryption and public key encryption. +With public-key encryption, the message is actually encrypted using a +conventional encryption algorithm. In this mode, each conventional key +is used only once. That is, a new key is generated as a random number +for each message. Since it is used only once, the "session key" is +bound to the message and transmitted with it. To protect the key, it +is encrypted with the receiver's public key. The sequence is as +follows: + + 1. The sender creates a message. + 2. The sending OP generates a random number to be used as a + session key for this message only. + 3. The session key is encrypted using each recipient's public key. + These "encrypted session keys" start the message. + 4. The sending OP encrypts the message using the session key, which + forms the remainder of the message. Note that the message is + also usually compressed. + 5. The receiving OP decrypts the session key using the recipient's + private key. + 6. The receiving OP decrypts the message using the session key. + If the message was compressed, it will be decompressed. + +Both digital signature and confidentiality services may be applied to +the same message. First, a signature is generated for the message and +attached to the message. Then, the message plus signature is encrypted +using a conventional session key. Finally, the session key is +encrypted using public-key encryption and prepended to the encrypted +block. + +2.2 Authentication via Digital signature + +The digital signature uses a hash code or message digest algorithm, and +a public-key signature algorithm. The sequence is as follows: + + 1. The sender creates a message. + 2. The sending software generates a hash code of the message + 3. The sending software generates a signature from the hash code using + the sender's private key. + 4. The binary signature is attached to the message. + 5. The receiving software keeps a copy of the message signature. + 6. The receiving software generates a new hash code for the received + message and verifies it using the message's signature. If the + + +Callas, et. al. Expires May 1998 [Page 4] +Internet Draft OpenPGP Message Format Nov 1998 + + + verification is successful, the message is accepted as authentic. + +2.3 Compression + +OP implementations MAY compress the message after applying the +signature but before encryption. + +2.4 Conversion to Radix-64 + +OP's underlying native representation for encrypted messages, signature +certificates, and keys is a stream of arbitrary octets. Some systems +only permit the use of blocks consisting of seven-bit, printable text. +For transporting OP's native raw binary octets through email channels, +a printable encoding of these binary octets is needed. OP provides the +service of converting the raw 8-bit binary octet stream to a stream of +printable ASCII characters, called Radix-64 encoding or ASCII Armor. + +In principle, any printable encoding scheme that met the requirements +of the email channel would suffice, since it would not change the +underlying binary bit streams of the native OP data structures. The OP +standard specifies one such printable encoding scheme to ensure +interoperability. + +OP's Radix-64 encoding is composed of two parts: a base64 encoding of +the binary data, and a checksum. The base64 encoding is identical to +the MIME base64 content-transfer-encoding [RFC 2045, Section 6.8]. An +OP implementation MAY use ASCII Armor to protect the raw binary data. + +The checksum is a 24-bit CRC converted to four characters of radix-64 +encoding by the same MIME base64 transformation, preceded by an equals +sign (=). The CRC is computed by using the generator 0x864CFB and an +initialization of 0xB704CE. The accumulation is done on the data +before it is converted to radix-64, rather than on the converted data. +(For more information on CRC functions, see chapter 19 of [CAMPBELL].) + +{{Editor's note: This is old text, dating back to RFC 1991. I have +never liked the glib way the CRC has been dismissed, but I also know +that this is no place to start a discussion of CRC theory. Should we +construct a sample implementation in C and put it in an appendix? -- +jdcc}} + +The checksum with its leading equal sign MAY appear on the first line +after the Base64 encoded data. + +Rationale for CRC-24: The size of 24 bits fits evenly into printable +base64. The nonzero initialization can detect more errors than a zero +initialization. + +2.4.1 Forming ASCII Armor + +When OP encodes data into ASCII Armor, it puts specific headers around +the data, so OP can reconstruct the data later. OP informs the user +what kind of data is encoded in the ASCII armor through the use of the + + +Callas, et. al. Expires May 1998 [Page 5] +Internet Draft OpenPGP Message Format Nov 1998 + + +headers. + +Concatenating the following data creates ASCII Armor: + +- An Armor Header Line, appropriate for the type of data - Armor +Headers - A blank (zero-length, or containing only whitespace) line - +The ASCII-Armored data - An Armor Checksum - The Armor Tail, which +depends on the Armor Header Line. + +An Armor Header Line consists of the appropriate header line text +surrounded by five (5) dashes ('-', 0x2D) on either side of the header +line text. The header line text is chosen based upon the type of data +that is being encoded in Armor, and how it is being encoded. Header +line texts include the following strings: + +BEGIN PGP MESSAGE used for signed, encrypted, or compressed files + +BEGIN PGP PUBLIC KEY BLOCK used for armoring public keys + +BEGIN PGP PRIVATE KEY BLOCK used for armoring private keys + +BEGIN PGP MESSAGE, PART X/Y used for multi-part messages, where the +armor is split amongst Y parts, and this is the Xth part out of Y. + +BEGIN PGP MESSAGE, PART X used for multi-part messages, where this is +the Xth part of an unspecified number of parts. Requires the MESSAGE-ID +Armor Header to be used. + +BEGIN PGP SIGNATURE used for detached signatures, OP/MIME signatures, +and signatures following clearsigned messages + +The Armor Headers are pairs of strings that can give the user or the +receiving OP message block some information about how to decode or use +the message. The Armor Headers are a part of the armor, not a part of +the message, and hence are not protected by any signatures applied to +the message. + +The format of an Armor Header is that of a key-value pair. A colon +(':' 0x38) and a single space (0x20) separate the key and value. OP +should consider improperly formatted Armor Headers to be corruption of +the ASCII Armor. Unknown keys should be reported to the user, but OP +should continue to process the message. Currently defined Armor Header +Keys include "Version" and "Comment", which define the OP Version used +to encode the message and a user-defined comment. + +The "MessageID" Armor Header specifies a 32-character string of +printable characters. The string must be the same for all parts of a +multi-part message that uses the "PART X" Armor Header. MessageID +strings should be chosen with enough internal randomness that no two +messages would have the same MessageID string. + +The MessageID should not appear unless it is in a multi-part message. +If it appears at all, it should be computed from the message in a + + +Callas, et. al. Expires May 1998 [Page 6] +Internet Draft OpenPGP Message Format Nov 1998 + + +deterministic fashion, rather than contain a purely random value. This +is to allow anyone to determine that the MessageID cannot serve as a +covert means of leaking cryptographic key information. + +{{Editor's note: This needs to be cleaned up, with a table of the +defined headers. Also, the MessageID description is too vague about +how random the id has to be.}} + +The Armor Tail Line is composed in the same manner as the Armor Header +Line, except the string "BEGIN" is replaced by the string "END." + +2.4.2 Encoding Binary in Radix-64 + +The encoding process represents 24-bit groups of input bits as output +strings of 4 encoded characters. Proceeding from left to right, a +24-bit input group is formed by concatenating three 8-bit input groups. +These 24 bits are then treated as four concatenated 6-bit groups, each +of which is translated into a single digit in the Radix-64 alphabet. +When encoding a bit stream with the Radix-64 encoding, the bit stream +must be presumed to be ordered with the most-significant-bit first. +That is, the first bit in the stream will be the high-order bit in the +first 8-bit byte, and the eighth bit will be the low-order bit in the +first 8-bit byte, and so on. + + +--first octet--+-second octet--+--third octet--+ + |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0| + +-----------+---+-------+-------+---+-----------+ + |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0| + +--1.index--+--2.index--+--3.index--+--4.index--+ + +Each 6-bit group is used as an index into an array of 64 printable +characters from the table below. The character referenced by the index +is placed in the output string. + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + + +Callas, et. al. Expires May 1998 [Page 7] +Internet Draft OpenPGP Message Format Nov 1998 + + +The encoded output stream must be represented in lines of no more than +76 characters each. + +Special processing is performed if fewer than 24 bits are available at +the end of the data being encoded. There are three possibilities: + +- The last data group has 24 bits (3 octets). No special processing is +needed. + +- The last data group has 16 bits (2 octets). The first two 6-bit +groups are processed as above. The third (incomplete) data group has +two zero-value bits added to it, and is processed as above. A pad +character (=) is added to the output. + +- The last data group has 8 bits (1 octet). The first 6-bit group is +processed as above. The second (incomplete) data group has four +zero-value bits added to it, and is processed as above. Two pad +characters (=) are added to the output. + +2.4.3 Decoding Radix-64 + +Any characters outside of the base64 alphabet are ignored in Radix-64 +data. Decoding software must ignore all line breaks or other +characters not found in the table above. + +In Radix-64 data, characters other than those in the table, line +breaks, and other white space probably indicate a transmission error, +about which a warning message or even a message rejection might be +appropriate under some circumstances. + +Because it is used only for padding at the end of the data, the +occurrence of any "=" characters may be taken as evidence that the end +of the data has been reached (without truncation in transit). No such +assurance is possible, however, when the number of octets transmitted +was a multiple of three and no "=" characters are present. + + +2.4.4 Examples of Radix-64 + +Input data: 0x14fb9c03d97e +Hex: 1 4 f b 9 c | 0 3 d 9 7 e +8-bit: 00010100 11111011 10011100 | 00000011 11011001 11111110 +6-bit: 000101 001111 101110 011100 | 000000 111101 100111 111110 +Decimal: 5 15 46 28 0 61 37 63 +Output: F P u c A 9 l / + +Input data: 0x14fb9c03d9 +Hex: 1 4 f b 9 c | 0 3 d 9 +8-bit: 00010100 11111011 10011100 | 00000011 11011001 + pad with 00 +6-bit: 000101 001111 101110 011100 | 000000 111101 100100 +Decimal: 5 15 46 28 0 61 36 + pad with = + + +Callas, et. al. Expires May 1998 [Page 8] +Internet Draft OpenPGP Message Format Nov 1998 + + +Output: F P u c A 9 k = + +Input data: 0x14fb9c03 +Hex: 1 4 f b 9 c | 0 3 +8-bit: 00010100 11111011 10011100 | 00000011 + pad with 0000 +6-bit: 000101 001111 101110 011100 | 000000 110000 +Decimal: 5 15 46 28 0 48 + pad with = = +Output: F P u c A w = = + + +2.5 Example of an ASCII Armored Message + + -----BEGIN PGP MESSAGE----- + Version: OP V0.0 + + owFbx8DAYFTCWlySkpkHZDKEFCXmFedmFhdn5ucpZKdWFiv4hgaHKPj5hygUpSbn + l6UWpabo8XIBAA== + =3m1o + -----END PGP MESSAGE----- + +Note that this example is indented by two spaces. + +2.6 Cleartext signature framework + +Sometimes it is necessary to sign a textual octet stream without ASCII +armoring the stream itself, so the signed text is still readable +without special software. In order to bind a signature to such a +cleartext, this framework is used. (Note that RFC 2015 defines another +way to clear sign messages for environments that support MIME.) + +The cleartext signed message consists of: + - The cleartext header '-----BEGIN PGP SIGNED MESSAGE-----' on a + single line, + - Zero or more "Hash" Armor Headers (3.1.2.4), + - Exactly one empty line not included into the message digest, + - The dash-escaped cleartext that is included into the message digest, + - The ASCII armored signature(s) including the Armor Header and Armor + Tail Lines. + +If the "Hash" armor header is given, the specified message digest +algorithm is used for the signature. If this header is missing, SHA-1 +is assumed. If more than one message digest is used in the signature, +the "Hash" armor header contains a comma-delimited list of used message +digests. As an abbreviation, the "Hash" armor header may be placed on +the cleartext header line, inserting a comma after the word 'MESSAGE', +as follows: + +'-----BEGIN PGP SIGNED MESSAGE, Hash: MD5, SHA1'. + +{{Editor's note: Should the above armor header line stay or go? +There's no reason that the "Hash:" armor header can't have multiple + + +Callas, et. al. Expires May 1998 [Page 9] +Internet Draft OpenPGP Message Format Nov 1998 + + +hashes in it. I think anything that reduces parsing complexity is a +Good Thing. --jdcc}} + +Current message digest names are: + + - "SHA1" + - "MD5" + - "RIPEMD160" + +Dash escaped cleartext is the ordinary cleartext where every line +starting with a dash '-' (0x2D) is prepended by the sequence dash '-' +(0x2D) and space ' ' (0x20). This prevents the parser from recognizing +armor headers of the cleartext itself. The message digest is computed +using the cleartext itself, not the dash escaped form. + +As with binary signatures on text documents (see below), the cleartext +signature is calculated on the text using canonical <CR><LF> line +endings. The line ending (i.e. the <CR><LF>) before the '-----BEGIN +PGP SIGNATURE-----' line that terminates the signed text is not +considered part of the signed text. + +Also, any trailing whitespace (spaces, and tabs, 0x09) at the end of +any line is ignored when the cleartext signature is calculated. + +3. Data Element Formats + +This section describes the data elements used by OP. + +3.1 Scalar numbers + +Scalar numbers are unsigned, and are always stored in big-endian +format. Using n[k] to refer to the kth octet being interpreted, the +value of a two-octet scalar is ((n[0] << 8) + n[1]). The value of a +four-octet scalar is ((n[0] << 24) + (n[1] << 16) + (n[2] << 8) + +n[3]). + +3.2 Multi-Precision Integers + +Multi-Precision Integers (also called MPIs) are unsigned integers used +to hold large integers such as the ones used in cryptographic +calculations. + +An MPI consists of two pieces: a two-octet scalar that is the length of +the MPI in bits followed by a string of octets that contain the actual +integer. + +These octets form a big-endian number; a big-endian number can be made +into an MPI by prefixing it with the appropriate length. + +Examples: + +(all numbers are in hexadecimal) + + + +Callas, et. al. Expires May 1998 [Page 10] +Internet Draft OpenPGP Message Format Nov 1998 + + +The string of octets [00 01 01] forms an MPI with the value 1. The +string [00 09 01 FF] forms an MPI with the value of 511. + +Additional rules: + +The size of an MPI is ((MPI.length + 7) / 8) + 2. + +The length field of an MPI describes the length starting from its most +significant non-zero bit. Thus, the MPI [00 02 01] is not formed +correctly. It should be [00 01 01]. + + +3.3 Counted Strings + +A counted string consists of a length and then N octets of string data. +Its default character set is UTF-8 [RFC2044] encoding of Unicode +[ISO10646]. + +3.4 Time fields + +A time field is an unsigned four-octet number containing the number of +seconds elapsed since midnight, 1 January 1970 UTC. + +3.5 String-to-key (S2K) specifiers + +String-to-key (S2K) specifiers are used to convert passphrase strings +into conventional encryption/decryption keys. They are used in two +places, currently: to encrypt the secret part of private keys in the +private keyring, and to convert passphrases to encryption keys for +conventionally encrypted messages. + +3.5.1 String-to-key (S2k) specifier types + +There are three types of S2K specifiers currently supported, as +follows: + +3.5.1.1 Simple S2K + +This directly hashes the string to produce the key data. See below for +how this hashing is done. + + Octet 0: 0x00 + Octet 1: hash algorithm + +3.5.1.2 Salted S2K + +This includes a "salt" value in the S2K specifier -- some arbitrary +data -- that gets hashed along with the passphrase string, to help +prevent dictionary attacks. + + Octet 0: 0x01 + Octet 1: hash algorithm + Octets 2-9: 8-octet salt value + + +Callas, et. al. Expires May 1998 [Page 11] +Internet Draft OpenPGP Message Format Nov 1998 + + + +3.5.1.3 Iterated and Salted S2K + +This includes both a salt and an octet count. The salt is combined +with the passphrase and the resulting value is hashed repeatedly. This +further increases the amount of work an attacker must do to try +dictionary attacks. + + Octet 0: 0x03 + Octet 1: hash algorithm + Octets 2-9: 8-octet salt value + Octet 10: count, in special format (described below) + +3.5.2 String-to-key usage + +Implementations MUST implement simple S2K and salted S2K specifiers. +Implementations MAY implement iterated and salted S2K specifiers. +Implementations SHOULD use salted S2K specifiers, as simple S2K +specifiers are more vulnerable to dictionary attacks. + +3.5.2.1 Secret key encryption + +An S2K specifier can be stored in the secret keyring to specify how to +convert the passphrase to a key that unlocks the secret data. Older +versions of PGP just stored a cipher algorithm octet preceding the +secret data or a zero to indicate that the secret data was unencrypted. +The MD5 hash function was always used to convert the passphrase to a +key for the specified cipher algorithm. + +For compatibility, when an S2K specifier is used, the special value 255 +is stored in the position where the hash algorithm octet would have +been in the old data structure. This is then followed immediately by a +one-octet algorithm identifier, and then by the S2K specifier as +encoded above. + +Therefore, preceding the secret data there will be one of these +possibilities: + + 0 secret data is unencrypted (no pass phrase) + 255 followed by algorithm octet and S2K specifier + Cipher alg use Simple S2K algorithm using MD5 hash + +This last possibility, the cipher algorithm number with an implicit use +of MD5 is provided for backward compatibility; it should be understood, +but not generated. + +These are followed by an 8-octet Initial Vector for the decryption of +the secret values, if they are encrypted, and then the secret key +values themselves. + +3.5.2.2 Conventional message encryption + +PGP 2.X always used IDEA with Simple string-to-key conversion when + + +Callas, et. al. Expires May 1998 [Page 12] +Internet Draft OpenPGP Message Format Nov 1998 + + +conventionally encrypting a message. PGP 5 can create a Conventional +Encrypted Session Key packet at the front of a message. This can be +used to allow S2K specifiers to be used for the passphrase conversion, +to allow other ciphers than IDEA to be used, or to create messages with +a mix of conventional ESKs and public key ESKs. This allows a message +to be decrypted either with a passphrase or a public key. + +3.5.3 String-to-key algorithms + +3.5.3.1 Simple S2K algorithm + +Simple S2K hashes the passphrase to produce the session key. The +manner in which this is done depends on the size of the session key +(which will depend on the cipher used) and the size of the hash +algorithm's output. If the hash size is greater than or equal to the +session key size, the leftmost octets of the hash are used as the key. + +If the hash size is less than the key size, multiple instances of the +hash context are created -- enough to produce the required key data. +These instances are preloaded with 0, 1, 2, ... octets of zeros (that +is to say, the first instance has no preloading, the second gets +preloaded with 1 octet of zero, the third is preloaded with two octets +of zeros, and so forth). + +As the data is hashed, it is given independently to each hash context. +Since the contexts have been initialized differently, they will each +produce different hash output. Once the passphrase is hashed, the +output data from the multiple hashes is concatenated, first hash +leftmost, to produce the key data, with any excess octets on the right +discarded. + +3.5.3.2 Salted S2K algorithm + +Salted S2K is exactly like Simple S2K, except that the input to the +hash function(s) consists of the 8 octets of salt from the S2K +specifier, followed by the passphrase. + +3.5.3.3 Iterated-Salted S2K algorithm + +{{Editor's note: This is very complex, with bizarre things like an +8-bit floating point format. Should we just drop it? --jdcc}} + +Iterated-Salted S2K hashes the passphrase and salt data multiple times. +The total number of octets to be hashed is encoded in the count octet +that follows the salt in the S2K specifier. The count value is stored +as a normalized floating-point value with 4 bits of exponent and 4 bits +of mantissa. The formula to convert from the count octet to a count of +the number of octets to be hashed is as follows, letting the high 4 +bits of the count octet be CEXP and the low four bits be CMANT: + + count of octets to be hashed = (16 + CMANT) << (CEXP + 6) + +This allows encoding hash counts as low as 16 << 6 or 1024 (using an + + +Callas, et. al. Expires May 1998 [Page 13] +Internet Draft OpenPGP Message Format Nov 1998 + + +octet value of 0), and as high as 31 << 21 or 65011712 (using an octet +value of 0xff). Note that the resulting count value is an octet count +of how many octets will be hashed, not an iteration count. + +Initially, one or more hash contexts are set up as with the other S2K +algorithms, depending on how many octets of key data are needed. Then +the salt, followed by the passphrase data is repeatedly hashed until +the number of octets specified by the octet count has been hashed. The +one exception is that if the octet count is less than the size of the +salt plus passphrase, the full salt plus passphrase will be hashed even +though that is greater than the octet count. After the hashing is done +the data is unloaded from the hash context(s) as with the other S2K +algorithms. + +4. Packet Syntax + +This section describes the packets used by OP. + +4.1 Overview + +An OP message is constructed from a number of records that are +traditionally called packets. A packet is a chunk of data that has a +tag specifying its meaning. An OP message, keyring, certificate, and +so forth consists of a number of packets. Some of those packets may +contain other OP packets (for example, a compressed data packet, when +uncompressed, contains OP packets). + +Each packet consists of a packet header, followed by the packet body. +The packet header is of variable length. + +4.2 Packet Headers + +The first octet of the packet header is called the "Packet Tag." It +determines the format of the header and denotes the packet contents. +The remainder of the packet header is the length of the packet. + +Note that the most significant bit is the left-most bit, called bit 7. +A mask for this bit is 0x80 in hexadecimal. + + +---------------+ + PTag |7 6 5 4 3 2 1 0| + +---------------+ + Bit 7 -- Always one + Bit 6 -- New packet format if set + + +PGP 2.6.X only uses old format packets. Thus, software that +interoperates with those versions of PGP must only use old format +packets. If interoperability is not an issue, either format may be +used. + +Old format packets contain: + Bits 5-2 -- content tag + + +Callas, et. al. Expires May 1998 [Page 14] +Internet Draft OpenPGP Message Format Nov 1998 + + + Bits 1-0 - length-type + +New format packets contain: + Bits 5-0 -- content tag + +The meaning of the length-type in old-format packets is: + +0 - The packet has a one-octet length. The header is 2 octets long. + +1 - The packet has a two-octet length. The header is 3 octets long. + +2 - The packet has a four-octet length. The header is 5 octets long. + +3 - The packet is of indeterminate length. The header is 1 byte long, +and the application must determine how long the packet is. If the +packet is in a file, this means that the packet extends until the end +of the file. In general, an application should not use indeterminate +length packets except where the end of the data will be clear from the +context. + +New format packets have three possible ways of encoding length. A +one-octet Body Length header encodes packet lengths of up to 191 +octets, and a two-octet Body Length header encodes packet lengths of +192 to 8383 octets. For cases where longer packet body lengths are +needed, or where the length of the packet body is not known in advance +by the issuer, Partial Body Length headers can be used. These are +one-octet length headers that encode the length of only part of the +data packet. + +Each Partial Body Length header is followed by a portion of the packet +body data. The Partial Body Length header specifies this portion's +length. Another length header (of one of the three types) follows that +portion. The last length header in the packet must always be a regular +Body Length header. Partial Body Length headers may only be used for +the non-final parts of the packet. + +A one-octet Body Length header encodes a length of from 0 to 191 +octets. This type of length header is recognized because the one octet +value is less than 192. The body length is equal to: + +bodyLen = length_octet; + +A two-octet Body Length header encodes a length of from 192 to 8383 +octets. It is recognized because its first octet is in the range 192 +to 223. The body length is equal to: + +bodyLen = (1st_octet - 192) * 256 + (2nd_octet) + 192 + +A Partial Body Length header is one octet long and encodes a length +which is a power of 2, from 1 to 2147483648 (2 to the 31st power). It +is recognized because its one octet value is greater than or equal to +224. The partial body length is equal to: + + + +Callas, et. al. Expires May 1998 [Page 15] +Internet Draft OpenPGP Message Format Nov 1998 + + +partialBodyLen = 1 << (length_octet & 0x1f); + +Examples: + +A packet with length 100 may have its length encoded in one octet: +0x64. This is followed by 100 octets of data. + +A packet with length 1723 may have its length coded in two octets: +0xC5, 0xFB. This header is followed by the 1723 octets of data. + +A packet with length 100000 might be encoded in the following octet +stream: 0xE1, first two octets of data, 0xE0, next one octet of data, +0xEF, next 32768 octets of data, 0xF0, next 65536 octets of data, 0xC5, +0xDD, last 1693 octets of data. This is just one possible encoding, +and many variations are possible on the size of the Partial Body Length +headers, as long as a regular Body Length header encodes the last +portion of the data. Note also that the last Body Length header can be +a zero-length header. + + +Please note that in all of these explanations, the total length of the +packet is the length of the header(s) plus the length of the body. + +4.3 Packet Tags + +The packet tag denotes what type of packet the body holds. Note that +old format packets can only have tags less than 16, whereas new format +packets can have tags as great as 63. The defined tags (in decimal) +are: + +0 -- Reserved. A packet must not have a tag with this value. +1 -- Encrypted Session Key Packet +2 -- Signature Packet +3 -- Conventionally Encrypted Session Key Packet +4 -- One-Pass Signature Packet +5 -- Secret Key Packet +6 -- Public Key Packet +7 -- Secret Subkey Packet +8 -- Compressed Data Packet +9 -- Symmetrically Encrypted Data Packet +10 -- Marker Packet +11 -- Literal Data Packet +12 -- Trust Packet +13 -- Name Packet +14 -- Subkey Packet +15 -- Reserved +16 -- Comment Packet +60 to 63 -- Private or Experimental Values + +5. Packet Types + +5.1 Encrypted Session Key Packets (Tag 1) + + + +Callas, et. al. Expires May 1998 [Page 16] +Internet Draft OpenPGP Message Format Nov 1998 + + +An Encrypted Session Key packet holds the key used to encrypt a message +that is itself encrypted with a public key. Zero or more Encrypted +Session Key packets and/or Conventional Encrypted Session Key packets +may precede a Symmetrically Encrypted Data Packet, which holds an +encrypted message. The message is encrypted with a session key, and +the session key is itself encrypted and stored in the Encrypted Session +Key packet or the Conventional Encrypted Session Key packet. The +Symmetrically Encrypted Data Packet is preceded by one Encrypted +Session Key packet for each OP key to which the message is encrypted. +The recipient of the message finds a session key that is encrypted to +their public key, decrypts the session key, and then uses the session +key to decrypt the message. + +The body of this packet consists of: + + - A one-octet number giving the version number of the packet type. + The currently defined value for packet version is 3. An + implementation should accept, but not generate a version of 2, + which is equivalent to V3 in all other respects. + - An eight-octet number that gives the key ID of the public key that + the session key is encrypted to. + - A one-octet number giving the public key algorithm used. + - A string of octets that is the encrypted session key. This + string takes up the remainder of the packet, and its contents are + dependent on the public key algorithm used. + + Algorithm Specific Fields for RSA encryption + - multiprecision integer (MPI) of RSA encrypted value m**e. + + Algorithm Specific Fields for Elgamal encryption: + - MPI of DSA value g**k. + - MPI of DSA value m * y**k. + +The encrypted value "m" in the above formulas is derived from the +session key as follows. First the session key is prepended with a +one-octet algorithm identifier that specifies the conventional +encryption algorithm used to encrypt the following Symmetrically +Encrypted Data Packet. Then a two-octet checksum is appended which is +equal to the sum of the preceding octets, including the algorithm +identifier and session key, modulo 65536. This value is then padded as +described in PKCS-1 block type 02 [PKCS1] to form the "m" value used in +the formulas above. + +5.2 Signature Packet (Tag 2) + +A signature packet describes a binding between some public key and some +data. The most common signatures are a signature of a file or a block +of text, and a signature that is a certification of a user ID. + +Two versions of signature packets are defined. Version 3 provides +basic signature information, while version 4 provides an expandable +format with subpackets that can specify more information about the +signature. PGP 2.6.X only accepts version 3 signatures. + + +Callas, et. al. Expires May 1998 [Page 17] +Internet Draft OpenPGP Message Format Nov 1998 + + + +Implementations MUST accept V3 signatures. Implementations SHOULD +generate V4 signatures, unless there is a need to generate a signature +that can be verified by PGP 2.6.x. + +5.2.1 Version 3 Signature Packet Format + +A version 3 Signature packet contains: + - One-octet version number (3). + - One-octet length of following hashed material. MUST be 5. + - One-octet signature type. + - Four-octet creation time. + - Eight-octet key ID of signer. + - One-octet public key algorithm. + - One-octet hash algorithm. + - Two-octet field holding left 16 bits of signed hash value. + - One or more multi-precision integers comprising the signature. + This portion is algorithm specific, as described below. + +The data being signed is hashed, and then the signature type and +creation time from the signature packet are hashed (5 additional +octets). The resulting hash value is used in the signature algorithm. +The high 16 bits (first two octets) of the hash are included in the +signature packet to provide a quick test to reject some invalid +signatures. + + Algorithm Specific Fields for RSA signatures: + - multiprecision integer (MPI) of RSA signature value m**d. + + Algorithm Specific Fields for DSA signatures: + - MPI of DSA value r. + - MPI of DSA value s. + +The signature calculation is based on a hash of the signed data, as +described above. The details of the calculation are different for DSA +signature than for RSA signatures. + +With RSA signatures, the hash value is encoded as described in PKCS-1 +section 10.1.2, "Data encoding", producing an ASN.1 value of type +DigestInfo, and then padded using PKCS-1 block type 01 [PKCS1]. This +requires inserting the hash value as an octet string into an ASN.1 +structure. The object identifier for the type of hash being used is +included in the structure. The hexadecimal representations for the +currently defined hash algorithms are: + + - SHA-1: 0x2b, 0x0e, 0x03, 0x02, 0x1a + - MD5: 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05 + - RIPEMD-160: 0x2b, 0x24, 0x03, 0x02, 0x01 + +The ASN.1 OIDs are: + - MD5: 1.2.840.113549.2.5 + - SHA-1: 1.3.14.3.2.26 + - RIPEMD160: 1.3.36.3.2.1 + + +Callas, et. al. Expires May 1998 [Page 18] +Internet Draft OpenPGP Message Format Nov 1998 + + + + +DSA signatures SHOULD use hashes with a size of 160 bits, to match q, +the size of the group generated by the DSA key's generator value. The +hash function result is treated as a 160 bit number and used directly +in the DSA signature algorithm. + +5.2.2 Version 4 Signature Packet Format + +A version 4 Signature packet contains: + - One-octet version number (4). + - One-octet signature type. + - One-octet public key algorithm. + - One-octet hash algorithm. + - Two-octet octet count for following hashed subpacket data. + - Hashed subpacket data. + - Two-octet octet count for following unhashed subpacket data. + - Unhashed subpacket data. + - Two-octet field holding left 16 bits of signed hash value. + - One or more multi-precision integers comprising the signature. + This portion is algorithm specific, as described above. + +The data being signed is hashed, and then the signature data from the +version number through the hashed subpacket data is hashed. The +resulting hash value is what is signed. The left 16 bits of the hash +are included in the signature packet to provide a quick test to reject +some invalid signatures. + +There are two fields consisting of signature subpackets. The first +field is hashed with the rest of the signature data, while the second +is unhashed. The second set of subpackets is not cryptographically +protected by the signature and should include only advisory +information. + +The algorithms for converting the hash function result to a signature +are described above. + +5.2.2.1 Signature Subpacket Specification + +The subpacket fields consist of zero or more signature subpackets. +Each set of subpackets is preceded by a two-octet count of the length +of the set of subpackets. + +Each subpacket consists of a subpacket header and a body. The header +consists of: + + - subpacket length (1 or 2 octets): + Length includes the type octet but not this length, + 1st octet < 192, then length is octet value + 1st octet >= 192, then length is 2 octets and equal to + (1st octet - 192) * 256 + (2nd octet) + 192 + - subpacket type (1 octet): + If bit 7 is set, subpacket understanding is critical, + + +Callas, et. al. Expires May 1998 [Page 19] +Internet Draft OpenPGP Message Format Nov 1998 + + + 2 = signature creation time, + 3 = signature expiration time, + 4 = exportable, + 5 = trust signature, + 6 = regular expression, + 7 = revocable, + 9 = key expiration time, + 10 = additional recipient request, + 11 = preferred symmetric algorithms, + 12 = revocation key, + 16 = issuer key ID, + 20 = notation data, + 21 = preferred hash algorithms, + 22 = preferred compression algorithms, + 23 = key server preferences, + 24 = preferred key server + + - subpacket specific data: + +Bit 7 of the subpacket type is the "critical" bit. If set, it implies +that it is critical that the subpacket be one which is understood by +the software. If a subpacket is encountered which is marked critical +but the software does not understand, the handling depends on the +relationship between the issuing key and the key that is signed. If +the signature is a valid self-signature (for which the issuer is the +key that is being signed, either directly or via a username binding), +then the key should not be used. In other cases, the signature +containing the critical subpacket should be ignored. + +5.2.2.2 Signature Subpacket Types + +Several types of subpackets are currently defined. Some subpackets +apply to the signature itself and some are attributes of the key. +Subpackets that are found on a self-signature are placed on a user name +certification made by the key itself. Note that a key may have more +than one user name, and thus may have more than one self-signature, and +differing subpackets. + +Implementing software should interpret a self-signature's preference +subpackets as narrowly as possible. For example, suppose a key has two +usernames, Alice and Bob. Suppose that Alice prefers the symmetric +algorithm CAST5, and Bob prefers IDEA or Triple-DES. If the software +locates this key via Alice's name, then the preferred algorithm is +CAST5, if software locates the key via Bob's name, then the preferred +algorithm is IDEA. If the key is located by key id, then algorithm of +the default user name of the key provides the default symmetric +algorithm. + +The descriptions below describe whether a subpacket is typically found +in the hashed or unhashed subpacket sections. If a subpacket is not +hashed, then it cannot be trusted. + + Signature creation time (4 octet time field) (Hashed) + + +Callas, et. al. Expires May 1998 [Page 20] +Internet Draft OpenPGP Message Format Nov 1998 + + + +The time the signature was made. Always included with new signatures. + + Issuer (8 octet key ID) (Non-hashed) + +The OP key ID of the key issuing the signature. + + Key expiration time (4 octet time field) (Hashed) + +The validity period of the key. This is the number of seconds after +the key creation time that the key expires. If this is not present or +has a value of zero, the key never expires. This is found only on a +self-signature. + + Preferred symmetric algorithms (array of one-octet values) (Hashed) + +Symmetric algorithm numbers that indicate which algorithms the key +holder prefers to use. This is an ordered list of octets with the most +preferred listed first. It should be assumed that only algorithms +listed are supported by the recipient's software. Algorithm numbers in +section 6. This is only found on a self-signature. + + Preferred hash algorithms (array of one-octet values) (Hashed) + +Message digest algorithm numbers that indicate which algorithms the key +holder prefers to receive. Like the preferred symmetric algorithms, +the list is ordered. Algorithm numbers are in section 6. This is only +found on a self-signature. + +{{Editor's note: The above preference (hash algs) is controversial. I +included it in for symmetry, because if someone wants to build a +minimal OP implementation, there needs to be a way to tell someone that +you won't be able to verify a signature unless it's made with some set +of algorithms. It also permits to prefer DSA with RIPEMD-160, for +example. If you have an opinion, please state it.}} + + Preferred compression algorithms (array of one-octet values) + (Hashed) + +Compression algorithm numbers that indicate which algorithms the key +holder prefers to use. Like the preferred symmetric algorithms, the +list is ordered. Algorithm numbers are in section 6. If this +subpacket is not included, ZIP is preferred. A zero denotes that no +compression is preferred; the key holder's software may not have +compression software. This is only found on a self-signature. + + Signature expiration time (4 octet time field) (Hashed) + +The validity period of the signature. This is the number of seconds +after the signature creation time that the signature expires. If this +is not present or has a value of zero, it never expires. + + Exportable (1 octet of exportability, 0 for not, 1 for exportable) + + +Callas, et. al. Expires May 1998 [Page 21] +Internet Draft OpenPGP Message Format Nov 1998 + + + (Hashed) + +Signature's exportability status. Packet body contains a boolean flag +indicating whether the signature is exportable. Signatures which are +not exportable are ignored during export and import operations. If +this packet is not present the signature is assumed to be exportable. + + Revocable (1 octet of revocability, 0 for not, 1 for revocable) + (Hashed) + +Signature's revocability status. Packet body contains a boolean flag +indicating whether the signature is revocable. Signatures which are +not revocable get any later revocation signatures ignored. They +represent a commitment by the signer that he cannot revoke his +signature for the life of his key. If this packet is not present the +signature is assumed to be revocable. + + Trust signature (1 octet of "level" (depth), 1 octet of trust amount) + (Hashed) +Signer asserts that the key is not only valid, but also trustworthy, at +the specified level. Level 0 has the same meaning as an ordinary +validity signature. Level 1 means that the signed key is asserted to +be a valid trusted introducer, with the 2nd octet of the body +specifying the degree of trust. Level 2 means that the signed key is +asserted to be trusted to issue level 1 trust signatures, i.e. that it +is a "meta introducer". Generally, a level n trust signature asserts +that a key is trusted to issue level n-1 trust signatures. The trust +amount is in a range from 0-255, interpreted such that values less than +120 indicate partial trust and values of 120 or greater indicate +complete trust. Implementations SHOULD emit values of 60 for partial +trust and 120 for complete trust. + + Regular expression (null-terminated regular expression) (Hashed) + +Used in conjunction with trust signature packets (of level > 0) to +limit the scope of trust which is extended. Only signatures by the +target key on user IDs which match the regular expression in the body +of this packet have trust extended by the trust packet. + + Additional recipient request (1 octet of class, 1 octet of algid, + 20 octets of fingerprint) (Hashed) + +Key holder requests encryption to additional recipient when data is +encrypted to this username. If the class octet contains 0x80, then the +key holder strongly requests that the additional recipient be added to +an encryption. Implementing software may treat this subpacket in any +way it sees fit. This is found only on a self-signature. + + Revocation key (1 octet of class, 1 octet of algid, 20 octets of + fingerprint) (Hashed) + +Authorizes the specified key to issue revocation self-signatures on +this key. Class octet must have bit 0x80 set, other bits are for + + +Callas, et. al. Expires May 1998 [Page 22] +Internet Draft OpenPGP Message Format Nov 1998 + + +future expansion to other kinds of signature authorizations. This is +found on a self-signature. + + Notation Data (4 octets of flags, 2 octets of name length, + 2 octets of value length, M octets of name data, + N octets of value data) (Hashed) + +This subpacket describes a "notation" on the signature that the issuer +wishes to make. The notation has a name and a value, each of which are +strings of octets. There may be more than one notation in a signature. +Notations can be used for any extension the issuer of the signature +cares to make. The "flags" field holds four octets of flags. All +undefined flags MUST be zero. Defined flags are: + First octet: 0x80 = human-readable. This note is text, a note + from one person to another, and has no + meaning to software. + Other octets: none. + + Key server preferences (N octets of flags) (Hashed) + +This is a list of flags that indicate preferences that the key holder +has about how the key is handled on a key server. All undefined flags +MUST be zero. + + First octet: 0x80 = No-modify -- the key holder requests that + this key only be modified or updated by the + key holder or an authorized administrator of + the key server. +This is found only on a self-signature. + + Preferred key server (String) (Hashed) + +This is a URL of a key server that the key holder prefers be used for +updates. Note that keys with multiple user names can have a preferred +key server for each user name. This is found only on a self-signature. + +Implementations SHOULD implement a "preference" and MAY implement a +"request." + +{{Editor's note: None of the preferences have a way to specify a +negative preference (for example, I like Triple-DES, don't use +algorithm X). Tacitly, the absence of an algorithm from a set is a +negative preference, but should there be an explicit way to give a +negative preference? -jdcc}} + +{{Editor's note: A missing feature is to invalidate (or revoke) a user +id, rather than the entire key. Lots of people want this, and many +people have keys cluttered with old work email addresses. There is +another related issue, that that is with key rollover -- suppose I'm +retiring an old key, but I don't want to have to lose all my +certification signatures. It would be nice if there were a way for a +key to transfer itself to a new one. Lastly, if either (or both) of +these is desirable, do we handle them with a new signature type, or + + +Callas, et. al. Expires May 1998 [Page 23] +Internet Draft OpenPGP Message Format Nov 1998 + + +with notations, which are an extension mechanism. I think that it +makes sense to make a revocation type (because it's analogous to the +other forms of revocation), but rollover might be best implemented as +an extension. --jdcc}} + +{{Editor's note: PGP 3 designed, but never implemented a number of +other subpacket types. They were: A signature version number; A set +of key usage flags (signing key, encryption key for communication, and +encryption key for storage); User ID of the signer; Policy URL; net +location of the key. + +Some of these options are things the WG has talked about as being a +Good Thing -- like flags denoting if a key is a comm key or a storage +key. My design of such a feature would be different than the other +one, though. I think it would be a great idea to have a URL that's a +location to find the key, so people who prefer to have a web, ftp, or +finger location can use those. However, some of them (like a URL) are +also perfect for designing in with extensions. After all, we only have +128 subpacket constants. + +--jdcc}} + +5.2.3 Signature Types + +There are a number of possible meanings for a signature, which are +specified in a signature type octet in any given signature. These +meanings are: + + - 0x00: Signature of a binary document. +Typically, this means the signer owns it, created it, or certifies that +it has not been modified. + + - 0x01: Signature of a canonical text document. +Typically, this means the signer owns it, created it, or certifies that +it has not been modified. The signature will be calculated over the +textual data with its line endings converted to <CR><LF>. + + - 0x02: Standalone signature. +This signature is a signature of only its own subpacket contents. It +is calculated identically to a signature over a zero-length binary +document. + + - 0x10: The generic certification of a User ID and Public Key + packet. +The issuer of this certification does not make any particular assertion +as to how well the certifier has checked that the owner of the key is +in fact the person described by the user ID. Note that all PGP "key +signatures" are this type of certification. + + - 0x11: This is a persona certification of a User ID and + Public Key packet. +It means that the issuer of this certification has not done any +verification of the claim that the owner of this key is the user ID + + +Callas, et. al. Expires May 1998 [Page 24] +Internet Draft OpenPGP Message Format Nov 1998 + + +specified. Note that no released version of PGP has generated this +type of certification. + + - 0x12: This is the casual certification of a User ID and + Public Key packet. +It means that the issuer of this certification has done some casual +verification of the claim of identity. Note that no version of PGP has +generated this type of certification, nor is there any definition of +what constitutes a casual certification. + + - 0x13: This is the positive certification of a User ID and + Public Key packet. +It means that the issuer of this certification has done substantial +verification of the claim of identity. Note that no version of PGP has +generated this type of certification, nor is there any definition of +what constitutes a positive certification. Please also note that the +vagueness of these certification systems is not a flaw, but a feature +of the system. Because PGP places final authority for validity upon +the receiver of a certification, it may be that one authority's casual +certification might be more rigorous than some other authority's +positive certification. + +{{Editor's note: While there is a scale of identification signatures +in the range 0x10 to 0x13, most of them have never been implemented or +used. Current implementations only use 0x10, the "generic +certification." Should the others be removed? RFC 1991 went to some +trouble to explain which ones were defined but not implemented, or read +but not generated. I think we should not do that. If we define them, +they should be MAY features at the very least. If we're not going to +use them, they shouldn't be in the spec. --jdcc}} + + - 0x18: This is used for a signature by a signature key to bind a + subkey which will be used for encryption. +The signature is calculated directly on the subkey itself, not on any +User ID or other packets. + + - 0x20: This signature is used to revoke a key. +The signature is calculated directly on the key being revoked. A +revoked key is not to be used. Only revocation signatures by the key +being revoked, or by an authorized revocation key, should be +considered. + + - 0x28: This is used to revoke a subkey. +The signature is calculated directly on the subkey being revoked. A +revoked subkey is not to be used. Only revocation signatures by the +top-level signature key which is bound to this subkey, or by an +authorized revocation key, should be considered. + + - 0x30: This signature revokes an earlier user ID certification + signature (signature class 0x10 - 0x13). +It should be issued by the same key which issued the revoked signature, +and should have a later creation date. + + + +Callas, et. al. Expires May 1998 [Page 25] +Internet Draft OpenPGP Message Format Nov 1998 + + + - 0x40: Timestamp signature. + +{{Editor's note: The timestamp signature is left over from RFC 1991, +and has never been fully designed nor implemented. Is this the sort of +thing best handled by notations? --jdcc}} + +{{Editor's note: It would be nice to have a signature that applied to +the key alone, rather than a key plus a user name. Perhaps this is +best done with a notation. --jdcc}} + +{{Editor's note: There is presently no way for a key-signer (a.k.a. +certifier) to sign a main key along with a subkey. There are a number +of useful situations for a set of keys (main plus subkeys) to all be +signed together. How do we solve this? --jdcc}} + +5.3 Conventional Encrypted Session-Key Packets (Tag 3) + +The Conventional Encrypted Session Key packet holds the +conventional-cipher encryption of a session key used to encrypt a +message. Zero or more Encrypted Session Key packets and/or +Conventional Encrypted Session Key packets may precede a Symmetrically +Encrypted Data Packet that holds an encrypted message. The message is +encrypted with a session key, and the session key is itself encrypted +and stored in the Encrypted Session Key packet or the Conventional +Encrypted Session Key packet. + +If the Symmetrically Encrypted Data Packet is preceded by one or more +Conventional Encrypted Session Key packets, each specifies a passphrase +which may be used to decrypt the message. This allows a message to be +encrypted to a number of public keys, and also to one or more pass +phrases. This packet type is new, and is not generated by PGP 2.x or +PGP 5.0. + +The body of this packet consists of: + - A one-octet version number. The only currently defined version is + 4. + - A one-octet number describing the symmetric algorithm used. + - A string-to-key (S2K) specifier, length as defined above. + - Optionally, the encrypted session key itself, which is decrypted + with the string-to-key object. + +If the encrypted session key is not present (which can be detected on +the basis of packet length and S2K specifier size), then the S2K +algorithm applied to the passphrase produces the session key for +decrypting the file, using the symmetric cipher algorithm from the +Conventional Encrypted Session Key packet. + +If the encrypted session key is present, the result of applying the S2K +algorithm to the passphrase is used to decrypt just that encrypted +session key field, using CFB mode with an IV of all zeros. The +decryption result consists of a one-octet algorithm identifier that +specifies the conventional encryption algorithm used to encrypt the +following Symmetrically Encrypted Data Packet, followed by the session + + +Callas, et. al. Expires May 1998 [Page 26] +Internet Draft OpenPGP Message Format Nov 1998 + + +key octets themselves. + +Note: because an all-zero IV is used for this decryption, the S2K +specifier MUST use a salt value, either a a Salted S2K or an +Iterated-Salted S2K. The salt value will insure that the decryption +key is not repeated even if the passphrase is reused. + + +5.4 One-Pass Signature Packets (Tag 4) + +The One-Pass Signature packet precedes the signed data and contains +enough information to allow the receiver to begin calculating any +hashes needed to verify the signature. It allows the Signature Packet +to be placed at the end of the message, so that the signer can compute +the entire signed message in one pass. + +A One-Pass Signature does not interoperate with PGP 2.6.x or earlier. + +The body of this packet consists of: + - A one-octet version number. The current version is 3. + - A one-octet signature type. Signature types are described + in section 5.2.3. + - A one-octet number describing the hash algorithm used. + - A one-octet number describing the public key algorithm used. + - An eight-octet number holding the key ID of the signing key. + - A one-octet number holding a flag showing whether the signature +is nested. A zero value indicates that the next packet is +another One-Pass Signature packet which describes another +signature to be applied to the same message data. + + +5.5 Key Material Packet + +A key material packet contains all the information about a public or +private key. There are four variants of this packet type, and two +major versions. Consequently, this section is complex. + +5.5.1 Key Packet Variants + +5.5.1.1 Public Key Packet (Tag 6) + +A Public Key packet starts a series of packets that forms an OP key +(sometimes called an OP certificate). + +5.5.1.2 Public Subkey Packet (Tag 14) + +A Public Subkey packet (tag 14) has exactly the same format as a Public +Key packet, but denotes a subkey. One or more subkeys may be +associated with a top-level key. By convention, the top-level key +provides signature services, and the subkeys provide encryption +services. + +Note: in PGP 2.6.X, tag 14 was intended to indicate a comment packet. + + +Callas, et. al. Expires May 1998 [Page 27] +Internet Draft OpenPGP Message Format Nov 1998 + + +This tag was selected for reuse because no previous version of PGP ever +emitted comment packets but they did properly ignore them. Public +Subkey packets are ignored by PGP 2.6.X and do not cause it to fail, +providing a limited degree of backwards compatibility. + +5.5.1.3 Secret Key Packet (Tag 5) + +A Secret Key packet contains all the information that is found in a +Public Key packet, including the public key material, but also includes +the secret key material after all the public key fields. + +5.5.1.4 Secret Subkey Packet (Tag 7) + +A Secret Subkey packet (tag 7) is the subkey analog of the Secret Key +packet, and has exactly the same format. + +5.5.2 Public Key Packet Formats + +There are two versions of key-material packets. Version 3 packets were +first generated PGP 2.6. Version 2 packets are identical in format to +Version 3 packets, but are generated by PGP 2.5 or before. PGP 5.0 +introduces version 4 packets, with new fields and semantics. PGP 2.6.X +will not accept key-material packets with versions greater than 3. + +OP implementations SHOULD create keys with version 4 format. An +implementation MAY generate a V3 key to ensure interoperability with +old software; note, however, that V4 keys correct some security +deficiencies in V3 keys. These deficiencies are described below. An +implementation MUST NOT create a V3 key with a public key algorithm +other than RSA. + +A version 3 public key or public subkey packet contains: + - A one-octet version number (3). + - A four-octet number denoting the time that the key was created. + - A two-octet number denoting the time in days that this key is + valid. If this number is zero, then it does not expire. + - A one-octet number denoting the public key algorithm of this key + - A series of multi-precision integers comprising the key + material: + - a multiprecision integer (MPI) of RSA public modulus n; + - an MPI of RSA public encryption exponent e. + +The fingerprint of the key is formed by hashing the body (but not the +two-octet length) of the MPIs that form the key material (public +modulus n, followed by exponent e) with MD5. + +The eight-octet key ID of the key consists of the low 64 bits of the +public modulus of an RSA key. + +Since the release of V3 keys, there have been a number of improvements +desired in the key format. For example, if the key ID is a function of +the public modulus, it is easy for a person to create a key that has +the same key ID as some existing key. Similarly, MD5 is no longer the + + +Callas, et. al. Expires May 1998 [Page 28] +Internet Draft OpenPGP Message Format Nov 1998 + + +preferred hash algorithm, and not hashing the length of an MPI with its +body increases the chances of a fingerprint collision. + +The version 4 format is similar to the version 3 format except for the +absence of a validity period. This has been moved to the signature +packet. In addition, fingerprints of version 4 keys are calculated +differently from version 3 keys, as described elsewhere. + +A version 4 packet contains: + - A one-octet version number (4). + - A four-octet number denoting the time that the key was created. + - A one-octet number denoting the public key algorithm of this key + - A series of multi-precision integers comprising the key + material. This algorithm-specific portion is: + + Algorithm Specific Fields for RSA public keys: + - multiprecision integer (MPI) of RSA public modulus n; + - MPI of RSA public encryption exponent e. + + Algorithm Specific Fields for DSA public keys: + - MPI of DSA prime p; + - MPI of DSA group order q (q is a prime divisor of p-1); + - MPI of DSA group generator g; + - MPI of DSA public key value y (= g**x where x is secret). + + Algorithm Specific Fields for Elgamal public keys: + - MPI of Elgamal prime p; + - MPI of Elgamal group generator g; + - MPI of Elgamal public key value y (= g**x where x + is secret). + + +5.5.3 Secret Key Packet Formats + +The Secret Key and Secret Subkey packets contain all the data of the +Public Key and Public Subkey packets, with additional +algorithm-specific secret key data appended, in encrypted form. + +The packet contains: + - A Public Key or Public Subkey packet, as described above + - One octet indicating string-to-key usage conventions. 0 indicates + that the secret key data is not encrypted. 255 indicates that a + string-to-key specifier is being given. Any other value + is a conventional encryption algorithm specifier. + - [Optional] If string-to-key usage octet was 255, a one-octet + conventional encryption algorithm. + - [Optional] If string-to-key usage octet was 255, a string-to-key + specifier. The length of the string-to-key specifier is implied + by its type, as described above. + - [Optional] If secret data is encrypted, eight-octet Initial Vector + (IV). + - Encrypted multi-precision integers comprising the secret key data. + These algorithm-specific fields are as described below. + + +Callas, et. al. Expires May 1998 [Page 29] +Internet Draft OpenPGP Message Format Nov 1998 + + + - Two-octet checksum of the plaintext of the algorithm-specific + portion (sum of all octets, mod 65536). + + Algorithm Specific Fields for RSA secret keys: + - multiprecision integer (MPI) of RSA secret exponent d. + - MPI of RSA secret prime value p. + - MPI of RSA secret prime value q (p < q). + - MPI of u, the multiplicative inverse of p, mod q. + + Algorithm Specific Fields for DSA secret keys: + - MPI of DSA secret exponent x. + + Algorithm Specific Fields for Elgamal secret keys: + - MPI of Elgamal secret exponent x. + +Secret MPI values can be encrypted using a passphrase. If a +string-to-key specifier is given, that describes the algorithm for +converting the passphrase to a key, else a simple MD5 hash of the +passphrase is used. Implementations SHOULD use a string-to-key +specifier; the simple hash is for backwards compatibility. The cipher +for encrypting the MPIs is specified in the secret key packet. + +Encryption/decryption of the secret data is done in CFB mode using the +key created from the passphrase and the Initial Vector from the packet. +A different mode is used with RSA keys than with other key formats. +With RSA keys, the MPI bit count prefix (i.e., the first two octets) is +not encrypted. Only the MPI non-prefix data is encrypted. +Furthermore, the CFB state is resynchronized at the beginning of each +new MPI value, so that the CFB block boundary is aligned with the start +of the MPI data. + +With non-RSA keys, a simpler method is used. All secret MPI values are +encrypted in CFB mode, including the MPI bitcount prefix. + +The 16-bit checksum that follows the algorithm-specific portion is the +algebraic sum, mod 65536, of the plaintext of all the +algorithm-specific octets (including MPI prefix and data). With RSA +keys, the checksum is stored in the clear. With non-RSA keys, the +checksum is encrypted like the algorithm-specific data. This value is +used to check that the passphrase was correct. + +5.6 Compressed Data Packet (Tag 8) + +The Compressed Data packet contains compressed data. Typically, this +packet is found as the contents of an encrypted packet, or following a +Signature or One-Pass Signature packet, and contains literal data +packets. + +The body of this packet consists of: + - One octet that gives the algorithm used to compress the packet. + - The remainder of the packet is compressed data. + +A Compressed Data Packet's body contains an RFC1951 DEFLATE block that + + +Callas, et. al. Expires May 1998 [Page 30] +Internet Draft OpenPGP Message Format Nov 1998 + + +compresses some set of packets. See section 7 for details on how +messages are formed. + +5.7 Symmetrically Encrypted Data Packet (Tag 9) + +The Symmetrically Encrypted Data packet contains data encrypted with a +conventional (symmetric-key) algorithm. When it has been decrypted, it +will typically contain other packets (often literal data packets or +compressed data packets). + +The body of this packet consists of: + + - Encrypted data, the output of the selected conventional cipher + operating in PGP's variant of Cipher Feedback (CFB) mode. + +The conventional cipher used may be specified in an Encrypted Session +Key or Conventional Encrypted Session Key packet which precedes the +Symmetrically Encrypted Data Packet. In that case, the cipher +algorithm octet is prepended to the session key before it is encrypted. +If no packets of these types precede the encrypted data, the IDEA +algorithm is used with the session key calculated as the MD5 hash of +the passphrase. + +The data is encrypted in CFB mode, with a CFB shift size equal to the +cipher's block size [Ref]. The Initial Vector (IV) is specified as all +zeros. Instead of using an IV, OP prepends a 10 octet string to the +data before it is encrypted. The first eight octets are random, and +the 9th and 10th octets are copies of the 7th and 8th octets, +respectivelly. After encrypting the first 10 octets, the CFB state is +resynchronized if the cipher block size is 8 octets or less. The last +8 octets of ciphertext are passed through the cipher and the block +boundary is reset. + +The repetition of 16 bits in the 80 bits of random data prepended to +the message allows the receiver to immediately check whether the +session key is correct. + +5.8 Marker Packet (Obsolete Literal Packet) (Tag 10) + +An experimental version of PGP used this packet as the Literal packet, +but no released version of PGP generated Literal packets with this tag. +With PGP 5.x, this packet has been re-assigned and is reserved for use +as the Marker packet. + +The body of this packet consists of: + - The three octets 0x60, 0x47, 0x60 (which spell "PGP" in UTF-8). + +Such a packet should be ignored on input. It may be placed at the +beginning of a message that uses features not available in PGP 2.6.X in +order to cause that version to report that newer software necessary to +process the message. + +5.9 Literal Data Packet (Tag 11) + + +Callas, et. al. Expires May 1998 [Page 31] +Internet Draft OpenPGP Message Format Nov 1998 + + + +A Literal Data packet contains the body of a message; data that is not +to be further interpreted. + +The body of this packet consists of: + - A one-octet field that describes how the data is formatted. +If it is a 'b' (0x62), then the literal packet contains binary data. If +it is a 't' (0x74), then it contains text data, and thus may need line +ends converted to local form, or other text-mode changes. RFC 1991 +also defined a value of 'l' as a 'local' mode for machine-local +conversions. This use is now deprecated. + + - File name as a string (one-octet length, followed by file name), + if the encrypted data should be saved as a file. +If the special name "_CONSOLE" is used, the message is considered to be +"for your eyes only". This advises that the message data is unusually +sensitive, and the receiving program should process it more carefully, +perhaps avoiding storing the received data to disk, for example. + + - A four-octet number that indicates the modification date of the +file, or the creation time of the packet, or a zero that indicates the +present time. + + - The remainder of the packet is literal data. + +Text data is stored with <CR><LF> text endings. This should be +converted to native line endings by the receiving software. + +5.10 Trust Packet (Tag 12) + +The Trust packet is used only within keyrings and is not normally +exported. Trust packets contain data that record the user's +specifications of which key holders are trustworthy introducers, along +with other information that implementing software uses for trust +information. + +Trust packets SHOULD NOT be emitted to output streams that are +transferred to other users, and they SHOULD be ignored on any input +other than local keyring files. + +{{Editor's note: I have brushed aside the description of the old PGP +trust packets for a number of reasons. They are context dependent; +their meaning depends on the packet preceding them in a keyring. + +There is also a security problem with trust packets. For example, +malicious software can write a new public key into a user's key ring +with trust packets that make it trusted. + +A number of us have discussed this problem, and think that trust +information should always be self-signed to act as an integrity check, +but other people may have other solutions. + +My solution is to make trust packets implementation dependent. They + + +Callas, et. al. Expires May 1998 [Page 32] +Internet Draft OpenPGP Message Format Nov 1998 + + +are not emitted on export and ignored on import. Because of this, they +are arguably out of scope of this document anyway. Given that the PGP +implementation of trust packets has security flaws, this seems to be +the best way to deal with them. + +--jdcc}} + +5.11 User ID Packet (Tag 13) + +A User ID packet consists of data which is intended to represent the +name and email address of the key holder. By convention, it includes +an RFC822 mail name, but there are no restrictions on its content. The +packet length in the header specifies the length of the user name. If +it is text, it is encoded in UTF-8. + +{{Editor's note: PRZ thinks there should be more types of "user ids" +other than the traditional name, such as photos, and so on. The above +definition, which assiduously avoids saying that the content of the +packet is a counted string, is one potential way to handle it. Another +would be to explicitly state that this packet is a string, and +introduce a free-form user identification packet. + +A related issue with this document is that sometimes it says "user id" +and sometimes "user name." We need some work here. Present plan is to +use "User ID" everywhere. --jdcc}} + +{{Editor's note: Carl Ellison pointed out to me that if we have +non-exportable (local to one's own keyring) usernames that I can assign +to keys I use, then essentially we have SDSI naming in PGP. This is a +Good Thing, in my opinion, but we have to have a way to define it. +--jdcc}} + +5.12 Comment Packet (Tag 16) + +A Comment packet is used for holding data that is not relevant to +software. Comment packets should be ignored. + +{{Editor's note: should? Must? What does it mean to ignore them? For +example, if it's desirable to show a comment to a user, then how does +that interact with should/must and a suitable definition of "ignore." I +believe that they MUST be ignored, but displaying them to a user is +ignoring them. Looking inside them for cryptographic content (like OP +packets) is *not* ignoring them.}} + +{{Editor's note: should we put in an X.509 encapsulation packet type?}} + +6. Constants + +This section describes the constants used in OP. + +Note that these tables are not exhaustive lists; an implementation MAY +implement an algorithm not on these lists. + + + +Callas, et. al. Expires May 1998 [Page 33] +Internet Draft OpenPGP Message Format Nov 1998 + + +6.1 Public Key Algorithms + +1 - RSA (Encrypt or Sign) +2 - RSA Encrypt-Only +3 - RSA Sign-Only +16 - Elgamal +17 - DSA (Digital Signature Standard) +100 to 110 - Private/Experimental algorithm. + +Implementations MUST implement DSA for signatures, and Elgamal for +encryption. Implementations SHOULD implement RSA encryption. +Implementations MAY implement any other algorithm. + +{{Editor's note: reserve an algorithm for elliptic curve? Note that +I've left Elgamal signatures completely unmentioned. I think this is +good. --jdcc}} + +6.2 Symmetric Key Algorithms + +0 - Plaintext +1 - IDEA +2 - Triple-DES (DES-EDE, as per spec - + 168 bit key derived from 192) +3 - CAST5 (128 bit key) +4 - Blowfish (128 bit key) +5 - ROT-N (128 bit N) +6 - SAFER-SK128 +7 - DES/SK +100 to 110 - Private/Experimental algorithm. + +Implementations MUST implement Triple-DES. Implementations SHOULD +implement IDEA and CAST5.Implementations MAY implement any other +algorithm. + +6.3 Compression Algorithms + +0 - Uncompressed +1 - ZIP +100 to 110 - Private/Experimental algorithm. + +Implementations MUST implement uncompressed data. Implementations +SHOULD implement ZIP. + +6.4 Hash Algorithms + +1 - MD5 +2 - SHA-1 +3 - RIPE-MD/160 +4 - HAVAL +100 to 110 - Private/Experimental algorithm. + +Implementations MUST implement SHA-1. Implementations SHOULD implement +MD5. + + +Callas, et. al. Expires May 1998 [Page 34] +Internet Draft OpenPGP Message Format Nov 1998 + + + +7. Packet Composition + +OP packets may be assembled into sequences in order to create messages +and transfer keys. Not all possible packet sequences are meaningful +and correct. This describes the rules for how packets should be placed +into sequences. + +7.1 Transferable Public Keys + +OP users may transfer public keys. The essential elements of a +transferable public key are: + + - One Public Key packet + - Zero or more revocation signatures + - One or more User ID packets + - After each User ID packet, zero or more Signature packets + - Zero or more Subkey packets + - After each Subkey packet, one or more Signature packets + +The Public Key packet occurs first. Each of the following User ID +packets provides the identity of the owner of this public key. If +there are multiple User ID packets, this corresponds to multiple means +of identifying the same unique individual user; for example, a user may +enjoy the use of more than one e-mail address, and construct a User ID +packet for each one. + +Immediately following each User ID packet, there are zero or more +signature packets. Each signature packet is calculated on the +immediately preceding User ID packet and the initial Public Key packet. +The signature serves to certify the corresponding public key and user +ID. In effect, the signer is testifying to his or her belief that this +public key belongs to the user identified by this user ID. + +After the User ID packets there may be one or more Subkey packets. +Subkeys are used in cases where the top-level public key is a +signature-only key. The subkeys are then encryption-only keys that are +bound to the signature key. Each Subkey packet must be followed by at +least one Signature packet, which should be of the subkey binding +signature type, and issued by the top level key. + +{{Editor's note: I think it is a good idea to have signature-only +subkeys, too (or even encrypt-and-sign subkeys), but no implementation +does this. Should we generalize here? --jdcc}} + +Subkey and Key packets may each be followed by a revocation Signature +packet to indicate that the key is revoked. Revocation signatures are +only accepted if they are issued by the key itself, or by a key which +is authorized to issue revocations via a revocation key subpacket in a +self-signature by the top level key. + +Transferable public key packet sequences may be concatenated to allow +transferring multiple public keys in one operation. + + +Callas, et. al. Expires May 1998 [Page 35] +Internet Draft OpenPGP Message Format Nov 1998 + + + +7.2 OP Messages + +An OP message is a packet or sequence of packets that corresponds to +the following grammatical rules (comma represents sequential +composition, and vertical bar separates alternatives): + + OP Message :- Encrypted Message | Signed Message | Compressed Message + | Literal Message. + + Compressed Message :- Compressed Data Packet. + + Literal Message :- Literal Data Packet. + + ESK :- Encrypted Session Key Packet | + Conventionally Encrypted Session Key Packet. + + ESK Sequence :- ESK | ESK Sequence, ESK. + + Encrypted Message :- Symmetrically Encrypted Data Packet | + ESK Sequence, Symmetrically Encrypted Data Packet. + + One-Pass Signed Message :- One-Pass Signature Packet, OP Message, + Signature Packet. + + Signed Message :- Signature Packet, OP Message | + One-Pass Signed Message. + +In addition, the decrypting a Symmetrically Encrypted Data packet and +decompressing a Compressed Data packet must yield a valid OP Message. + + +8. Enhanced Key Formats + +8.1 Key Structures + +The format of V3 OP key using RSA is as follows. Entries in square +brackets are optional and ellipses indicate repetition. + + RSA Public Key + [Revocation Self Signature] + User ID [Signature ...] + [User ID [Signature ...] ...] + +Each signature certifies the RSA public key and the preceding user ID. +The RSA public key can have many user IDs and each user ID can have +many signatures. + +The format of an OP V4 key that uses two public keys is very similar +except that the second key is added to the end as a 'subkey' of the +primary key. + + Primary-Key + + +Callas, et. al. Expires May 1998 [Page 36] +Internet Draft OpenPGP Message Format Nov 1998 + + + [Revocation Self Signature] + User ID [Signature ...] + [User ID [Signature ...] ...] + [Subkey Primary-Key-Signature] + +The subkey always has a single signature after it that is issued using +the primary key to tie the two keys together. The new format can use +either the new signature packets or the old signature packets. + +In an Elgamal/DSA key, the DSA public key is the primary key, the +Elgamal public key is the subkey, and either version 3 or 4 of the +signature packet can be used. There may be other types of V4 keys, +too. For example, there may be a single-key RSA key in V4 format, a DSA +primary key with an RSA encryption key, etc, or RSA primary key with an +Elgamal subkey. + +It is also possible to have a signature-only subkey. This permits a +primary key that collects certifications (key signatures) but is used +only used for certifying subkeys that are used for encryption and +signatures. + + +8.2 V4 Key IDs and Fingerprints + +A V4 fingerprint is the 160-bit SHA-1 hash of the one-octet Packet Tag, +followed by the two-octet packet length, followed by the entire Public +Key packet starting with the version field. The key ID is either the +low order 32 bits or 64 bits of the fingerprint. Here are the fields +of the hash material, with the example of a DSA key: + + a.1) 0x99 (1 byte) + a.2) high order length byte of (b)-(f) (1 byte) + a.3) low order length byte of (b)-(f) (1 byte) + b) version number = 4 (1 byte); + c) time stamp of key creation (4 bytes); + e) algorithm (1 byte): + 17 = DSA; + f) Algorithm specific fields. + + Algorithm Specific Fields for DSA keys (example): + f.1) MPI of DSA prime p; + f.2) MPI of DSA group order q (q is a prime divisor of p-1); + f.3) MPI of DSA group generator g; + f.4) MPI of DSA public key value y (= g**x where x is secret). + + +9. Security Considerations + +As with any technology involving cryptography, you should check the +current literature to determine if any algorithms used here have been +found to be vulnerable to attack. + +This specification uses Public Key Cryptography technologies. + + +Callas, et. al. Expires May 1998 [Page 37] +Internet Draft OpenPGP Message Format Nov 1998 + + +Possession of the private key portion of a public-private key pair is +assumed to be controlled by the proper party or parties. + +Certain operations in this specification involve the use of random +numbers. An appropriate entropy source should be used to generate +these numbers. See RFC 1750. + +The MD5 hash algorithm has been found to have weaknesses +(pseudo-collisions in the compress function) that make some people +deprecate its use. They consider the SHA-1 algorithm better. + +If you are building an authentication system, the recipient may specify +a preferred signing algorithm. However, the signer would be foolish to +use a weak algorithm simply because the recipient requests it. + +Some of the encryption algorithms mentioned in this document have been +analyzed less than others. For example, although CAST5 is presently +considered strong, it has been analyzed less than Triple-DES. Other +algorithms may have other controversies surrounding them. + +Some technologies mentioned here may be subject to government control +in some countries. + +10. Authors and Working Group Chair + +The working group can be contacted via the current chair: + +John W. Noerenberg, II Qualcomm, Inc 6455 Lusk Blvd San Diego, CA +92131 USA Email: jwn2@qualcomm.com Tel: +1 619 658 3510 + +The principal authors of this draft are (in alphabetical order): + +Jon Callas Pretty Good Privacy, Inc. 555 Twin Dolphin Drive, #570 +Redwood Shores, CA 94065, USA Email: jon@pgp.com Tel: +1-650-596-1960 + +Lutz Donnerhacke IKS GmbH Wildenbruchstr. 15 07745 Jena, Germany EMail: +lutz@iks-jena.de Tel: +49-3641-675642 + +Hal Finney Pretty Good Privacy, Inc. 555 Twin Dolphin Drive, #570 +Redwood Shores, CA 94065, USA Email: hal@pgp.com Tel: +1-650-572-0430 + +Rodney Thayer Sable Technology Corporation 246 Walnut Street Newton, MA +02160 USA Email: rodney@sabletech.com Tel: +1-617-332-7292 + + + +This draft also draws on much previous work from a number of other +authors who include: Derek Atkins, Charles Breed, Dave Del Torto, Marc +Dyksterhouse, Gail Haspert, Gene Hoffman, Paul Hoffman, Raph Levine, +Colin Plumb, Will Price, William Stallings, Mark Weaver, and Philip R. +Zimmermann. + +11. References + + +Callas, et. al. Expires May 1998 [Page 38] +Internet Draft OpenPGP Message Format Nov 1998 + + + +[CAMPBELL} Campbell, Joe, "C Programmer's Guide to Serial +Communications" + +[DONNERHACKE] Donnerhacke, L., et. al, "PGP263in - an improved +international version of PGP", +ftp://ftp.iks-jena.de/mitarb/lutz/crypt/software/pgp/ + +[ISO-10646] ISO/IEC 10646-1:1993. International Standard -- +Information technology -- Universal Multiple-Octet Coded Character Set +(UCS) -- Part 1: Architecture and Basic Multilingual Plane. UTF-8 is +described in Annex R, adopted but not yet published. UTF-16 is +described in Annex Q, adopted but not yet published. + +[PKCS1] RSA Laboratories, "PKCS #1: RSA Encryption Standard," version +1.5, November 1993 + +[RFC822] D. Crocker, "Standard for the format of ARPA Internet text +messages", RFC 822, August 1982 + +[RFC1423] D. Balenson, "Privacy Enhancement for Internet Electronic +Mail: Part III: Algorithms, Modes, and Identifiers", RFC 1423, +October 1993 + +[RFC1641] Goldsmith, D., and M. Davis, "Using Unicode with MIME", RFC +1641, Taligent inc., July 1994. + +[RFC1750] Eastlake, Crocker, & Schiller., Randomness Recommendations +for Security. December 1994. + +[RFC1951] Deutsch, P., DEFLATE Compressed Data Format Specification +version 1.3. May 1996. + +[RFC1983] G. Malkin., Internet Users' Glossary. August 1996. + +[RFC1991] Atkins, D., Stallings, W., and P. Zimmermann, "PGP Message +Exchange Formats", RFC 1991, August 1996. + +[RFC2015] Elkins, M., "MIME Security with Pretty Good Privacy (PGP)", +RFC 2015, October 1996. + +[RFC2044] F. Yergeau., UTF-8, a transformation format of Unicode and +ISO 10646. October 1996. + +[RFC2045] Borenstein, N., and Freed, N., "Multipurpose Internet Mail +Extensions (MIME) Part One: Format of Internet Message Bodies.", +November 1996 + +[RFC2119] Bradner, S., Key words for use in RFCs to Indicate +Requirement Level. March 1997. + +12. Full Copyright Statement + + + +Callas, et. al. Expires May 1998 [Page 39] +Internet Draft OpenPGP Message Format Nov 1998 + + +Copyright 1997 by The Internet Society. All Rights Reserved. + +This document and translations of it may be copied and furnished to +others, and derivative works that comment on or otherwise explain it or +assist in its implementation may be prepared, copied, published and +distributed, in whole or in part, without restriction of any kind, +provided that the above copyright notice and this paragraph are +included on all such copies and derivative works. However, this +document itself may not be modified in any way, such as by removing the +copyright notice or references to the Internet Society or other +Internet organizations, except as needed for the purpose of developing +Internet standards in which case the procedures for copyrights defined +in the Internet Standards process must be followed, or as required to +translate it into languages other than English. + +The limited permissions granted above are perpetual and will not be +revoked by the Internet Society or its successors or assigns. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Callas, et. al. Expires May 1998 [Page 40] diff --git a/Protocol/PGP/Info/penguin1.tgz b/Protocol/PGP/Info/penguin1.tgz Binary files differnew file mode 100644 index 0000000..db1b947 --- /dev/null +++ b/Protocol/PGP/Info/penguin1.tgz diff --git a/Protocol/PGP/Info/pgformat.doc b/Protocol/PGP/Info/pgformat.doc new file mode 100644 index 0000000..a2c97f3 --- /dev/null +++ b/Protocol/PGP/Info/pgformat.doc @@ -0,0 +1,343 @@ +File Formats Used by PGP 2.6 (22 May 94) +======================================== + +***Note: packets that contain a version byte of 2 will contain a version + byte of 3 when using versions of PGP >= 2.6 after 9/1/94. + +This appendix describes the file formats used externally by Pretty +Good Privacy (PGP), the RSA public key cryptography application. The +intended audience includes software engineers trying to port PGP to +other hardware environments or trying to implement other PGP- +compatible cryptography products, or anyone else who is curious. + +[To be included: a description of ASCII armor. An ASCII armored +file is just like a binary file described here, but with an extra +layer of encoding added, framing lines, and a 24-bit CRC at the end.] + + +Byte Order +---------- + +All integer data used by PGP is externally stored most significant byte +(MSB) first, regardless of the byte order used internally by the host +CPU architecture. This is for portability of messages and keys between +hosts. This covers multiprecision RSA integers, bit count prefix +fields, byte count prefix fields, checksums, key IDs, and timestamps. + +The MSB-first byte order for external packet representation was +chosen only because many other crypto standards use it. + + +Multiprecision Integers +----------------------- + +RSA arithmetic involves a lot of multiprecision integers, often +having hundreds of bits of precision. PGP externally stores a +multiprecision integer (MPI) with a 16-bit prefix that gives the +number of significant bits in the integer that follows. The integer +that follows this bitcount field is stored in the usual byte order, +with the MSB padded with zero bits if the bitcount is not a multiple +of 8. The bitcount always specifies the exact number of significant +bits. For example, the integer value 5 would be stored as these +three bytes: + + 00 03 05 + +An MPI with a value of zero is simply stored with the 16-bit bitcount +prefix field containing a 0, with no value bytes following it. + + + +Key ID +------ + +Some packets use a "key ID" field. The key ID is the least +significant 64 bits of the RSA public modulus that was involved in +creating the packet. For all practical purposes it unique to each +RSA public key. + + +User ID +------- + +Some packets contain a "user ID", which is an ASCII string that +contains the user's name. Unlike a C string, the user ID has a +length byte at the beginning that has a byte count of the rest of the +string. This length byte does not include itself in the count. + + +Timestamp +--------- + +Some packets contain a timestamp, which is a 32-bit unsigned integer +of the number of seconds elapsed since 1970 Jan 1 00:00:00 GMT. This +is the standard format used by Unix timestamps. It spans 136 years. + + + +Cipher Type Byte (CTB) +---------------------- + +Many of these data structures begin with a Cipher Type Byte (CTB), +which specifies the type of data structure that follows it. The CTB +bit fields have the following meaning (bit 0 is the LSB, bit 7 is the +MSB): + +Bit 7: Always 1, which designates this as a CTB +Bit 6: Reserved. +Bits 5-2: CTB type field, specifies type of packet that follows + 0001 - public-key-encrypted packet + 0010 - secret-key-encrypted (signature) packet + 0101 - Secret key certificate + 0110 - Public key certificate + 1000 - Compressed data packet + 1001 - Conventional-Key-Encrypted data + 1011 - Raw literal plaintext data, with filename and mode + 1100 - Keyring trust packet + 1101 - User ID packet, associated with public or secret key + 1110 - Comment packet + Other CTB packet types are unimplemented. +Bits 1-0: Length-of-length field: + 00 - 1 byte packet length field follows CTB + 01 - 2 byte packet length field follows CTB + 10 - 4 byte packet length field follows CTB + 11 - no length field follows CTB, unknown packet length. + The 8-, 16-, or 32-bit packet length field after the CTB + gives the length in bytes of the rest of the packet, not + counting the CTB and the packet length field. + + + +RSA public-key-encrypted packet +------------------------------- + +Offset Length Meaning +0 1 CTB for RSA public-key-encrypted packet +1 2 16-bit (or maybe 8-bit) length of packet +3 1 Version byte (=2). May affect rest of fields that follow. +4 8 64-bit Key ID +12 1 Algorithm byte for RSA (=1 for RSA). + --Algorithm byte affects field definitions that follow. +13 ? RSA-encrypted integer, encrypted conventional key + packet. (MPI with bitcount prefix) + +The conventionally-encrypted ciphertext packet begins right after the +RSA public-key-encrypted packet that contains the conventional key. + + + +Signature packet +---------------- + +Offset Length Meaning +0 1 CTB for secret-key-encrypted (signed) packet +1 2 16-bit (or maybe 8-bit) length of packet +3 1 Version byte (=2). May affect rest of fields that follow. + Version byte (=3) for >= PGP2.6 after 9/1/94 +4 1 Length of following material that is implicitly included + in MD calculation (=5). +5 1 Signature classification field (see below). + Implicitly append this to message for MD calculation. +6 4 32-bit timestamp of when signature was made. + Implicitly append this to message for MD calculation. +10 8 64-bit Key ID +18 1 Algorithm byte for public key scheme (RSA=0x01). + --Algorithm byte affects field definitions that follow. +19 1 Algorithm byte for message digest (MD5=0x01). +20 2 First 2 bytes of the Message Digest inside the + RSA-encrypted integer, to help us figure out if we + used the right RSA key to check the signature. +22 ? RSA-encrypted integer, encrypted message digest + (MPI with bitcount prefix). + +If the plaintext that was signed is included in the same file as the +signature packet, it begins right after the RSA secret-key-signed +packet that contains the message digest. The plaintext has a +"literal" CTB prefix. + +The original idea had a variable length field following the length +of following material byte, before the Key ID. In particular, the +possibility of a 2-byte validity period was defined, although no +previous version of PGP ever generated those bytes. + +Owing to the way the MD5 is computed for the signature, if that field +is variable length, it is possible to generate two different messages +with the same MD5 hash. One would be a file of length N, with a 7-byte +following section consisting of a signature type byte, 4 bytes of +timestamp, and 2 of validity period, while the other would be a file of +length N+2, whose last two bytes would be the siganture type byte and +the first byte of timestamp, and the last three bytes of timestamp and +the validity period would instead be interpreted as a signature type +byte and a timestmap. + +It should be emphasized that the messages are barely different and +special circumstances must arise for this to be possible, so it is +extremely unlilely that this would be exploitable, but it is a +potential weakness. It has been plugged by allowing only the currently +implemented 5-byte option. Validity periods will be added later with +a different format. + +The signature classification field describes what kind of +signature certificate this is. There are various hex values: + 00 - Signature of a message or document, binary image. + 01 - Signature of a message or document, canonical text. + 10 - Key certification, generic. Only version of key + certification supported by PGP 2.5. + Material signed is public key pkt and User ID pkt. + 11 - Key certification, persona. No attempt made at all + to identify the user with a real name. + Material signed is public key pkt and User ID pkt. + 12 - Key certification, casual identification. Some + casual attempt made to identify user with his name. + Material signed is public key pkt and User ID pkt. + 13 - Key certification, positive ID. Heavy-duty + identification efforts, photo ID, direct contact + with personal friend, etc. + Material signed is public key pkt and User ID pkt. + 20 - Key compromise. User signs his own compromise + certificate. Independent of user ID associations. + Material signed is public key pkt ONLY. + 30 - Key/userid revocation. User can sign his own + revocation to dissolve an association between a key + and a user ID, or certifier may revoke his previous + certification of this key/userid pair. + Material signed is public key pkt and User ID pkt. + 40 - Timestamping a signature certificate made by someone + else. Can be used to apply trusted timestamp, and + log it in notary's log. Signature of a signature. + (Planned, not implemented.) + +When a signature is made to certify a key/UserID pair, it is computed +across two packets-- the public key packet, and the separate User ID +packet. See below. + +The packet headers (CTB and length fields) for the public key packet +and the user ID packet are both omitted from the signature +calculation for a key certification. + +A key compromise certificate may be issued by someone to revoke his +own key when his secret key is known to be compromised. If that +happens, a user would sign his own key compromise certificate with +the very key that is being revoked. A key revoked by its own +signature means that this key should never be used or trusted again, +in any form, associated with any user ID. A key compromise +certificate issued by the keyholder shall take precedence over any +other key certifications made by anyone else for that key. A key +compromise signed by someone other than the key holder is invalid. + +Note that a key compromise certificate just includes the key packet +in its signature calculation, because it kills the whole key without +regard to any userid associations. It isn't tied to any particular +userid association. It should be inserted after the key packet, +before the first userid packet. + +When a key compromise certificate is submitted to PGP, PGP will place +it on the public keyring. A key compromise certificate is always +accompanied in its travels by the public key and userIDs it affects. +If the affected key is NOT already on the keyring, the compromise +certificate (and its key and user ID) is merely added to the keyring +anywhere. If the affected key IS already on the keyring, the +compromise certificate is inserted after the affected key packet. +This assumes that the actual key packet is identical to the one +already on the key ring, so no duplicate key packet is needed. +If a key has been revoked, PGP will not allow its use to encipher any +messages, and if an incoming signature uses it, PGP will display a +stern warning that this key has been revoked. + +NOTE: Key/userid revocation certificates ARE NOT SUPPORTED in +this version of PGP. But if we ever get around to supporting them, +here are some ideas on how they should work... + +A key/userid revocation certificate may be issued by someone to +dissolve the association between his own key and a user ID. He would +sign it with the very key that is being revoked. A key/userid +revocation certificate issued by the keyholder shall take precedence +over any other key certifications made by anyone else for that +key/userid pair. Also, a third party certifier may revoke his own +previous certification of this key/userid pair by issuing a +key/userid revocation certificate. Such a revocation should not +affect the certifications by other third parties for this same +key/userid pair. + +When a key/userid revocation certificate is submitted to PGP, PGP +will place it on the public keyring. A key/userid revocation +certificate is always accompanied in its travels by the public key it +affects (the key packet and user ID packet precedes the revocation +certificate). If the affected key is NOT already on the keyring, the +revocation certificate (and its key and user ID) is merely added to +the keyring anywhere. If the affected key IS already on the keyring, +the revocation certificate is integrated in with the key's other +certificates as though it were just another key certification. This +assumes that the actual key packet is identical to the one already on +the key ring, so no duplicate key packet is needed. + + + +Message digest "packet" +----------------------- + +The Message digest has no CTB packet framing. It is stored +packetless and naked, with padding, encrypted inside the MPI in the +Signature packet. + +PGP versions 2.3 and later use a new format for encoding the message +digest into the MPI in the signature packet, a format which is +compatible with RFC1425 (formerly RFC1115). This format is accepted +but not written by version 2.2. The older format used by versions 2.2 +is acepted by versions up to 2.4, but the RSAREF code in 2.5 is +not capable of parsing it. + +PGP versions 2.2 and earlier encode the MD into the MPI as follows: + + MSB . . . LSB + + 0 1 MD(16 bytes) 0 FF(n bytes) 1 + +Enough bytes of FF padding are added to make the length of this +whole string equal to the number of bytes in the modulus. + +PGP versions 2.3 and later encode the MD into the MPI as follows: + + MSB . . . LSB + + 0 1 FF(n bytes) 0 ASN(18 bytes) MD(16 bytes) + +See RFC1423 for an explanation of the meaning of the ASN string. +It is the following 18 byte long hex value: + + 3020300c06082a864886f70d020505000410 + +Enough bytes of FF padding are added to make the length of this +whole string equal to the number of bytes in the modulus. + +All this mainly affects the rsa_private_encrypt() and rsa_public_decrypt() +functions in rsaglue.c. + +There is no checksum included. The padding serves to verify that the +correct RSA key was used. + + +Conventional Data Encryption Key (DEK) "packet" +----------------------------------------------- + +The DEK has no CTB packet framing. The DEK is stored packetless and +naked, with padding, encrypted inside the MPI in the RSA +public-key-encrypted packet. + +PGP versions 2.3 and later use a new format for encoding the message +digest into the MPI in the signature packet. (This format is not +presently based on any RFCs due to the use of the IDEA encryption +system.) This format is accepted but not written by version 2.2. The +older format used by versions 2.2 and earlier is also accepted by +versions up to 2.4, but the RSAREF code in 2.5 is unable to cope +with it. + +PGP versions 2.2 and earlier encode the MD into the MPI as follows: + + MSB . . . LSB + + 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2 + +CSUM refers to a 16-bit checksum appended to the high end of the DEK. +RND is a string of NONZERO pseudorandom bytes, enough to make the length +of t
\ No newline at end of file diff --git a/Protocol/PGP/Info/pgpdoc1.txt b/Protocol/PGP/Info/pgpdoc1.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Protocol/PGP/Info/pgpdoc1.txt diff --git a/Protocol/PGP/Info/pgpdoc2.txt b/Protocol/PGP/Info/pgpdoc2.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Protocol/PGP/Info/pgpdoc2.txt diff --git a/Protocol/PGP/Info/rfc1991.txt b/Protocol/PGP/Info/rfc1991.txt new file mode 100644 index 0000000..aa50183 --- /dev/null +++ b/Protocol/PGP/Info/rfc1991.txt @@ -0,0 +1,1179 @@ + + + + + + +Network Working Group D. Atkins +Request for Comments: 1991 MIT +Category: Informational W. Stallings + Comp-Comm Consulting + P. Zimmermann + Boulder Software Engineering + August 1996 + + + PGP Message Exchange Formats + +Status of This Memo + + This memo provides information for the Internet community. This memo + does not specify an Internet standard of any kind. Distribution of + this memo is unlimited. + +Table of Contents + + 1. Introduction............................................2 + 2. PGP Services............................................2 + 2.1 Digital signature.......................................3 + 2.2 Confidentiality.........................................3 + 2.3 Compression.............................................4 + 2.4 Radix-64 conversion.....................................4 + 2.4.1 ASCII Armor Formats.....................................5 + 3. Data Element Formats....................................6 + 3.1 Byte strings............................................6 + 3.2 Whole number fields.....................................7 + 3.3 Multiprecision fields...................................7 + 3.4 String fields...........................................8 + 3.5 Time fields.............................................8 + 4. Common Fields...........................................8 + 4.1 Packet structure fields.................................8 + 4.2 Number ID fields.......................................10 + 4.3 Version fields.........................................10 + 5. Packets................................................10 + 5.1 Overview...............................................10 + 5.2 General Packet Structure...............................11 + 5.2.1 Message component......................................11 + 5.2.2 Signature component....................................11 + 5.2.3 Session key component..................................11 + 6. PGP Packet Types.......................................12 + 6.1 Literal data packets...................................12 + 6.2 Signature packets......................................13 + 6.2.1 Message-digest-related fields..........................14 + 6.2.2 Public-key-related fields..............................15 + 6.2.3 RSA signatures.........................................16 + + + +Atkins, et. al. Informational [Page 1] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + 6.2.4 Miscellaneous fields...................................16 + 6.3 Compressed data packets................................17 + 6.4 Conventional-key-encrypted data packets................17 + 6.4.1 Conventional-encryption type byte......................18 + 6.5 Public-key-encrypted packets...........................18 + 6.5.1 RSA-encrypted data encryption key (DEK)................19 + 6.6 Public-key Packets.....................................19 + 6.7 User ID packets........................................20 + 7. Transferable Public Keys...............................20 + 8. Acknowledgments........................................20 + 9. Security Considerations................................21 + 10. Authors' Addresses.....................................21 + +1. Introduction + + PGP (Pretty Good Privacy) uses a combination of public-key and + conventional encryption to provide security services for electronic + mail messages and data files. These services include confidentiality + and digital signature. PGP is widely used throughout the global + computer community. This document describes the format of "PGP + files", i.e., messages that have been encrypted and/or signed with + PGP. + + PGP was created by Philip Zimmermann and first released, in Version + 1.0, in 1991. Subsequent versions have been designed and implemented + by an all-volunteer collaborative effort under the design guidance of + Philip Zimmermann. PGP and Pretty Good Privacy are trademarks of + Philip Zimmermann. + + This document describes versions 2.x of PGP. Specifically, versions + 2.6 and 2.7 conform to this specification. Version 2.3 conforms to + this specification with minor differences. + + A new release of PGP, known as PGP 3.0, is anticipated in 1995. To + the maximum extent possible, this version will be upwardly compatible + with version 2.x. At a minimum, PGP 3.0 will be able to read messages + and signatures produced by version 2.x. + +2. PGP Services + + PGP provides four services related to the format of messages and data + files: digital signature, confidentiality, compression, and radix-64 + conversion. + + + + + + + + +Atkins, et. al. Informational [Page 2] + +RFC 1991 PGP Message Exchange Formats August 1996 + + +2.1 Digital signature + + The digital signature service involves the use of a hash code, or + message digest, algorithm, and a public-key encryption algorithm. The + sequence is as follows: + + -the sender creates a message + -the sending PGP generates a hash code of the message + -the sending PGP encrypts the hash code using the sender's private + key + -the encrypted hash code is prepended to the message + -the receiving PGP decrypts the hash code using the sender's public + key + -the receiving PGP generates a new hash code for the received + message and compares it to the decrypted hash code. If the two + match, the message is accepted as authentic + + Although signatures normally are found attached to the message or + file that they sign, this is not always the case: detached signatures + are supported. A detached signature may be stored and transmitted + separately from the message it signs. This is useful in several + contexts. A user may wish to maintain a separate signature log of all + messages sent or received. A detached signature of an executable + program can detect subsequent virus infection. Finally, detached + signatures can be used when more than one party must sign a document, + such as a legal contract. Each person's signature is independent and + therefore is applied only to the document. Otherwise, signatures + would have to be nested, with the second signer signing both the + document and the first signature, and so on. + +2.2 Confidentiality + + PGP provides confidentiality by encrypting messages to be transmitted + or data files to be stored locally using conventional encryption. In + PGP, each conventional key is used only once. That is, a new key is + generated as a random 128-bit number for each message. Since it is to + be used only once, the session key is bound to the message and + transmitted with it. To protect the key, it is encrypted with the + receiver's public key. The sequence is as follows: + + -the sender creates a message + -the sending PGP generates a random number to be used as a session + key for this message only + -the sending PGP encrypts the message using the session key + -the session key is encrypted using the recipient's public key and + prepended to the encrypted message + -the receiving PGP decrypts the session key using the recipient's + private key + + + +Atkins, et. al. Informational [Page 3] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + -the receiving PGP decrypts the message using the session key + + Both digital signature and confidentiality services may be applied to + the same message. First, a signature is generated for the message and + prepended to the message. Then, the message plus signature is + encrypted using a conventional session key. Finally, the session key + is encrypted using public-key encryption and prepended to the + encrypted block. + +2.3 Compression + + As a default, PGP compresses the message after applying the signature + but before encryption. + +2.4 Radix-64 conversion + + When PGP is used, usually part of the block to be transmitted is + encrypted. If only the signature service is used, then the message + digest is encrypted (with the sender's private key). If the + confidentiality service is used, the message plus signature (if + present) are encrypted (with a one-time conventional key). Thus, part + or all of the resulting block consists of a stream of arbitrary 8-bit + bytes. However, many electronic mail systems only permit the use of + blocks consisting of ASCII text. To accommodate this restriction, PGP + provides the service of converting the raw 8-bit binary stream to a + stream of printable ASCII characters, called ASCII Armor. + + The scheme used for this purpose is radix-64 conversion. Each group + of three bytes of binary data is mapped into 4 ASCII characters. This + format also appends a CRC to detect transmission errors. This + radix-64 conversion, also called Ascii Armor, is a wrapper around the + binary PGP messages, and is used to protect the binary messages + during transmission over non-binary channels, such as Internet Email. + + The following table defines the mapping. The characters used are the + upper- and lower-case letters, the digits 0 through 9, and the + characters + and /. The carriage-return and linefeed characters + aren't used in the conversion, nor is the tab or any other character + that might be altered by the mail system. The result is a text file + that is "immune" to the modifications inflicted by mail systems. + + + + + + + + + + + +Atkins, et. al. Informational [Page 4] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + 6-bit character 6-bit character 6-bit character 6-bit character + value encoding value encoding value encoding value encoding + 0 A 16 Q 32 g 48 w + 1 B 17 R 33 h 49 x + 2 C 18 S 34 i 50 y + 3 D 19 T 35 j 51 z + 4 E 20 U 36 k 52 0 + 5 F 21 V 37 l 53 1 + 6 G 22 W 38 m 54 2 + 7 H 23 X 39 n 55 3 + 8 I 24 Y 40 o 56 4 + 9 J 25 Z 41 p 57 5 + 1 K 26 a 42 q 58 6 + 11 L 27 b 43 r 59 7 + 12 M 28 c 44 s 60 8 + 13 N 29 d 45 t 61 9 + 14 O 30 e 46 u 62 + + 15 P 31 f 47 v 63 / + (pad) = + + It is possible to use PGP to convert any arbitrary file to ASCII + Armor. When this is done, PGP tries to compress the data before it + is converted to Radix-64. + +2.4.1 ASCII Armor Formats + + When PGP encodes data into ASCII Armor, it puts specific headers + around the data, so PGP can reconstruct the data at a future time. + PGP tries to inform the user what kind of data is encoded in the + ASCII armor through the use of the headers. + + ASCII Armor is created by concatenating the following data: + + - An Armor Headerline, appropriate for the type of data + - Armor Headers + - A blank line + - The ASCII-Armored data + - An Armor Checksum + - The Armor Tail (which depends on the Armor Headerline). + + An Armor Headerline is composed by taking the appropriate headerline + text surrounded by five (5) dashes (-) on either side of the + headerline text. The headerline text is chosen based upon the type + of data that is being encoded in Armor, and how it is being encoded. + Headerline texts include the following strings: + + BEGIN PGP MESSAGE -- used for signed, encrypted, or compressed files + BEGIN PGP PUBLIC KEY BLOCK -- used for transferring public keys + + + +Atkins, et. al. Informational [Page 5] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + BEGIN PGP MESSAGE, PART X/Y -- used for multi-part messages, where + the armor is split amongst Y files, + and this is the Xth file out of Y. + + The Armor Headers are pairs of strings that can give the user or the + receiving PGP program some information about how to decode or use the + message. The Armor Headers are a part of the armor, not a part of + the message, and hence should not be used to convey any important + information, since they can be changed in transport. + + The format of an Armor Header is that of a key-value pair, the + encoding of RFC-822 headers. PGP should consider improperly + formatted Armor Headers to be corruption of the ASCII Armor. Unknown + Keys should be reported to the user, but so long as the RFC-822 + formatting is correct, PGP should continue to process the message. + Currently defined Armor Header Keys include "Version" and "Comment", + which define the PGP Version used to encode the message and a user- + defined comment. + + The Armor Checksum is a 24-bit CRC converted to four bytes of radix- + 64 encoding, prepending an equal-sign (=) to the four-byte code. The + CRC is computed by using the generator 0x864CFB and an initialization + of 0xB704CE. The accumulation is done on the data before it is + converted to radix-64, rather than on the converted data. For more + information on CRC functions, the reader is asked to look at chapter + 19 of the book "C Programmer's Guide to Serial Communications," by + Joe Campbell. + + The Armor Tail is composed in the same manner as the Armor + Headerline, except the string "BEGIN" is replaced by the string + "END". + +3. Data Element Formats + +3.1 Byte strings + + The objects considered in this document are all "byte strings." A + byte string is a finite sequence of bytes. The concatenation of byte + string X of length M with byte string Y of length N is a byte string + Z of length M + N; the first M bytes of Z are the bytes of X in the + same order, and the remaining N bytes of Z are the bytes of Y in the + same order. + + Literal byte strings are written from left to right, with pairs of + hex nibbles separated by spaces, enclosed by angle brackets: for + instance, <05 ff 07> is a byte string of length 3 whose bytes have + numeric values 5, 255, and 7 in that order. All numbers in this + document outside angle brackets are written in decimal. + + + +Atkins, et. al. Informational [Page 6] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + The byte string of length 0 is called "empty" and written <>. + +3.2 Whole number fields + + Purpose. A whole number field can represent any nonnegative integer, + in a format where the field length is known in advance. + + Definition. A whole number field is any byte string. It is stored + in radix-256 MSB-first format. This means that a whole number field + of length N with bytes b_0 b_1 ... b_{N-2} b_{N-1} in that order has + value + + b_0 * 256^{N-1} + b_1 * 256^{N-2} + ... + b_{N-2} * 256 + b_{N-1}. + + Examples. The byte string <00 0D 64 11 00 00> is a valid whole + number field with value 57513410560. The byte string <FF> is a valid + whole number field with value 255. The byte string <00 00> is a + valid whole number field with value 0. The empty byte string <> is a + valid whole number field with value 0. + +3.3 Multiprecision fields + + Purpose. A multiprecision field can represent any nonnegative + integer which is not too large. The field length need not be known + in advance. Multiprecision fields are designed to waste very little + space: a small integer uses a short field. + + Definition. A multiprecision field is the concatenation of two + fields: + + (a) a whole number field of length 2, with value B; + (b) a whole number field, with value V. + + Field (b) is of length [(B+7)/8], i.e., the greatest integer which is + no larger than (B+7)/8. The value of the multiprecision field is + defined to be V. V must be between 2^{B-1} and 2^B - 1 inclusive. + In other words B must be exactly the number of significant bits in V. + + Some implementations may limit the possible range of B. The + implementor must document which values of B are allowed by an + implementation. + + Examples. The byte string <00 00> is a valid multiprecision integer + with value 0. The byte string <00 03 05> is a valid multiprecision + field with value 5. The byte strings <00 03 85> and <00 00 00> are + not valid multiprecision fields. The former is invalild because <85> + has 8 significant bits, not 3; the latter is invalid because the + second field has too many bytes of data given the value of the first + + + +Atkins, et. al. Informational [Page 7] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + field. The byte string <00 09 01 ff> is a valid multiprecision field + with value 511. The byte string <01 00 80 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07> is + a valid multiprecision field with value 2^255 + 7. + +3.4 String fields + + Purpose. A string field represents any sequence of bytes of length + between 0 and 255 inclusive. The length need not be known in + advance. By convention, the content of a string field is normally + interpreted as ASCII codes when it is displayed. + + Definition. A string field is the concatenation of the following: + + (a) a whole number field of length 1, with value L; + (b) a byte string of length L. + + The content of the string field is defined to be field (b). + + Examples: <05 48 45 4c 4c 4f> is a valid string field which would + normally be displayed as the string HELLO. <00> is a valid string + field which would normally be displayed as the empty string. <01 00> + is a valid string field. + +3.5 Time fields + + Purpose. A time field represents the number of seconds elapsed since + 1970 Jan 1 00:00:00 GMT. It is compatible with the usual + representation of times under UNIX. + + Definition. A time field is a whole number field of length 4, with + value V. The time represented by the time field is the one-second + interval beginning V seconds after 1970 Jan 1 00:00:00 GMT. + +4. Common Fields + + This section defines fields found in more than one packet format. + +4.1 Packet structure fields + + Purpose. The packet structure field distinguishes between different + types of packets, and indicates the length of packets. + + Definition. A packet structure field is a byte string of length 1, + 2, 3, or 5. Its first byte is the cipher type byte (CTB), with bits + labeled 76543210, 7 the most significant bit and 0 the least + significant bit. As indicated below the length of the packet + structure field is determined by the CTB. + + + +Atkins, et. al. Informational [Page 8] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + CTB bits 76 have values listed in the following table: + + 10 - normal CTB + 11 - reserved for future experimental work + all others - reserved + + CTB bits 5432, the "packet type bits", have values listed in the + following table: + + 0001 - public-key-encrypted packet + 0010 - signature packet + 0101 - secret-key certificate packet + 0110 - public-key certificate packet + 1000 - compressed data packet + 1001 - conventional-key-encrypted packet + 1011 - literal data packet + 1100 - keyring trust packet + 1101 - user id packet + 1110 - comment packet (*) + all others - reserved + + CTB bits 10, the "packet-length length bits", have values listed in + the following table: + + 00 - 1-byte packet-length field + 01 - 2-byte packet-length field + 10 - 4-byte packet-length field + 11 - no packet length supplied, unknown packet length + + As indicated in this table, depending on the packet-length length + bits, the remaining 1, 2, 4, or 0 bytes of the packet structure field + are a "packet-length field". The packet-length field is a whole + number field. The value of the packet-length field is defined to be + the value of the whole number field. + + A value of 11 is currently used in one place: on compressed data. + That is, a compressed data block currently looks like <A3 01 . . .>, + where <A3>, binary 10 1000 11, is an indefinite-length packet. The + proper interpretation is "until the end of the enclosing structure", + although it should never appear outermost (where the enclosing + structure is a file). + + Options marked with an asterisk (*) are not implemented yet; PGP + 2.6.2 will never output this packet type. + + + + + + + +Atkins, et. al. Informational [Page 9] + +RFC 1991 PGP Message Exchange Formats August 1996 + + +4.2 Number ID fields + + Purpose. The ID of a whole number is its 64 least significant bits. + The ID is a convenient way to distinguish between large numbers such + as keys, without having to transmit the number itself. Thus, a number + that may be hundreds or thousands of decimal digits in length can be + identified with a 64-bit identifier. Two keys may have the same ID by + chance or by malice; although the probability that two large keys + chosen at random would have the same ID is extremely small. + + Definition. A number ID field is a whole number field of length 8. + The value of the number ID field is defined to be the value of the + whole number field. + +4.3 Version fields + + Many packet types include a version number as the first byte of the + body. The format and meaning of the body depend on the version + number. More versions of packets, with new version numbers, may be + defined in the future. An implementation need not support every + version of each packet type. However, the implementor must document + which versions of each packet type are supported by the + implementation. + + A version number of 2 or 3 is currently allowed for each packet + format. New versions will probably be numbered sequentially up from + 3. For backwards compatibility, implementations will usually be + expected to support version N of a packet whenever they support + version N+1. Version 255 may be used for experimental purposes. + +5. Packets + +5.1 Overview + + A packet is a digital envelope with data inside. A PGP file, by + definition, is the concatenation of one or more packets. In addition, + one or more of the packets in a file may be subject to a + transformation using encryption, compression, or radix-64 conversion. + + A packet is the concatenation of the following: + + (a) a packet structure field; + (b) a byte string of some length N. + + Byte string (b) is called the "body" of the packet. The value of the + packet-length field inside the packet structure field (a) must equal + N, the length of the body. + + + + +Atkins, et. al. Informational [Page 10] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + Other characteristics of the packet are determined by the type of the + packet. See the definitions of particular packet types for further + details. The CTB packet-type bits inside the packet structure always + indicate the packet type. + + Note that packets may be nested: one digital envelope may be placed + inside another. For example, a conventional-key-encrypted packet + contains a disguised packet, which in turn might be a compressed data + packet. + +5.2 General packet structure + + A pgp file consists of three components: a message component, a + signature (optional), and a session key component (optional). + +5.2.1 Message component + + The message component includes the actual data to be stored or + transmitted as well as a header that includes control information + generated by PGP. The message component consists of a single literal + data packet. + +5.2.2 Signature component + + The signature component is the signature of the message component, + formed using a hash code of the message component and the public key + of the sending PGP entity. The signature component consists of a + single signature packet. + + If the default option of compression is chosen, then the block + consisting of the literal data packet and the signature packet is + compressed to form a compressed data packet. + +5.2.3 Session key component + + The session key component includes the encrypted session key and the + identifier of the recipients public key used by the sender to encrypt + the session key. The session key component consists of a single + public-key-encrypted packet for each recipient of the message. + + If compression has been used, then conventional encryption is applied + to the compressed data packet formed from the compression of the + signature packet and the literal data packet. Otherwise, conventional + encryption is applied to the block consisting of the signature packet + and the literal data packet. In either case, the cyphertext is + referred to as a conventional-key-encrypted data packet. + + + + + +Atkins, et. al. Informational [Page 11] + +RFC 1991 PGP Message Exchange Formats August 1996 + + +6. PGP Packet Types + + PGP includes the following types of packets: + + -literal data packet + -signature packet + -compressed data packet + -conventional-key-encrypted data packet + -public-key-encrypted packet + -public-key packet + -User ID packet + +6.1 Literal data packets + + Purpose. A literal data packet is the lowest level of contents of a + digital envelope. The data inside a literal data packet is not + subject to any further interpretation by PGP. + + Definition. A literal data packet is the concatenation of the + following fields: + + (a) a packet structure field; + (b) a byte, giving a mode; + (c) a string field, giving a filename; + (d) a time field; + (e) a byte string of literal data. + + + Fields (b), (c), and (d) suggest how the data should be written to a + file. Byte (b) is either ASCII b <62>, for binary, or ASCII t <74>, + for text. Byte (b) may also take on the value ASCII 1, indicating a + machine-local conversion. It is not defined how PGP will convert this + across platforms. + + Field (c) suggests a filename. Field (d) should be the time at which + the file was last modified, or the time at which the data packet was + created, or 0. + + Note that only field (e) of a literal data packet is fed to a + message-digest function for the formation of a signature. The + exclusion of the other fields ensures that detached signatures are + exactly the same as attached signatures prefixed to the message. + Detached signatures are calculated on a separate file that has none + of the literal data packet header fields. + + + + + + + +Atkins, et. al. Informational [Page 12] + +RFC 1991 PGP Message Exchange Formats August 1996 + + +6.2 Signature packet + + Purpose. Signatures are attached to data, in such a way that only + one entity, called the "writer," can create the signature. The + writer must first create a "public key" K and distribute it. The + writer keeps certain private data related to K. Only someone + cooperating with the writer can sign data using K, enveloping the + data in a signature packet (also known as a private-key-encrypted + packet). Anyone can look through the glass in the envelope and + verify that the signature was attached to the data using K. If the + data is altered in any way then the verification will fail. + + Signatures have different meanings. For example, a signature might + mean "I wrote this document," or "I received this document." A + signature packet includes a "classification" which expresses its + meaning. + + Definition. A signature packet, version 2 or 3, is the concatenation + of the following fields: + + (a) packet structure field (2, 3, or 5 bytes); + (b) version number = 2 or 3 (1 byte); + (c) length of following material included in MD calculation + (1 byte, always the value 5); + (d1) signature classification (1 byte); + (d2) signature time stamp (4 bytes); + (e) key ID for key used for singing (8 bytes); + (f) public-key-cryptosystem (PKC) type (1 byte); + (g) message digest algorithm type (1 byte); + (h) first two bytes of the MD output, used as a checksum + (2 bytes); + (i) a byte string of encrypted data holding the RSA-signed digest. + + The message digest is taken of the bytes of the file, followed by the + bytes of field (d). It was originally intended that the length (c) + could vary, but now it seems that it will alwaye remain a constant + value of 5, and that is the only value that will be accepted. Thus, + only the fields (d1) and (d2) will be hashed into the signature along + with the main message. + + + + + + + + + + + + +Atkins, et. al. Informational [Page 13] + +RFC 1991 PGP Message Exchange Formats August 1996 + + +6.2.1 Message-digest-related fields + + The message digest algorithm is specified by the message digest (MD) + number of field (g). The following MD numbers are currently defined: + + 1 - MD5 (output length 16) + 255 - experimental + + More MD numbers may be defined in the future. An implementation need + not support every MD number. The implementor must document the MD + numbers understood by an implementation. + + A message digest algorithm reads a byte string of any length, and + writes a byte string of some fixed length, as indicated in the table + above. + + The input to the message digest algorithm is the concatenation of + some "primary input" and some "appended input." + + The appended input is specified by field (c), which gives a number of + bytes to be taken from the following fields: (d1), (d2), and so on. + The current implementation uses the value 5 for this number, for + fields (d1) and (d2). Any field not included in the appended input + is not "signed" by field (i). + + The primary input is determined by the signature classification byte + (d1). Byte (d1) is one of the following hex numbers, with these + meanings: + + <00> - document signature, binary image ("I wrote this document") + <01> - document signature, canonical text ("I wrote this document") + <10> - public key packet and user ID packet, generic certification + ("I think this key was created by this user, but I won't say + how sure I am") + <11> - public key packet and user ID packet, persona certification + ("This key was created by someone who has told me that he is + this user") (#) + <12> - public key packet and user ID packet, casual certification + ("This key was created by someone who I believe, after casual + verification, to be this user") (#) + <13> - public key packet and user ID packet, positive certification + ("This key was created by someone who I believe, after + heavy-duty identification such as picture ID, to be this + user") (#) + <20> - public key packet, key compromise ("This is my key, and I + have revoked it") + + + + + +Atkins, et. al. Informational [Page 14] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + <30> - public key packet and user ID packet, revocation ("I retract + all my previous statements that this key is related to this + user") (*) + <40> - time stamping ("I saw this document") (*) + + More classification numbers may be defined in the future to handle + other meanings of signatures, but only the above numbers may be used + with version 2 or version 3 of a signature packet. It should be + noted that PGP 2.6.2 has not implemented the packets marked with an + asterisk (*), and the packets marked with a hash (#) are not output + by PGP 2.6.2. + + Signature packets are used in two different contexts. One (signature + type <00> or <01>) is of text (either the contents of a literal + packet or a separate file), while types <10> through <1F> appear only + in key files, after the keys and user IDs that they sign. Type <20> + appears in key files, after the keys that it signs, and type <30> + also appears after a key/userid combination. Type <40> is intended to + be a signature of a signature, as a notary seal on a signed document. + + The output of the message digest algorithm is a message digest, or + hash code. Field i contains the cyphertext produced by encrypting the + message digest with the signer's private key. Field h contains the + first two bytes of the unencrypted message digest. This enables the + recipient to determine if the correct public key was used to decrypt + the message digest for authentication, by comparing this plaintext + copy of the first two byes with the first two bytes of the decrypted + digest. These two bytes also serve as a 16-bit frame check sequence + for the message. + +6.2.2 Public-key-related fields + + The message digest is signed by encrypting it using the writer's + private key. Field (e) is the ID of the corresponding public key. + + The public-key-encryption algorithm is specified by the public-key + cryptosystem (PKC) number of field (f). The following PKC numbers are + currently defined: + + 1 - RSA + 255 - experimental + + More PKC numbers may be defined in the future. An implementation + need not support every PKC number. The implementor must document the + PKC numbers understood by an implementation. + + + + + + +Atkins, et. al. Informational [Page 15] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + A PKC number identifies both a public-key encryption method and a + signature method. Both of these methods are fully defined as part of + the definition of the PKC number. Some cryptosystems are usable only + for encryption, or only for signatures; if any such PKC numbers are + defined in the future, they will be marked appropriately. + +6.2.3 RSA signatures + + An RSA-signed byte string is a multiprecision field that is formed by + taking the message digest and filling in an ASN structure, and then + encrypting the whole byte string in the RSA key of the signer. + + PGP versions 2.3 and later encode the MD into a PKCS-format signature + string, which has the following format: + + MSB . . . LSB + 0 1 <FF>(n bytes) 0 ASN(18 bytes) MD(16 bytes) + + See RFC1423 for an explanation of the meaning of the ASN string. It + is the following 18 byte long hex value: + + <30 20 30 0C 06 08 2A 86 48 86 F7 0D 02 05 05 00 04 10> + + Enough bytes of <FF> padding are added to make the length of this + whole string equal to the number of bytes in the modulus. + +6.2.4 Miscellaneous fields + + The timestamp field (d2) is analogous to the date box next to a + signature box on a form. It represents a time which is typically + close to the moment that the signature packet was created. However, + this is not a requirement. Users may choose to date their signatures + as they wish, just as they do now in handwritten signatures. + + If an application requires the creation of trusted timestamps on + signatures, a detached signature certificate with an untrusted + timestamp may be submitted to a trusted timestamp notary service to + sign the signature packet with another signature packet, creating a + signature of a signature. The notary's signature's timestamp could + be used as the trusted "legal" time of the original signature. + + + + + + + + + + + +Atkins, et. al. Informational [Page 16] + +RFC 1991 PGP Message Exchange Formats August 1996 + + +6.3 Compressed data packets + + Purpose. A compressed data packet is an envelope which safely + squeezes its contents into a small space. + + Definition. A compressed data packet is the concatenation of the + following fields: + + (a) a packet structure field; + (b) a byte, giving a compression type; + (c) a byte string of compressed data. + + Byte string (c) is a packet which may be decompressed using the + algorithm identified in byte (b). Typically, the data that are + compressed consist of a literal data packet or a signature packet + concatenated to a literal data packet. + + A compression type selects a compression algorithm for use in + compressed data packets. The following compression numbers are + currently defined. + + 1 - ZIP + 255 - experimental + + More compression numbers may be defined in the future. An + implementation need not support every MD number. The implementor + must document the compression numbers understood by an + implementation. + +6.4 Conventional-key-encrypted data packets + + Purpose. A conventional-key-encrypted data packet is formed by + encrypting a block of data with a conventional encryption algorithm + using a one-time session key. Typically, the block to be encrypted is + a compressed data packet. + + Definition. A conventional-key-encrypted data packet is the + concatenation of the following fields: + + (a) a packet structure field; + (b) a byte string of encrypted data. + + The plaintext or compressed plaintext that is encrypted to form field + (b) is first prepended with 64 bits of random data plus 16 "key + check" bits. The random prefix serves to start off the cipher + feedback chaining process with 64 bits of random material; this + serves the same function as an initialization vector (IV) for a + cipher-block-chaining encryption scheme. The key check prefix is + + + +Atkins, et. al. Informational [Page 17] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + equal to the last 16 bits of the random prefix. During decryption, a + comparison is made to see if the 7th and 8th byte of the decrypted + material match the 9th and 10th bytes. If so, then the conventional + session key used for decryption is assumed to be correct. + +6.4.1 Conventional-encryption type byte + + Purpose. The conventional-encryption type byte is used to determine + what conventional encryption algorithm is in use. The algorithm type + byte will also define how long the conventional encryption key is, + based upon the algorithm in use. + + Definition. A conventional-encryption type byte is a single byte + which defines the algorithm in use. It is possible that the + algorithm in use may require further definition, such as key-length. + It is up to the implementor to document the supported key-length in + such a situation. + + 1 - IDEA (16-byte key) + 255 - experimental + +6.5 Public-key-encrypted packets + + Purpose. The public-key-encrypted packet is the format for the + session key component of a message. The purpose of this packet is to + convey the one-time session key used to encrypt the message to the + recipient in a secure manner. This is done by encrypting the session + key with the recipient's public key, so that only the recipient can + recover the session key. + + Definition. A public-key-encrypted packet, version 2 or 3, is the + concatenation of the following fields: + + (a) a packet structure field; + (b) a byte, giving the version number, 2 or 3; + (c) a number ID field, giving the ID of a key; + (d) a byte, giving a PKC number; + (e) a byte string of encrypted data (DEK). + + Byte string (e) represents the value of the session key, encrypted + using the reader's public key K, under the cryptosystem identified in + byte (d). + + The value of field (c) is the ID of K. + + Note that the packet does not actually identify K: two keys may have + the same ID, by chance or by malice. Normally it will be obvious + from the context which key K was used to create the packet. But + + + +Atkins, et. al. Informational [Page 18] + +RFC 1991 PGP Message Exchange Formats August 1996 + + + sometimes it is not obvious. In this case field (c) is useful. If, + for example, a reader has created several keys, and receives a + message, then he should attempt to decrypt the message only with the + key whose ID matches the value of field (c). If he has accidentally + generated two keys with the same ID, then he must attempt to decrypt + the message with both keys, but this case is highly unlikely to occur + by chance. + +6.5.1 RSA-encrypted data encryption key (DEK) + + The Data Encryption Key (DEK) is a multiprecision field which stores + an RSA encrypted byte string. The byte string is a PKCS encoding of + the secret key used the encrypt the message, with random padding for + each Public-Key encrypted packet. + + PGP version 2.3 and later encode the DEK into an MPI using the + following format: + + MSB . . . LSB + 0 2 RND(n bytes) 0 ALG(1 byte) DEK(k bytes) CSUM(2 bytes) + + ALG refers to the algorithm byte for the secret key algorithm used to + encrypt the data packet. The DEK is the actual Data Encryption Key, + and its size is dependent upon the encryption algorithm defined by + ALG. For the IDEA encryption algorithm, type byte 1, the DEK is 16 + bytes long. CSUM is a 16-bit checksum of the DEK, used to determine + that the correct Private key was used to decrypt this packet. The + checksum is computed by the 16-bit sum of the bytes in the DEK. RND + is random padding to expand the byte to fill the size of the RSA + Public Key that is used to encrypt the whole byte. + +6.6 Public Key Packet + + Purpose. A public key packet defines an RSA public key. + + Definition. A public key packet is the concatenation of the + following fields: + + (a) packet structure field (2 or 3 bytes); + (b) version number = 2 or 3 (1 byte);; + (c) time stamp of key creation (4 bytes); + (d) validity period in days (0 means forever) (2 bytes); + (e) public-key-cryptosystem (PKC) type (1 byte); + (f) MPI of RSA public modulus n; + (g) MPI of RSA public encryption exponent e. + + The validity period is always set to 0. + + + + +Atkins, et. al. Informational [Page 19] + +RFC 1991 PGP Message Exchange Formats August 1996 + + +6.7 User ID Packet + + Purpose. A user ID packet identifies a user and is associated with a + public or private key. + + Definition. A user ID packet is the concatenation of the following + fields: + + (a) packet structure field (2 bytes); + (b) User ID string. + + The User ID string may be any string of printable ASCII characters. + However, since the purpose of this packet is to uniquely identify an + individual, the usual practice is for the User ID string to consist + of the user's name followed by an e-mail address for that user, the + latter enclosed in angle brackets. + +7. Transferable Public Keys + + Public keys may transferred between PGP users. The essential elements + of a transferable public key are + + (a) One public key packet; + (b) One or more user ID packets; + (c) Zero or more signature packets + + The public key packet occurs first. Each of the following user ID + packets provides the identity of the owner of this public key. If + there are multiple user ID packets, this corresponds to multiple + means of identifying the same unique individual user; for example, a + user may enjoy the use of more than one e-mail address, and construct + a user ID packet for each one. Immediately following each user ID + packet, there are zero or more signature packets. Each signature + packet is calculated on the immediately preceding user ID packet and + the initial public key packet. The signature serves to certify the + corresponding public key and user ID. In effect, the signer is + testifying to his or her belief that this public key belongs to the + user identified by this user ID. + +8. Acknowledgments + + Philip Zimmermann is the creator of PGP 1.0, which is the precursor + of PGP 2.x. Major parts of later versions of PGP have been + implemented by an international collaborative effort involving a + large number of contributors, under the design guidance of Philip + Zimmermann. + + + + + +Atkins, et. al. Informational [Page 20] + +RFC 1991 PGP Message Exchange Formats August 1996 + + +9. Security Considerations + + Security issues are discussed throughout this memo. + +10. Authors' Addresses + + Derek Atkins + 12 Rindge Ave. #1R + Cambridge, MA + + Phone: +1 617 868-4469 + EMail: warlord@MIT.EDU + + + William Stallings + Comp-Comm Consulting + P. O. Box 2405 + Brewster, MA 02631 + + EMail: stallings@ACM.org + + + Philip Zimmermann + Boulder Software Engineering + 3021 Eleventh Street + Boulder, Colorado 80304 USA + + Phone: +1-303-541-0140 + EMail: prz@acm.org + + + + + + + + + + + + + + + + + + + + + + +Atkins, et. al. Informational [Page 21] + diff --git a/Protocol/PGP/Info/scaleweb.txt b/Protocol/PGP/Info/scaleweb.txt new file mode 100644 index 0000000..c072e01 --- /dev/null +++ b/Protocol/PGP/Info/scaleweb.txt @@ -0,0 +1,719 @@ + +-----BEGIN PGP SIGNED MESSAGE----- + + Scaling the Web of Trust: + Combining Kerberos and PGP to Provide Large Scale Authentication + + Jeffrey I. Schiller + Massachusetts Institute of Technology + + Derek Atkins + Massachusetts Institute of Technology + +Abstract + +Internet Security has become more important recently as the Internet +grows exponentially and security breaches become more publicized. An +important area of concern for many Internet users is the privacy and +integrity of their electronic files and messages. Phil Zimmermann's +Pretty Good Privacy (PGP) provides a general purpose utility for file +and message protection. However PGP requires that communicating users be +"introduced" to each other. This paper describes a scheme that permits +an enterprise using Kerberos to create an automated introducer called +the PGP Key Signer Service. Using this service people in the enterprise +who have no common acquaintances to act as introducers can be introduced +through the Key Signer. + +1. Introduction + +The Internet Community has grown from a small collection of Computer +Science researchers into the International Information +Infrastructure. Yet the security technology currently deployed on the +Network reflects its original experimental roots. There are many and +varied reasons why things are today the way they are. However, one of +the key reasons is because most Internet users are unaware of the +inherent lack of security on the net. When most people login using their +password they assume that only they may use their computer +accounts. Similarly when they send electronic mail they assume that only +the marked recipients will see the message. When they receive electronic +mail they assume that it originated from the person labeled in the +"From" field of the message. + +As the Internet has grown, so has the quantity of security +problems. Recently these problems have received significant publicity +and the public's understanding of the lack of security is growing. With +this growth in understanding comes a demand for security. + +Several systems now exist to provide security for computers and +networks. Two of the more popular security programs are Kerberos [Kerb1, +Kerb2] and PGP [PGP]. Kerberos is MIT's real time authentication system, +it provides for the security of login sessions and client server +transactions. PGP is Phil Zimmermann's "Pretty Good Privacy" public key +encryption program. It provides for confidentiality and authentication +of electronic mail (e-mail) and other "store and forward" types of +transactions as well as encryption of private files. + +Each of these systems has its strengths, weaknesses and applicable +problem domains. This paper will show how the two can be combined to +provide a hybrid security service which neither alone can provide. + +In the sections that follow we will provide a brief introduction to PGP +and Public Key Cryptography, the underlying technology that makes PGP +possible. We will then briefly describe Kerberos. Readers who desire a +more thorough background should read the papers referenced +above. Finally we will discuss how PGP can make use of Kerberos in a +significant way. + +1.1 The Problem with Electronic Mail + +SMTP, or Simple Mail Transport Protocol is the Internet standard for the +delivery of electronic mail between computer systems. A tried and true +protocol, it provides message services to millions of Internet users +everyday. Yet it provides for no security services at all. Perhaps more +to the point, SMTP servers believe that mail originates from where it +claims to. + +Forging e-mail messages is child's play. On the MIT campus alone we have +seen several independently written shell scripts that permit even the +most novice of computer users to send forged messages that are +indistinguishable form the real McCoy. + +An additional threat to e-mail is that very few networks are protected +against eavesdroppers. Recently, bulletins form Carnegie Mellon's +Computer Emergency Response Team (CERT) [1] have pointed out many +incidents of computer crackers "sniffing" passwords from the Internet +[CertSniff]. The same technology that provides for the sniffing of +passwords can easily read e-mail in flight as well. + +Today the only thing protecting e-mail from these prying eyes is the +sheer boredom that most e- mail represents! However as e-mail is used +increasingly to discuss confidential corporate plans, commercial +transactions and even credit card numbers, it will be a target for +network eavesdroppers. + +Another important feature lacking in e-mail is the ability to prove +after the fact that a message was "signed" by its sender. This ability +is needed not only for casual proof of origination, but also for formal +uses of e-mail such as electronic purchase orders, bid documents and +other commercial interactions. Luckily there is a technology to address +this requirement. Digital Signatures permit people to electronically +sign documents in a fashion that allows anyone to prove that a document +originated from whom it claims to have originated from. + +Digital Signatures are provided by Public Key Encryption systems such as +the RSA [RSA] system. RSA is used by Internet Privacy Enhanced Mail +[PEM] and by PGP. Its use in PGP will be discussed here. + +2. PGP + +PGP was written by Phil Zimmermann in 1991 in reaction to a move by the +U.S. Federal Government to require providers of communication services +to provide plaintext copies of encrypted information to the government +upon its request. PGP was designed to make this impossible by providing +a general purpose cryptographic utility which people can use to protect +information that they keep for themselves (say in private files) and +share with others (for example electronic mail). As such the primary +focus of the development of PGP was on privacy. + +PGP provides privacy by using a combination of the IDEA [IDEA] symmetric +cipher and the RSA public key system. PGP also includes the ability to +make digital signatures. This makes PGP a complete electronic mail +security tool. It provides confidentiality, authentication of +origination and integrity protection for files and messages. + +PGP originally had patent difficulties. Because RSA is patented within +the United States, users require a license from Public Key Partners or +software from RSA Data Security in order to make use of PGP without the +threat of legal action being taken against them. In addition, +cryptographic systems, be they hardware or software, currently may not +be exported from the United States without a license from the State +Department Office of Defense Trade Controls. + +Version 2 of PGP was coded in Europe and imported into the United +States. Starting in May, 1994 the Massachusetts Institute of Technology +(MIT) has been distributing a version of PGP which includes the RSAREF +software toolkit licensed from RSA Data Security. This release of PGP, +although not exportable from the United States, provides non-commercial +users of PGP a legally usable version. The ViaCrypt company [2] sells a +commercial version that is also licensed from Public Key Partners. The +effect is that PGP is just about globally available. + +2.1. Public Key Cryptography and its use in PGP + +Before we continue we should talk a little about Public Key +Cryptography. Invented in 1976 by Whitfield Diffie and Martin Hellman +[DiffieHellman], Public Key Encryption is a kind of cipher system where +instead of the traditional single encryption key, two keys are +employed. One key is used for encryption while the other is used for +decryption. Knowing one key does not imply knowledge of the other. + +PGP uses the RSA system, named for its three inventors: Ronald Rivest, +Adi Shamir and Leonard Adleman. Each user of PGP generates a key +pair. This pair consists of a public key and a private key. As their +names imply, the public key may be made public while the private key is +kept secret by its owner. Both the public and private keys are large +values (not simple words or small numbers that can be memorized) so PGP +stores them in files. To protect the private key, PGP prompts for a pass +phrase during key pair generation. This pass phrase is converted into an +IDEA key which is then used to encrypt (using the IDEA cipher) the file +that contains the user's RSA private key. A prudent user will protect +this file against unwanted perusal even though it is also protected by +the pass phrase. Some people go to the length of storing their private +key only on a removable floppy which they lock up when not in use. + +PGP may be used to encipher a private message between two users if they +know each others' public keys. A sender can send a message to a +recipient by encrypting it in the public key of the recipient. The +recipient can then decrypt the message by making use of her private +key. This is shown graphically in Figure 1. A private key can also be +used to construct an unforgeable digital signature which can be affixed +to any message. The sender would use her own private key to construct +the digital signature, before encrypting the message, and the recipient +uses the sender's public key to verify the integrity of a message, after +first decrypting it. + + Encipherment Decipherment + +---------------+ +---------------+ + | | | | + ------->| |---------->| |---------> + Message | | Encrypted | | Message + +---------------+ Message +---------------+ + ^ ^ + | | + | | + | | + + Public Key Private Key + + Figure 1: Message Flow and Encipherment + +Note carefully that all sensitive operations (reading a private message +or creating the unforgeable signature) make use of the private +keys. Private keys are never exchanged or disclosed. Public keys are +never used for these sensitive operations and therefore it is not a +problem for them to be exchanged unencrypted (or published in a public +place). + +To facilitate the exchange of PGP public keys, a network of key servers +has arisen that make use of software written by Michael Graff of Iowa +State University. The key servers are electronic mail based automated +responders. To fetch someone's public key a user sends an electronic +message in a particular format requesting the key and the responder +automatically sends back the requested key via electronic mail. Brian +LaMacchia of MIT has added a World Wide Web server interfaceto this +network of servers so keys may be retrieved and stored using popular +World Wide Web browsing software. [3] + +However there is an important issue that needs to be considered. How +does someone know that a public key that claims to belong to a +particular person in fact does so? After all anyone can generate a key +pair and publish the public key along with a claim that the key belongs +to, say, the President. There needs to be a way to securely associate a +public key with a name. + +PGP addresses this problem by the creation of the PGP Web of Trust. [4] +The Web of Trust starts by having two people each generate and exchange +public keys and names. Because this is done in person, and presumably +between two people who know each other, they can now communicate with +each other using signed messages and know beyond a shadow of a doubt +that they are talking with each other. The Web grows from this simple +one on one association when one of the participants performs a similar +key exchange with a third person. An example will illustrate this best. + +Assume that Alice and Bob meet in person and exchange public keys. Now +Alice and Toni meet later on and exchange keys. Because Alice securely +has Bob's key, Alice can provide a "signed" copy of Bob's key to +Toni. Alice signs Bob's key by computing a digital signature on it using +PGP (which has built in features for doing exactly this exchange). In +this example Alice is acting as an Introducer between Bob and +Toni. Although they have never met, Alice has introduced them digitally +to each other. Figure 2 illustrates this concept. + + +------------------------------------+ + | +--------------+ | + | |PGP Public Key| | + | +--------------+ | + | /---+----\ + | Bob / \ + | | Digital | + +------------------------------+ Signature | + | | + / \ / \ + / \--------/ \ + --- /| | | | \ +- + |/ | | | | \| + |/\| |/\| + + Signed by: Alice + + Figure 2: Graphical Representation of a Signed Key + +PGP permits multiple people to digitally sign an association of a name +with a public key. If Bob has another friend, Bill, Bob can ask Bill to +sign his key as well. Now if someone gets Bob's key (either from Bob or +from a key server) and they trust Bill, they can know that they have +Bob's key, even if they never heard of Alice. Figure 3 illustrates this, +Bob's key has two signatures on it now, one from Bill and one from +Alice. + + + +------------------------------------+ + | +--------------+ | + | |PGP Public Key| | + | +--------------+ | + | | + |/---+----\ Bob /---+----\ + / \ / \ + | Digital | | Digital | + | Signature +------------------+ Signature | + | | | | + / \ / \ / \ / \ + / \--------/ \ / \--------/ \ + --- /| | | | \ +- --- /| | | | \ +- + |/ | | | | \| |/ | | | | \| + |/\| |/\| |/\| |/\| + + Signed by: Bill Signed by: Alice + + Figure 3: Graphical Representation of a Multiply Signed Key + + +---------------------------+ +----------------------------+ + | +--------------+ | | +--------------+ | + | |PGP Public Key| |------>| |PGP Public Key| | + | +--------------+ |-+ | +--------------+ | + | John Doe <jdoe@mit.edu> |\ \ | Jane Smoot <jsmoot@mit.edu>| + +-----------+---------------+ \ \ +----------------------------+ + | | \ + v | | + +----------------------------+ | | +-----------------------------+ + | +--------------+ | | | | +--------------+ | + | |PGP Public Key| | | | | |PGP Public Key| | + | +--------------+ | | \->| +--------------+ | + | Joe Admin <jsysadm@mit.edu>| | | Ben Bitdiddl <bitdidl@mit> | + +----------------------------+ | +------------------+----------+ + ^ | | + | | | + | v | + +--+-----------------------+ | + | +--------------+ | + + | |PGP Public Key| |<---/ + | +--------------+ | + | Bill Clinton@whitehouse> | + +--------------------------+ + + John Doe vouches for: + Jane Smoot + Ben Bitdiddl + Bill Cliton + Joe Admin + Joe Admin vouches for: + *no one* + Bill Clinton vouches for: + Joe Admin + Ben Bitdiddl vouches for: + Bill Clinton + Jane Smoot vouches for: + *no one* + + Figure 4: A Sample Web of Trust + +As more people sign each others' keys, the Web of Trust builds +up. Figure 4 schematically depicts a more complicated situation. The +arrows between the keys represent the signatures. So the arrow pointing +between John Doe and Jane Smoot represents that John has signed Jane's +key. + +The fundamental problem with the Web of Trust is that it depends on +personal contact between many of the Web participants to build it up. It +also depends on all of these participants properly verifying the +identity of people whose keys they sign. It is not reasonable to expect +a large group of people to be able to perform the necessary +verifications without some significant number of them cheating. By +cheating we mean that they sign someone's key without really knowing +that the key really belongs to them. For example they may sign the key +of someone who merely sends it by e-mail. Such a key cannot be trusted +because the authenticity of e-mail is suspect in the absence of PGP, +which is why we want the Web in the first place! + +The Internet's Privacy and Security Research Group was aware of this +problem when they designed the Internet standard Privacy Enhanced Mail +(PEM). The PEM solution is to create a strict hierarchy of trust. At the +root of this hierarchy is the Internet Society which will sign the keys +of special organizations called Policy Certification Authorities +(PCAs). In turn PCAs will sign the keys of organizations which finally +will sign the keys of individuals. + +Although this rigid hierarchical approach scales better then the PGP Web +of Trust, it requires a fair amount of infrastructure to be in place +before two people may use PEM to secure their e-mail. With PGP, if Alice +and Bob wish to exchange secure messages and they have no common +associates to Web their keys together, they can still meet in person and +manually exchange keys. To use PEM, on the other hand, Alice's company +will have to be registered with a PCA and be in a position to issue key +signatures (called Certificates in the PEM terminology) to Alice. Bob's +company will also have to be part of the game. Very few organizations +are currently setup to use PEM. Most likely Alice and Bob are out of +luck! + +2.2. A Word on Names + +PEM requires the use of X.500 style names. These names are of a special +form and are not directly printable. By comparison, each PGP name is +chosen by an end user. This flexibility is another reason why PGP has +found quick acceptance in the community. + +Although the PGP program will happily accept any string as the name for +a key, Internet users typically use a name that would easily fit in the +"To:" field of an e-mail message. For example a PGP key may be named: + + John H. Doe <JDoe@xyzzy.com> + +where John Doe's e-mail address is JDoe@xyzzy.com. Such a name may be +provided to most RFC822 e-mail compliant systems without difficulty. + +PGP is oriented toward electronic mail. Electronic mail does not have a +strong performance requirement. Basically PGP processes mail messages +and needs to be fast enough to run faster then reasonable human +perception. However many applications require many transactions per +second. Kerberos is designed to work in such an environment. + +3. Kerberos + +Kerberos was developed by MIT's Project Athena in 1986. It provides for +cryptographically based secure real-time authentication of individuals +and computers. Kerberos users are given tickets when they login which +they can then present to network services to prove their +identity. Kerberos was designed for real-time client server +applications; it is fast and provides protection against replay of +sessions and other security threats to a real time system. + +In order to make Kerberos fast, and to avoid patent issues during its +design, Kerberos today does not make use of Public Key +technology. Instead it relies on the U.S. Data Encryption Standard [5] +(DES) [DES] cipher system. + +What this means is that with Kerberos one can prove to a particular +service that a user is who she claims to be, but one cannot generate a +digital document that is digitally signed such that anyone, now and in +the future, can verify it. + +Kerberos provides authentication services to a large number of users. A +Kerberos Realm [6] can support over 100,000 users. At MIT we have +approximately 25,000 users in our "ATHENA.MIT.EDU" realm and about 7,000 +of them login every day. In general, each organization will have a +Kerberos realm of its own. Kerberos can also be used to provide +authentication between realms when a shared key is established by the +realm administrators. + +3.1. A Word on Names + +Kerberos names have structure to them. In version 4 (and version 5 as +provided by MIT) names have the form name.instance@REALM. Typically a +company will use its Internet Domain name as its Kerberos realm +name. [7] The instance portion of a name is typically only present in +the names of computer services and in special circumstances. Most user +names do not have an instance component. Therefore the John Doe of our +previous example will likely have a Kerberos name of: + + JDoe@XYZZY.COM + +4. Combining Kerberos and PGP + +Kerberos is designed for an organization size body of users, but doesn't +provide digital signatures. PGP on the other hand provides document +authentication via digital signatures, but its Web of Trust is +cumbersome to use on an organization level. Below we present the design +of a service which provides a way to integrate the PGP Web of Trust with +the Kerberos authentication model. By doing so an organization can use +Kerberos to leverage its use of PGP. + +Our design goal is to provide e-mail and document security across an +organization the size of MIT [8] and yet make no modifications to the +basic Kerberos or PGP systems. + +We do this by introducing a new Kerberos authentication service. We call +this service the PGP Signer service. The PGP Signer service appears to +PGP as just another user who has a public key. To Kerberos it is simply +another service which makes use of Kerberos authentication. + +To use the PGP Signer service, a user invokes the signer service client +program. This program takes the user's public key (and name) and sends +this in a Kerberos authenticated transaction to the PGP Signer +server. The server, upon receipt of the transaction, compares the name +in the PGP public key with the Kerberos authenticated name. This +comparison is made using a set of rules that determine if the claimed +PGP name is congruent with the authenticated Kerberos name. If they are +not congruent the request is rejected; If the names are congruent, then +the PGP Signer service signs the PGP public key and name using its +private key and returns the result. + +To take full advantage of the PGP Signer service, each PGP user in the +organization needs to obtain (usually at the same time as they obtain +the PGP Signer client program) the PGP Signer's public key and add it to +their personal PGP keyring as a trusted signer. [9] When this is done, +the PGP Signer can act as a trusted introducer between people. Each user +securely meets with the PGP Signer courtesy of Kerberos authentication. + +When a group of users wish to exchange e-mail with each other, securing +it with PGP, they merely need to use the PGP Signer as their +introducer. As a program, the signer never sleeps and is always +available to provide service! + +4.1. Name Congruency + +For PGP names and Kerberos names to be congruent, they need to describe +the same entity. A simple congruency rule is to require that submitted +PGP key names have the structure "Real Name <user@realm>" format. The +PGP Signer may choose to ignore the "Real Name" portion of the PGP name +and ensure that the portion between the angle brackets is equal to the +Kerberos name. So: + + John H. Doe <JDoe@xyzzy.com> + +would be congruent to: + + JDoe@XYZZY.COM + +If the PGP signer has access to it a database mapping Kerberos user +names to real names, then it can enforce that the supplied real name +also corresponds to the actual user name. This permits the PGP Signer to +reject names of the form: + + Chief Executive Officer and King <jdoe@xyzzy.com> + +assuming that John isn't the Chief and King! + +4.2. The Security of the PGP Key Signer + +The PGP Signer service must run on a secure computer system. This is +because it requires access to the PGP Signer's private RSA key. The PGP +Signer's Private key is the private key generated along with the PGP +Signer's public key, which is published. The PGP Signer never has access +to any user's private key.Whoever obtains this private key may construct +trusted PGP keys that do not belong to their claimed owner and the +organization's security can be compromised. + +Organizations that operate Kerberos servers are familiar with the +requirement for a physically secure computer, as Kerberos key +distribution servers also need to be managed securely. However the PGP +Key Signer does not have to be as guarded as the Kerberos server because +recovery from the compromise of the PGP Key Signer is not as disastrous. + +4.2.1. Operating a Secure Signer Server + +Along with using good host security techniques and providing physical +security for the PGP Signer, some additional steps taken in advance can +make recovery from a compromise easier. + +The first step is to keep a copy of the PGP Signer private key in a safe +place off-line. A floppy disk can easily store a PGP private key and +should suffice for the off-line backup. We recommend that two such +floppies be created and stored in safe and secure locations. + +The PGP Signer should keep a copy of all public keys that it signs. This +database of public keys should be backed up frequently. Because it +contains no confidential information, this backup of public keys need +not be protected against reading, but should be protected against +unauthorized modification. An off-line periodic tape backup suffices. + +4.2.2. If the PGP Signer is Compromised + +Unlike Kerberos, where compromise of the server is a disaster, security +can be restored to a compromised PGP Signer with just a little effort. + +The first step is to repair whatever host security or physical security +problem resulted in the compromise in the first place. Once you are sure +that the PGP Signer computing environment is safe, destroy the copy of +the public key database and the file containing the PGP Signer private +key (these will be "pubring.pgp" and "secring.pgp", the standard PGP +keyring files). Restore them from a safe backup. Generate a new PGP +Signer key and use this to sign all the keys that are in the backup +public key database. Use the old PGP Signer key private key to revoke +the old PGP Signer public key. Finally distribute the new PGP Signer +public key and ensure that the key revocation certificate [10] that PGP +generated for the old key is distributed as well. + +Although you will have to "go public" over the compromise of the PGP +Signer, the steps that end-users will have to take to recover are +minimal. + +In the future it may be possible to store the PGP Signer private key in +special purpose hardware. BBN manufactures a device that may be used +for this purpose. Designed for PEM, the BBN Safekeyper(tm) may be +adaptable for the use with the PGP Signer. The advantage that this +hardware brings is that the PGP Signer private key can be stored in it +in a fashion that makes it impossible to read it out. An intruder who +gains physical access to the PGP Signer server may steal the Safekeyper, +but a "read only" compromise where she secretly learns the PGP Signer +private key is not possible. + +5. Why not just use Kerberos? + +At this point the reader may be wondering why we bother to integrate +Kerberos and PGP in the fashion that we do here. Wouldn't it make more +sense to simply extend Kerberos so that it can generate digital +signatures and provide encrypted e-mail messages? + +The answer to this has two parts. The first has to do with creating +digital signatures. + +To create a digital signature that is verifiable to anyone after it is +created requires the use of Public Key Cryptography. Because Kerberos +does not make use of Public Key Cryptography it cannot create a digital +signature. The best that Kerberos can do is prove to one party that a +message is from another party. However, before you can create the +equivalent of a digital signature using Kerberos, you need to know the +identities of the parties who will be verifying the signature. Using +Public Key Technology one can create a signature that anyone, both known +and unknown, can verify. + +One might consider adding public key cryptographic algorithms to +Kerberos in order to implement this function. However one of our design +goals was to not make any modifications to the basic Kerberos system. It +is also worth noting that if you add public key technology to Kerberos +to implement digital signatures for messages you ultimately wind up +re-inventing PGP (or PEM). + +The other answer is more philosophical and has to do with +confidentiality. Because Kerberos does not use Public Key Cryptography, +a Kerberos "super-user" can always decrypt a message that was encrypted +from one person to another using Kerberos techniques. Perhaps more +ominous is that such a "super-user" can be compelled to decrypt messages +presented to her under court order. + +It is our belief that confidential messages should be exactly that: +messages that are only readable by the parties communicating, which +usually does not include the government! + +6. Project Status + +The PGP Signer design is described in this paper. A server is operating +at MIT on the Internet host RFA.MIT.EDU. A client program exists for +UNIX( systems. As of the time of the writing of this paper (November +1994) MIT has not released the signer system code. However, it is our +intention to do so. + +7. Future Work + +Today PGP has a significant presence in the Internet community, a +presence not shared by the standard PEM. However PEM may yet take on an +important role as the required certificate infrastructure comes into +existence, as it eventually will. A similar PEM signer can be created +using most of the functions of the PGP Signer, but this signer will +issue PEM certificates as well. + +8. Conclusion + +This paper has described the design of a simple service that permits a +digital signature/privacy application, PGP, to leverage and bootstrap +its authentication infrastructure from a real-time authentication +service, Kerberos. It demonstrates that these technologies are not +antagonistic to each other but instead are quite synergistic. + +9. References + +[IDEA] X. Lai, On the Design and Security of Block Ciphers, ETH Series +in Information Processing, v. 1, Konstanz: Hartung-Gorre Verlag, 1992 + +[CertSniff] Cert Advisory CA-94:01 Ongoing Network Monitoring Attacks, +Computer Emergency Response Team, Carnegie Mellon University, +Pittsburgth, Pennsylvania (Februrary 3, 1994). + +[DES] Federal Information Processing Standards Publication (FIPS PUB) +46-1, Data Encryption Standard, Reaffirmed 1988 January 22 (supersedes +FIPS PUB 46, 1977 January 15) + +[DiffieHellman] W. Diffie and M. E. Hellman, New Directions in +Cryptography, IEEE Transactions on Information Theory, v. IT-22, n. 6, +November 1976, pp. 644-654 + +[Kerb1] J G. Steiner, B. C. Neuman, J. I. Schiller, Kerberos: An +Authentication Service for Open Network Systems, Usenix Conference +Proceedings pp191-202, Dallas, Texas (February 1988). + +[Kerb2] S. P. Miller, B. C. Neuman, J. I. Schiller, and J. H. Saltzer, +Project Athena Technical Plan Section E.2.1: Kerberos Authentication and +Authorization System, M.I.T. Project Athena, Cambridge, Massachusetts +(December 21, 1987). + +[PEM] Linn, J., Privacy Enhancement for Internet Electronic Mail: Part +I: Message Encryption and Authentication Procedures, RFC 1421, Internet +Engineering Task Force, (February 1993). + +[PGP] P. Zimmermann, PGP User's Guide Volumes I & II, MIT PGP Release +2.6, Cambridge, Massachusetts (May 1994). + +[RSA] R. Rivest, A. Shamir, L. Adleman, A method for obtaining digital +signatures and public key cryptosystems, CACM, Vol 21 No 2, pp 120-128 +(February 1978). + +10. Authors + +JEFFREY I. SCHILLER received his S.B. in Electrical Engineering (1979) +from the Massachusetts Institute of Technology. As MIT Network Manager +he has managed the MIT Campus Computer Network since its inception in +1984. Prior to his work in the Network Group he maintained MIT's +Multics timesharing system during the timeframe of the ArpaNet TCP/IP +conversion. He is an author of MIT's Kerberos Authentication system. +Mr. Schiller is the Internet Engineering Steering Group's (IESG) Area +Director for Security. He is responsible for overseeing security related +Working Groups of the Internet Engineering Task Force (IETF). He is +also a member of the Privacy and Security Research Group (PSRG) of the +Internet Research Task Force His recent efforts have involved work on +the Internet Privacy Enhanced Mail standards (and implementation) as +well as releasing a U.S. legal freeware version of the popular PGP +encryption program. Mr. Schiller is also a founding member of the +Steering Group of the New England Academic and Research Network +(NEARnet). NEARnet provides Internet Access to institutions in New +England. + +DEREK ATKINS is currently a graduate student in Media Arts and Sciences +at MIT. He is studying distributed, secure systems for multimedia data +distribution. His intrests include, among other things, cryptography, +flying, and playing guitar. He can be reached at warlord@MIT.EDU. + +FootNotes: + +[1] CERT was founded after the Internet Worm Incident of November +1988. Funded by ARPA, it is operated by Carnegie Mellon's Software +Engineering Institute. + +[2] ViaCrypt, 9033 North 24th Avenue, Suite 7, Phoenix, Arizona 85021, +USA, Phone: (602) 944-0773, viacrypt@acm.org + +[3] http://www-swiss.ai.mit.edu/~bal/pks-toplev.html + +[4] Not to be confused with the World Wide Web + +[5] DES is a U.S. Standard Symmetric cipher. Kerberos uses it instead of +IDEA simply because IDEA was not around when Kerberos was designed. + +[6] A realm is the administrative domain of a set of Kerberos servers +and the users they authenticate. + +[7] Because Kerberos is case sensitive whereas Internet domain names are +case insensitive, by convention realm names are always in all +upper-case. + +[8] The MIT community is roughly 14,000 people, 4,500 undergraduate +students, 5,300 graduate students and about 4,000 faculty and staff. + +[9] The Signer's key will be provided in a file. When PGP processes this +file it will automatically recognize that it contains a PGP key and will +ask the user if she wishes to add the key to her keyring. PGP will also +ask if the key should be "trusted" to sign other keys. + +[10] When PGP is instructed to revoke a key it generates a key +revocation certificate. It is a datum that instructs any invocation of +PGP that receives it to consider the revoked key as invalid. + +-----BEGIN PGP SIGNATURE----- +Version: 2.6.2 + +iQCVAwUBLsQQoMUtR20Nv5BtAQHkUgP/ZPVjAyyV4GdvgSrY9n2HXCSEtN9IskQI +86JiSFOJ6+LFVEJRiF02gkR8rFwliddMpkXPgR9asjVCjLSEgE8WbfSP5HS8H78C +wmV0jkXI0T6UUjHirppVvgVtNf9QSI+35sO81nqzqdJmT+OO3HR3BNrSB3sFIPmq +ZsqdkDrzqmw= +=Jvm/ +-----END PGP SIGNATURE----- + diff --git a/Protocol/PGP/Info/stealth b/Protocol/PGP/Info/stealth new file mode 100644 index 0000000..6383b78 --- /dev/null +++ b/Protocol/PGP/Info/stealth @@ -0,0 +1,173 @@ +From andrewk@cst.ca Tue Feb 27 09:49 EST 1996 +Received: from lapis.cst.ca by emerald.cst.ca with SMTP + (1.38.193.4/15.6) id AA09225; Tue, 27 Feb 1996 09:49:32 -0500 +Return-Path: <andrewk@cst.ca> +Received: by lapis.cst.ca + (1.38.193.5/15.6) id AA00422; Tue, 27 Feb 1996 10:00:37 -0500 +From: Andrew Kuchling <andrewk@cst.ca> +Subject: Re: Stealth PGP work (fwd) +To: develop@emerald.cst.ca +Date: Tue, 27 Feb 96 10:00:37 EST +Mailer: Elm [revision: 70.85.2.1] + + + +(migrating to coderpunks) + +Bill Stewart <stewarts@ix.netcom.com> writes on cpunks: +> Derek wrote: +> > I'm not familiar with the exact details of what stealth does, which is +> > why I asked for more details. The problem is that PGP API, when +> > decrypting a message, keys off the PGP packet types in order to +> > operate. If stealth can work outside of PGP 2.6.2, then it should be +> > possible to add it on to PGP 3, theoretically. +> +> My assumption was that the request for "stealth" was for the more general +> feature of stealthiness rather than the specific implementation provided +> by the Stealth program that "Harry Hastur" wrote to solve it. +> I suspect that the new toolkits in PGP3 will make it much easier to +> implement a good stealth program, but it may or may not be perfect. +> +> The basic requirement is that you can't tell whether a message is +> PGP or Random Bits unless it's for you, for applications like steganography; + +that's Henry's stealth approach, + +> a milder requirement is that you at least can't tell who it's for and +> also can't verify your guess about the recipient if you guess right. + +This could be useful for mail drops to alt.anonymous.messages, it +would save the necessity of generating a new key, when you don't mind +the sender knowing the recipients normal key, but don't want anyone +else to know. Yet you don't care that anyone knows it is a PGP message. + +> Sure, most people don't need it, but most people don't need 1024-bit crypto +> either; + +I think PGP should cater for less rosy situations than that in the US +at present: there are other countries where clandestine use may be +necessary because of local laws, and the way the NSA is headed you +can't be too sure of the US future either. (How's the idea future +odds for mandatory key escrow in the near future?) + +I'd vote for both of your suggested modes, I think they would be +useful to have. + +> 512 bits will protect just about everybody from just about every real threat +> for the next decade or so. One requirement is getting rid of identifiable +> packet type and length indicators, + +separately from the requirement to strip off the keyid? People can +see it's from you, but they can't tell what length and what packets +are in there? + +> another is getting rid of the key id, + +yep. + +> and another is the more subtle problem that M**e mod n isn't +> distributed evenly. + +yep. m**e % n not being evenly distrubited would mean that you should +do the normalising transformation on the RSA block even if you are +just stripping the keyid and leaving the packet types and lengths; not +only does it give away that it is a PGP message (which is now obvious +due to packet type fields), but with enough messages it gives away n, +and hence the keyid. + +> Stealthing the block type and length indicators isn't a requirement for +> ascii-armored and clearsigned versions (obviously not for clearsigned, +> and if you don't like your ascii version starting with ----- BEGIN PGP, +> you can encode the stealth binary with uuencode or MIME instead.) + +You'd need to strip the length indicators if you wanted the option to +pad the message with random junk to a fixed size (eg much like +mixmaster fixed sized packets). + +> [...] +> +> I also don't remember if the block with the public-key-encrypted session key +> has a length indicator or not; + +it does: + +: RSA public-key-encrypted packet +: ------------------------------- +: +: Offset Length Meaning +: 0 1 CTB for RSA public-key-encrypted packet +: 1 2 16-bit (or maybe 8-bit) length of packet + +> if it doesn't, parts of the job become easier. + +> For all the following blocks, you can xor the packet type and length indictor +> with shared-secret random bits. An obvious source, assuming the API lets +> you have it, +> is the random padding that PGP adds to the session key when PK-encrypting, +> or some PRNG output derived from that, and you can do the xoring quickly +> without having to open up packets beyond that. + +This is an interesting approach to allowing a stealthy operation which +can deal with multiple blocks, Henry's method will only recover one +IDEA block. + +Another method would be to rely on all outer packets after the RSA +packet(s) being IDEA encrypted (is this the case?) to reconstruct the +multiple packet lengths. Literal plaintext blocks have length fields +also, so the length information in the outer IDEA block which was +stripped in a stealth operation was redundant anyway. So you could +modify the IDEA decrypt code to use the length field encoded within it +if there is no external length field. + +(saves arguments about whether revealing some of the padding +theoretically weakens anything, but may not fit cleanly in to Derek +and Colins software architecture.) + +(It is possible with Henry's existing code to pad data with random +junk because of the redundant internal length fields: pgp does IDEA +decrypt the packet (including a meaningless decrypt of trailing junk), +but the internal packet length means that it then goes on to ignore +the decrypted padding junk). + +> The initial block's types can be played with by an external program; +> it wouldn't leak too much data to always use a value other than the +> value a normal binary-flavor PGP program would use, so Stealth can +> just pass non-stealthed files through to PGP instead of +> destealthing. + +Stealth autodetect, so you could just point PGP at any message, +steatlhed or not, and have it try to make sense of it? Because it is +a fairly densely packed format (bit fields etc), the legal CTB values +cover a fair proportion of the 256 values a byte can take. It would +break the stealthiness of it to do that. + +Just checking the probablities of getting white noise that would make +it as a valid PGP message: + +probability for mistaken RSA public key enc packet = 2 / 256**3 +(CTB fixed, version = 2 or 3, and algorithm byte = 1) + +probability for mistaken IDEA encrypted packet = 1 / 256 +(CTB fixed) + +So a ~ 1/8,000,000 chance of getting asked for a non existant public +key, sound ok? + +The IDEA encrypted packet is more troublesome, it'll ask for +conventional key passphrase, it'll notice if it is junk due to the +check bytes inside the IDEA encrypted packet, but only after giving +the passphrase. + +Due to the IDEA packet thing, it would seem that you have to forgo the +normal PGP point and go approach, and would have to request unstealthing? + +> Can stealthing the PK-encrypted session key be done transparently in +> a separate program, or does it really gain from integrating with +> PGP's handling. + +I'd argue for integrating, as a core feature so that stego programs, +and remailer programs can rely on and make use of the features. + +Adam + + diff --git a/Protocol/PGP/Makefile b/Protocol/PGP/Makefile new file mode 100644 index 0000000..447762b --- /dev/null +++ b/Protocol/PGP/Makefile @@ -0,0 +1,7 @@ + +clean: + rm -f *.pyc *~ core *.bak + +distrib: clean + cd .. ; tar -zcvf ~/public_html/files/py/pypgp.tgz -X pgp/excludefiles pgp + diff --git a/Protocol/PGP/README b/Protocol/PGP/README new file mode 100644 index 0000000..774accf --- /dev/null +++ b/Protocol/PGP/README @@ -0,0 +1,26 @@ + +The code in this directory is included because my intention is to +eventually provide support for PGP messaging, compatible with PGP 5.x. +However, as of the time of writing (January 1998), I haven't even run +this code in months, and it may not work at all. Once I've finished a +first version of a Python ssh implementation, I'll then return to +working on PGP support. + + + Andrew Kuchling + amk@magnet.com + http://starship.skyport.net/crew/amk/ + + +The original README was: +======================== +This is version 0.02 of the Python/PGP (PyPGP) distribution. It +contains Python classes and modules that read and write PGP-format +files. A PGP binary is not required to encrypt and decrypt PGP +messages. + +The doc/ subdirectory contains the documentation. + +Most of the code in version 0.01 was discarded and rewritten afresh, +starting from new code written by Mark Shuttleworth of Thawte +Consulting (http://XXX) diff --git a/Protocol/PGP/TODO b/Protocol/PGP/TODO new file mode 100644 index 0000000..852e07f --- /dev/null +++ b/Protocol/PGP/TODO @@ -0,0 +1,24 @@ + + Things to do: + + * Are all the parse_bin and write_bin methods completely implemented? + + * Documentation : table of packet classes and their public attributes + + * Signatures aren't yet implemented. + + * There's no high-level interface to PGP messaging, with callbacks +to retrieve keys. + + Medium-term things: + + * I've punted on the problem of random number generation, and +just read from Linux's /dev/urandom device all the time. + + * Use RSAREF if available, to avoid getting in trouble with RSADSI + + Longer-term, once things have stablized: + + * Add more algorithms: 3DES, SHA, ElGamal, ... (Suggestions?) + + * A GUI interface to PGP encryption written in Tkinter. diff --git a/Protocol/PGP/config.txt b/Protocol/PGP/config.txt new file mode 100644 index 0000000..249534a --- /dev/null +++ b/Protocol/PGP/config.txt @@ -0,0 +1,3 @@ +armor=off +PubRing="./pubring.pgp" +SecRing="./secring.pgp" diff --git a/Protocol/PGP/doc/bugreport b/Protocol/PGP/doc/bugreport new file mode 100644 index 0000000..7bfc992 --- /dev/null +++ b/Protocol/PGP/doc/bugreport @@ -0,0 +1,89 @@ +I've come across something that is either a bug in PGP, or a bug in +the doc/pgformat.doc specification included with PGP. In crypto.c, +idea_file seems to assume that the length of its input is known; it +doesn't handle the case where lenfile==-1. + +I'm (still) working on a PGP-compatible library, and was testing its +output versus the output of 'pgp -c'. My code could unpack a +PGP-generated message, but PGP choked with a 'Bad pass phrase' error +on processing a file generated by my code. A bit of exploration found +the problem; it wasn't due to the passphrase at all. + +The output of 'pgp -c' is a single ciphered packet, which contains a +plaintext or compressed packet. 'pgp -c' always generates a ciphered +packet where the length is explicitly given after the CTB; however, my +code is attempting to use only one pass at all times, and generates an +indefinite-length packet, since it doesn't know the length ahead of +time. This causes the lenfile parameter passed to idea_file to be +negative, which causes an fread() for a negative number of items. The +fread() returns zero, and idea_file reports an error. I expect I'll +run into this again, as my code prefers to create indefinite-length +packets whenever it can. + +So... should idea_file be fixed to handle lenfile==-1? (A *very +lightly tested* patch to do this is appended--treat it with +suspicion.) Or is this a specification bug in pgformat.doc? + + + Andrew Kuchling + andrewk@cst.ca + + +*** ../orig-src/crypto.c Fri Feb 23 09:25:30 1996 +--- crypto.c Fri Feb 23 09:50:51 1996 +*************** +*** 530,534 **** + /* See if the redundancy is present after the random prefix */ + count = fread(textbuf,1,RAND_PREFIX_LENGTH+2,f); +! lenfile -= count; + if (count==(RAND_PREFIX_LENGTH+2)) { + ideaCfbDecrypt(&cfb, textbuf, textbuf, +--- 530,534 ---- + /* See if the redundancy is present after the random prefix */ + count = fread(textbuf,1,RAND_PREFIX_LENGTH+2,f); +! if (lenfile!=4294967295) lenfile -= count; + if (count==(RAND_PREFIX_LENGTH+2)) { + ideaCfbDecrypt(&cfb, textbuf, textbuf, +*************** +*** 548,558 **** + + /* read and write the whole file in CFB mode... */ +! count = (lenfile < DISKBUFSIZE) ? (int)lenfile : DISKBUFSIZE; + while (count && status == 0) { +! if ((count = fread(textbuf,1,count,f)) <= 0) { + status = -3; + break; + } +! lenfile -= count; + if (decryp) + ideaCfbDecrypt(&cfb, textbuf, textbuf, count); +--- 548,559 ---- + + /* read and write the whole file in CFB mode... */ +! count = (lenfile!=4294967295 && lenfile < DISKBUFSIZE) ? (int)lenfile : DISKBUFSIZE; + while (count && status == 0) { +! if ((count = fread(textbuf,1,count,f)) < 0) { + status = -3; + break; + } +! if (count==0) break; +! if (lenfile!=4294967295) lenfile -= count; + if (decryp) + ideaCfbDecrypt(&cfb, textbuf, textbuf, count); +*************** +*** 560,565 **** + ideaCfbEncrypt(&cfb, textbuf, textbuf, count); + if (fwrite(textbuf,1,count,g) != count) +! status = -3; +! count = (lenfile < DISKBUFSIZE) ? (int)lenfile : DISKBUFSIZE; + } + +--- 561,567 ---- + ideaCfbEncrypt(&cfb, textbuf, textbuf, count); + if (fwrite(textbuf,1,count,g) != count) +! status = -3; +! count = (lenfile!=4294967295 && lenfile < DISKBUFSIZE) +! ? (int)lenfile : DISKBUFSIZE; + } + + diff --git a/Protocol/PGP/doc/pgp.texi b/Protocol/PGP/doc/pgp.texi new file mode 100644 index 0000000..39a605c --- /dev/null +++ b/Protocol/PGP/doc/pgp.texi @@ -0,0 +1,298 @@ +\input texinfo.tex @c -*-texinfo-*- +@c %** start of header +@setfilename pct.info +@settitle Python/PGP Interfaces +@syncodeindex fn cp +@syncodeindex vr cp +@c %** end of header + +@titlepage +@title Python/PGP Interfaces (OUT OF DATE) +@subtitle Release 0.0.1 +@subtitle Manual Edition 0.0.1 +@author A.M. Kuchling +@author 762 Williams Road +@author Hemmingford, Quebec, Canada +@author E-mail: @samp{andrewk@@cst.ca} +@end titlepage + +@node Top, Introduction, (dir), (dir) +@ifinfo +Certain sections require some familiarity with PGP. +@menu +* PGP Compatibility:: Maniuplating PGP keys and messages. +@end menu + +@end ifinfo + +@node PGP Compatibility, , , Top +@chapter PGP Compatibility +This module is intended to allow you to manipulate files and keyrings +created by Philip Zimmermann's Pretty Good Privacy program. If you +aren't familar with PGP, get a copy and read Philip Zimmermann's +excellent documentation. Otherwise, this chapter will be incomprehensible to you. + +Several important disclaimers: +@itemize @bullet +@item Currently the module cannot handle encrypted files that use PGP's +data compression. This will require implementing a data compression +module for Python, and will not be completed any time in the near future. + +@item This Python module should be perfectly +compatible with PGP 2.6; if you find any incompatibilities, please inform me +@emph{immediately}, as they are serious bugs. + +@item PGP has been used more extensively than this code has; treat +it with care. Bugs in software are always annoying, but this code will be +handling irreplaceable private keys. If your private key is lost, there's +no way to recover it. So back up your key rings before experimenting with +them, and try to verify that encrypted files you produce can be decrypted by +PGP. This will also protect you against your own mistakes while learning +how to use the module. + +@item Any flaws in the Python PGP module are due to my error, +and should not be considered to reflect on the reputations of Philip +Zimmermann, Guido van Rossum, or the current PGP development team; +none of them had anything to do with it. + +@item The PCT's design makes it easy to change from one algorithm to +another. This means it would be at most a few hours of work (and more +likely a few minutes) to create your own hacked PGP module that uses +triple DES or some other cipher instead of IDEA, or to add new fields +to PGP packets. This freedom is seductive, and also dangerous; it +could lead to a twisty maze of PGP versions, all different. That +would severely damage the usefulness of PGP, and could lead to a +collapse of PGP's usefulness as a common means of secure +communication. Please think very carefully before creating strange +variations of PGP, and don't distribute them. + +@item As a corollary to the previous item, this module may not share +some limitations that PGP possesses; for example, RSA keys can be of +arbitrary length, while PGP is currently limited to 2048 bits. It may +seem fun to generate an 8192-bit key, but the resulting key couldn't +be handled by any program other than this module. Again, think before +you do something strange... +@end itemize + +@section Basic design + +For generality, the basic PGP module does not perform any file +handling. Instead, all operations are performed on strings of binary +data. So if you want to read the keys in your @code{secring.pgp} +file, you must write code to read the file, and then pass a string +containing the contents of the file to the PGP module. This doesn't +constrain keys to being present in file-like storage, and gives you +the freedom to pass keys around in any way you like. + +PGP files are broken up into packets. A packet consists of a header, +and a given length of data. A keyring or message is therefore a +collection of packets, arranged in some defined order. Packets are +PGP's fundamental unit of data, but they're a bit too low-level for +convienient usage. For example, a single key is a collection of +packets: one for the key itself, one for each user ID for the key, one +for each signature, and possibly one containing a revocation +certificate. + +The Python PGP module defines a hierarchy of objects. There are two +basic object classes: @code{Key} and @code{Data}. There +is a lower-level @code{Packet} class that is used throughout the module. +with it. @code{Data} has several +more subclasses: XXX The Data classes have not been designed yet. + +The publicly-accessible attributes and methods for the objects are +documented below. Internal attributes and methods are accessible, +since Python doesn't yet offer data hiding, but don't use them or rely +on their existence. + +The @code{__init__} functions of objects always require the same +parameter: a string of binary data that contains the data. If the +string only contains part of a packet, an exception will be raised. +The caller can then read in more data and try again. +The exceptions that can be raised are: + +@defcv Exception {PGP module} Exception +Raised on other, miscellaneous error conditions. The associated value +is a tuple; the first element is an explanatory string, and the other +elements are associated data. +@end defcv + +@defcv Exception {PGP module} PossibleEnd +This is something of an ``optional'' exception, raised if the string of +data is exactly the right length. If you're reading keys from a file, +this may mean that the string was too short, and you were just lucky in +where it ended. Or, you may know you have all the key data, in which +case this error can be ignored. The associated value is the PGP object +that would have been returned; if you choose to ignore this error, just +use that object. +@end defcv + +@defcv Exception {PGP module} TooShort +Raised when the string of data only contains a portion of a needed +packet of PGP data. +The associated value is an integer, giving the number of missing bytes. +You should obtain that number of bytes, and retry the instantiation. +@end defcv + +@subsection Key objects +@code{Key} objects contain information about a single key, and +the User ID's associated with that key. At the packet level, there +are different packets for the key, user IDs, and signatures. However, +you don't have to worry about that; there are only public and private +keys. All information about signatures and user IDs are stored as +attributes of key objects. +XXX Doesn't support revocation certificates yet! + +@defivar Key ?MPI +@cindex RSA +@cindex MPI +Currently the only public-key algorithm used by PGP is RSA. For RSA +public keys, the binary data read from the packet is stored in +@code{eMPI} and @code{nMPI}, @footnote{MPI is an acronym for +Multi-Precision Integer, the data format which PGP uses to store long +integers.} but you'll probably never need to use them, since they're +automatically converted to long integers and kept in the @code{e}, and +@code{n} attributes. @code{e} is the encryption exponent, and +@code{n} is the modulus. +@end defivar + +@defivar Key CryptAlg +An 8-bit integer identifying the algorithm + for which this public key is appropriate. Currently the only + reasonable value is 1, for the RSA algorithm. +@end defivar + +@defivar Key KeyID +A 64-bit long integer identifying the key. This can be used as a unique +index for storing and retrieving keys, if desired. +@end defivar + +@defivar Key TimeStamp +The time that the key was generated, expressed in the standard Unix +fashion, as seconds from a reference date. +@end defivar + +@defivar Key UserID +A list of tuples containing the User IDs for this key. Each tuple is of +the form @code{(@var{IDpacket}, @var{Signaturelist})}. +@end defivar + +@defivar Key Validity +Eventually, this attribute will be used to indicate a key's lifetime, +enabling you to create keys only valid for a given period of time. +Currently this field is unused, and will always be equal to zero. +@end defivar + +@defivar Key Version +The version of PGP that generated this key. Possible values are 2 or 3. +@end defivar + +Some functions are only applicable to private keys, and there are +additional attributes containing the secret key, possibly in encrypted +form. The @code{Unlock} method must be called to transform the +encrypted data to a form suitable for use, and requires a password if +the key is encrypted. The @code{Lock} method simply deletes the +decrypted key information, and the @code{Relock} method re-encrypts the +key, thus allowing you to change key data or generate new keys. (XXX +@code{Relock} is implemented-but is its existence a good idea? +Carelessness could cost you a secret key, and it might be possible to +attempt a denial-of-service attack by destroying your key.) + + +@defivar Key ?MPI +The raw, possibly encrypted data for the private key is kept in the +@code{dMPI}, @code{pMPI}, @code{qMPI}, and @code{uMPI} attributes. +While you could manipulate the MPI data yourself, it's probably easiest to use +the @code{Unlock} method. +@end defivar + +@defivar Key CipherAlg +8-bit integer giving the secret-key algorithm used to protect the +secret key. A value of 0 means the key is stored in unencrypted form, +while 1 signals a key encrypted with IDEA. +@end defivar + +@defivar Key IV +A string containing an initial block of data, used for starting the +cipher feedback for an encrypted secret key. You'll want to set this +to a string containing random data. +@end defivar + +The additional method functions allow access to the data for the +private key. + +@defmethod Key Lock +Deletes the cleartext secret key data. Currently this simply deletes + the @code{d}, @code{p}, @code{q}, and @code{u} attributes, +since there + doesn't seem to be an obvious way of erasing them securely. +@end defmethod + +@defmethod Key Relock password +Recreates the secret key data by applying @var{password}, and deletes +the cleartext key data. +@end defmethod + +@defmethod Key Unlock [password] +Decodes the secret key by applying @var{password}. If the secret key +data is not encrypted, the value of @var{password} is ignored, but you +still must call the @code{Unlock} method. After calling this method, +the key will have four additional attributes: @code{d}, @code{p}, +@code{q}, and @code{u}. All of the new attributes are long integers +containing the secret key data. +@end defmethod + +An example usage is given in the following example, where the user +accesses a private key protected with the password ``secret''. + +@example +Python 1.1 (Nov 7 1994) +Copyright 1991-1994 Stichting Mathematisch Centrum, Amsterdam +>>> import pgp +>>> f=open('secring.pgp') +>>> binary=f.read() # Read the entire file into a string +>>> f.close() +>>> key=pgp.Key(binary) # Read the first private key +>>> key +<Key instance at 5ce40> +>>> key.n # Modulus +963122082828533347002151137300146220135009078073956904069973 +630338075190337890351958426139975067122956338320608624652745 +4945250019523010908959292342378629L +>>> key.e # Encryption exponent +17L +>>> key.d # Decryption exponent not available +Traceback (innermost last): + File "<stdin>", line 1, in ? +AttributeError: d +>>> key.CipherAlg # Algorithm for key data +1 +>>> key.Unlock('phlezofligle') # Wrong password, so exception raised +Traceback (innermost last): + File "<stdin>", line 1, in ? + File "./pgp.py", line 189, in Unlock + raise Exception, 'Incorrect password' +pgp.Exception: Incorrect password +>>> key.Unlock('secret') # Apply correct password +>>> key.d # The decryption exponent +396579681164690201706768115358883737702650796853982254617047 +965433325078374417342219491501744606387578757984207954329749 +1347256349281128383401567984775749L +>>> ciph=pow(19721015L, key.e, key.n) # RSA encryption +>>> ciph +103227991689207176390261587823791277903759628199625167641034 +860240242223522848082032689408309240059530813895260337829589 +84375L +>>> pow(ciph, key.d, key.n) # RSA decryption +19721015L +>>> key.Lock() # Delete sensitive data +>>> key.d # d no longer exists +Traceback (innermost last): + File "<stdin>", line 1, in ? +AttributeError: d +@end example + +@node Concept Index, ,Extending the Toolkit, Top +@unnumbered Concept Index +@printindex cp +@contents +@bye diff --git a/Protocol/PGP/docerrors.txt b/Protocol/PGP/docerrors.txt new file mode 100644 index 0000000..97ccdf4 --- /dev/null +++ b/Protocol/PGP/docerrors.txt @@ -0,0 +1,5 @@ + +* Signature packets can only have a 16-bit length. The documentation + claims it can be either 8 or 16-bit. + + diff --git a/Protocol/PGP/dump.py b/Protocol/PGP/dump.py new file mode 100644 index 0000000..34b14cb --- /dev/null +++ b/Protocol/PGP/dump.py @@ -0,0 +1,17 @@ + +import pgp, sys + +indent=0 +def loop(s=None, indent=0): + import sys + while (1): + if s==None: p,dummy=pgp.readPacket(sys.stdin) + else: p,s=pgp.readPacket(s) + if p==None: break + print indent*'\t', p.CTBT, p + if p.CTBT==pgp.CTBT_COMPR: + loop(p.decompress(),indent+1) + if p.CTBT==pgp.CTBT_SIG: + print p.__dict__ + +loop() diff --git a/Protocol/PGP/excludefiles b/Protocol/PGP/excludefiles new file mode 100644 index 0000000..c96cbc4 --- /dev/null +++ b/Protocol/PGP/excludefiles @@ -0,0 +1,33 @@ +oldpgp +excludefiles +*.tgz +core +out +err +*.toc +*.aux +*.log +*.cp +*.cps +*.fn +*.fns +*.vr +*.tp +*.dvi +*.kys +*.pg +*.pgs +*.ind +*.pyc +*.pgp +*.ky +*.html +*~ +RCS +Info +Distrib +present +util +*,v +#* +LINUXELF diff --git a/Protocol/PGP/filepgp b/Protocol/PGP/filepgp new file mode 100755 index 0000000..d832d15 --- /dev/null +++ b/Protocol/PGP/filepgp @@ -0,0 +1,61 @@ +#!/usr/local/bin/python +# Hey Emacs, this is -*-Python-*- code! + +import pgp, sys, md5 + +if len(sys.argv)==1: + print 'Usage: filepgp <filename>' + print "If <filename> ends in '.pgp', it will be decrypted." + print "Otherwise, it will be encrypted." + sys.exit(0) + +def raw_input(): return 'test' + +filename=sys.argv[1] +if filename[-4:]=='.pgp': + # Decrypt the file + f=open(filename, 'r') + print 'Enter passphrase:', + passphrase=raw_input() + idea_key=md5.new(passphrase).digest() + p1=pgp.Cipher(f, pgp.UNPACK, DEK=idea_key) + p2=pgp.ReadPacket(p1, pgp.UNPACK) + if p2.Type==pgp.COMPRESSED: + input=pgp.Plaintext(p2, pgp.UNPACK) + elif p2.Type==pgp.PLAINTEXT: + input=p2 + else: + print 'Unexpected packet type', p2.Type + sys.exit(1) + + # Copy the contents to stdout + while (1): + s=input.read(50) + sys.stdout.write(s) + if s=="": break + f.close() + +else: + + def getbytes(N): + """Returns N bytes of random data. + Here it'll just return a string full of the letter 'a'. """ + return 'a'*N + + # Encrypt the file + f=open(filename, 'r') + print 'Enter passphrase:', + passphrase=raw_input() + idea_key=md5.new(passphrase).digest() + + p3=pgp.Plaintext(f, pgp.PACK) # Create a Plaintext filter... +# p2=pgp.Compress(p3, pgp.PACK) # ...which feeds a Compress filter... + # ... which goes into a Cipher filter. + p1=pgp.Cipher(p3, pgp.PACK, DEK=idea_key, randfunc=getbytes) + # Copy the contents to an output file + output=open(filename+'.pgp', 'w') + while (1): + s=p1.read(50) + output.write(s) + if s=="": break + f.close() diff --git a/Protocol/PGP/format.doc b/Protocol/PGP/format.doc new file mode 100644 index 0000000..cc4905a --- /dev/null +++ b/Protocol/PGP/format.doc @@ -0,0 +1,841 @@ +File Formats Used by PGP 2.x +============================ + +***Note: Packets generated with PGP 2.6.3i normally contain a version + byte of 3. However, by using the +legal_kludge=off option, you + can force PGP to use a version byte of 2 instead. This will + make all messages and keys generated with PGP 2.6.3i compatible + with any PGP 2.x version. + +This appendix describes the file formats used externally by Pretty +Good Privacy (PGP), the RSA public key cryptography application. The +intended audience includes software engineers trying to port PGP to +other hardware environments or trying to implement other PGP- +compatible cryptography products, or anyone else who is curious. + +[To be included: a description of ASCII armor. An ASCII armored +file is just like a binary file described here, but with an extra +layer of encoding added, framing lines, and a 24-bit CRC at the end.] + + +Byte Order +---------- + +All integer data used by PGP is externally stored most significant byte +(MSB) first, regardless of the byte order used internally by the host +CPU architecture. This is for portability of messages and keys between +hosts. This covers multiprecision RSA integers, bit count prefix +fields, byte count prefix fields, checksums, key IDs, and timestamps. + +The MSB-first byte order for external packet representation was +chosen only because many other crypto standards use it. + + +Multiprecision Integers +----------------------- + +RSA arithmetic involves a lot of multiprecision integers, often +having hundreds of bits of precision. PGP externally stores a +multiprecision integer (MPI) with a 16-bit prefix that gives the +number of significant bits in the integer that follows. The integer +that follows this bitcount field is stored in the usual byte order, +with the MSB padded with zero bits if the bitcount is not a multiple +of 8. The bitcount always specifies the exact number of significant +bits. For example, the integer value 5 would be stored as these +three bytes: + + 00 03 05 + +An MPI with a value of zero is simply stored with the 16-bit bitcount +prefix field containing a 0, with no value bytes following it. + + + +Key ID +------ + +Some packets use a "key ID" field. The key ID is the least +significant 64 bits of the RSA public modulus that was involved in +creating the packet. For all practical purposes it unique to each +RSA public key. + + +User ID +------- + +Some packets contain a "user ID", which is an ASCII string that +contains the user's name. Unlike a C string, the user ID has a +length byte at the beginning that has a byte count of the rest of the +string. This length byte does not include itself in the count. + + +Timestamp +--------- + +Some packets contain a timestamp, which is a 32-bit unsigned integer +of the number of seconds elapsed since 1970 Jan 1 00:00:00 GMT. This +is the standard format used by Unix timestamps. It spans 136 years. + + + +Cipher Type Byte (CTB) +---------------------- + +Many of these data structures begin with a Cipher Type Byte (CTB), +which specifies the type of data structure that follows it. The CTB +bit fields have the following meaning (bit 0 is the LSB, bit 7 is the +MSB): + +Bit 7: Always 1, which designates this as a CTB +Bit 6: Reserved. +Bits 5-2: CTB type field, specifies type of packet that follows + 0001 - public-key-encrypted packet + 0010 - secret-key-encrypted (signature) packet + 0101 - Secret key certificate + 0110 - Public key certificate + 1000 - Compressed data packet + 1001 - Conventional-Key-Encrypted data + 1011 - Raw literal plaintext data, with filename and mode + 1100 - Keyring trust packet + 1101 - User ID packet, associated with public or secret key + 1110 - Comment packet + Other CTB packet types are unimplemented. +Bits 1-0: Length-of-length field: + 00 - 1 byte packet length field follows CTB + 01 - 2 byte packet length field follows CTB + 10 - 4 byte packet length field follows CTB + 11 - no length field follows CTB, unknown packet length. + The 8-, 16-, or 32-bit packet length field after the CTB + gives the length in bytes of the rest of the packet, not + counting the CTB and the packet length field. + + + +RSA public-key-encrypted packet +------------------------------- + +Offset Length Meaning +0 1 CTB for RSA public-key-encrypted packet +1 2 16-bit (or maybe 8-bit) length of packet +3 1 Version byte, may affect rest of fields that follow. + (=2) for PGP versions <= 2.5 + (=3) for PGP versions >= 2.6 +4 8 64-bit Key ID +12 1 Algorithm byte for RSA (=1 for RSA). + --Algorithm byte affects field definitions that follow. +13 ? RSA-encrypted integer, encrypted conventional key + packet. (MPI with bitcount prefix) + +The conventionally-encrypted ciphertext packet begins right after the +RSA public-key-encrypted packet that contains the conventional key. + + + +Signature packet +---------------- + +Offset Length Meaning +0 1 CTB for secret-key-encrypted (signed) packet +1 2 16-bit (or maybe 8-bit) length of packet +3 1 Version byte, may affect rest of fields that follow. + (=2) for PGP versions <= 2.5 + (=3) for PGP versions >= 2.6 +4 1 Length of following material that is implicitly included + in MD calculation (=5). +5 1 Signature classification field (see below). + Implicitly append this to message for MD calculation. +6 4 32-bit timestamp of when signature was made. + Implicitly append this to message for MD calculation. +10 8 64-bit Key ID +18 1 Algorithm byte for public key scheme (RSA=0x01). + --Algorithm byte affects field definitions that follow. +19 1 Algorithm byte for message digest (MD5=0x01). +20 2 First 2 bytes of the Message Digest inside the + RSA-encrypted integer, to help us figure out if we + used the right RSA key to check the signature. +22 ? RSA-encrypted integer, encrypted message digest + (MPI with bitcount prefix). + +If the plaintext that was signed is included in the same file as the +signature packet, it begins right after the RSA secret-key-signed +packet that contains the message digest. The plaintext has a +"literal" CTB prefix. + +The original idea had a variable length field following the length +of following material byte, before the Key ID. In particular, the +possibility of a 2-byte validity period was defined, although no +previous version of PGP ever generated those bytes. + +Owing to the way the MD5 is computed for the signature, if that field +is variable length, it is possible to generate two different messages +with the same MD5 hash. One would be a file of length N, with a 7-byte +following section consisting of a signature type byte, 4 bytes of +timestamp, and 2 of validity period, while the other would be a file of +length N+2, whose last two bytes would be the siganture type byte and +the first byte of timestamp, and the last three bytes of timestamp and +the validity period would instead be interpreted as a signature type +byte and a timestmap. + +It should be emphasized that the messages are barely different and +special circumstances must arise for this to be possible, so it is +extremely unlilely that this would be exploitable, but it is a +potential weakness. It has been plugged by allowing only the currently +implemented 5-byte option. Validity periods will be added later with +a different format. + +The signature classification field describes what kind of +signature certificate this is. There are various hex values: + 00 - Signature of a message or document, binary image. + 01 - Signature of a message or document, canonical text. + 10 - Key certification, generic. Only version of key + certification supported by PGP 2.5. + Material signed is public key pkt and User ID pkt. + 11 - Key certification, persona. No attempt made at all + to identify the user with a real name. + Material signed is public key pkt and User ID pkt. + 12 - Key certification, casual identification. Some + casual attempt made to identify user with his name. + Material signed is public key pkt and User ID pkt. + 13 - Key certification, positive ID. Heavy-duty + identification efforts, photo ID, direct contact + with personal friend, etc. + Material signed is public key pkt and User ID pkt. + 20 - Key compromise. User signs his own compromise + certificate. Independent of user ID associations. + Material signed is public key pkt ONLY. + 30 - Key/userid revocation. User can sign his own + revocation to dissolve an association between a key + and a user ID, or certifier may revoke his previous + certification of this key/userid pair. + Material signed is public key pkt and User ID pkt. + 40 - Timestamping a signature certificate made by someone + else. Can be used to apply trusted timestamp, and + log it in notary's log. Signature of a signature. + (Planned, not implemented.) + +When a signature is made to certify a key/UserID pair, it is computed +across two packets-- the public key packet, and the separate User ID +packet. See below. + +The packet headers (CTB and length fields) for the public key packet +and the user ID packet are both omitted from the signature +calculation for a key certification. + +A key compromise certificate may be issued by someone to revoke his +own key when his secret key is known to be compromised. If that +happens, a user would sign his own key compromise certificate with +the very key that is being revoked. A key revoked by its own +signature means that this key should never be used or trusted again, +in any form, associated with any user ID. A key compromise +certificate issued by the keyholder shall take precedence over any +other key certifications made by anyone else for that key. A key +compromise signed by someone other than the key holder is invalid. + +Note that a key compromise certificate just includes the key packet +in its signature calculation, because it kills the whole key without +regard to any userid associations. It isn't tied to any particular +userid association. It should be inserted after the key packet, +before the first userid packet. + +When a key compromise certificate is submitted to PGP, PGP will place +it on the public keyring. A key compromise certificate is always +accompanied in its travels by the public key and userIDs it affects. +If the affected key is NOT already on the keyring, the compromise +certificate (and its key and user ID) is merely added to the keyring +anywhere. If the affected key IS already on the keyring, the +compromise certificate is inserted after the affected key packet. +This assumes that the actual key packet is identical to the one +already on the key ring, so no duplicate key packet is needed. +If a key has been revoked, PGP will not allow its use to encipher any +messages, and if an incoming signature uses it, PGP will display a +stern warning that this key has been revoked. + +NOTE: Key/userid revocation certificates ARE NOT SUPPORTED in +this version of PGP. But if we ever get around to supporting them, +here are some ideas on how they should work... + +A key/userid revocation certificate may be issued by someone to +dissolve the association between his own key and a user ID. He would +sign it with the very key that is being revoked. A key/userid +revocation certificate issued by the keyholder shall take precedence +over any other key certifications made by anyone else for that +key/userid pair. Also, a third party certifier may revoke his own +previous certification of this key/userid pair by issuing a +key/userid revocation certificate. Such a revocation should not +affect the certifications by other third parties for this same +key/userid pair. + +When a key/userid revocation certificate is submitted to PGP, PGP +will place it on the public keyring. A key/userid revocation +certificate is always accompanied in its travels by the public key it +affects (the key packet and user ID packet precedes the revocation +certificate). If the affected key is NOT already on the keyring, the +revocation certificate (and its key and user ID) is merely added to +the keyring anywhere. If the affected key IS already on the keyring, +the revocation certificate is integrated in with the key's other +certificates as though it were just another key certification. This +assumes that the actual key packet is identical to the one already on +the key ring, so no duplicate key packet is needed. + + + +Message digest "packet" +----------------------- + +The Message digest has no CTB packet framing. It is stored +packetless and naked, with padding, encrypted inside the MPI in the +Signature packet. + +PGP versions 2.3 and later use a new format for encoding the message +digest into the MPI in the signature packet, a format which is +compatible with RFC1425 (formerly RFC1115). This format is accepted +but not written by version 2.2. The older format used by versions 2.2 +is acepted by versions up to 2.4, but the RSAREF code in 2.5 is +not capable of parsing it. + +PGP versions 2.2 and earlier encode the MD into the MPI as follows: + + MSB . . . LSB + + 0 1 MD(16 bytes) 0 FF(n bytes) 1 + +Enough bytes of FF padding are added to make the length of this +whole string equal to the number of bytes in the modulus. + +PGP versions 2.3 and later encode the MD into the MPI as follows: + + MSB . . . LSB + + 0 1 FF(n bytes) 0 ASN(18 bytes) MD(16 bytes) + +See RFC1423 for an explanation of the meaning of the ASN string. +It is the following 18 byte long hex value: + + 3020300c06082a864886f70d020505000410 + +Enough bytes of FF padding are added to make the length of this +whole string equal to the number of bytes in the modulus. + +All this mainly affects the rsa_private_encrypt() and rsa_public_decrypt() +functions in rsaglue.c. + +There is no checksum included. The padding serves to verify that the +correct RSA key was used. + + +Conventional Data Encryption Key (DEK) "packet" +----------------------------------------------- + +The DEK has no CTB packet framing. The DEK is stored packetless and +naked, with padding, encrypted inside the MPI in the RSA +public-key-encrypted packet. + +PGP versions 2.3 and later use a new format for encoding the message +digest into the MPI in the signature packet. (This format is not +presently based on any RFCs due to the use of the IDEA encryption +system.) This format is accepted but not written by version 2.2. The +older format used by versions 2.2 and earlier is also accepted by +versions up to 2.4, but the RSAREF code in 2.5 is unable to cope +with it. + +PGP versions 2.2 and earlier encode the DEK into the MPI as follows: + + MSB . . . LSB + + 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2 + +CSUM refers to a 16-bit checksum appended to the high end of the DEK. +RND is a string of NONZERO pseudorandom bytes, enough to make the length +of this whole string equal to the number of bytes in the modulus. + +PGP versions 2.3 and later encode the DEK into the MPI as follows: + + MSB . . . LSB + + 0 2 RND(n bytes) 0 1 DEK(16 bytes) CSUM(2 bytes) + +CSUM refers to a 16-bit checksum appended to the high end of the DEK. +RND is a string of NONZERO pseudorandom bytes, enough to make the length +of this whole string equal to the number of bytes in the modulus. + +For both versions, the 16-bit checksum is computed on the rest of the +bytes in the DEK key material, and does not include any other material +in the calculation. In the above MSB-first representation, the +checksum is also stored MSB-first. The checksum is there to help us +determine if we used the right RSA secret key for decryption. + + +All this mainly affects the rsa_public_encrypt() and rsa_private_decrypt() +functions in rsaglue.c. + + + +Conventional Key Encrypted data packet +-------------------------------------- + +Offset Length Meaning +0 1 CTB for Conventional-Key-Encrypted data packet +1 4 32-bit (or maybe 16-bit) length of packet +5 ? conventionally-encrypted data. + plaintext has 64 bits of random data prepended, + plus 16 bits prepended for "key check" purposes + +The decrypted ciphertext may contain a compressed data packet or a +literal plaintext packet. + +After decrypting the conventionally-encrypted data, a special 8-byte +random prefix and 2 "key check" bytes are revealed. The random prefix +and key check prefix are inserted before encryption and discarded after +decryption. This prefix group is visible after decrypting the +ciphertext in the packet. + +The random prefix serves to start off the cipher feedback chaining +process with 64 bits of random material. It may be discarded after +decryption. The first 8 bytes is the random prefix material, followed +by the 2-byte "key-check" prefix. + +The key-check prefix is composed of two identical copies of the last +2 random bytes in the random prefix, in the same order. During +decryption, the 9th and 10th bytes of decrypted plaintext are checked +to see if they match the 7th and 8th bytes, respectively. If these +key-check bytes meet this criterion, then the conventional key is +assumed to be correct. + +One unusual point about the way encryption is done. Using the IDEA +cipher in CFB mode, the first 10 bytes are decrypted normally, +but bytes 10 to 17, the first 8 bytes of the data proper, are +encrypted using bytes 2 to 9 (the last 8 bytes of the key check +prefix) as the IV. This is essentially using CFB-16 for one +part of the encryption, while CFB-64 is used elsewhere. + + +Compressed data packet +---------------------- + +Offset Length Meaning +0 1 CTB for Compressed data packet +1 1 Compression algorithm selector byte (1=ZIP) +2 ? compressed data + +The compressed data begins right after the algorithm selector byte. +The compressed data may decompress into a raw literal plaintext data +packet with its own CTB. Currently, compressed data packets +are always the last ones in their enclosing object, and the decompressor +knows when to stop, so the length field is omitted. The low two bits +of the CTB are set to 11. This is the only case in PGP where this +is currently done. + + +Literal data packet, with filename and mode +------------------------------------------- + +Offset Length Meaning +0 1 CTB for raw literal data packet +1 4 32-bit (or maybe 16-bit) length of packet +5 1 mode byte, 'b'= binary or 't'= canonical text +6 ? filename, with leading string length byte +? 4 Timestamp of last-modified date, or 0, or right now +? ? raw literal plaintext data + +The timestamp may be have to be derived in a system dependent manner. +ANSI C functions should be used to get it if available, otherwise +store the current time in it. Or maybe store 0 if it's somehow not +applicable. + +Whne calculating a signature on a literal packet, the signature +calculation only includes the raw literal plaintext data that begins +AFTER the header fields in the literal packet-- after the CTB, the +length, the mode byte, the filename, and the timestamp. The reason +for this is to guarantee that detached signatures are exactly the +same as attached signatures prefixed to the message. Detached +signatures are calculated on a separate file that has no packet +encapsulation. + + + +Comment packet +-------------- + +A comment packet is generally just skipped over by PGP, although it +may be displayed to the user when processed. It can be put in a +keyring, or anywhere else. + +Offset Length Meaning +0 1 CTB for Comment packet +1 1 8-bit length of packet +2 ? ASCII comment, size is as in preceding length byte + +Comment packets are currently not generated by PGP. + + + +Secret key certificate +---------------------- + +Offset Length Meaning +0 1 CTB for secret key certificate +1 2 16-bit (or maybe 8-bit) length of packet +3 1 Version byte, may affect rest of fields that follow. + (=2) for PGP versions <= 2.5 + (=3) for PGP versions >= 2.6 +4 4 Timestamp +8 2 Validity period, in number of DAYS (0 means forever) +10 1 Algorithm byte for RSA (=1 for RSA). + --Algorithm byte affects field definitions that follow. +? ? MPI of RSA public modulus n +? ? MPI of RSA public encryption exponent e + +? 1 Algorithm byte for cipher that protects following + secret components (0=unencrypted, 1=IDEA cipher) +? 8 Cipher Feedback IV for cipher that protects secret + components (not present if unencrypted) +? ? MPI of RSA secret decryption exponent d +? ? MPI of RSA secret factor p +? ? MPI of RSA secret factor q +? ? MPI of RSA secret multiplicative inverse u + (All MPI's have bitcount prefixes) +? 2 16-bit checksum of all preceding secret component bytes + +All secret fields in the secret key certificate may be password- +encrypted, including the checksum. The checksum is calculated from +all of the bytes of the unenciphered secret components. The public +fields are not encrypted. The encrypted fields are done in CFB mode, +and the checksum is used to tell if the password was good. The CFB +IV field is just encrypted random data, assuming the "true" IV was +zero. + +NOTE: The secret key packet does not contain a User ID field. The +User ID is enclosed in a separate packet that always follows the secret +key packet on a keyring or in any other context. + + +Public key certificate +---------------------- + +Offset Length Meaning +0 1 CTB for public key certificate +1 2 16-bit (or maybe 8-bit) length of packet +3 1 Version byte, may affect rest of fields that follow. + (=2) for PGP versions <= 2.5 + (=3) for PGP versions >= 2.6 +4 4 Timestamp of key creation +8 2 Validity period, in number of DAYS (0 means forever) +10 1 Algorithm byte for RSA (=1 for RSA). + --Algorithm byte affects field definitions that follow. +? ? MPI of RSA public modulus n +? ? MPI of RSA public encryption exponent e + (All MPI's have bitcount prefixes) + +NOTE: The public key packet does not contain a User ID field. The +User ID is enclosed in a separate packet that always follows +somewhere after the public key packet on a keyring or in any other +context. + +The validity period is currently always set to 0. + + + +User ID packet +-------------- + +Offset Length Meaning +0 1 CTB for User ID packet +1 1 8-bit length of packet +2 ? User ID string, size is as in preceding length byte + +The User ID packet follows a public key on a public key ring. It +also follows a secret key on a secret key ring. + +When a key is certified by a signature, the signature covers both the +public key packet and the User ID packet. The signature certificate +thereby logically "binds" together the user ID with the key. The +user ID packet is always associated with the most recently occurring +public key on the key ring, regardless of whether there are other +packet types appearing between the public key packet and the +associated user ID packet. + +There may be more than one User ID packet after a public key packet. +They all would be associated with the preceding public key packet. + + +Keyring trust packet +-------------------- + +The three different forms of this packet each come after: a public key +packet, a user ID packet, or a signature packet on the public key +ring. They exist only on a public key ring, and are never extracted +with a key. Don't copy this separate trust byte packet from keyring, +and do add it in back in when adding to keyring. + +The meaning of the keyring trust packet is context sensitive. The +trust byte has three different definitions depending on whether it +follows a key packet on the ring, or follows a user ID packet on the +ring, or follows a signature on the ring. + +Offset Length Meaning +0 1 CTB for Keyring trust packet +1 1 8-bit length of packet (always 1 for now) +2 1 Trust flag byte, with context-sensitive bit + definitions given below. + + +For trust bytes that apply to the preceding key packet, the following +bit definitions apply: + + Bits 0-2 - OWNERTRUST bits - Trust bits for this key owner. Values are: + 000 - undefined, or uninitialized trust. + 001 - unknown, we don't know the owner of this key. + 010 - We usually do not trust this key owner to sign other keys. + 011 - reserved + 100 - reserved + 101 - We usually do trust this key owner to sign other keys. + 110 - We always trust this key owner to sign other keys. + 111 - This key is also present in the secret keyring. + Bits 3-4 - Reserved. + Bit 5 - DISABLED bit - Means that this key is disabled, and + should not be used. + Bit 6 - Reserved + Bit 7 - BUCKSTOP bit - Means this key also appears in secret key ring. + Signifies the ultimately-trusted "keyring owner". + "The buck stops here". This bit computed from looking + at secret key ring. If this bit is set, then all the + KEYLEGIT fields are set to maximum for all the user IDs for + this key, and OWNERTRUST is also set to ultimate trust. + +For trust bytes that apply to the preceding user ID packet, the +following bit definitions apply: + + Bit 0-1 - KEYLEGIT bits - Validity bits for this key. + Set if we believe the preceding key is legitimately owned by + who it appears to belong to, specified by the preceding user + ID. Computed from various signature trust packets that + follow. Also, always fully set if BUCKSTOP is set. + To define the KEYLEGIT byte does not require that + OWNERTRUST be nonzero, but OWNERTRUST nonzero does require + that KEYLEGIT be fully set to maximum trust. + 00 - unknown, undefined, or uninitialized trust. + 01 - We do not trust this key's ownership. + 10 - We have marginal confidence of this key's ownership. + Totally useless for certifying other keys, but may be useful + for checking message signatures with an advisory warning + to the user. + 11 - We completely trust this key's ownership. + This requires either: + - 1 ultimately trusted signature (a signature from + yourself, SIGTRUST=111) + - COMPLETES_NEEDED completely trusted signatures + (SIGTRUST=110) + - MARGINALS_NEEDED marginally trusted signatures + (SIGTRUST=101) + COMPLETES_NEEDED and MARGINALS_NEEDED are configurable + constants. + Bit 7 - WARNONLY bit - If the user wants to use a not fully validated + key for encryption, he is asked if he really wants to use this + key. If the user answers 'yes', the WARNONLY bit gets set, + and the next time he uses this key, only a warning will be + printed. This bit gets cleared during the maintenance pass. + +For a trust byte that applies to the preceding signature, the +following bit definitions apply: + + Bits 0-2 - SIGTRUST bits - Trust bits for this signature. Value is + copied directly from OWNERTRUST bits of signer: + 000 - undefined, or uninitialized trust. + 001 - unknown + 010 - We do not trust this signature. + 011 - reserved + 100 - reserved + 101 - We reasonably trust this signature. + 110 - We completely trust this signature. + 111 - ultimately trusted signature (from the owner of the ring) + Bits 3-6 - Reserved. + Bit 6 - CHECKED bit - This means that the key checking pass (pgp -kc, + also invoked automatically whenever keys are added to the + keyring) has tested this signature and found it good. If + this bit is not set, the maintenance pass considers this + signature untrustworthy. + Bit 7 - CONTIG bit - Means this signature leads up a contiguous trusted + certification path all the way back to the ultimately- + trusted keyring owner, where the buck stops. This bit is derived + from other trust packets. It is currently not used for anything + in PGP. + +The OWNERTRUST bits are set by the user. PGP does not modify them. +PGP computes the BUCKSTOP bit by checking to see if the key is on the +secret key ring. If it is, it was created by this user, and thus +controlled by him. + +All other trust is derived from the BUCKSTOP keys in a special +maintenance pass over the keyring. Any good signature made by a given +key has its SIGTRUST equal to the key's OWNERTRUST. Based on +COMPLETES_NEEDED and MARGINALS_NEEDED, if enough trusted signatures are +on a key/userID pair, the key/userid association is considered +legitimate. + +To be precise, an ultimately trusted key has weight 1, a completely +trusted key has weight 1/COMPLETES_NEEDED (or 0 if COMPLETES_NEEDED +is 0), and a marginally trsuted key has weight 1/MARGINALS_NEEDED. +Other trust values have weight 0. If the total weight of the signatures +on a key/userid pair is 1 or more, the userid is considered legitimate. + +When a key has a legitimate userid, the user is asked to set the +OWNERTRUST for the corresponding key. Ths idea is that the userid +identifies someone the user knows, at least by reputation, so once it +has been established who holds the secret key, that person's +trustworthiness as an introducer can be established and assigned to the +key. + +Once that is done, the key's signatures then have weight establishing +other key/userid associations. + +There is a limit to the depth to which this can go. Keys on the secret +keyring are at depth 0. Keys signed by those keys are at depth 1. +Keys which are fully certified using only signatures from keys at depth +1 or less are at depth 2. Keys which are fully certified using only +signatures from keys at depth 2 or less are at depth 3, and so on. + +If you know all of your trusted introducers personally, and have signed +their keys, then you will never have a key at a depth of greater than 2. +The maximum depth is limited my MAX_CERT_DPETH. It never gets very large +in a well-connected "web of trust". + +This redundant and decentralized method of determining public key +legitimacy is one of the principal strengths of PGP's key management +architecture, as compared with PEM, when used in social structures +that are not miltiary-style rigid hierarchies. + +The trust of a key owner (OWNERTRUST) does not just reflect our +estimation of their personal integrity, it also reflects how competent +we think they are at understanding key management and using good +judgement in signing keys. The OWNERTRUST bits are not computed from +anything -- it requires asking the user for his opinion. + +To define the OWNERTRUST bits for a key owner, ask: + Would you always trust "Oliver North" + to certify other public keys? + (1=Yes, 2=No, 3=Usually, 4=I don't know) ? _ + +When a key is added to the key ring the trust bytes are initialized +to zero (undefined). + + +[--manual setting of SIGTRUST/OWNERTRUST not implemented] +Normally, we derive the value of the SIGTRUST field by copying it +directly from the signer key's OWNERTRUST field. Under special +circumstances, if the user explicitly requests it with a special PGP +command, we may let the user override the copied value for SIGTRUST +by displaying an advisory to him and asking him for ratification, +like so: + This key is signed by "Oliver North", + whom you usually trust to sign keys. + Do you trust "Oliver North" + to certify the key for "Daniel Ellsberg"? + (1=Yes, 2=No, 3=Somewhat, 4=I don't know) ? _ <default is yes> + +Or: + This key is signed by "Oliver North", + whom you usually do not trust to sign keys. + Do you trust "Oliver North" + to certify the key for "Daniel Ellsberg"? + (1=Yes, 2=No, 3=Somewhat, 4=I don't know) ? _ <default is no> + +An "I don't know" response to this question would have the same +effect as a response of "no". + +If we had no information about the trustworthiness of the signer (the +OWNERTRUST field was uninitialized), we would leave the advisory note +off. + + +Certifying a public key is a serious matter, essentially promising to +the world that you vouch for this key's ownership. But sometimes I +just want to make a "working assumption" of trust for someone's +public key, for my own purposes on my own keyring, without taking the +serious step of actually certifying it for the rest of the world. In +that case, we can use a special PGP keyring management command to +manually set the KEYLEGIT field, without relying on it being computed +during a maintenance pass. Later, if a maintenance pass discovers a +KEYLEGIT bit set that would not have been otherwise computed as set +by the maintenance pass logic, it alerts me and asks me to confirm +that I really want it set. + +[--end of not implemented section] + + +During routine use of the public keyring, we don't actually check the +associated signatures certifying a public key. Rather, we always +rely on trust bytes to tell us whether to trust the key in question. +We depend on a separate checking pass (pgp -kc) to actually check the key +signature certificates against the associated keys, and to set the +trust bytes accordingly. This pass checks signatures, and if a +signature fails to verify, obnoxiously alerts the user and drops it from +the key ring. Then it tuns a maintenance pass to calculate the +ring-wide effects of this. + +A failed signature should be exceedingly rare, and it may not even +result in a KEYLEGIT field being downgraded. Having several signatures +certifying each key should prevent damage from spreading too far from a +failed certificate. But if dominoes do keep falling from this, it may +indicate the discovery of an important elaborate attack. + +The maintenance pass is run every time the keyring changes, and +operates in a top-of-pyramid-down manner as follows. + +If at any time during any of these steps the KEYLEGIT field goes from +not fully set to fully set, and the OWNERTRUST bits are still undefined, +the user is asked a question to define the OWNERTRUST bits. First, for +all keys with BUCKSTOP set, check if they are really present in the +secret keyring, if not, the BUCKSTOP bit is cleared. SIGTRUST and +KEYLEGIT is initialized to zero for non-buckstop keys. + +The real maintenance pass is done in a recursive scan: Start with +BUCKSTOP keys, find all userid/key pairs signed by a key and update +the trust value of these signatures by copying the OWNERTRUST of the +signer to the SIGTRUST of the signature. If this makes a key fully +validated, start looking for signatures made by this key, and update +the trust value for them. Repeat until everything has settled down. + + + + +Public Key Ring Overall Structure +================================= + +A public key ring is comprised of a series of public key packets, +keyring trust packets, user ID packets, and signature certificates. + +Here is an example of an ordered collection of packets on a ring: + +-------------------------------------------------------------------- + Public key packet + Keyring trust packet for preceding key + User ID packet for preceding key + Keyring trust packet for preceding user ID/key association + Signature certificate to bind preceding User ID and key pkt + Keyring trust packet for preceding signature certificate + Signature certificate to bind preceding User ID and key pkt + Keyring trust packet for preceding signature certificate + Signature certificate to bind preceding User ID and key pkt + Keyring trust packet for preceding signature certificate + + Public key packet + Keyring trust packet for preceding key + User ID packet for preceding key + Keyring trust packet for preceding user ID/key association + Signature certificate to bind preceding User ID and key pkt + Keyring trust packet for preceding signature certificate + User ID packet for preceding key + Keyring trust packet for preceding user ID/key association + Signature certificate to bind preceding User ID and key pkt + Keyring trust packet for preceding signature certificate + Signature certificate to bind preceding User ID and key pkt + Keyring trust packet for preceding signature certificate + + Public key packet + Keyring trust packet for preceding key + Compromise certificate for preceding key + User ID packet for preceding key + Keyring trust packet for preceding user ID/key association + Signature certificate to bind preceding User ID and key pkt + Keyring trust packet for preceding signature certificate +-------------------------------------------------------------------- diff --git a/Protocol/PGP/keyring.shelve b/Protocol/PGP/keyring.shelve Binary files differnew file mode 100644 index 0000000..718839c --- /dev/null +++ b/Protocol/PGP/keyring.shelve diff --git a/Protocol/PGP/parsepgp b/Protocol/PGP/parsepgp new file mode 100755 index 0000000..2c6822a --- /dev/null +++ b/Protocol/PGP/parsepgp @@ -0,0 +1,39 @@ +#!/usr/local/bin/python +# -*- Python -*- +import pgp, sys + +PacketType={1:'PKEncrypted', 2:'Signature', 5:'PrivKey', 6:'PubKey', + 8:'Compressed', 9:'Ciphered', 11:'Plaintext', 12:'Trust', + 13:'UserID', 14:'Comment'} + +def ParseString(s, packet={}): + try: + while (s!=""): + p=pgp.Packet(s) + s=s[p.PacketLength:] + packet[p.Type]=p + if PacketType.has_key(p.Type): typename=PacketType[p.Type] + else: typename='Unknown type' + print p.Type, typename + except pgp.PossibleEnd, message: + pass + +import md5 +DEK=md5.new('test').digest() + +def DumpPacket(p, indent=0): + if indent>20: return + while (1): + packet=pgp.ReadPacket(p, DEK=DEK) + if packet==None: break + if PacketType.has_key(packet.Type): typename=PacketType[packet.Type] + else: typename='' + sys.stderr.write(' '*indent+str(packet.Type)+' '+typename+'\n') + if packet.Type in [pgp.CIPHERED, pgp.COMPRESSED]: + DumpPacket(packet, indent+4) + +DumpPacket(sys.stdin) + + + + diff --git a/Protocol/PGP/pgp-api.txt b/Protocol/PGP/pgp-api.txt new file mode 100644 index 0000000..99fe9e4 --- /dev/null +++ b/Protocol/PGP/pgp-api.txt @@ -0,0 +1,74 @@ +Over a year ago, I started work on a PGP library for the Python +programming language, but it never really got finished. All of the +pieces are present--cryptographic modules for MD5, IDEA and RSA, a +zlib module for data compression, and Python code to read the packets +that make up PGP keyrings and messages--but the pieces were never +assembled into a coherent high-level interface. Recently a few people +have expressed interest in the project, so I've revived it. + +So... here's a proposal for an interface: + +ASCII armoring: +=============== + + ArmorFile( <file1 object> [ , output=<file2 object> ]) + +Read from file1 and ASCII-armor its contents. If file2 is provided, +the output will be written to file2; otherwise it will return a string +containing the data instead.) + + UnarmorFile( <file1 object> [ , output=<file2 object> ]) +Read from file1 and remove ASCII armor from its contents. If file2 is +provided, the output will be written to file2; otherwise it will be +written to sys.stdout. An exception will be raised if the CRC check +is failed or the file is incomplete. + +(XXX Do we need versions of the above that operate on strings? One + could simply do ArmorFile( StringIO.StringIO(s) ), at the cost of + some speed.) + +Keyring operations: +=================== + +class PGPKey(RSA.key): + def __init__(self, file=None, string=""): + Create a PGPKey object; the key data will either be + taken from the given string or read from the given file + object. + + Only zero or one of file, string can be given--not both. + +XXX Annoyingly, if the data is being read from a file, +we have to read PGP packets until we either hit EOF or another key. +If it's another key, then we've read one packet too far, and have to +seek back to reset the file pointer. Maybe instead we should have a + + +PGPKey objects have the following methods: + + unlock(password) +Decrypt the RSA private key (if present), using the provided password. + + lock() +Discard the decoded RSA key data, if present. + + relock(password) +Re-encrypt the RSA private key data using the provided password. + + encrypt(string) + RSA-encrypt the given string with the key; if string is too +big, raise an exception. + + decrypt(packet) + RSA-decrypt the given packet with the key. + +Messages: +========= + +EncryptMessage(recipients= <list of keys>, # Who the message will be encrypted for + signer=<unlocked key>, # Key used to sign the message + compress=<Boolean> # Compression on/off + ) + + + diff --git a/Protocol/PGP/pgp.py b/Protocol/PGP/pgp.py new file mode 100644 index 0000000..e5a6fae --- /dev/null +++ b/Protocol/PGP/pgp.py @@ -0,0 +1,1061 @@ +# +# PGP PACKET CLASSES and KEYRING MANAGER +# +# + +from pgpconst import * +import md5 + +# Find a big number library +try: + import gmp ; bignum=gmp.mpz +except ImportError: + try: + import mpz ; bignum=mpz.mpz + except ImportError: bignum=long + +Error = 'PGPError' + +# +# +# UTILITY FUNCTIONS +# +# + +def get_hash_module(v, module_dict={}): + "Return the module for a given hash algorithm" + if module_dict.has_key(v): return module_dict[v] + if v==HASH_MD5: import md5 ; mod=md5 + else: raise Error, 'Unknown hash algorithm '+str(v) + module_dict[v]=mod ; return mod + +def get_cipher_module(v, module_dict={}): + "Return the module for a given private-key encryption algorithm" + if module_dict.has_key(v): return module_dict[v] + if v==CIPHER_IDEA: import idea ; mod=idea + else: raise Error, 'Unknown cipher algorithm '+str(v) + module_dict[v]=mod ; return mod + +def get_random_bytes(N): + "Return N bytes of random data" + if N<0: + raise ValueError, 'Attempt to retrieve infinite amount of random data' + f=open('/dev/urandom', 'r') + data=f.read(N) + f.close() + return data[0:N] + +def Str2Int(astr): + "Convert a string into a long integer" + curr = bignum(0) + for char in astr: curr = (curr<<8) + ord(char) + return curr + +def Int2Str(i, bits=0): + "Convert a long integer into a big-endian string, padding if desired." + if (i==0): res='\0' + elif i<0: raise Error, 'Cannot convert negative INT to string.' + else: + res = '' + while i > 0: + res = chr(int(i&255)) + res + i = i>>8 + if bits!=0: + bytes=bits/8 + if len(res)<bytes: res=('\0'*(bytes-len(res)))+res + return res + +def MPILen(str): + "Extract the length of an MPI from the string encoding" + length=ord(str[0])*256+ord(str[1]) + length, remainder = divmod(length, 8) + if remainder!=0: length=length+1 + return length + +def MPI2Int(str): + "Turn a string-encoded MPI into a long integer" + length=MPILen(str) + return (Str2Int(str[2:2+length]), (2+length)) + +def Int2MPI(i): + "Turn a long integer into an MPI string" + s=Int2Str(i) +# while (ord(s[0])==0): s=s[1:] # XXX why is this required? + if (s==''): return '\000\000' + first=ord(s[0]) + bits= 1+(len(s)-1) *8 + mask=1 + while (mask<first and mask <256): + first = first & (255-mask) + mask=mask << 1 + bits=bits+1 + s=chr(bits/256)+chr(bits & 255)+s + return s + +def Checksum(data, cksum=0): + "Compute a simple checksum, which is just the sum of the bytes." + for i in range(0,len(data)): + cksum= ( cksum + ord(data[i]) ) & 65535 + return cksum + +def getLen(CTB, pgpstring): + "Return a length-of-length, and the length, from the CTB and length string" + LEN = (CTB&3) + if not PACKET_LENGTH.has_key(LEN): + raise Error, "Unknown length of length" + lenlen=PACKET_LENGTH[LEN] + if lenlen!=0: + length=int(Str2Int(pgpstring[:lenlen])) + else: length=None + return lenlen, length + +# +# +# CLASSES +# +# + +# +# Base class for PGP packets +# + +class Packet: + def __init__(self, input=None): + self.CTBT = 0 + + def binary(self, CTBT=0): + "Convert object to its string representation, preceded by the packet header." + data=self.write_bin() + header=self.mkHeader(len(data), bits=0, CTBT=CTBT) + return header+data + + def mkHeader(self, length, bits=0, CTBT=0): + "Return a packet header from the type and the length" + + # To allow PrivateKey objects to be written either as + # public or private objects, there has to be a way to + # override the object's CTBT. + if CTBT==0: packet_type=self.CTBT + + # Check for a constraint on the length-of-length field + if bits==0 and PACKET_SIZES.has_key(packet_type): + bits=PACKET_SIZES[packet_type] + + if packet_type<1 or packet_type>14: + raise Error, 'Invalid packettype '+str(packet_type) + CTB = 128 | (packet_type<<2) + if length==None: return chr(CTB|3) + elif length<256 and (bits==0 or bits==8): + bits=8 + elif length<65536 and (bits==0 or bits==16): + bits=16 ; CTB = CTB|1 + elif length<4294967296 and (bits==0 or bits==32): + bits=32 ; CTB = CTB|2 + else: raise Error, 'Packet too long.' + return chr(CTB)+Int2Str(length, bits) + + def parse_bin(self, binstr): + """Parse a string containing a PGP packet, into the 'self' object.""" + raise Error, 'Called unimplemented method parse_bin.' + + def write_bin(self): + "Convert object to its string representation, without the packet header." + raise Error, 'Called unimplemented method write_bin.' + + +class PKEncrypted(Packet): + def __init__(self, version=2): + Packet.__init__(self) + self.CTBT=CTBT_PKEP + self.Version = version + self.KeyID=bignum(0) + self.PubkeyAlg=PK_NONE + self.Data=bignum(0) + + def parse_bin(self, binstring): + pos=0 + self.Version=ord(binstring[pos]) + if self.Version<>2: raise Error, 'Unsupported packet version '+str(version) + pos=pos+1 + self.KeyID=Str2Int(binstring[pos:pos+8]) + pos=pos+8 + self.PubkeyAlg=ord(binstring[pos]) + pos=pos+1 + if self.PubkeyAlg==PK_RSA: + self.Data, mpilen = MPI2Int(binstring[pos:]) + pos=pos+mpilen + else: + raise Error, 'Unknown PK Algorithm '+str(self.PubkeyAlg) + if pos<>len(binstring): + print 'WARNING: length mismatch in PK-encrypted packet.' + def write_bin(self): + data = (chr(self.Version) + + Int2Str( self.KeyID, 64) +chr(self.PubkeyAlg) ) + if self.PubkeyAlg==PK_RSA: + d=self.Data + if type(d)==type(''): d=Str2Int(d) + data=data+Int2MPI(d) + else: + raise Error, 'Unknown PK Algorithm '+str(self.PubkeyAlg) + return data + +class Compressed(Packet): + def __init__(self): + Packet.__init__(self) + self.CompressAlg=COMPRESS_NONE + self.CTBT=CTBT_COMPR + self.Data="" + def compress(self, data): + if self.CompressAlg==COMPRESS_ZLIB: + import zlib + compr_obj=zlib.compressobj(5, 8, -15) + self.Data=compr_obj.compress(data) + self.Data=self.Data+compr_obj.flush() + else: + raise Error, 'Unknown compression algorithm' + + def decompress(self): + if self.CompressAlg==COMPRESS_ZLIB: + import zlib + decompr_obj=zlib.decompressobj(-15) + data=decompr_obj.decompress(self.Data) + data=data+decompr_obj.flush() + return data + else: + raise Error, 'Unknown compression algorithm' + + def parse_bin(self, binstring): + pos=0 + self.CompressAlg=ord(binstring[pos]) + pos=pos+1 + if self.CompressAlg==COMPRESS_ZLIB: + self.Data=binstring[pos:] + else: + raise Error, 'Unknown compression algorithm' + def write_bin(self): + return chr(self.CompressAlg)+self.Data + +class ConvEncrypted(Packet): + def __init__(self): + Packet.__init__(self) + self.Data="" ; self.CTBT = CTBT_CKEP + def encrypt(self, key, data, cipheralg=CIPHER_IDEA): + if cipheralg==CIPHER_IDEA: + import idea + IV=get_random_bytes(idea.blocksize) + cipherobj=idea.new(key, idea.PGP, '\000'*idea.blocksize) + output=cipherobj.encrypt(IV+IV[-2:]) + cipherobj.sync() + self.Data=output+cipherobj.encrypt(data) + else: raise Error, 'Unsupported cipher algorithm '+str(cipheralg) + def decrypt(self, key, cipheralg): + if cipheralg==CIPHER_IDEA: + import idea + cipherobj=idea.new(key, idea.PGP, '\000'*idea.blocksize) + data=cipherobj.decrypt(self.Data[:10]) + if data[6:8]!=data[8:10]: + raise Error, 'Error during conventional decryption' + cipherobj.sync() + else: raise Error, 'Unsupported cipher algorithm '+str(cipheralg) + return cipherobj.decrypt(self.Data[10:]) + def parse_bin(self, binstring): + self.Data=binstring + def write_bin(self): + return self.Data + +class Plaintext(Packet): + def __init__(self): + Packet.__init__(self) + self.CTBT = CTBT_PLAIN + self.Mode='b' + self.Filename="" + self.Timestamp=0 + self.Data="" + def isBinary(self): + if self.Mode in 'bB': return 1 + elif self.Mode in 'tT': return 0 + else: raise Error, 'Unknown Plaintext mode byte' + def parse_bin(self, binstring): + pos=0 + self.Mode=binstring[pos] ; pos=pos+1 + if self.Mode not in 'bBtT': + raise Error, 'Unknown Plaintext mode byte' + filename_len=ord(binstring[pos]) ; pos=pos+1 + self.Filename=binstring[pos:pos+filename_len] + pos=pos+filename_len + self.Timestamp=Str2Int(binstring[pos:pos+4]) + pos=pos+4 + self.Data=binstring[pos:] + def write_bin(self): + if len(self.Filename)>256: + raise Error, "Filename is too long...256 chars maximum" + return (self.Mode + + chr(len(self.Filename)) + self.Filename + + Int2Str(self.Timestamp, 32) + self.Data ) + +class Comment(Packet): + def __init__(self): + Packet.__init__(self) + self.CTBT = CTBT_COMMENT + self.Comment=None + def parse_bin(self, binstring): + self.Comment=binstring + def write_bin(self, binstring): + return self.Comment + def __str__(self): return self.Comment + +# +# Base class for Keys (private and public) +# +class Key(Packet): + def __init__(self, version=3): + Packet.__init__(self) + self.ModulusSize = 0 + self.Version = version + self.KeyID = bignum(0) + self.Timestamp = 0 + self.Validity = 0 + self.PubkeyAlg = PK_RSA + self.IDList = [] + self.Trust = None + + def padEncryptionKey(self, M): + "Given a Data Encryption Key (DEK), pad it suitably for PK encryption" + if type(M)!=type(''): M=Int2Str(M) + L=len(M) + if L>=384/8: + # We'll assume that anything larger than 384 bits doesn't + # need padding. + return M + + if self.Version==2: + pad=self.ModulusSize/8-L-6 + csum=Int2Str(Checksum(M), 16) + randbytes=get_random_bytes(pad) + M='\000\001'+M+csum+'\000'+randbytes+'\002' + return M + elif self.Version==3: + pad=self.ModulusSize/8-L-6 + csum=Int2Str(Checksum(M), 16) + randbytes=get_random_bytes(pad) + M='\000\002'+randbytes+'\000\001'+M+csum + return M + else: + raise Error, 'Unsupported packet version '+str(self.Version) + + def unpadEncryptionKey(self, M): + "Return the DEK, by removing the padding." + if type(M)!=type(''): M=Int2Str(M) + import string + if self.Version==2: + start=string.find(M, '\001') + if start==-1 or M[0]!='\001' or M[-1]!='\002': + raise Error, "Can't undo v2.2 key padding" + DEK, csum = M[start:start+16], M[start+16:start+18] + # XXX check csum! + return DEK + elif self.Version==3: + if M[0]!='\002' or M[-20:-18]!='\000\001': + raise Error, "Can't undo v2.3 key padding" + DEK, csum = M[-18:-2], M[-2:] + # XXX check csum! + return DEK + else: + raise Error, 'Unsupported packet version '+str(self.Version) + + def padMessageDigest(self, M): + "Pad a message digest appropriately." + L=len(M) + if L>=384/8: + # We'll assume that anything larger than 384 bits doesn't + # need padding. + return M + if self.PubkeyAlg==PK_RSA: + # RSA encryption + if L<self.ModulusSize/8: + if self.Version==2: + # PGP 2.2 padding + pad=self.ModulusSize/8-L-4 + M='\000\001'+M+'\000'+('\377'*pad)+'\001' + return M + elif self.Version==3: + pad=self.ModulusSize/8-L-21 + M='\000\001'+('\377'*pad)+'\000'+ASN_STRING+M + return M + else: + raise Error, 'Unsupported packet version '+str(self.Version) + else: raise Error, "Unknown public-key algorithm "+str(PubkeyAlg) + + def unpadMessageDigest(self, M): + "Return the message digest, after removing the padding" + import string + if self.PubkeyAlg==PK_RSA: + if self.Version==2: + end=string.rfind(M, '\000') + start=string.find(M, '\001') + if start==-1 or end==-1: + raise Error, "Can't undo v2.2 digest padding" + return M[start+1:end] + elif self.Version==3: + start=string.find(M, ASN_STRING) + if start==-1: + raise Error, "Can't undo v2.3 digest padding" + return M[start+len(ASN_STRING):] + else: raise Error, "Unknown public-key algorithm "+str(PubkeyAlg) + + def isPrivate(self): return 0 + def isUnlocked(self): return 0 + +# +# Public Keys +# + +class PublicKey(Key): + def __init__(self, version=3): + Key.__init__(self, version) + self.CTBT = CTBT_PKCERT + if (version==2) or (version==3): + self.PubkeyAlg = PK_RSA + self.KeyCompromise = None + self.n = 0 + self.e = 0 + else: raise Error, 'Unsupported packet version '+str(version) + + def __repr__(self): + return '<Public '+hex(self.KeyID)+'>' + + def encrypt(self, M): + "Encrypt a message with this key object. Padding the caller's responsibility." + if type(M)==type(''): M=Str2Int(M) + if self.PubkeyAlg==PK_RSA: + result=pow(M, self.e, self.n) +# Mt=pow(result, self.d, self.n) +# if M!=Mt: print 'Encryption mismatch' + return Int2Str(result) + else: + raise Error, "Unknown public-key algorithm "+str(self.PubkeyAlg) + + def decrypt(self, M): + "Decrypt with this key object" + if not self.isPrivate(): + raise Error, "Cannot decrypt with public key" + elif not self.isUnlocked(): + raise Error, "Private key is not unlocked." + if type(M)==type(''): M=Str2Int(M) + if self.PubkeyAlg==PK_RSA: + result=pow(M, self.d, self.n) + # XXX remove the following check + Mt=pow(result, self.e, self.n) + if M!=Mt: print 'Decryption mismatch' + return Int2Str(result) + else: + raise Error, "Unknown public-key algorithm "+str(self.PubkeyAlg) + + def sign(self, data, hashAlg=HASH_MD5): + import time + hash=get_hash_module(hashAlg) + hash_obj=hash.new(data) + sig=Signature() + sig.Timestamp = long(time.time()) + sig.SigClass=0 ; sig.HashAlg=hashAlg + sig.SigKeyID, sig.PubkeyAlg=self.KeyID, self.PubkeyAlg + hash_obj.update(chr(sig.SigClass) + + Int2Str(sig.Timestamp, 32)) + print `chr(sig.SigClass) + Int2Str(sig.Timestamp, 32)` + MD=hash_obj.digest() + sig.HashClue=MD[0:2] + MD=self.padMessageDigest(MD) + sig.signature=Str2Int(self.decrypt(MD)) + print sig.__dict__ + return sig + + def is_self_signed(self): + "Test if a key is self-signed, returning a Boolean value" + for item in self.IDList: + for sig in item.SigList: + if (sig.SigKeyID==self.KeyID and + sig.verify_key_sig(self, self, item)): + return 1 + return 0 + + def measureModulus(self): + "Determine the maximum message size that this key can handle" + if self.PubkeyAlg==PK_RSA: + t,bits=1L,0 + while t<self.n: t,bits = t<<1L, bits+1 + self.ModulusSize=bits + else: + raise Error, 'Unknown PK Algorithm '+str(self.PubkeyAlg) + + def parse_bin(self, binstring): + pos = 0 + self.Version = ord(binstring[pos]) + if self.Version not in [2, 3]: raise Error, 'Unknown Public Key version '+str(self.Version) + pos = pos + 1 + self.Timestamp = Str2Int(binstring[pos:pos+4]) + pos = pos + 4 + self.Validity = Str2Int(binstring[pos:pos+2]) + pos = pos + 2 + self.PubkeyAlg = ord(binstring[pos]) + if self.PubkeyAlg not in PK_LIST: + raise Error, 'Unknown PK Algorithm '+str(self.PubkeyAlg) + pos = pos + 1 + self.n, mpilen = MPI2Int(binstring[pos:]) + self.KeyID = Str2Int(binstring[pos+mpilen-8:pos+mpilen]) + pos = pos + mpilen + self.e, mpilen = MPI2Int(binstring[pos:]) + pos = pos + mpilen + if pos<>len(binstring): + print 'WARNING: length mismatch in Public Key packet.' + self.measureModulus() + + def write_bin(self): + data = (chr(self.Version) + Int2Str(self.Timestamp, 32) + + Int2Str(self.Validity, 16) + chr(self.PubkeyAlg) ) + if self.PubkeyAlg==PK_RSA: + data=data+Int2MPI(self.n)+Int2MPI(self.e) + else: raise Error, "Unknown public-key algorithm "+str(self.PubkeyAlg) + return data + + +# +# Private Keys +# + +class PrivateKey(PublicKey): + def __init__(self, version=3): + Key.__init__(self, version) + self.CTBT = CTBT_SKCERT ; self.CipherAlg=CIPHER_NONE + self.PubkeyAlg = PK_RSA + + def __repr__(self): + return '<Secret key '+hex(self.KeyID)+'>' + + def isPrivate(self): return 1 + def isUnlocked(self): + if self.PubkeyAlg==PK_RSA: + return hasattr(self, 'd') + else: + raise Error, 'Unknown PK Algorithm '+str(self.PubkeyAlg) + + def Unlock(self, passphrase = ""): + "Decrypt the private key data, given a passphrase." + binstring=self.Data ; pos=0 + if (self.CipherAlg==CIPHER_NONE): + # Unencrypted private key, so just unpack + self.d, mpilen = MPI2Int(binstring[pos:]) + pos = pos + mpilen + self.p, mpilen = MPI2Int(binstring[pos:]) + pos = pos + mpilen + self.q, mpilen = MPI2Int(binstring[pos:]) + pos = pos + mpilen + self.u, mpilen = MPI2Int(binstring[pos:]) + pos = pos + mpilen + checksum=Str2Int(binstring[pos:pos+2]) + pos = pos + 2 + elif self.CipherAlg==CIPHER_IDEA: + cipher_mod=get_cipher_module(CIPHER_IDEA) + hash_mod=get_hash_module(HASH_MD5) + key=hash_mod.new(passphrase).digest() + cipherobj=cipher_mod.new(key, cipher_mod.PGP, + cipher_mod.blocksize*'\000') + + IV=binstring[pos:pos+8] ; pos=pos+8 + s=cipherobj.decrypt(IV) + cipherobj.sync() + + if (self.PubkeyAlg==PK_RSA): + d={} + cksum=Checksum("") + for i in 'dpqu': + dummy, mpilen = MPI2Int(binstring[pos:]) + MPI=binstring[pos:pos+mpilen] + pos = pos + mpilen + + plain=MPI[:2]+cipherobj.decrypt(MPI[2:]) + cipherobj.sync() + cksum=Checksum(plain,cksum) + value, dummy = MPI2Int(plain) + setattr(self, i, value) + + checksum=Str2Int(binstring[pos:pos+2]) + pos = pos + 2 + + # If the checksum doesn't match, the passphrase must be wrong + if (cksum!=checksum): + raise Error, 'Incorrect passphrase' + + else: + raise Error, "Unknown public-key algorithm "+str(self.PubkeyAlg) + + def Lock(self, passphrase=None): + """Reencipher a PGP private key with a new passphrase, or with + the previous passphrase implied. Note that once a key's private + data has been encrypted, this interface will never let you + revert to having no password, even though PGP permits this. + It's probably not a big loss.""" + + if passphrase!=None: + # Transform passphrase to a 128-bit IDEA key + import md5 + key=md5.new(passphrase).digest() + + # Create encryption object + if self.CipherAlg==CIPHER_NONE: self.CipherAlg=CIPHER_IDEA + cipher_mod=get_cipher_module(self.CipherAlg) + cipherobj=cipher_mod.new(key, cipher_mod.PGP, + '\000'*cipher_mod.blocksize) + data=IV=get_random_bytes(cipher_mod.blocksize) + cipherobj.decrypt(IV) + cksum=Checksum("") + for i in 'dpqu': + cipherobj.sync() + MPI=Int2MPI(getattr(self, i)) + cksum=Checksum(MPI, cksum) + MPI=MPI[:2]+cipherobj.encrypt(MPI[2:]) + data=data+MPI + self.Data=data+Int2Str(cksum, 16) + elif not hasattr(self, 'Data'): + raise Error, "Can't lock a generated key without a passphrase" + # Delete the secret attributes + for i in 'dpqu': delattr(self, i) + + def public_binary(self): + "Return the string representation of this key as a public certificate" + data=PublicKey.write_bin(self) + header=self.mkHeader(len(data), CTBT=CTBT_PKCERT) + return header+data + + def generate(self, PKalg=PK_RSA, *args): + "Generate a new public/private key pair" + self.PubkeyAlg=PKalg + if PKalg==PK_RSA: self.generateRSA(args) + else: + raise Error, 'Unknown PK Algorithm '+str(self.PubkeyAlg) + + def generateRSA(self, args): + "Generate a new RSA public/private key pair" + # XXX Comment this section! + import pubkey + if len(args)==1: bits, = args ; ebits=5 + elif len(args)==2: bits, ebits = args + bits=(bits+7)/8 + if bits%2==1: bits=bits+1 # Ensure that an odd number of bytes are required + p=Str2Int(get_random_bytes(bits/2)) | pow(2L, bits*8/2-1) | 3 + q=Str2Int(get_random_bytes(bits-bits/2)) | pow(2L, bits*8/2) | 3 + p=pubkey.findPrime(p) ; q=pubkey.findPrime(q) + if p>q: p,q=q,p + phi=(p-1)*(q-1) + ebits=(ebits+7)/8 + exp= Str2Int(get_random_bytes(ebits))|1 + exp = exp & (pow(bignum(2), ebits+1) - 1) + while pubkey.GCD(exp, phi)!=1: exp=exp+2 + self.p, self.q, self.n = p, q, p*q + self.e, self.d = exp, pubkey.Inverse(exp, phi) + self.u = pubkey.Inverse(p, q) + + import time + self.Timestamp=int(time.time()) + self.KeyID = self.n & 0xFFFFffffL + self.measureModulus() + + print hex(self.p),hex(self.q) + print hex(self.n) + print hex(self.e),hex(self.d) +## M=1972L +## C=pow(M, self.e, self.n) +## M=pow(C, self.d, self.n) +## print M + + def parse_bin(self, binstring): + pos = 0 + self.Version = ord(binstring[pos]) + if self.Version not in [2, 3]: raise Error, 'Unknown Private Key version '+str(self.Version) + pos = pos + 1 + self.Timestamp = Str2Int(binstring[pos:pos+4]) + pos = pos + 4 + self.Validity = Str2Int(binstring[pos:pos+2]) + pos = pos + 2 + self.PubkeyAlg = ord(binstring[pos]) + if self.PubkeyAlg==PK_RSA: + pos = pos + 1 + self.n, mpilen = MPI2Int(binstring[pos:]) + self.KeyID = Str2Int(binstring[pos+mpilen-8:pos+mpilen]) + pos = pos + mpilen + self.e, mpilen = MPI2Int(binstring[pos:]) + pos = pos + mpilen + self.CipherAlg=ord(binstring[pos]) + pos = pos + 1 + if self.CipherAlg in [CIPHER_NONE, CIPHER_IDEA]: + self.Data=binstring[pos:] + pos=len(binstring) + else: + raise Error, 'Unknown PK Algorithm '+str(self.PubkeyAlg) + self.measureModulus() + if pos<>len(binstring): + print 'WARNING: length mismatch in Private Key packet.' + + def write_bin(self): + data=(chr(self.Version)+Int2Str(self.Timestamp, 32)+ + Int2Str(self.Validity, 16)+chr(self.PubkeyAlg)) + if self.PubkeyAlg==PK_RSA: + data=data+Int2MPI(self.n)+Int2MPI(self.e)+self.Data + else: + raise Error, 'Unknown PK Algorithm '+str(self.PubkeyAlg) + return data + +# +# User ID's +# +class UserID(Packet): + def __init__(self): + Packet.__init__(self) + self.CTBT=CTBT_USERID + self.UserID = "" + self.SigList = [] + self.Trust = None + def __repr__(self): return '< User ID "'+str(self.UserID)+'" >' + def parse_bin(self, binstring): + self.UserID = binstring + def write_bin(self): + return self.UserID + + +# +# Signatures +# +class Signature(Packet): + def __init__(self, version=3): + Packet.__init__(self) + self.CTBT = CTBT_SIG + self.Version = version + if (version==2) or (version==3): + self.SigClass = None + self.SigKeyID = 0 + self.Timestamp = 0 + self.PubkeyAlg = PK_RSA + +# +# Verify that key was used to sign target. Target is: +# +# - a binary string for sig classes 0x0 and 0x1 +# - a tuple (pubkey, uid) for a key certification signature +# - None for key compromise signature (since it should be self-signed) +# - a signature for a timestamp signature + + def verify(self, key, target): + if self.SigClass == None: raise Error, "Can't verify uninitialised signature." + elif (self.SigClass==0 or self.SigClass==1): + raise Error, "We don't yet check document signatures." + elif (self.SigClass==0x10 or self.SigClass==0x11 or self.SigClass==0x12 or self.SigClass==0x13): + return self.verify_key_sig(self, key, target[0], target[1]) + elif (self.SigClass==0x20): + raise Error, "We don't yet do key compromise signatures." + elif (self.SigClass==0x30): + raise Error, "We don't yet do certificate revokation." + elif (self.SigClass==0x40): + raise Error, "We don't yet do timestamps." + else: raise Error, 'Unknown signature class '+str(hex(self.SigClass)) + + def verify_key_sig(self, signerkey, signedkey, userid): + if (self.SigClass<0x10) or (self.SigClass>0x13): + raise Error, "Can't verify key signature on non key signature." + hash_mod=get_hash_module(self.HashAlg) + hash_obj=hash_mod.new() + if signerkey.KeyID!=self.SigKeyID: + raise Error, "Incorrect signator key" + if signedkey.CTBT!=CTBT_PKCERT: + raise Error, "Can only verify signatures on a public key cert" + data=signedkey.write_bin() + data=signedkey.mkHeader(len(data),16) + data + hash_obj.update (data) + hash_obj.update (userid.UserID) + hash_obj.update (chr(self.SigClass)) + hash_obj.update (Int2Str(self.Timestamp, 32)) + hash = hash_obj.digest() + signature = signerkey.encrypt(self.signature) + sighash = signerkey.unpadMessageDigest(signature) + if self.HashClue!=sighash[0:2]: + raise Error, 'Signature checking failed--message digest mismatch' + return sighash==hash + + def parse_bin(self, binstring): + pos = 0 + self.Version = ord(binstring[pos]) + if self.Version not in [2, 3]: raise Error, 'Unknown signature version '+str(self.Version) + pos = pos + 1 + if ord(binstring[pos]) <> 5: raise Error, 'Variable-length signature material <> 5.' + pos = pos + 1 + self.SigClass = ord(binstring[pos]) + pos = pos + 1 + self.Timestamp = Str2Int(binstring[pos:pos+4]) + pos = pos + 4 + self.SigKeyID = Str2Int(binstring[pos:pos+8]) + pos = pos + 8 + self.PubkeyAlg = ord(binstring[pos]) + pos = pos + 1 + if self.PubkeyAlg not in PK_LIST: + raise Error, 'Unknown signature PK Algorithm.' + self.HashAlg = ord(binstring[pos]) + pos = pos + 1 + if self.HashAlg not in HASH_LIST: + raise Error, 'Unknown hashing Algorithm.' + self.HashClue = binstring[pos:pos+2] + pos = pos + 2 + self.signature, siglen = MPI2Int(binstring[pos:]) + if (pos + siglen) <> len(binstring): + print 'WARNING: length mismatch in signature packet.' + def write_bin(self): + data = (chr(self.Version) + chr(5) + chr(self.SigClass) + + Int2Str(self.Timestamp, 32) + + Int2Str(self.SigKeyID, 64) + + chr(self.PubkeyAlg)) + if self.PubkeyAlg==PK_RSA: + data=data+ chr(self.HashAlg) + self.HashClue + if type(self.signature)==type(''): data=data+Int2MPI(Str2Int(self.signature)) + else: data=data+Int2MPI(self.signature) + return data + else: raise Error, "Unknown public-key algorithm "+str(PubkeyAlg) + +# +# Trust packet +# +class Trust(Packet): + def __init__(self): + Packet.__init__(self) + self.CTBT=CTBT_TRUST + self.Trust = 0 ; self.Disabled = 0 ; self.Ultimate=0 + + + def parse_bin(self, binstring): + pos = 0 + trust = ord(binstring[pos]) + self.Trust = trust & 7 + self.Disabled = ((trust & 32) != 0) + self.Ultimate = ((trust & 128) != 0) + + def write_bin(self): + trust=(self.Trust & 7) + if self.Disabled: trust=trust | 32 + if self.Ultimate: trust=trust | 128 + return chr(trust) + + +# +# +# KeyRing class +# +# + +class KeyRing: + def __init__(self): + self.keyring = {} + + # The following 6 methods simulate a dictionary's interface + def __getitem__(self, key): + return self.keyring[key] + def __delitem__(self, key): + del self.keyring[key] + def __setitem__(self, key, value): + self.keyring[key]=value + def values(self): return self.keyring.values() + def items(self): return self.keyring.items() + def keys(self): return self.keyring.keys() + +# +# Parse a binary PGP key string into a keyring +# + def parseKeyRing(self, input): + curr_key = None + curr_uid = None + while 1: + p, dummy = readPacket(input) + if p==None: break + elif p.CTBT==CTBT_PKCERT or p.CTBT==CTBT_SKCERT: + self[p.KeyID]=p + self[p.KeyID & 0xffffFFFFL]=p + curr_key = p + curr_uid = None + elif p.CTBT==CTBT_USERID: + if curr_key: curr_key.IDList.append(p) + else: print 'WARNING: dangling USERID packet.' + curr_uid = p + elif p.CTBT==CTBT_TRUST: + if curr_key: + if curr_key.Trust: pass#print 'WARNING: multiple Trusts for Key.' + else: curr_key.Trust = p + elif curr_uid: + if curr_uid.Trust: print 'WARNING: multiple Trusts for USERID.' + else: curr_uid.Trust = p + else: print 'WARNING: dangling Trust packet.' + elif p.CTBT==CTBT_SIG: + if not curr_uid: print 'WARNING: dangling signature.' + else: + curr_uid.SigList.append(p) + else: raise Error, 'Unknown KeyRing Packet type: '+str(p.CTBT) + + +class ShelfKeyRing(KeyRing): + def __init__(self, filename): + import shelve + self.__shelf=shelve.open(filename) + + # The following 6 methods simulate a dictionary's interface + def __getitem__(self, key): + key=repr(key) + return self.__shelf[key] + def __delitem__(self, key): + key=repr(key) + del self.__shelf[key] + def __setitem__(self, key, value): + key=repr(key) + self.__shelf[key]=value + def values(self): return self.__shelf.values() + def items(self): return self.__shelf.items() + def keys(self): return self.__shelf.keys() + def __del__(self): + self.__shelf.close() + +def readPacket(input): + if input=="": return None, "" + retval="" + import types + if type(input)==types.StringType: + CTB=ord(input[0]) + lenlen, length=getLen(CTB, input[1:]) + if length!=None: + s=input[1+lenlen:1+lenlen+length] + retval=input[1+lenlen+length:] + else: s=input[1+lenlen:] + elif type(input)==types.FileType: + CTB=input.read(1) + if CTB=="": return None, "" + CTB=ord(CTB) + lenlen=PACKET_LENGTH[CTB & 3] + if lenlen==0: + s=input.read() + else: + lenstr=input.read(lenlen) + length=Str2Int(lenstr) + s=input.read(length) + + if (CTB&128) <> 128: raise Error, 'Keystring contains format error.' + CTBT = (CTB&60)>>2 + if CTBT==CTBT_PKEP: packet=PKEncrypted() + elif CTBT==CTBT_SIG: packet = Signature() + elif CTBT==CTBT_SKCERT: packet = PrivateKey() + elif CTBT==CTBT_PKCERT: packet = PublicKey() + elif CTBT==CTBT_COMPR: packet = Compressed() + elif CTBT==CTBT_CKEP: packet = ConvEncrypted() + elif CTBT==CTBT_PLAIN: packet = Plaintext() + elif CTBT==CTBT_USERID: packet = UserID() + elif CTBT==CTBT_TRUST: packet = Trust() + elif CTBT==CTBT_COMMENT: packet = Comment() + else: raise Error, 'Unknown KeyRing Packet type: '+str(CTBT) + packet.CTBT=CTBT + packet.parse_bin(s) + return packet, retval + +def EncryptMessage(message, signer=None, + recipients=None, DEK=None, + compression=COMPRESS_ZLIB, + cipherAlg=CIPHER_IDEA, hashAlg=HASH_MD5): + p=Plaintext() ; p.Data=message ; data=p.binary() + + if signer!=None: + sig=signer.sign(message, hashAlg=hashAlg) + data=sig.binary() + data + + if compression!=COMPRESS_NONE: + p=Compressed() ; p.CompressAlg=compression + p.compress(data) ; data=p.binary() + + output=data + cipher_mod=get_cipher_module(cipherAlg) + if DEK==None: DEK=get_random_bytes(cipher_mod.keysize) + if recipients!=None and recipients!=[]: + if cipherAlg!=CIPHER_NONE: + p=ConvEncrypted() ; p.encrypt(DEK, data, cipherAlg) + output=data=p.binary() + + if type(recipients)!=type([]): recipients=[recipients] + for rec in recipients: + c = PKEncrypted() + c.KeyID, c.PubkeyAlg = rec.KeyID, rec.PubkeyAlg + paddedKey=rec.padEncryptionKey(DEK) + c.Data = rec.encrypt(paddedKey) + output=c.binary()+output + return output + +# +# Tools to handle PGP Armoring and DeArmoring +# + +import regex, string +import binascii +import zlib + +MAX_LINE_SIZE=80 +LINE_LEN=48 + +begin_whole_pat = regex.compile("^[-]+BEGIN[ \t]+PGP") +end_whole_pat = regex.compile("^[-]+END[ \t]+PGP") + +begin_partial_pat = regex.compile("^[-]+BEGIN[ \t]+PGP[ \t]+MESSAGE,[ \t]+PART[ \t]+\([0-9]+\)/\([0-9]+\)[-]+[ \t\n]+$") +end_partial_pat = regex.compile("^[-]+END[ \t]+PGP[ \t]+MESSAGE,[ \t]+PART[ \t]+\([0-9]+\)/\([0-9]+\)[-]+[ \t\n]+$") + +def ArmorFile(input, output, blocktype="MESSAGE"): + s=[] + output.write("-----BEGIN PGP "+blocktype+"-----\n") + output.write("Version: "+pgp.Version+'\n') + data="" + crc=zlib.pgp24("") + while (1): + data=input.read(LINE_LEN) + if data=="": break + output.write(binascii.b2a_base64(data)) + crc=zlib.pgp24(data, crc) + crc=chr((crc >> 16) & 0xff) + chr((crc >> 8) & 0xff) + chr(crc & 0xff) + output.write("="+binascii.b2a_base64(crc)) + output.write("-----END PGP "+blocktype+"-----\n") + +def UnarmorFile(input, output): + s=[] + started = 0 + busy = 0 + crc=zlib.pgp24("") + while (1): + data=input.readline() + if not started: + if begin_whole_pat.match(data)>=0: started = 1 + if data=="": break + else: continue + if not busy: + if len(string.strip(data))<>0: continue + busy = 1 + if data=="" or end_whole_pat.match(data)!=-1: break + if data[0]=='=': + # The CRC line begins with a padding character + chkcrc=binascii.a2b_base64(data[1:]) + break + else: + data=binascii.a2b_base64(data) + output.write(data) + crc=zlib.pgp24(data, crc) + crc=chr((crc >> 16) & 0xff) + chr((crc >> 8) & 0xff) + chr(crc & 0xff) + if chkcrc!=crc: raise Error, "CRC checksums don't match" + +def armor(binstr): + import StringIO + i = StringIO.StringIO(ascstr) + o = StringIO.StringIO() + ArmorFile(i,o) + return o.getvalue() + +def unarmor(ascstr): + import StringIO + i = StringIO.StringIO(ascstr) + o = StringIO.StringIO() + UnarmorFile(i, o) + return o.getvalue() + diff --git a/Protocol/PGP/pgpapi.html b/Protocol/PGP/pgpapi.html new file mode 100644 index 0000000..0ee142a --- /dev/null +++ b/Protocol/PGP/pgpapi.html @@ -0,0 +1,191 @@ +<HTML> +<HEAD><TITLE>A Proposed PGP API for Python</TITLE></HEAD> +<BODY> + +<H1>A Proposed PGP API for Python</H1> + +<P>This document resides at <A +HREF="http://www.magnet.com/%7Eamk/python/pgpapi.html">http://www.magnet.com/%7Eamk/python/pgpapi.html</A>. + +<P>Comments on this proposed API are welcomed; please write me at <A +HREF="mailto:amk@magnet.com"><CODE>amk@magnet.com</CODE></A>. + +<P>I would like to write a collection of modules and classes for the +Python programming language to perform PGP en/decryption. The code +would not execute a PGP subprocess, but would be a complete +re-implementation of PGP's format. Currently all the low-level pieces +are in place; there are interfaces to a compression library, IDEA, +MD5, and other crypto algorithms that might be in PGP 3.0 (SHA, 3DES, +Blowfish); reading of PGP packets is implemented; the Key objects are +implemented to a significant degree, etc. However, the high-level +interface is the tricky part. Suggestions are welcomed at <A +HREF="mailto:amk@magnet.com"><CODE>amk@magnet.com</CODE></A>. + +<P>The following tasks must be supported: +<OL> +<LI> Reading keys from keyrings, and writing them out. +<LI> Verifying signatures on keys +<LI> Encrypting messages for recipients, optionally signing them. +<LI> Decrypting messages, and finding out whether the signature was valid or not. +<LI> Encoding and decoding ASCII armor. Should this be integrated with +encryption and decryption, or will the caller have to do that ahead of +time when decoding, and afterwards when encoding? +</OL> + +<H2>PGP Keys</H2> + +<P>Keys are produced by <CODE>pgp.KeyRing</CODE> objects, which are +passed a file object. <CODE>KeyRing</CODE> objects support a +dictionary-style interface, where the key is a 64-bit or 32-bit long +integer corresponding to the key's ID. (PGP uses the 64-bit form +internally, though the 32-bit form is what's printed in the output of +pgp -kv.) + +<P>Keys have various methods: + +<DL> +<DT><CODE>isPrivate()</CODE> +<DD>Returns a Boolean (true if it's a secret key). +<DT><CODE>isUnlocked()</CODE> +<DD>Returns a Boolean (true if the secret key data is available). +<dt><CODE>Unlock(<EM>passphrase</EM>)</CODE> +<dd>Decrypt the private key info, using the given passphrase. +<dt><CODE>Lock(<EM>passphrase</EM>=None)</CODE> +<dd>Encrypt the private key info, using the given passphrase; if the +passphrase is <CODE>None</CODE>, the secret key info is simply +deleted, without reencrypting the key data. +</DL> + +<P>There will also be various private methods to perform RSA +operations; I can't think of a reason to expose these operations to +the caller. Keys also have various attributes: <CODE>PKalgorithm, Version, KeyID, +IDList (a list of User ID packets), Trust, ...</CODE> + +<H2>Encrypting/decrypting messages</H2> +<P>Encrypting messages is fairly simple; there's one function call, +<CODE>pgp.Encrypt()</CODE>: +<PRE> +Encrypt(input, # File object + output, # File object + recipients=[], # List of recipients + cipher_alg={one of pgp.IDEA, pgp.DES3, ...}, + hash_alg={one of pgp.MD5, pgp.SHA, ...}, + compression= true/false, + signer=<key to sign message with>, + passphrase=<conventional passphrase> + ) </PRE> + +<P>That's a lot of options! However, usually callers will only be +guaranteed to include the first two, and then the other options would +be set using keyword arguments; otherwise, sensible defaults would be +assumed. It doesn't make sense to specify all the above arguments, so +you wouldn't have to include them all, or remember their order. + +<P>(Note that the above interface doesn't provide for clearsigning +files. Should that be an extra parameter, or a separate function?) + +<PRE> +Decrypt(input, output, + recipient # The recipient's key + ) +</PRE> + +If it succeeds, <CODE>Decrypt()</CODE> returns a +<CODE>Signature</CODE> object, whose methods are: + +<DL> +<DT><CODE>isEncrypted(), isSigned()</CODE> +<DD>Both these methods return Booleans. +<DT><CODE>signerID()</CODE> +<DD>The 64-bit Key ID of the signer's key; <CODE>None</CODE> if the +message isn't signed. It would be the caller's responsibility to retrieve +the ID, somehow retrieve a <CODE>pgp.Key</CODE> object corresponding +to that ID, and pass it to the next function. +<DT><CODE>verifySignature(<EM>K</EM>)</CODE> +<DD>Verify the signature on the message with the key K; an exception +is raised if the message wasn't signed with K, or if the signature +verification failed. +</DL> + +<H3>Sample usages</H3> + +<P>In all the following examples, <CODE>input</CODE> and +<CODE>output</CODE> are two file objects, or two objects that imitate +file objects; should the API require that these objects be seekable? +(That would rule out using sockets as the input or the output. +Strings can be used by passing them to the <CODE>StringIO</CODE> +module first, which simulates a file on a string. The functions could +check the type of their parameters and perform this conversion +automatically, if desired.) <P><CODE>RK</CODE> is a +<CODE>pgp.Key</CODE> instance for the recipient's keys, and +<CODE>SK</CODE> is the sender's key, a <CODE>pgp.PrivateKey</CODE> +instance. + +<P>To conventionally encrypt a message with just IDEA, using the +password "TomServo": + +<PRE>pgp.Encrypt(input, output, passphrase="TomServo") +</PRE> +<P>To encrypt (but not sign) a message, with IDEA, MD5, and compression: +<PRE>pgp.Encrypt(input, output, recipient=RK) + or +pgp.Encrypt(input, output, recipient=[RK]) +</PRE> + +<P>To encrypt and sign a message, with IDEA, MD5, and no compression: +(SK must be a private key instance, and must be unlocked. + +<PRE>pgp.Encrypt(input, output, recipient=[RK], signer=SK, compression=0) +</PRE> + +<P>To decrypt a message: +<PRE>pgp.Decrypt(input, output, recipient=RK) +</PRE> + +<H2>ASCII armoring and dearmoring</H2> +I'm not sure what the interface to this should look like; assuming +that it's a separate function (and not part of <CODE>Encrypt()</CODE> and <CODE>Decrypt()</CODE>, how about the following? + +<PRE> +pgp.Enarmor(input, output) # These functions assume one single file +pgp.Dearmor(input, output) # and return nothing +</PRE> + +<P>Multipart encoding and decoding will be the caller's problem with +this interface. The "PART 01/05" notation in the armor headers means +that you have to know how large the file will be before you can write +the headers. Perhaps the input file will simply have to be seekable, to allow +measuring the file's size before processing. + +<H2>Unanswered Questions</H2> + +<UL> +<LI>Should the API require that the input and output objects be +seekable? +<LI>Should ASCII armoring and dearmoring be integrated into encryption +and decryption, or should that be the responsibility of the caller? +(In the latter case, the API would then always expect binary data.) +<LI>Should everything be done in memory, or in temporary files? (Possible +clever idea: generate a random IDEA key, and encrypt all the temp +files with it in ECB or OFB mode.) +<LI>Should the caller be able to select a random number generation +function to be used? +<LI>Clear-signed messages could be generated by an additional option +to <CODE>Encrypt()</CODE>; how should clearsigned messages be read? +(Probably a utility function that calls <CODE>Decrypt()</CODE> for +you, after assembling the clearsigned text.) +<LI>Is the above API useful for various applications? Applications I +can think of: +<UL><LI>A PGP clone<LI>A GUI PGP clone<LI>Remailer software +<LI>Writing an app that run under Grail or the Python plug-in for +Netscape. +</UL> +</UL> + +<P>This document resides at <A +HREF="http://www.magnet.com/%7Eamk/python/pgpapi.html">http://www.magnet.com/%7Eamk/python/pgpapi.html</A>. + +<P>Comments on this proposed API are welcomed; please write me at <A +HREF="mailto:amk@magnet.com"><CODE>amk@magnet.com</CODE></A>. + +</BODY></HTML> diff --git a/Protocol/PGP/pgpconst.py b/Protocol/PGP/pgpconst.py new file mode 100644 index 0000000..5061206 --- /dev/null +++ b/Protocol/PGP/pgpconst.py @@ -0,0 +1,81 @@ +# +# PGP CONSTANTS +# + +# +# Cipher Type Byte Types +# +CTBT_PKEP = 0x1 +CTBT_SIG = 0x2 +CTBT_SKCERT = 0x5 +CTBT_PKCERT = 0x6 +CTBT_COMPR = 0x8 +CTBT_CKEP = 0x9 +CTBT_PLAIN = 0xB +CTBT_TRUST = 0xC +CTBT_USERID = 0xD +CTBT_COMMENT = 0xE + + +# +# Length Types +# +LEN_1 = 0x0 +LEN_2 = 0x1 +LEN_4 = 0x2 +LEN_UNKNOWN = 0x3 +PACKET_LENGTH={LEN_1:1, LEN_2: 2, LEN_4: 4, LEN_UNKNOWN: 0} + +# +# Signature Types +# +SIG_BIN = 0x0 +SIG_TXT = 0x1 +SIG_KEY = 0x10 +SIG_KEY1 = 0x11 +SIG_KEY2 = 0x12 +SIG_KEY3 = 0x13 +SIG_COMP = 0x20 +SIG_REVOKE = 0x30 +SIG_TIME = 0x40 + +# Ciphering algorithms +CIPHER_NONE = 0 +CIPHER_IDEA = 1 + +# List of the supported ciphering algorithms +CIPHER_LIST = [CIPHER_NONE, CIPHER_IDEA] + +# PK algorithms +PK_NONE = 0 +PK_RSA = 1 + +# List of the supported PK algorithms +PK_LIST = [PK_RSA] + +# Hashing algorithms +HASH_MD5=1 + +# List of the supported hashing algorithms +HASH_LIST = [HASH_MD5] + +# Compression algorithms +COMPRESS_NONE = 0 +COMPRESS_ZLIB = 1 + +# List of the supported compression algorithms +COMPRESS_LIST = [COMPRESS_ZLIB] + +# ASN.1 identifier string that precedes v2.3 message digests +ASN_STRING='0 0\014\006\010*\206H\206\367\015\002\005\005\000\004\020' + +# Packet sizes: despite the flexibility of PGP's packet structure, +# much code assumes things about the size of the field giving +# the packet's length. For example, secret and public key +# certificates are assumed to have a 16-bit length field. +# The following dictionary maps packet types to the expected size. +# If the type isn't here, then PGP doesn't have any expectation +# of its size. + +PACKET_SIZES={CTBT_SKCERT:16, CTBT_PKCERT:16, CTBT_SIG:16} + diff --git a/Protocol/PGP/pgpdoc1.txt b/Protocol/PGP/pgpdoc1.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Protocol/PGP/pgpdoc1.txt diff --git a/Protocol/PGP/pgpdoc2.txt b/Protocol/PGP/pgpdoc2.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Protocol/PGP/pgpdoc2.txt diff --git a/Protocol/PGP/pgpops.py b/Protocol/PGP/pgpops.py new file mode 100644 index 0000000..3187cbf --- /dev/null +++ b/Protocol/PGP/pgpops.py @@ -0,0 +1,350 @@ + +# pgpops.py : Various low-level operations related to PGP packets. +# +# To speed things up, this module could be rewritten as a C extension. +# +# Copyright (C) 1995, 1996, A.M. Kuchling +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. +# + +import idea, md5 + +def MPILength(str): + length=ord(str[0])*256+ord(str[1]) + length, remainder = divmod(length, 8) + if remainder!=0: length=length+1 + return length + +def MPI2Integer(str): + length=MPILength(str) + return (String2Int(str[2:2+length]), str[2+length:]) + +def Integer2MPI(i): + s=Int2String(i) + while (ord(s[0])==0): s=s[1:] + if (s==''): return '\000\000' + first=ord(s[0]) + bits= 1+(len(s)-1) *8 + mask=1 + while (mask<first and mask <256): + first = first & (255-mask) + mask=mask << 1 + bits=bits+1 + s=chr(bits/256)+chr(bits & 255)+s + return s + +def String2Int(str): + value=0L + for i in range(0, len(str)): + value=value*256+ord(str[i]) + return(value) + +def Int2String(i): + str='' + if (i==0): return '\000' + while(i>0): + str= chr(int(i & 255)) +str + i=i >> 8 + return str + +# The following two routines read and write PGP packets. Originally, +# I wrote a huge set of if...elif...else statements to handle different +# packet types and versions. This was simple to write, but lengthy and +# repetitive. Implementing the writing of packets would require another +# long if statement, and would result in two separate sections of code that +# would have to be kept consistent. So, I decided to create a single +# description that would be used for both reading and writing packets. +# The actual description (current up to PGP 2.6.2) can be found below. +# +# A PGP packet is described as a list of tuples. A tuple can be in +# one of two forms: +# 1) (DataFormat, Variable) : Reads a piece of data and stores it in the +# attribute named 'Variable' of the packet object. DataFormat must be +# one of 'Byte', 'U16', 'U32', 'U64', 'String', 'SizedString', 'MPI', +# or 'Error'. +# 2) (Condition, Sublist) : Evaluates the condition string; if it is true, +# the sublist is used for further parsing. + +def ReadPacket(object, Type): + templist=Format[Type] + while (len(templist)!=0): + data="" + s1, s2 = templist[0] + templist = templist[1:] + if s1=='Byte': + data=object._input.read(1) + setattr(object, s2, ord(data[0])) + elif s1=='U16': + data=object._input.read(2) + setattr(object, s2, ord(data[0])*256+ord(data[1])) + elif s1=='U32': + data=object._input.read(4) + value=0L + for j in range(0, 4): value = value * 256 + ord(data[j]) + setattr(object, s2, value) + elif s1=='U64': + data=object._input.read(8) + value=0L + for j in range(0, 8): + value = value * 256 + ord(data[j]) + setattr(object, s2, value) + elif s1[0:6]=='String': + if (s1=='String'): + if object.LengthType==3: setattr(object, s2, "") + else: + data=object._input.read(object._length) + setattr(object, s2, data) + else: + import string + v=string.atoi(s1[6:]) + data=object._input.read(v) + setattr(object, s2, data) + elif s1=='SizedString': + data=object._input.read(1) + length=ord(data[0]) + data=data+object._input.read(length) + setattr(object, s2, data[1:]) + elif s1=='MPI': + data=object._input.read(2) + length=ord(data[0])*256+ord(data[1]) + if ((length % 8)==0): length=length/8 + else: length=(length-(length%8))/8+1 + data=data+object._input.read(length) + setattr(object, s2, data) + elif s1=='Error': + raise 'pgp.Exception', s2 + else: + if eval(s1, vars(), object.__dict__): + templist=s2+templist + object._length=object._length-len(data) + +def WritePacket(object): + print `object` + output='' + templist=Format[object.Type] + while (len(templist)!=0): + s1, s2 = templist[0] + templist = templist[1:] + if (type(s2)!=type([])): + v=getattr(object, s2) + if s1=='Byte': + output=output+chr(v) + elif s1=='U16': + output=output+chr(v/256) + output=output+chr(v & 255) + elif s1=='U32': + s='' + for j in range(0, 4): + s=chr(v & 255)+s + v=v/256 + output=output+s + elif s1=='U64': + s='' + for j in range(0, 8): + s=chr(v & 255)+s + v=v/256 + output=output+s + elif s1[0:6]=='String': + if s1=='String': + if object.LengthType==3: pass + else: + output=output+v + else: + length=string.atoi(s1[6:]) + output=output+v + elif s1=='SizedString': + output=output+chr(len(v)) + output=output+v + elif s1=='MPI': + output=output+v + elif s1=='Error': + raise 'pgp.Exception', s2 + else: + if eval(s1, vars(), object.__dict__): templist=s2+templist + l=long(len(output)) + if object.LengthType==3: lengthType=object.LengthType + else: + bytes, lengthType = 4,2 + if object.PacketLength!=None: + if l<65536: bytes,lengthType=2,1 + if l<256: bytes,lengthType=1,0 + # XXX For key ring packets, it seems that the length *must* be + # two bytes, or PGP 2.6.2 won't read it. This is broken + # behaviour on PGP's part. + if object.Type==5 or object.Type==6: bytes,lengthType=2,1 + for i in range(0,bytes): + output=chr( int(l & 255) ) + output + l=l>>8 + output=chr(128 + (object.Type<<2)+ lengthType)+output + return output + + +# The master description of PGP packets (current up to 2.6.2) + +# Packet constants (this table is duplicated in pgp.py) + +PK_ENCRYPTED = 1 +SIGNATURE = 2 +PRIVKEY = 5 +PUBKEY = 6 +COMPRESSED = 8 +CIPHERED = 9 +PLAINTEXT = 11 +TRUST = 12 +USERID = 13 +COMMENT = 14 + +# Values in CTB for the length of the length of the following packet. +Lengths = {0:1, 1:2, 2:4, 3:-1} + +# +# Here comes a gigantic dictionary from hell! +# + +Format = { +PK_ENCRYPTED:[ + ('Byte', 'Version'), + ('U64', 'KeyID'), + ('Byte', 'PKCalg'), + ('PKCalg==1', + [ + ('MPI', 'IntegerMPI') + ] + ), + ('PKCalg!=1', + [ + ('Error', 'Unknown encryption algorithm in PK-encrypted packet') + ] + ) + ], +SIGNATURE: [ + ('Byte', 'Version'), + ('Version==2 or Version==3', + [ + ('Byte', 'MatLength'), + ('MatLength==5', + [ + ('Byte', 'SecClass'), + ('U32', 'TimeStamp'), + ('U64', 'KeyID'), + ('Byte', 'PKCalg'), + ('PKCalg==1', + [ + ('Byte', 'DigestAlg'), + ('U16', 'Checksum'), + ('MPI', 'IntegerMPI') + ] + ), + ('PKCalg!=1', + [ + ('Error', 'Unknown encryption algorithm in signature packet') + ] + ) + ] + ), + ('MatLength!=5', + [ + ('Error', 'Unknown length of material in signature packet') + ] + ) + ] + ), + ('Version!=2 and Version!=3', + [ + ('Error', 'Unknown version number in signature packet') + ] + ) + ], +PRIVKEY: [ + ('Byte', 'Version'), + ('U32', 'TimeStamp'), + ('U16', 'Validity'), + ('Byte', 'PKCalg'), + ('PKCalg==1', + [ + ('MPI', 'nMPI'), + ('MPI', 'eMPI'), + ('Byte', 'CipherAlg'), + ('CipherAlg==0', + [ + ('MPI', 'dMPI'), + ('MPI', 'pMPI'), + ('MPI', 'qMPI'), + ('MPI', 'uMPI') + ] + ), + ('CipherAlg==1', + [ + ('String+8', 'IV'), + ('MPI', 'dMPI'), + ('MPI', 'pMPI'), + ('MPI', 'qMPI'), + ('MPI', 'uMPI') + ] + ), + ('CipherAlg!=0 and CipherAlg!=1', + [ + ('Error', 'Unknown ciphering algorithm in public key') + ] + ), + ('U16', 'CkSum') + ] + ), + ('PKCalg!=1', + [ + ('Error', 'Unknown encryption algorithm in private key') + ] + ) + ], +PUBKEY: [ + ('Byte', 'Version'), + ('Version==2 or Version==3', + [ + ('U32', 'TimeStamp'), + ('U16', 'Validity'), + ('Byte', 'PKCalg'), + ('PKCalg==1', + [ + ('MPI', 'nMPI'), + ('MPI', 'eMPI') + ] + ), + ('PKCalg!=1', + [ + ('Error', 'Unknown encryption algorithm in Public Key packet') + ] + ) + ] + ), + ('Version!=2 and Version!=3', + [ + ('Error', 'Unknown version in Public Key packet') + ] + ) + ], +CIPHERED: [ + ('String', 'Ciphertext') + ], +PLAINTEXT: [ + ('Byte', 'TextMode'), + ('SizedString', 'Filename'), + ('U32', 'TimeStamp'), + ('String', 'Plaintext') + ], +COMPRESSED: [ + ('Byte', 'CompressAlg'), + ('String', 'CompressedData') + ], +TRUST: [ + ('Byte', 'Trust') + ], +USERID: [ + ('String', 'UserID') + ], +COMMENT: [ + ('String', 'Comment') + ] +} diff --git a/Protocol/PGP/pgpsort.py b/Protocol/PGP/pgpsort.py new file mode 100755 index 0000000..cb92cff --- /dev/null +++ b/Protocol/PGP/pgpsort.py @@ -0,0 +1,51 @@ +#!/usr/local/bin/python + +# PGP keyring sorter: +# Usage: pgpsort <keyring file> + +import os, pgp, string, sys +if len(sys.argv)!=2: + print 'Usage: pgpsort keyring' + sys.exit(1) + +filename=sys.argv[1] +if filename[-4:]!='.pgp': filename=filename+'.pgp' + +try: + keyfile=open(sys.argv[1], 'r') +except IOError, value: + print value + print "pgpsort: Can't open "+filename + sys.exit(1) + +sys.stderr.write('Reading...\n') + +keylist=[] +while (1): + key=pgp.Key(keyfile) + sys.stderr.write('['+key.UserID[0][0].UserID+']\n') + data=data[key.PacketLength:] + keylist.append(key) + +def Compare(key1, key2): + id1=string.upper(key1.UserID[0][0].UserID) + id2=string.upper(key2.UserID[0][0].UserID) + if id1<id2: return -1 + elif id1>id2: return 1 + else: return 0 + +backup=filename[:-4]+'.bak' +try: + os.unlink(backup) +except os.error, (value, message): + if value!=2: raise os.error, (value, message) +os.rename(filename, backup) + +sys.stderr.write('Sorting...') +keylist.sort(Compare) +sys.stderr.write('Writing...\n') +f=open(filename, 'wb') +for i in keylist: + f.write(i.Write()) +f.close() + diff --git a/Protocol/PGP/prof.py b/Protocol/PGP/prof.py new file mode 100644 index 0000000..edb38e5 --- /dev/null +++ b/Protocol/PGP/prof.py @@ -0,0 +1,3 @@ +import pstats +p = pstats.Stats('read.profile') +p.sort_stats('cumulative').print_stats() diff --git a/Protocol/PGP/pubkey.py b/Protocol/PGP/pubkey.py new file mode 100644 index 0000000..1c85188 --- /dev/null +++ b/Protocol/PGP/pubkey.py @@ -0,0 +1,39 @@ + +ITER=50 + +def isPrime(N): + N1=N-1 ; exp=N1/2 + minusOneSeen=0 + for i in range(2, ITER+2): + r=pow(i, exp, N) + if r==N1: minusOneSeen=1 + elif r!=1: return 0 + return minusOneSeen + +def findPrime(N): + while not isPrime(N): N=N+2 + return N + +def GCD(x,y): + if x<0: x=-x + if y<0: y=-y + while x>0: x,y = y%x, x + return y + +def Inverse(u, v): + u3, v3 = long(u), long(v) + u1, v1 = 1L, 0L + while v3>0: + q=u3/v3 + u1, v1 = v1, u1-v1*q + u3, v3 = v3, u3-v3*q + return u1 % v + + + + + + + + + diff --git a/Protocol/PGP/pubring.pgp b/Protocol/PGP/pubring.pgp Binary files differnew file mode 100644 index 0000000..a775023 --- /dev/null +++ b/Protocol/PGP/pubring.pgp diff --git a/Protocol/PGP/secring.pgp b/Protocol/PGP/secring.pgp Binary files differnew file mode 100644 index 0000000..bde5625 --- /dev/null +++ b/Protocol/PGP/secring.pgp diff --git a/Protocol/PGP/shelvepgp b/Protocol/PGP/shelvepgp new file mode 100755 index 0000000..74cd2c4 --- /dev/null +++ b/Protocol/PGP/shelvepgp @@ -0,0 +1,35 @@ +#!/usr/local/bin/python +# Hey Emacs, this is -*-Python-*- code! + +# Places PGP keys into a shelved file, where they can be instantly +# retrieved with the KeyID. + +# Usage: shelvepgp <keyring file> + +import pgp, sys +if len(sys.argv)!=2: + print 'Usage: shelvepgp keyring' + sys.exit(1) + +input=pgp.KeyRing() +input.parseKeyRing(open(sys.argv[1], 'r')) + +output=pgp.ShelfKeyRing('keyring.shelve') +for i in input.keys(): + output[i]=input[i] + + + + + + + + + + + + + + + + diff --git a/Protocol/PGP/test.pgp b/Protocol/PGP/test.pgp Binary files differnew file mode 100644 index 0000000..4a698a6 --- /dev/null +++ b/Protocol/PGP/test.pgp diff --git a/Protocol/PGP/test.py b/Protocol/PGP/test.py new file mode 100644 index 0000000..4109d4a --- /dev/null +++ b/Protocol/PGP/test.py @@ -0,0 +1,79 @@ +#!/usr/local/bin/python + +import sys, pgp + +abcID=0x57FBBFB1L ; amkID=0x0E304991L +abcPass='secret' ; amkPass='super secret' + +def testKeyGeneration(): + key=pgp.PrivateKey() + key.generate(pgp.PK_RSA, 1024) + key.Lock('super') + newID=pgp.UserID() + newID.UserID='Python' + key.IDList.append(newID) + trust=pgp.Trust() + # Write the secret keyring + f=open('secring.pgp', 'a') + f.write(key.binary()) + f.write(newID.binary()) + f.close() + # Write the public keyring, with trust packets + f=open('pubring.pgp', 'a') + f.write(key.public_binary()) + f.write(trust.binary()) + f.write(newID.binary()) + f.write(trust.binary()) + f.close() + +def testMessaging(): + pass + +def getpubkeys(): + pubring=pgp.KeyRing() + pubring.parseKeyRing(open('./pubring.pgp', 'r')) + pub_abc=pubring[abcID] + pub_amk=pubring[amkID] + return pub_abc, pub_amk + +def getseckeys(): + secring=pgp.KeyRing() + secring.parseKeyRing(open('./secring.pgp', 'r')) + sec_abc=secring[abcID] + sec_amk=secring[amkID] + return sec_abc, sec_amk + +sec_abc, sec_amk = getseckeys() +pub_abc, pub_amk = getpubkeys() + +print 'amk=', hex(sec_amk.KeyID) +print 'abc=', hex(sec_abc.KeyID) + +f=open('books.txt', 'r') ; message=f.read() ; f.close() + +import md5 +DEK=md5.new('testp').digest() +sec_amk.Unlock(amkPass) +output=pgp.EncryptMessage(message, recipients=[pub_amk,pub_abc], + signer=sec_amk ) + +f=open('books.txt.pgp', 'w') ; f.write(output) ; f.close() + +sec_amk.Unlock(amkPass) +sec_abc.Unlock(abcPass) +for sig in pub_abc.IDList[0].SigList: + if sig.SigKeyID==pub_amk.KeyID: + print sig.verify_key_sig(pub_amk, pub_abc, u) + +print "abc's key is self signed:", pub_abc.is_self_signed() +print "amk's key is self signed:", pub_amk.is_self_signed() + +# Test the Lock() method of key objects +L=[] ; L2=[] +for i in 'ednpqu': L.append(getattr(sec_amk, i)) +sec_amk.Lock(amkPass) +sec_amk.Unlock(amkPass) +for i in 'ednpqu': L2.append(getattr(sec_amk, i)) +if L!=L2: + print "ERROR: key data doesn't match after Lock() and Unlock()" +print L==L2 diff --git a/Protocol/PGP/testpub.pgp b/Protocol/PGP/testpub.pgp Binary files differnew file mode 100644 index 0000000..1cbcc66 --- /dev/null +++ b/Protocol/PGP/testpub.pgp diff --git a/Protocol/PGP/testsec.pgp b/Protocol/PGP/testsec.pgp Binary files differnew file mode 100644 index 0000000..2139b5a --- /dev/null +++ b/Protocol/PGP/testsec.pgp diff --git a/Protocol/__init__.py b/Protocol/__init__.py new file mode 100644 index 0000000..1d027cd --- /dev/null +++ b/Protocol/__init__.py @@ -0,0 +1,2 @@ + +__all__ = ['AllOrNothing', 'Chaffing', 'Winnow'] diff --git a/PublicKey/DSA.py b/PublicKey/DSA.py new file mode 100644 index 0000000..ff364a3 --- /dev/null +++ b/PublicKey/DSA.py @@ -0,0 +1,132 @@ + +# +# DSA.py : Digital Signature Algorithm +# +# Part of the Python Cryptography Toolkit, version 1.1 +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +from pubkey import * +from Crypto.Hash import SHA + +error = 'DSA module' + +def generateQ(randfunc): + S=randfunc(20) + hash1=SHA.new(S).digest() + hash2=SHA.new(longtobytes(bytestolong(S)+1)).digest() + q = bignum(0) + for i in range(0,20): + c=ord(hash1[i])^ord(hash2[i]) + if i==0: c=c | 128 + if i==19: c= c | 1 + q=q*256+c + while (not isPrime(q)): + q=q+2 + if pow(2,159L)<q<pow(2,160L): return S, q + raise error, 'Bad q value generated' + +# Generate a DSA modulus with L bits +def generate(bits, randfunc, progress_func=None): + if bits<160: raise error, 'Key length <160 bits' + obj=DSAobj() + # Generate string S and prime q + if progress_func: apply(progress_func, ('p,q\n',)) + while (1): + S, obj.q = generateQ(randfunc) + n=(bits-1)/160 + C, N, V = 0, 2, {} + b=(obj.q >> 5) & 15 + powb=pow(bignum(2), b) + powL1=pow(bignum(2), bits-1) + while C<4096: + for k in range(0, n+1): + V[k]=bytestolong(SHA.new(S+str(N)+str(k)).digest()) + W=V[n] % powb + for k in range(n-1, -1, -1): W=(W<<160L)+V[k] + X=W+powL1 + p=X-(X%(2*obj.q)-1) + if powL1<=p and isPrime(p): break + C, N = C+1, N+n+1 + if C<4096: break + if progress_func: apply(progress_func, ('4096 multiples failed\n',) ) + obj.p = p + power=(p-1)/obj.q + if progress_func: apply(progress_func, ('h,g\n',)) + while (1): + h=bytestolong(randfunc(bits)) % (p-1) + g=pow(h, power, p) + if 1<h<p-1 and g>1: break + obj.g=g + if progress_func: apply(progress_func, ('x,y\n',)) + while (1): + x=bytestolong(randfunc(20)) + if 0<x<obj.q: break + obj.x, obj.y=x, pow(g, x, p) + return obj + +def construct(tuple): + obj=DSAobj() + if len(tuple) not in [4,5]: + raise error, 'argument for construct() wrong length' + for i in range(len(tuple)): + field = obj.keydata[i] + setattr(obj, field, tuple[i]) + return obj + +class DSAobj(pubkey): + keydata=['y', 'g', 'p', 'q', 'x'] + + def _encrypt(self, s, Kstr): + raise error, 'Algorithm cannot en/decrypt data' + def _decrypt(self, s): + raise error, 'Algorithm cannot en/decrypt data' + def _sign(self, M, K): + if (K<2 or self.q<=K): raise error, 'K is not between 2 and q' + r=pow(self.g, K, self.p) % self.q + s=(inverse(K, self.q)*(M+self.x*r)) % self.q + return (r,s) + + def _verify(self, M, sig): + r, s = sig + if r<=0 or r>=self.q or s<=0 or s>=self.q: return 0 + w=inverse(s, self.q) + u1, u2 = (M*w) % self.q, (r*w) % self.q + v1=pow(self.g, u1, self.p) + v2=pow(self.y, u2, self.p) + v=((v1*v2) % self.p) + v=v % self.q + if v==r: return 1 + return 0 + + def size(self): + "Return the maximum number of bits that can be handled by this key." + bits, power = 0,1L + while (power<self.p): bits, power = bits+1, power<<1 + return bits-1 + + def hasprivate(self): + """Return a Boolean denoting whether the object contains + private components.""" + if hasattr(self, 'x'): return 1 + else: return 0 + + def cansign(self): + """Return a Boolean value recording whether this algorithm can generate signatures.""" + return 1 + def canencrypt(self): + """Return a Boolean value recording whether this algorithm can encrypt data.""" + return 0 + + def publickey(self): + """Return a new key object containing only the public information.""" + return construct((self.y, self.g, self.p, self.q)) + +object=DSAobj + + diff --git a/PublicKey/ElGamal.py b/PublicKey/ElGamal.py new file mode 100644 index 0000000..1802b4a --- /dev/null +++ b/PublicKey/ElGamal.py @@ -0,0 +1,103 @@ +# +# ElGamal.py : ElGamal encryption/decryption and signatures +# +# Part of the Python Cryptography Toolkit, version 1.1 +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +from pubkey import * + +error = 'ElGamal module' + +# Generate an ElGamal key with N bits +def generate(bits, randfunc, progress_func=None): + obj=ElGamalobj() + # Generate prime p + if progress_func: apply(progress_func, ('p\n',)) + obj.p=bignum(getPrime(bits, randfunc)) + # Generate random number g + if progress_func: apply(progress_func, ('g\n',)) + size=bits-1-(ord(randfunc(1)) & 63) # g will be from 1--64 bits smaller than p + if size<1: size=bits-1 + while (1): + obj.g=bignum(getPrime(size, randfunc)) + if obj.g<obj.p: break + size=(size+1) % bits + if size==0: size=4 + # Generate random number x + if progress_func: apply(progress_func, ('x\n',)) + while (1): + size=bits-1-ord(randfunc(1)) # x will be from 1 to 256 bits smaller than p + if size>2: break + while (1): + obj.x=bignum(getPrime(size, randfunc)) + if obj.x<obj.p: break + size=(size+1) % bits + if size==0: size=4 + if progress_func: apply(progress_func, ('y\n',)) + obj.y=pow(obj.g, obj.x, obj.p) + return obj + +def construct(tuple): + obj=ElGamalobj() + if len(tuple) not in [3,4]: + raise error, 'argument for construct() wrong length' + for i in range(len(tuple)): + field = obj.keydata[i] + setattr(obj, field, tuple[i]) + return obj + +class ElGamalobj(pubkey): + keydata=['p', 'g', 'y', 'x'] + + def _encrypt(self, M, K): + a=pow(self.g, K, self.p) + b=( M*pow(self.y, K, self.p) ) % self.p + return ( a,b ) + def _decrypt(self, M): + if (not hasattr(self, 'x')): + raise error, 'Private key not available in this object' + ax=pow(M[0], self.x, self.p) + plaintext=(M[1] * inverse(ax, self.p ) ) % self.p + return plaintext + def _sign(self, M, K): + if (not hasattr(self, 'x')): + raise error, 'Private key not available in this object' + p1=self.p-1 + if (GCD(K, p1)!=1): + raise error, 'Bad K value: GCD(K,p-1)!=1' + a=pow(self.g, K, self.p) + t=(M-self.x*a) % p1 + while t<0: t=t+p1 + b=(t*inverse(K, p1)) % p1 + return (a, b) + def _verify(self, M, sig): + v1=pow(self.y, sig[0], self.p) + v1=(v1*pow(sig[0], sig[1], self.p)) % self.p + v2=pow(self.g, M, self.p) + if v1==v2: return 1 + return 0 + + def size(self): + "Return the maximum number of bits that can be handled by this key." + bits, power = 0,1L + while (power<self.p): bits, power = bits+1, power<<1 + return bits-1 + + def hasprivate(self): + """Return a Boolean denoting whether the object contains + private components.""" + if hasattr(self, 'x'): return 1 + else: return 0 + + def publickey(self): + """Return a new key object containing only the public information.""" + return construct((self.p, self.g, self.y)) + + +object=ElGamalobj diff --git a/PublicKey/RSA.py b/PublicKey/RSA.py new file mode 100644 index 0000000..f1532df --- /dev/null +++ b/PublicKey/RSA.py @@ -0,0 +1,85 @@ +# +# RSA.py : RSA encryption/decryption +# +# Part of the Python Cryptography Toolkit, version 1.1 +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +import pubkey + +error = 'RSA module' + +# Generate an RSA key with N bits +def generate(bits, randfunc, progress_func=None): + obj=RSAobj() + # Generate random number from 0 to 7 + difference=ord(randfunc(1)) & 7 + # Generate the prime factors of n + if progress_func: apply(progress_func, ('p\n',)) + obj.p=pubkey.getPrime(bits/2, randfunc) + if progress_func: apply(progress_func, ('q\n',)) + obj.q=pubkey.getPrime((bits/2)+difference, randfunc) + obj.n=obj.p*obj.q + # Generate encryption exponent + if progress_func: apply(progress_func, ('e\n',)) + obj.e=pubkey.getPrime(17, randfunc) + if progress_func: apply(progress_func, ('d\n',)) + obj.d=pubkey.inverse(obj.e, (obj.p-1)*(obj.q-1)) + return obj + +# Construct an RSA object +def construct(tuple): + obj=RSAobj() + if len(tuple) not in [2,3,5]: + raise error, 'argument for construct() wrong length' + for i in range(len(tuple)): + field = obj.keydata[i] + setattr(obj, field, tuple[i]) + return obj + +class RSAobj(pubkey.pubkey): + keydata=['n', 'e', 'd', 'p','q'] + def _encrypt(self, plaintext, K=''): + if self.n<=plaintext: + raise error, 'Plaintext too large' + return (pow(plaintext, self.e, self.n),) + + def _decrypt(self, ciphertext): + if (not hasattr(self, 'd')): + raise error, 'Private key not available in this object' + if self.n<=ciphertext[0]: + raise error, 'Ciphertext too large' + return pow(ciphertext[0], self.d, self.n) + + def _sign(self, M, K=''): + return (self._decrypt((M,)),) + def _verify(self, M, sig): + m2=self._encrypt(sig[0]) + if m2[0]==M: return 1 + else: return 0 + + def size(self): + "Return the maximum number of bits that can be handled by this key." + bits, power = 0,1L + while (power<self.n): bits, power = bits+1, power<<1 + return bits-1 + + def hasprivate(self): + """Return a Boolean denoting whether the object contains + private components.""" + if hasattr(self, 'd'): return 1 + else: return 0 + + def publickey(self): + """Return a new key object containing only the public information.""" + return construct((self.n, self.e)) + + +object = RSAobj + + diff --git a/PublicKey/__init__.py b/PublicKey/__init__.py new file mode 100644 index 0000000..0556e26 --- /dev/null +++ b/PublicKey/__init__.py @@ -0,0 +1,3 @@ + +__all__ = ['RSA', 'DSA', 'ElGamal', 'qNEW'] + diff --git a/PublicKey/pubkey.py b/PublicKey/pubkey.py new file mode 100644 index 0000000..dba131c --- /dev/null +++ b/PublicKey/pubkey.py @@ -0,0 +1,103 @@ +# +# pubkey.py : Internal functions for public key operations +# +# Part of the Python Cryptography Toolkit, version 1.1 +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +from Crypto.Util.number import * + +# Basic public key class +import types +class pubkey: + def __init__(self): + pass + def __getstate__(self): + """To keep key objects platform-independent, the key data is + converted to standard Python long integers before being + written out. It will then be reconverted as necessary on + restoration.""" + d=self.__dict__ + for key in self.keydata: + if d.has_key(key): d[key]=long(d[key]) + return d + + def __setstate__(self, d): + """On unpickling a key object, the key data is converted to the big +number representation being used, whether that is Python long +integers, MPZ objects, or whatever.""" + for key in self.keydata: + if d.has_key(key): self.__dict__[key]=bignum(d[key]) + + def encrypt(self, plaintext, K): + """Encrypt the string or integer plaintext. K is a random + parameter required by some algorithms.""" + wasString=0 + if type(plaintext)==types.StringType: + plaintext=bytestolong(plaintext) ; wasString=1 + if type(K)==types.StringType: + K=bytestolong(K) + ciphertext=self._encrypt(plaintext, K) + if wasString: return tuple(map(longtobytes, ciphertext)) + else: return ciphertext + + def decrypt(self, ciphertext): + """Decrypt the string or integer ciphertext.""" + wasString=0 + if type(ciphertext)!=types.TupleType: + ciphertext=(ciphertext,) + if types.StringType in map(type, ciphertext): + ciphertext=tuple(map(bytestolong, ciphertext)) ; wasString=1 + plaintext=self._decrypt(ciphertext) + if wasString: return longtobytes(plaintext) + else: return plaintext + + def sign(self, M, K): + """Return a tuple containing the signature for the message M. K is a random + parameter required by some algorithms.""" + if (not self.hasprivate()): + raise error, 'Private key not available in this object' + if type(M)==types.StringType: M=bytestolong(M) + if type(K)==types.StringType: K=bytestolong(K) + return self._sign(M, K) + + def verify(self, M, signature): + """Verify that the signature is valid for the message M; + returns true if the signature checks out.""" + if type(M)==types.StringType: M=bytestolong(M) + return self._verify(M, signature) + + validate = verify # alias to compensate for the old validate() name + + # The following methods will usually be left alone, except for + # signature-only algorithms. They both return Boolean values + # recording whether this key's algorithm can sign and encrypt. + def cansign(self): + """Return a Boolean value recording whether this algorithm can generate signatures.""" + return 1 + def canencrypt(self): + """Return a Boolean value recording whether this algorithm can encrypt data.""" + return 1 + + # The following methods will certainly be overridden by + # subclasses. + + def size(self): + "Return the maximum number of bits that can be handled by this key." + return 0 + + def hasprivate(self): + """Return a Boolean denoting whether the object contains + private components.""" + return 0 + + def publickey(self): + """Return a new key object containing only the public information.""" + return self + + diff --git a/PublicKey/qNEW.py b/PublicKey/qNEW.py new file mode 100644 index 0000000..dc89153 --- /dev/null +++ b/PublicKey/qNEW.py @@ -0,0 +1,146 @@ +# +# qNEW.py : The q-NEW signature algorithm. +# +# Part of the Python Cryptography Toolkit, version 1.1 +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +import pubkey +from Crypto.Util.number import * +import Crypto.Hash.SHA + +error = 'qNEW module' +HASHBITS = 160 # Size of SHA digests + +# Generate an qNEW key with N bits +def generate(bits, randfunc, progress_func=None): + obj=qNEWobj() + + # Generate prime numbers p and q. q is a 160-bit prime + # number. p is another prime number (the modulus) whose bit + # size is chosen by the caller, and is generated so that p-1 + # is a multiple of q. + # + # Note that only a single seed is used to + # generate p and q; if someone generates a key for you, you can + # use the seed to duplicate the key generation. This can + # protect you from someone generating values of p,q that have + # some special form that's easy to break. + if progress_func: progress_func('p,q\n') + while (1): + obj.q = getPrime(160, randfunc) + # assert pow(2, 159L)<obj.q<pow(2, 160L) + obj.seed = S = longtobytes(obj.q) + C, N, V = 0, 2, {} + # Compute b and n such that bits-1 = b + n*HASHBITS + n= (bits-1) / HASHBITS + b= (bits-1) % HASHBITS ; powb=pow(long(2), b) + powL1=pow(long(2), bits-1) + while C<4096: + # The V array will contain (bits-1) bits of random + # data, that are assembled to produce a candidate + # value for p. + for k in range(0, n+1): + V[k]=bytestolong(Crypto.Hash.SHA.new(S+str(N)+str(k)).digest()) + p = V[n] % powb + for k in range(n-1, -1, -1): + p= (p << long(HASHBITS) )+V[k] + p = p+powL1 # Ensure the high bit is set + + # Ensure that p-1 is a multiple of q + p = p - (p % (2*obj.q)-1) + + # If p is still the right size, and it's prime, we're done! + if powL1<=p and isPrime(p): break + + # Otherwise, increment the counter and try again + C, N = C+1, N+n+1 + if C<4096: break # Ended early, so exit the while loop + if progress_func: progress_func('4096 values of p tried\n') + + obj.p = p + power=(p-1)/obj.q + + # Next parameter: g = h**((p-1)/q) mod p, such that h is any + # number <p-1, and g>1. g is kept; h can be discarded. + if progress_func: progress_func('h,g\n') + while (1): + h=bytestolong(randfunc(bits)) % (p-1) + g=pow(h, power, p) + if 1<h<p-1 and g>1: break + obj.g=g + + # x is the private key information, and is + # just a random number between 0 and q. + # y=g**x mod p, and is part of the public information. + if progress_func: progress_func('x,y\n') + while (1): + x=bytestolong(randfunc(20)) + if 0<x<obj.q: break + obj.x, obj.y=x, pow(g, x, p) + + return obj + +# Construct an qNEW object +def construct(tuple): + obj=qNEWobj() + if len(tuple) not in [4,5]: + raise error, 'argument for construct() wrong length' + for i in range(len(tuple)): + field = obj.keydata[i] + setattr(obj, field, tuple[i]) + return obj + +class qNEWobj(pubkey.pubkey): + keydata=['p', 'q', 'g', 'y', 'x'] + + def _sign(self, M, K=''): + if (self.q<=K): + raise error, 'K is greater than q' + if M<0: + raise error, 'Illegal value of M (<0)' + if M>=pow(2,161L): + raise error, 'Illegal value of M (too large)' + r=pow(self.g, K, self.p) % self.q + s=(K- (r*M*self.x % self.q)) % self.q + return (r,s) + def _verify(self, M, sig): + r, s = sig + if r<=0 or r>=self.q or s<=0 or s>=self.q: return 0 + if M<0: + raise error, 'Illegal value of M (<0)' + if M<=0 or M>=pow(2,161L): return 0 + v1=pow(self.g, s, self.p) + v2=pow(self.y, M*r, self.p) + v=((v1*v2) % self.p) + v=v % self.q + if v==r: return 1 + return 0 + + def size(self): + "Return the maximum number of bits that can be handled by this key." + return 160 + + def hasprivate(self): + """Return a Boolean denoting whether the object contains + private components.""" + return hasattr(self, 'x') + + def cansign(self): + """Return a Boolean value recording whether this algorithm can generate signatures.""" + return 1 + def canencrypt(self): + """Return a Boolean value recording whether this algorithm can encrypt data.""" + return 0 + + def publickey(self): + """Return a new key object containing only the public information.""" + return construct((self.p, self.q, self.g, self.y)) + +object = qNEWobj + @@ -0,0 +1,86 @@ + + This is version 1.1 of the Python cryptography modules, a +collection of cryptographic code for the Python programming language. + + This is a collection of both secure hash functions (such as MD5 +and SHA), and various encryption algorithms (IDEA, DES, RSA, ElGamal, +etc.). The package is structured to make adding new modules easy. I +consider this section to be essentially complete, and the software +interface will almost certainly not change in an incompatible way in +the future; all that remains to be done is to fix any bugs that show +up. If you encounter a bug, please inform me immediately. If you +implement a new algorithm, please send me a copy. + + A sample usage of the MD5 module is: +>>> from Crypto.Hash import MD5 +>>> hash=MD5.new() +>>> hash.update(message) +>>> hash.digest() +'\235\361\034\357\217MX\2246\226\367\366Ebx\326' + + A sample use of an encryption algorithm (IDEA, in this case) is: +>>> from Crypto.Cipher import IDEA +>>> obj=IDEA.new('This is a key456', IDEA.ECB) +>>> message="The answer is no" +>>> ciphertext=obj.encrypt(message) +>>> ciphertext +'\2325$\343=)d\341^\025<\344\013\204 T' +>>> obj.decrypt(ciphertext) +'The answer is no' + + One possible application of the modules is writing secure +administration tools. Another application is in writing daemons and +servers. Clients and servers can encrypt the data being exchanged and +mutually authenticate themselves; daemons can encrypt private data for +added security. Python also provides a pleasant framework for +prototyping and experimentation with cryptographic algorithms; thanks +to its arbitrary-length integers, public key algorithms are easily +implemented. + + For discussion about using Python for cryptography-related +tasks, join the python-crypto mailing list. Point your Web browser to +<URL:http://www.findmail.com/list/python-crypto/> and fill out the +form, or send a message with "subscribe" in the Subject: line to +python-crypto-request@makelist.com . + +TO INSTALL: + + You must already have Python installed to build the +cryptography modules since the buildkit script (which the Makefile +uses) is written in Python. Be sure you ran the "libainstall" and +"inclinstall" targets when you built Python, which install various +libraries and *.h files needed to compile modified versions of the +Python interpreter. + + Once Python has been built and installed, this package can be +compiled. Simply run "make" in the same directory as this file. The +default setup is to compile the modules so they can be dynamically +linked into the Python interpreter when they're imported; edit +Setup.in and/or Makefile.pre.in if desired. If you installed Python +in a non-standard location, you might want to call make like so: + + make -k installdir=/path/to/exec/prefix/when/i/installed/Python + + To verify that everything is in order, run "make test". It +will test all the cryptographic modules, skipping ones that aren't +available. If the test script reports an error on your machine, +please inform me immediately, because that means there's a serious bug +somewhere in the cryptographic routines. (Alternatively, track down +the bug and send me a patch.) + + Running test.py without the --quiet argument will let you see +what it's doing, and will also run a crude benchmark to give you an +impression of the comparative speeds of the algorithms and feedback +modes. + + To install the package under the site-packages directory of +your Python installation, run "make install" once you've run "make +test" successfully. + + If you have any comments, corrections, or improvements for +this package, please e-mail me at the address below, or send it to the +python-crypto mailing list at python-crypto@makelist.com . Good luck! + + + Andrew Kuchling + akuchling@acm.org diff --git a/Setup.in b/Setup.in new file mode 100644 index 0000000..cd2d941 --- /dev/null +++ b/Setup.in @@ -0,0 +1,30 @@ + +*shared* + +# Hash algorithms + +MD2 MD2module.c # MD2 +MD4 MD4module.c # MD4 +MD5 MD5module.c # MD5 +RIPEMD RIPEMDmodule.c # RIPEMD +SHA SHAmodule.c # SHA +HAVAL HAVALmodule.c # HAVAL + +# Block encryption algorithms +ARC2 ARC2module.c +Blowfish Blowfishmodule.c +CAST CASTmodule.c +DES DESmodule.c +DES3 DES3module.c +Diamond Diamondmodule.c +IDEA IDEAmodule.c +RC5 RC5module.c +Skipjack Skipjackmodule.c + +# Stream encryption algorithms + +ARC4 ARC4module.c +Sapphire Sapphiremodule.c + + + diff --git a/Setup.in-export b/Setup.in-export new file mode 100644 index 0000000..4c2eca4 --- /dev/null +++ b/Setup.in-export @@ -0,0 +1,15 @@ + +*shared* + +# Hash algorithms + +MD2 MD2module.c # MD2 +MD4 MD4module.c # MD4 +MD5 MD5module.c # MD5 +RIPEMD RIPEMDmodule.c # RIPEMD +SHA SHAmodule.c # SHA +HAVAL HAVALmodule.c # HAVAL + + + + @@ -0,0 +1,23 @@ + +Core algorithms: + Elliptic-curve algs should be added; they're not a priority for now. + A secret sharing module should be added to Util or Protocols. + +Protocols: + Implement ssh + Add PGP modules to the package, rearranging them to fit. + +Generic cryptographic stuff: + Optimize the feedback modes a bit + +Demo programs: + Shadowing of files into X parts + Secure talk or telnet + +Documentation: + Document chaff/winnow better + Add docstrings everywhere. + +Config stuff: + Smarter distribution building + diff --git a/Util/RFC1751.py b/Util/RFC1751.py new file mode 100644 index 0000000..e3b059b --- /dev/null +++ b/Util/RFC1751.py @@ -0,0 +1,340 @@ +#!/usr/local/bin/python +# rfc1751.py : Converts between 128-bit strings and a human-readable +# sequence of words, as defined in RFC1751: "A Convention for +# Human-Readable 128-bit Keys", by Daniel L. McDonald. + +error = 'rfc1751 error' +import string + +binary={0:'0000', 1:'0001', 2:'0010', 3:'0011', 4:'0100', 5:'0101', + 6:'0110', 7:'0111', 8:'1000', 9:'1001', 10:'1010', 11:'1011', + 12:'1100', 13:'1101', 14:'1110', 15:'1111'} + +def key2bin(s): + "Convert a key into a string of binary digits" + kl=map(lambda x: ord(x), s) + kl=map(lambda x: binary[x/16]+binary[x&15], kl) + return reduce(lambda x,y: x+y, kl, '') + +def extract(key, start, length): + """Extract a bitstring from a string of binary digits, and return its + numeric value.""" + k=key[start:start+length] + return reduce(lambda x,y: x*2+ord(y)-48, k, 0) + +def Key2English(key): + """Transform an arbitrary key into a string containing English words. + The key length must be a multiple of 8.""" + english='' + for index in range(0, len(key), 8): # Loop over 8-byte subkeys + subkey=key[index:index+8] + # Compute the parity of the key + skbin=key2bin(subkey) ; p=0 + for i in range(0, 64, 2): p=p+extract(skbin, i, 2) + # Append parity bits to the subkey + skbin=key2bin(subkey+chr((p<<6) & 255)) + for i in range(0, 64, 11): + english=english+wordlist[extract(skbin, i, 11)]+' ' + return english[:-1] # Remove the trailing space + +def English2Key(seq): + """Transform a string into a corresponding key. + The string must contain words separated by whitespace; the number + of words must be a multiple of 6.""" + L=string.split(string.upper(seq)) ; key='' + for index in range(0, len(L), 6): + sublist=L[index:index+6] ; char=9*[0] ; bits=0 + for i in sublist: + index=wordlist.index(i) + shift=(8-(bits+11)%8) %8 + y=index<<shift + cl, cc, cr = (y>>16), (y>>8)&0xff, y & 0xff + if (shift>5): + char[bits/8] = char[bits/8] | cl + char[bits/8+1] = char[bits/8+1] | cc + char[bits/8+2] = char[bits/8+2] | cr + elif shift>-3: + char[bits/8] = char[bits/8] | cc + char[bits/8+1] = char[bits/8+1] | cr + else: char[bits/8] = char[bits/8] | cr + bits=bits+11 + subkey=reduce(lambda x,y:x+chr(y), char, '') + + # Check the parity of the resulting key + skbin=key2bin(subkey) + p=0 + for i in range(0, 64, 2): p=p+extract(skbin, i, 2) + if (p&3) != extract(skbin, 64, 2): + raise error, "Parity error in resulting key" + key=key+subkey[0:8] + return key + +wordlist=[ "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", + "AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN", "ANA", + "AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK", + "ARM", "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", "AVE", + "AWE", "AWK", "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM", + "BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", "BET", + "BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO", + "BOP", "BOW", "BOY", "BUB", "BUD", "BUG", "BUM", "BUN", "BUS", "BUT", + "BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT", + "CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", "COY", + "CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN", + "DAR", "DAY", "DEE", "DEL", "DEN", "DES", "DEW", "DID", "DIE", "DIG", + "DIN", "DIP", "DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB", + "DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", "EGG", "EGO", + "ELI", "ELK", "ELM", "ELY", "EM", "END", "EST", "ETC", "EVA", "EVE", + "EWE", "EYE", "FAD", "FAN", "FAR", "FAT", "FAY", "FED", "FEE", "FEW", + "FIB", "FIG", "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", + "FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM", "GAP", + "GAS", "GAY", "GEE", "GEL", "GEM", "GET", "GIG", "GIL", "GIN", "GO", + "GOT", "GUM", "GUN", "GUS", "GUT", "GUY", "GYM", "GYP", "HA", "HAD", + "HAL", "HAM", "HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM", + "HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", "HIS", "HIT", + "HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT", "HOW", "HUB", "HUE", + "HUG", "HUH", "HUM", "HUT", "I", "ICY", "IDA", "IF", "IKE", "ILL", + "INK", "INN", "IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", + "ITS", "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", "JAY", "JET", + "JIG", "JIM", "JO", "JOB", "JOE", "JOG", "JOT", "JOY", "JUG", "JUT", + "KAY", "KEG", "KEN", "KEY", "KID", "KIM", "KIN", "KIT", "LA", "LAB", + "LAC", "LAD", "LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", + "LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", "LIP", "LIT", + "LO", "LOB", "LOG", "LOP", "LOS", "LOT", "LOU", "LOW", "LOY", "LUG", + "LYE", "MA", "MAC", "MAD", "MAE", "MAN", "MAO", "MAP", "MAT", "MAW", + "MAY", "ME", "MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT", + "MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD", "MUG", + "MUM", "MY", "NAB", "NAG", "NAN", "NAP", "NAT", "NAY", "NE", "NED", + "NEE", "NET", "NEW", "NIB", "NIL", "NIP", "NIT", "NO", "NOB", "NOD", + "NON", "NOR", "NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF", + "OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", "OH", "OIL", + "OK", "OLD", "ON", "ONE", "OR", "ORB", "ORE", "ORR", "OS", "OTT", + "OUR", "OUT", "OVA", "OW", "OWE", "OWL", "OWN", "OX", "PA", "PAD", + "PAL", "PAM", "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG", + "PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN", "PIT", + "PLY", "PO", "POD", "POE", "POP", "POT", "POW", "PRO", "PRY", "PUB", + "PUG", "PUN", "PUP", "PUT", "QUO", "RAG", "RAM", "RAN", "RAP", "RAT", + "RAW", "RAY", "REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM", + "RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY", "RUB", + "RUE", "RUG", "RUM", "RUN", "RYE", "SAC", "SAD", "SAG", "SAL", "SAM", + "SAN", "SAP", "SAT", "SAW", "SAY", "SEA", "SEC", "SEE", "SEN", "SET", + "SEW", "SHE", "SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", + "SLY", "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", "SPA", "SPY", + "SUB", "SUD", "SUE", "SUM", "SUN", "SUP", "TAB", "TAD", "TAG", "TAN", + "TAP", "TAR", "TEA", "TED", "TEE", "TEN", "THE", "THY", "TIC", "TIE", + "TIM", "TIN", "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", + "TOW", "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", "UN", "UP", + "US", "USE", "VAN", "VAT", "VET", "VIE", "WAD", "WAG", "WAR", "WAS", + "WAY", "WE", "WEB", "WED", "WEE", "WET", "WHO", "WHY", "WIN", "WIT", + "WOK", "WON", "WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE", + "YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", "ABUT", + "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", "ADDS", + "ADEN", "AFAR", "AFRO", "AGEE", "AHEM", "AHOY", "AIDA", "AIDE", + "AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", "ALIA", + "ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", "AMEN", + "AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", "ANEW", + "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", "AREA", + "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", "ATOM", + "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", "AVOW", + "AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", "BAIL", + "BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", "BALM", + "BAND", "BANE", "BANG", "BANK", "BARB", "BARD", "BARE", "BARK", + "BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", "BATH", + "BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", "BEAT", + "BEAU", "BECK", "BEEF", "BEEN", "BEER", + "BEET", "BELA", "BELL", "BELT", "BEND", "BENT", "BERG", "BERN", + "BERT", "BESS", "BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE", + "BIEN", "BILE", "BILK", "BILL", "BIND", "BING", "BIRD", "BITE", + "BITS", "BLAB", "BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT", + "BLOW", "BLUE", "BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK", + "BODE", "BODY", "BOGY", "BOHR", "BOIL", "BOLD", "BOLO", "BOLT", + "BOMB", "BONA", "BOND", "BONE", "BONG", "BONN", "BONY", "BOOK", + "BOOM", "BOON", "BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS", + "BOTH", "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN", + "BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", + "BUFF", "BULB", "BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG", + "BURL", "BURN", "BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST", + "BUSY", "BYTE", "CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF", + "CALL", "CALM", "CAME", "CANE", "CANT", "CARD", "CARE", "CARL", + "CARR", "CART", "CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL", + "CELL", "CENT", "CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF", + "CHEN", "CHEW", "CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG", + "CHUM", "CITE", "CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY", + "CLOD", "CLOG", "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", + "COCK", "COCO", "CODA", "CODE", "CODY", "COED", "COIL", "COIN", + "COKE", "COLA", "COLD", "COLT", "COMA", "COMB", "COME", "COOK", + "COOL", "COON", "COOT", "CORD", "CORE", "CORK", "CORN", "COST", + "COVE", "COWL", "CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB", + "CROW", "CRUD", "CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY", + "CURB", "CURD", "CURE", "CURL", "CURT", "CUTS", "DADE", "DALE", + "DAME", "DANA", "DANE", "DANG", "DANK", "DARE", "DARK", "DARN", + "DART", "DASH", "DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS", + "DEAD", "DEAF", "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", + "DEEM", "DEER", "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", + "DIAL", "DICE", "DIED", "DIET", "DIME", "DINE", "DING", "DINT", + "DIRE", "DIRT", "DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES", + "DOLE", "DOLL", "DOLT", "DOME", "DONE", "DOOM", "DOOR", "DORA", + "DOSE", "DOTE", "DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG", + "DRAM", "DRAW", "DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK", + "DUCT", "DUEL", "DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK", + "DUSK", "DUST", "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", + "EASY", "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT", + "EDNA", "EGAN", "ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", + "EMMA", "ENDS", "ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED", + "FACE", "FACT", "FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL", + "FAME", "FANG", "FARM", "FAST", "FATE", "FAWN", "FEAR", "FEAT", + "FEED", "FEEL", "FEET", "FELL", "FELT", "FEND", "FERN", "FEST", + "FEUD", "FIEF", "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", + "FINK", "FIRE", "FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE", + "FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW", + "FLIT", "FLOC", "FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM", + "FOGY", "FOIL", "FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL", + "FOOT", "FORD", "FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL", + "FOUR", "FOWL", "FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY", + "FROG", "FROM", "FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY", + "FUSE", "FUSS", "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA", + "GALE", "GALL", "GALT", "GAME", "GANG", "GARB", "GARY", "GASH", + "GATE", "GAUL", "GAUR", "GAVE", "GAWK", "GEAR", "GELD", "GENE", + "GENT", "GERM", "GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT", + "GINA", "GIRD", "GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN", + "GLIB", "GLOB", "GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", + "GOAL", "GOAT", "GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", + "GOOD", "GOOF", "GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB", + "GRAD", "GRAY", "GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN", + "GRIT", "GROW", "GRUB", "GULF", "GULL", "GUNK", "GURU", "GUSH", + "GUST", "GWEN", "GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR", + "HALE", "HALF", "HALL", "HALO", "HALT", "HAND", "HANG", "HANK", + "HANS", "HARD", "HARK", "HARM", "HART", "HASH", "HAST", "HATE", + "HATH", "HAUL", "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", + "HEAT", "HEBE", "HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL", + "HELM", "HERB", "HERD", "HERE", "HERO", "HERS", "HESS", "HEWN", + "HICK", "HIDE", "HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT", + "HIRE", "HISS", "HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE", + "HOLM", "HOLT", "HOME", "HONE", "HONK", "HOOD", "HOOF", "HOOK", + "HOOT", "HORN", "HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL", + "HOYT", "HUCK", "HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK", + "HULL", "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE", + "HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", + "INTO", "IONS", "IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE", + "ITCH", "ITEM", "IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE", + "JAVA", "JEAN", "JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL", + "JILT", "JIVE", "JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", + "JOIN", "JOKE", "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY", + "JUJU", "JUKE", "JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST", + "JUTE", "KAHN", "KALE", "KANE", "KANT", "KARL", "KATE", "KEEL", + "KEEN", "KENO", "KENT", "KERN", "KERR", "KEYS", "KICK", "KILL", + "KIND", "KING", "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", + "KNIT", "KNOB", "KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD", + "KURT", "KYLE", "LACE", "LACK", "LACY", "LADY", "LAID", "LAIN", + "LAIR", "LAKE", "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", + "LARK", "LASS", "LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS", + "LAYS", "LEAD", "LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER", + "LEFT", "LEND", "LENS", "LENT", "LEON", "LESK", "LESS", "LEST", + "LETS", "LIAR", "LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU", + "LIFE", "LIFT", "LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB", + "LIME", "LIND", "LINE", "LINK", "LINT", "LION", "LISA", "LIST", + "LIVE", "LOAD", "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", + "LOIS", "LOLA", "LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD", + "LORE", "LOSE", "LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK", + "LUCY", "LUGE", "LUKE", "LULU", "LUND", "LUNG", "LURA", "LURE", + "LURK", "LUSH", "LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE", + "MADE", "MAGI", "MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI", + "MALL", "MALT", "MANA", "MANN", "MANY", "MARC", "MARE", "MARK", + "MARS", "MART", "MARY", "MASH", "MASK", "MASS", "MAST", "MATE", + "MATH", "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK", + "MEET", "MELD", "MELT", "MEMO", "MEND", "MENU", "MERT", "MESH", + "MESS", "MICE", "MIKE", "MILD", "MILE", "MILK", "MILL", "MILT", + "MIMI", "MIND", "MINE", "MINI", "MINK", "MINT", "MIRE", "MISS", + "MIST", "MITE", "MITT", "MOAN", "MOAT", "MOCK", "MODE", "MOLD", + "MOLE", "MOLL", "MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON", + "MOOR", "MOOT", "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", + "MOVE", "MUCH", "MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK", + "MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL", + "NAIR", "NAME", "NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR", + "NEAT", "NECK", "NEED", "NEIL", "NELL", "NEON", "NERO", "NESS", + "NEST", "NEWS", "NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA", + "NINE", "NOAH", "NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON", + "NORM", "NOSE", "NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB", + "OATH", "OBEY", "OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY", + "OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE", + "ONES", "ONLY", "ONTO", "ONUS", "ORAL", "ORGY", "OSLO", "OTIS", + "OTTO", "OUCH", "OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY", + "OWNS", "QUAD", "QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT", + "RAGE", "RAID", "RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE", + "RASH", "RATE", "RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", + "RECK", "REED", "REEF", "REEK", "REEL", "REID", "REIN", "RENA", + "REND", "RENT", "REST", "RICE", "RICH", "RICK", "RIDE", "RIFT", + "RILL", "RIME", "RING", "RINK", "RISE", "RISK", "RITE", "ROAD", + "ROAM", "ROAR", "ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME", + "ROOD", "ROOF", "ROOK", "ROOM", "ROOT", "ROSA", "ROSE", "ROSS", + "ROSY", "ROTH", "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", + "RUDE", "RUDY", "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", + "RUSH", "RUSK", "RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE", + "SAID", "SAIL", "SALE", "SALK", "SALT", "SAME", "SAND", "SANE", + "SANG", "SANK", "SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR", + "SCAT", "SCOT", "SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK", + "SEEM", "SEEN", "SEES", "SELF", "SELL", "SEND", "SENT", "SETS", + "SEWN", "SHAG", "SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN", + "SHOD", "SHOE", "SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE", + "SIFT", "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", "SINE", + "SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", + "SKID", "SKIM", "SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY", + "SLED", "SLEW", "SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT", + "SLOW", "SLUG", "SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB", + "SNOW", "SNUB", "SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA", + "SOFT", "SOIL", "SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE", + "SORT", "SOUL", "SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR", + "STAY", "STEM", "STEW", "STIR", "STOW", "STUB", "STUN", "SUCH", + "SUDS", "SUIT", "SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF", + "SWAB", "SWAG", "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", + "TACK", "TACT", "TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK", + "TASK", "TATE", "TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM", + "TEEN", "TEET", "TELL", "TEND", "TENT", "TERM", "TERN", "TESS", + "TEST", "THAN", "THAT", "THEE", "THEM", "THEN", "THEY", "THIN", + "THIS", "THUD", "THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER", + "TILE", "TILL", "TILT", "TIME", "TINA", "TINE", "TINT", "TINY", + "TIRE", "TOAD", "TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG", + "TONY", "TOOK", "TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR", + "TOUT", "TOWN", "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", + "TRIM", "TRIO", "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", + "TUCK", "TUFT", "TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK", + "TWIG", "TWIN", "TWIT", "ULAN", "UNIT", "URGE", "USED", "USER", + "USES", "UTAH", "VAIL", "VAIN", "VALE", "VARY", "VASE", "VAST", + "VEAL", "VEDA", "VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY", + "VETO", "VICE", "VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE", + "WACK", "WADE", "WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK", + "WALL", "WALT", "WAND", "WANE", "WANG", "WANT", "WARD", "WARM", + "WARN", "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY", + "WAYS", "WEAK", "WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR", + "WELD", "WELL", "WELT", "WENT", "WERE", "WERT", "WEST", "WHAM", + "WHAT", "WHEE", "WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE", + "WILD", "WILL", "WIND", "WINE", "WING", "WINK", "WINO", "WIRE", + "WISE", "WISH", "WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD", + "WORE", "WORK", "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", + "YANG", "YANK", "YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR", + "YELL", "YOGA", "YOKE" ] + +if __name__=='__main__': + def hex2str(s): + s2='' + for i in range(0, len(s), 2): + s2=s2+chr(string.atoi(s[i:i+2], 16)) + return s2 + + data = [('EB33F77EE73D4053', 'TIDE ITCH SLOW REIN RULE MOT'), + ('CCAC2AED591056BE4F90FD441C534766', + 'RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE'), + ('EFF81F9BFBC65350920CDD7416DE8009', + 'TROD MUTE TAIL WARM CHAR KONG HAAG CITY BORE O TEAL AWL') + ] + + for key, words in data: + print 'Trying key', key + key=hex2str(key) + w2=Key2English(key) + if w2!=words: + print 'Key2English fails on key', repr(key), ', producing', str(w2) + k2=English2Key(words) + if k2!=key: + print 'English2Key fails on key', repr(key), ', producing', repr(k2) + + diff --git a/Util/__init__.py b/Util/__init__.py new file mode 100644 index 0000000..f88e88e --- /dev/null +++ b/Util/__init__.py @@ -0,0 +1,3 @@ + +__all__ = ['randpool', 'RFC1751', 'number'] + diff --git a/Util/number.py b/Util/number.py new file mode 100644 index 0000000..2be1c35 --- /dev/null +++ b/Util/number.py @@ -0,0 +1,156 @@ +# +# number.py : Number-theoretic functions +# +# Part of the Python Cryptography Toolkit, version 1.1 +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + + +bignum = long +try: + import gmp +except ImportError: + try: + import mpz + #bignum=mpz.mpz # Temporarily disabled; the 'outrageous exponent' + # error messes things up. + except ImportError: + pass + +# Commented out and replaced with faster versions below +## def long2str(n): +## s='' +## while n>0: +## s=chr(n & 255)+s +## n=n>>8 +## return s + +## import types +## def str2long(s): +## if type(s)!=types.StringType: return s # Integers will be left alone +## return reduce(lambda x,y : x*256+ord(y), s, 0L) + +def getRandomNumber(N, randfunc): + "Return an N-bit random number." + str=randfunc(N/8) + char=ord(randfunc(1))>>(8-(N%8)) + return str2long(chr(char)+str) + +def GCD(x,y): + "Return the GCD of x and y." + if x<0: x=-x + if y<0: y=-y + while x>0: x,y = y%x, x + return y + +def inverse(u, v): + "Return the inverse of u mod v." + u3, v3 = long(u), long(v) + u1, v1 = 1L, 0L + while v3>0: + q=u3/v3 + u1, v1 = v1, u1-v1*q + u3, v3 = v3, u3-v3*q + print u1,u3,v1,v3 + while u1<0: u1=u1+v + return u1 + +# Given a number of bits to generate and a random generation function, +# find a prime number of the appropriate size. + +def getPrime(N, randfunc): + "Return a random N-bit prime number." + number=getRandomNumber(N, randfunc) | 1 + while (not isPrime(number)): + number=number+2 + return number + +def isPrime(N): + "Return true if N is prime." + if N in sieve: return 1 + for i in sieve: + if (N % i)==0: return 0 + + # Compute the highest bit that's set in N + N1=N - 1L ; n=1L + while (n<N): n=n<<1L + n = n >> 1L + + # Rabin-Miller test + for c in sieve[:7]: + a=long(c) ; d=1L ; t=n + while (t): # Iterate over the bits in N1 + x=(d*d) % N + if x==1L and d!=1L and d!=N1: return 0 # Square root of 1 found + if N1 & t: d=(x*a) % N + else: d=x + t = t >> 1L + if d!=1L: return 0 + return 1 + +# Small primes used for checking primality; these are all the primes +# less than 256. This should be enough to eliminate most of the odd +# numbers before needing to do a Rabin-Miller test at all. + +sieve=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, + 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191] + + +# Improved conversion functions contributed by Barry Warsaw, after +# careful benchmarking + +import struct + +def longtobytes(n, blocksize=0): + """Convert a long integer to a byte string + + If optional blocksize is given and greater than zero, pad the front of the + byte string with binary zeros so that the length is a multiple of + blocksize. + """ + # after much testing, this algorithm was deemed to be the fastest + s = '' + pack = struct.pack + while n > 0: + s = pack('>I', n & 0xffffffffL) + s + n = n >> 32 + # strip off leading zeros + for i in range(len(s)): + if s[i] <> '\000': + break + else: + # only happens when n == 0 + s = '\000' + i = 0 + s = s[i:] + # add back some pad bytes. this could be done more efficiently w.r.t. the + # de-padding being done above, but sigh... + if blocksize > 0 and len(s) % blocksize: + s = (blocksize - len(s) % blocksize) * '\000' + s + return s + +def bytestolong(s): + """Convert a byte string to a long integer. + + This is (essentially) the inverse of longtobytes(). + """ + acc = 0L + unpack = struct.unpack + length = len(s) + if length % 4: + extra = (4 - length % 4) + s = '\000' * extra + s + length = length + extra + for i in range(0, length, 4): + acc = (acc << 32) + unpack('>I', s[i:i+4])[0] + return acc + +# For backwards compatibility... +long2str = longtobytes +str2long = bytestolong diff --git a/Util/randpool.py b/Util/randpool.py new file mode 100644 index 0000000..71710d1 --- /dev/null +++ b/Util/randpool.py @@ -0,0 +1,223 @@ +# +# randpool.py : Cryptographically strong random number generation +# +# Part of the Python Cryptography Toolkit, version 1.1 +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + + +"""randpool.py : Cryptographically strong random number generation. + +The implementation here is similar to the one in PGP. To be +cryptographically strong, it must be difficult to determine the RNG's +output, whether in the future or the past. This is done by using +encryption algorithms to "stir" the random data. + +Entropy is gathered in the same fashion as PGP; the highest-resolution +clock around is read and the data is added to the random number pool. +A conservative estimate of the entropy is then kept. +""" + +import time, array + +from Crypto.Util.number import longtobytes + +class RandomPool: + def __init__(self, numbytes = 160, cipher=None, hash='SHA'): + # The cipher argument is vestigial; it was removed from + # version 1.1 so RandomPool would work even in the limited + # exportable subset of the code + + if type(hash) == type(''): + # ugly hack to force __import__ to give us the end-path module + hash = __import__('Crypto.Hash.'+hash, + None, None, ['new']) + + self.entropy, self._addPos = 0,0 + self._event1, self._event2 = 0,0 + self._addPos, self._getPos = 0,hash.digestsize + self.bytes, self.hash=numbytes, hash + self.bits=self.bytes*8 + self.__counter = 0 + + # Construct an array to hold the random pool + a = [] + while len(a) < self.bytes: + a = a + range( min(256, self.bytes-len(a)) ) + self._randpool = array.array('B', a) + + # Linux supports a /dev/urandom device; soon other OSes will, too. + # We'll grab some randomness from it. + try: + f=open('/dev/urandom') + data=f.read(self.bytes) + f.close() + self._addBytes(data) + # Conservative entropy estimate: The number of bits of + # data obtained from /dev/urandom, divided by 4. + self.entropy = self.entropy + (8/2)*len(data) + except IOError, (num, msg): + if num!=2: raise IOError, (num, msg) + # If the file wasn't found, ignore the error + + def stir(self): + entropy=self.entropy + self.addEvent(time.time()) + + for i in range( self.bytes / self.hash.digestsize): + h = self.hash.new(self._randpool) + h.update(str(self.__counter) + str(i) + str(self._addPos) ) + self._addBytes( h.digest() ) + self.__counter = (self.__counter + 1) & 0xFFFFffff + + self._addPos, self._getPos = 0, self.hash.digestsize + self.addEvent(time.time()) + + # Paranoia is a Good Thing in cryptographic applications. + # While the call to addEvent() may be adding entropy to the + # pool, we won't take that into account. + self.entropy=entropy + + def getBytes(self, N): + "Return num bytes of random data" + s='' + i, pool = self._getPos, self._randpool + h=self.hash.new() + dsize = self.hash.digestsize + num = N + while num>0: + h.update( self._randpool[i:i+dsize] ) + s = s + h.digest() + num = num - dsize + i = (i + dsize) % self.bytes + if i<dsize: + self.stir() + i=self._getPos + + self._getPos = i + self.entropy=self.entropy-8*num + if self.entropy<0: self.entropy=0 + return s[:N] + + def addEvent(self, event, s=''): + event=long(event*1000) + delta=self._noise() + s=s+longtobytes(event)+4*chr(0xaa)+longtobytes(long(delta)) + self._addBytes(s) + if event==self._event1 and event==self._event2: + bits=0 + else: + bits=0 + while (delta): delta, bits = delta>>1, bits+1 + if (bits>8): bits=8 + self._event1, self._event2 = event, self._event1 + self.entropy=self.entropy+bits + if self.entropy>self.bytes*8: + self.entropy=self.bytes*8 + return self.entropy + + # Private functions + def _noise(self): + if not self.__dict__.has_key('_lastcounter'): + self._lastcounter=time.time() + if not self.__dict__.has_key('_ticksize'): + self._noiseTickSize() + t=time.time() + delta = (t - self._lastcounter)/self._ticksize*1e6 + self._lastcounter = t + self._addBytes(longtobytes(long(1000*time.time()))) + self._addBytes(longtobytes(long(1000*time.clock()))) + self._addBytes(longtobytes(long(1000*time.time()))) + self._addBytes(longtobytes(long(delta))) + delta=delta % 0x1000000 # Reduce delta so it fits into an int + return int(delta) + + def _noiseTickSize(self): + interval=[] + t=time.time() + for i in range(0,100): + t2=time.time() + delta=int((t2-t)*1e6) + t=t2 + if delta: interval.append(delta) + interval.sort() + self._ticksize=interval[len(interval)/2] + + def _addBytes(self, s): + "XOR the contents of the string S into the random pool" + i, pool = self._addPos, self._randpool + for j in range(0, len(s)): + pool[i]=pool[i] ^ ord(s[j]) + i=(i+1) % self.bytes + self._addPos = i + + +class KeyboardRandomPool(RandomPool): + def __init__(self, filename='', numbytes = 384, cipher=None, hash='MD5'): + self.filename=filename + if filename: + try: + import pickle + f=open(filename, 'r') + temp=pickle.load(f) + for key in temp.__dict__.keys(): + self.__dict__[key]=temp.__dict__[key] + f.close() + self.stir() + except IOError: + RandomPool.__init__(self, numbytes, cipher, hash) + else: + RandomPool.__init__(self, numbytes, cipher, hash) + + self.stir() # Wash the random pool + self.stir() + self.stir() + + def save(self): + import pickle + if self.filename == "": + raise ValueError, "No filename set for this object" + self.stir() # Wash the random pool + self.stir() + self.stir() + f=open(self.filename, 'w') + pickle.dump(self, f) + f.close() + + def randomize(self): + import os, string, termios, TERMIOS, time + bits=self.bits-self.entropy + if bits==0: return # No entropy required, so we exit. + print bits,'bits of entropy are now required. Please type on the keyboard' + print 'until enough randomness has been accumulated.' + fd=0 + old=termios.tcgetattr(fd) + new=termios.tcgetattr(fd) + new[3]=new[3] & ~TERMIOS.ICANON & ~TERMIOS.ECHO + termios.tcsetattr(fd, TERMIOS.TCSANOW, new) + s='' # We'll save the characters typed and add them to the pool. + hash = self.hash + try: + while (self.entropy<self.bits): + temp=string.rjust(str(self.bits-self.entropy), 6) + os.write(1, temp) + termios.tcflush(0, TERMIOS.TCIFLUSH) # XXX Leave this in? + s=s+os.read(0, 1) + self.addEvent(time.time()) + os.write(1, 6*chr(8)) + self.addEvent(time.time(), s+hash.new(s).digest() ) + finally: + termios.tcsetattr(fd, TERMIOS.TCSAFLUSH, old) + print '\n\007 Enough.\n' + time.sleep(3) + termios.tcflush(0, TERMIOS.TCIFLUSH) + + +if __name__ == '__main__': + pool = RandomPool() + print `pool.getBytes(100)` diff --git a/Util/test.py b/Util/test.py new file mode 100644 index 0000000..83bfc2e --- /dev/null +++ b/Util/test.py @@ -0,0 +1,803 @@ +# +# test.py : Functions used for testing the modules +# +# Part of the Python Cryptography Toolkit, version 1.1 +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +def die(string): + import sys + print '***ERROR: ', string +# sys.exit(0) # Will default to continuing onward... + +def hex2str(str): + s='' + for i in range(0,len(str),2): + s=s+chr(string.atoi(str[i:i+2], 16)) + return s + +def exerciseBlockCipher(cipher, verbose): + import string, time + try: + from Crypto.Cipher import * ; ciph = eval(cipher, locals() ) + except ImportError: + print cipher, 'module not available' + return None + print cipher+ ':' + str='1' # Build 128K of test data + for i in xrange(0, 17): + str=str+str + if ciph.keysize==0: ciph.keysize=16 + password = 'password12345678Extra text for password'[0:ciph.keysize] + IV = 'Test IV Test IV Test IV Test'[0:ciph.blocksize] + + if verbose: print ' Testing ECB mode with key '+ `password` + obj=ciph.new(password, ciph.ECB) + if verbose: print ' Sanity check' + if obj.blocksize != ciph.blocksize: + die("Module and cipher object blocksize don't match") + + text='1234567812345678'[0:ciph.blocksize] + c=obj.encrypt(text) + if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') + text='KuchlingKuchling'[0:ciph.blocksize] + c=obj.encrypt(text) + if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') + text='NotTodayNotEver!'[0:ciph.blocksize] + c=obj.encrypt(text) + if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') + + start=time.time() + s=obj.encrypt(str) + s2=obj.decrypt(s) + end=time.time() + if (str!=s2): + die('Error in resulting plaintext from ECB mode') + if verbose: print ' Benchmark for 256K: ', 256/(end-start), 'K/sec' + del obj + + if verbose: print ' Testing CFB mode with key ' + `password`+ ' IV "Test IV@"' + obj1=ciph.new(password, ciph.CFB, IV) + obj2=ciph.new(password, ciph.CFB, IV) + start=time.time() + ciphertext=obj1.encrypt(str[0:65536]) + plaintext=obj2.decrypt(ciphertext) + end=time.time() + if (plaintext!=str[0:65536]): + die('Error in resulting plaintext from CFB mode') + if verbose: print ' Benchmark for 64K: ', 64/(end-start), 'K/sec' + del obj1, obj2 + + if verbose: print ' Testing CBC mode with key ' + `password`+ ' IV "Test IV@"' + obj1=ciph.new(password, ciph.CBC, IV) + obj2=ciph.new(password, ciph.CBC, IV) + start=time.time() + ciphertext=obj1.encrypt(str) + plaintext=obj2.decrypt(ciphertext) + end=time.time() + if (plaintext!=str): + die('Error in resulting plaintext from CBC mode') + if verbose: print ' Benchmark for 256K: ', 256/(end-start), 'K/sec' + del obj1, obj2 + + if verbose: print ' Testing PGP mode with key ' + `password`+ ' IV "Test IV@"' + obj1=ciph.new(password, ciph.PGP, IV) + obj2=ciph.new(password, ciph.PGP, IV) + start=time.time() + ciphertext=obj1.encrypt(str) + plaintext=obj2.decrypt(ciphertext) + end=time.time() + if (plaintext!=str): + die('Error in resulting plaintext from PGP mode') + if verbose: print ' Benchmark for 256K: ', 256/(end-start), 'K/sec' + del obj1, obj2 + + # Test the IV handling + if verbose: print ' Testing IV handling' + obj1=ciph.new(password, ciph.CBC, IV) + plaintext='Test'*(ciph.blocksize/4)*3 + ciphertext1=obj1.encrypt(plaintext) + obj1.IV=IV + ciphertext2=obj1.encrypt(plaintext) + if ciphertext1!=ciphertext2: + die('Error in setting IV') + + # Test keyword arguments + obj1=ciph.new(key=password) + obj1=ciph.new(password, mode=ciph.CBC) + obj1=ciph.new(mode=ciph.CBC, key=password) + obj1=ciph.new(IV=IV, mode=ciph.CBC, key=password) + + return ciph + +def exerciseStreamCipher(cipher, verbose): + import string, time + try: + from Crypto.Cipher import * ; ciph = eval(cipher, locals() ) + except (ImportError): + print cipher, 'module not available' + return None + print cipher + ':' + str='1' # Build 128K of test data + for i in xrange(0, 17): + str=str+str + if ciph.keysize==0: ciph.keysize=16 + password = 'password12345678Extra text for password'[0:ciph.keysize] + + obj1=ciph.new(password) + obj2=ciph.new(password) + if verbose: print ' Sanity check' + if obj1.blocksize != ciph.blocksize: + die("Module and cipher object blocksize don't match") + if obj1.keysize != ciph.keysize: + die("Module and cipher object keysize don't match") + + text='1234567812345678Python' + c=obj1.encrypt(text) + if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') + text='B1FF I2 A R3A11Y |<00L D00D!!!!!' + c=obj1.encrypt(text) + if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') + text='SpamSpamSpamSpamSpamSpamSpamSpamSpam' + c=obj1.encrypt(text) + if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') + + start=time.time() + s=obj1.encrypt(str) + str=obj2.decrypt(s) + end=time.time() + if verbose: print ' Benchmark for 256K: ', 256/(end-start), 'K/sec' + del obj1, obj2 + + return ciph + +def exercisePublicKey(randfunc, module, verbose): + N=256 # Key size, measured in bits + + if verbose: print ' Generating', N, 'bit key' + import sys + import Crypto.Util.number + def write(s): + import sys ; sys.stdout.write(' '+s) + if verbose: key=module.generate(N, randfunc, write) + else: key=module.generate(N, randfunc) + + if verbose: + print ' Key data:' + for field in key.keydata: + print " ", field, ':', hex(getattr(key,field)) + + def testkey(key, randfunc, verbose): + plaintext="Hello" + + if key.canencrypt(): + if verbose: print ' Encryption/decryption test' + K=Crypto.Util.number.getPrime(10, randfunc) + ciphertext=key.encrypt(plaintext, K) + if key.decrypt(ciphertext)!=plaintext: + print '***ERROR: Mismatch decrypting plaintext' + + if key.cansign(): + if verbose: print ' Signature test' + K=Crypto.Util.number.getPrime(30, randfunc) + signature=key.sign(plaintext, K) + result=key.verify(plaintext, signature) + if not result: + print "***ERROR 1: Sig. verification failed when it should have succeeded" + result=key.verify(plaintext[:-1], signature) + if result: + print "***ERROR 2: Sig. verification succeeded when it should have failed" + # Change a single bit in the plaintext + badtext=plaintext[:-3]+chr( 1 ^ ord(plaintext[-3]) )+plaintext[-3:] + result=key.verify(badtext, signature) + if result: + print "***ERROR 3: Sig. verification succeeded when it should have failed" + if verbose: print ' Removing private key data' + pubonly=key.publickey() + result=pubonly.verify(plaintext, signature) + if not result: + print "***ERROR 4: Sig. verification failed when it should have succeeded" + + if verbose: print " Testing newly generated key" + testkey(key, randfunc, verbose) + if verbose: print " Testing pickled/unpickled key" + import pickle + s = pickle.dumps(key) ; key2 = pickle.loads(s) + testkey(key2, randfunc, verbose) + + if verbose: print " Testing cPickled key" + import cPickle + s = cPickle.dumps(key) ; key2 = cPickle.loads(s) + testkey(key2, randfunc, verbose) + if verbose: print + +import string +def compareHashResult(hash, strg, result): + obj=hash.new(strg) + s=obj.digest() + s1=s + temp=0L + while (s!=''): + temp=temp*256+ord(s[0]) + s=s[1:] + + # Check that the right hash result is produced + if (result!=temp): + die(`hash`+' produces incorrect result on string "'+strg+'"') + return + + # Check that .hexdigest() produces the same output + hex_result = string.lower( hex(result)[2:-1] ) + if len(hex_result) % 2: hex_result = '0'+hex_result + if hex_result != obj.hexdigest(): + die(`hash`+' produces incorrect result on string "'+strg+'" using hexdigest()') + return + + # Test second hashing, and copying of a hashing object + s2=obj.digest() + if s2!=s1: die(`hash`+' produces incorrect result on second hashing') + s3=obj.copy().digest() + if s3!=s1: die(`hash`+' produces incorrect result after copying') + + del temp, s + + +import Crypto.Util.testdata + +def TestHashModules(args=['ripemd', 'md2', 'md4', 'md5', 'sha', 'haval'], + verbose=1): + import string + args=map(string.lower, args) + + teststr='1' # Build 128K of test data + for i in xrange(0, 17): + teststr=teststr+teststr + + if 'ripemd' in args: + # Test/benchmark RIPEMD hash algorithm ; the test data is taken from + # the README in rmd.zip + try: + from Crypto.Hash import RIPEMD + except ImportError: + print 'RIPEMD module not available' + else: + print 'RIPEMD:' + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for text, hash in Crypto.Util.testdata.ripemd: + compareHashResult(RIPEMD, text, hash) + # Compute value for 1 megabyte of a's... + obj, astring=RIPEMD.new(), 1000*'a' + for i in range(0,1000): obj.update(astring) + result=obj.digest() + if result!=hex2str("52783243c1697bdbe16d37f97f68f08325dc1528"): + die('RIPEMD produces incorrect result on 1E6*"a"') + + if verbose: print ' Completed' + import time + obj=RIPEMD.new() + start=time.time() + s=obj.update(teststr) + end=time.time() + if verbose: print ' Benchmark for 128K: ', 128/(end-start), 'K/sec' + del obj + + if 'md2' in args: + # Test/benchmark MD2 hash algorithm ; the test data is taken from + # RFC1319, "The MD2 Message-Digest Algorithm" + try: + from Crypto.Hash import MD2 + except ImportError: + print 'MD2 module not available' + else: + print 'MD2:' + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for text, hash in Crypto.Util.testdata.md2: + compareHashResult(MD2, text, hash) + if verbose: print ' Completed' + import time + obj=MD2.new() + start=time.time() + s=obj.update(teststr) + end=time.time() + if verbose: print ' Benchmark for 128K: ', 128/(end-start), 'K/sec' + del obj + + if 'md4' in args: + # Test/benchmark MD4 hash algorithm ; the test data is taken from + # RFC1186B, "The MD4 Message-Digest Algorithm" + try: + from Crypto.Hash import MD4 + except ImportError: + print 'MD4 module not available' + else: + print 'MD4:' + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for text, hash in Crypto.Util.testdata.md4: + compareHashResult(MD4, text, hash) + if verbose: print ' Completed' + import time + obj=MD4.new() + start=time.time() + s=obj.update(teststr) + end=time.time() + if verbose: print ' Benchmark for 128K: ', 128/(end-start), 'K/sec' + del obj + + if 'md5' in args: + # Test/benchmark MD5 hash algorithm ; the test data is taken from + # RFC1321, "The MD5 Message-Digest Algorithm" + try: + from Crypto.Hash import MD5 + except ImportError: + print 'MD5 module not available' + else: + print 'MD5:' + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for text, hash in Crypto.Util.testdata.md5: + compareHashResult(MD5, text, hash) + if verbose: print ' Completed' + import time + obj=MD5.new() + start=time.time() + s=obj.update(teststr) + end=time.time() + if verbose: print ' Benchmark for 128K: ', 128/(end-start), 'K/sec' + del obj + + if 'haval' in args: + # Test/benchmark HAVAL + try: + from Crypto.Hash import HAVAL + except ImportError: + print 'HAVAL module not available' + else: + print 'HAVAL:' + try: + import Crypto.Util.testdata + if verbose: print ' Verifying against test suite...' + for (passes, length, text, hash) in Crypto.Util.testdata.haval: + ID=str(passes)+'-pass, '+str(length)+'-bit HAVAL ' + obj=HAVAL.new('', rounds=passes, digestsize=length) + obj.update(text) + s1=obj.digest() + if (s1!=hex2str(hash)): + die(ID+'produces incorrect result on string "'+text+'"') + s2=obj.digest() + if s2!=s1: die(ID+'produces incorrect result on second hashing') + s3=obj.copy().digest() + if s3!=s1: die(ID+'produces incorrect result after copying') + if verbose: print ' Completed' + except ImportError: + if verbose: print ' Test suite data not available' + obj=HAVAL.new() + import time + start=time.time() + s=obj.update(teststr) + end=time.time() + if verbose: print ' Benchmark for 128K: ', 128/(end-start), 'K/sec' + del obj + + if 'sha' in args: + # Test/benchmark SHA hash algorithm + try: + from Crypto.Hash import SHA + except ImportError: + print 'SHA module not available' + else: + print 'SHA:' + if verbose: print ' Verifying against test suite...' + for text, hash in Crypto.Util.testdata.sha: + compareHashResult(SHA, text, hash) + # Compute value for 1 megabyte of a's... + obj, astring=SHA.new(), 1000*'a' + for i in range(0,1000): obj.update(astring) + result=obj.digest() + if result!=hex2str('34AA973CD4C4DAA4F61EEB2BDBAD27316534016F'): + die('SHA produces incorrect result on 1E6*"a"') + if verbose: print ' Completed' + obj=SHA.new() + start=time.time() + s=obj.update(teststr) + end=time.time() + if verbose: print ' Benchmark for 128K: ', 128/(end-start), 'K/sec' + del obj, astring + +def TestStreamModules(args=['arc4', 'sapphire', 'XOR'], verbose=1): + import sys, string + args=map(string.lower, args) + + if 'arc4' in args: + # Test ARC4 stream cipher + arc4=exerciseStreamCipher('ARC4', verbose) + if (arc4!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + for entry in Crypto.Util.testdata.arc4: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=arc4.new(key) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('ARC4 failed on entry '+`entry`) + if verbose: print ' ARC4 test suite completed' + + if 'sapphire' in args: + # Test Sapphire stream cipher + sapphire=exerciseStreamCipher('Sapphire', verbose) + if (sapphire!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + result=hex2str(Crypto.Util.testdata.sapphire) + obj=sapphire.new('testSapphirekey') + s='' + for i in range(0,256): + s=s+chr(i) + s=obj.encrypt(s) + if (s!=result): + die('Sapphire fails verification test') + if verbose: print ' Sapphire test suite completed' + + print args + if 'xor' in args: + # Test XOR stream cipher + XOR=exerciseStreamCipher('XOR', verbose) + if (XOR!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + for entry in Crypto.Util.testdata.XOR: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=XOR.new(key) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('XOR failed on entry '+`entry`) + if verbose: print ' XOR test suite completed' + + +def TestBlockModules(args=['arc2', 'blowfish', 'cast', 'des', 'des3', + 'diamond', 'idea', 'rc5', 'skipjack'], + verbose=1): + import string + args=map(string.lower, args) + + if 'arc2' in args: + ciph=exerciseBlockCipher('ARC2', verbose) # Alleged RC2 + if (ciph!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for entry in Crypto.Util.testdata.arc2: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=ciph.new(key, ciph.ECB) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('ARC2 failed on entry '+`entry`) + for i in ciphertext: + if verbose: print hex(ord(i)), + print + if verbose: print ' Completed' + + if 'blowfish' in args: + ciph=exerciseBlockCipher('Blowfish',verbose)# Bruce Schneier's Blowfish cipher + if (ciph!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for entry in Crypto.Util.testdata.blowfish: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=ciph.new(key, ciph.ECB) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('Blowfish failed on entry '+`entry`) + for i in ciphertext: + if verbose: print hex(ord(i)), + if verbose: print + if verbose: print ' Completed' + + if 'cast' in args: + ciph=exerciseBlockCipher('CAST', verbose) # CAST-128 + if (ciph!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for entry in Crypto.Util.testdata.cast: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=ciph.new(key, ciph.ECB) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('CAST failed on entry '+`entry`) + for i in ciphertext: + if verbose: print hex(ord(i)), + if verbose: print + + if 0: + # The full-maintenance test; it requires 4 million encryptions, + # and correspondingly is quite time-consuming. I've disabled + # it; it's faster to compile block/cast.c with -DTEST and run + # the resulting program. + a = b = '\x01\x23\x45\x67\x12\x34\x56\x78\x23\x45\x67\x89\x34\x56\x78\x9A' + + for i in range(0, 1000000): + obj = cast.new(b, cast.ECB) + a = obj.encrypt(a[:8]) + obj.encrypt(a[-8:]) + obj = cast.new(a, cast.ECB) + b = obj.encrypt(b[:8]) + obj.encrypt(b[-8:]) + + if a!="\xEE\xA9\xD0\xA2\x49\xFD\x3B\xA6\xB3\x43\x6F\xB8\x9D\x6D\xCA\x92": + if verbose: print 'CAST test failed: value of "a" doesn\'t match' + if b!="\xB2\xC9\x5E\xB0\x0C\x31\xAD\x71\x80\xAC\x05\xB8\xE8\x3D\x69\x6E": + if verbose: print 'CAST test failed: value of "b" doesn\'t match' + if verbose: print ' Completed' + + if 'des' in args: + # Test/benchmark DES block cipher + des=exerciseBlockCipher('DES', verbose) + if (des!=None): + # Various tests taken from the DES library packaged with Kerberos V4 + obj=des.new(hex2str('0123456789abcdef'), des.ECB) + s=obj.encrypt('Now is t') + if (s!=hex2str('3fa40e8a984d4815')): + die('DES fails test 1') + obj=des.new(hex2str('08192a3b4c5d6e7f'), des.ECB) + s=obj.encrypt('\000\000\000\000\000\000\000\000') + if (s!=hex2str('25ddac3e96176467')): + die('DES fails test 2') + obj=des.new(hex2str('0123456789abcdef'), des.CBC, + hex2str('1234567890abcdef')) + s=obj.encrypt("Now is the time for all ") + if (s!=hex2str('e5c7cdde872bf27c43e934008c389c0f683788499a7c05f6')): + die('DES fails test 3') + obj=des.new(hex2str('0123456789abcdef'), des.CBC, + hex2str('fedcba9876543210')) + s=obj.encrypt("7654321 Now is the time for \000\000\000\000") + if (s!=hex2str("ccd173ffab2039f4acd8aefddfd8a1eb468e91157888ba681d269397f7fe62b4")): + die('DES fails test 4') + del obj,s + + # R. Rivest's test: see http://theory.lcs.mit.edu/~rivest/destest.txt + x=hex2str('9474B8E8C73BCA7D') + for i in range(0, 16): + obj=des.new(x, des.ECB) + if (i & 1): x=obj.decrypt(x) + else: x=obj.encrypt(x) + if x!=hex2str('1B1A2DDB4C642438'): + die("DES fails Rivest's test") + + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for entry in Crypto.Util.testdata.des: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=des.new(key, des.ECB) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('DES failed on entry '+`entry`) + for entry in Crypto.Util.testdata.des_cbc: + key, iv, plain, cipher=entry + key, iv, cipher=hex2str(key),hex2str(iv),hex2str(cipher) + obj1=des.new(key, des.CBC, iv) + obj2=des.new(key, des.CBC, iv) + ciphertext=obj1.encrypt(plain) + if (ciphertext!=cipher): + die('DES CBC mode failed on entry '+`entry`) + if verbose: print ' Completed' + + if 'des3' in args: + ciph=exerciseBlockCipher('DES3', verbose) # Triple DES + if (ciph!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for entry in Crypto.Util.testdata.des3: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=ciph.new(key, ciph.ECB) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('DES3 failed on entry '+`entry`) + for i in ciphertext: + if verbose: print hex(ord(i)), + if verbose: print + for entry in Crypto.Util.testdata.des3_cbc: + key, iv, plain, cipher=entry + key, iv, cipher=hex2str(key),hex2str(iv),hex2str(cipher) + obj1=ciph.new(key, ciph.CBC, iv) + obj2=ciph.new(key, ciph.CBC, iv) + ciphertext=obj1.encrypt(plain) + if (ciphertext!=cipher): + die('DES3 CBC mode failed on entry '+`entry`) + if verbose: print ' Completed' + + if 'diamond' in args: + ciph=exerciseBlockCipher('Diamond', verbose) # M.P. Johnson's Diamond + if (ciph!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for entry in Crypto.Util.testdata.diamond: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=ciph.new(key[1:], ciph.ECB, rounds = ord(key[0]) ) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('Diamond failed on entry '+`entry`) + if verbose: print ' Completed' + + + if 'idea' in args: + ciph=exerciseBlockCipher('IDEA', verbose) # IDEA block cipher + if (ciph!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for entry in Crypto.Util.testdata.idea: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=ciph.new(key, ciph.ECB) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('IDEA failed on entry '+`entry`) + if verbose: print ' Completed' + + if 'rc5' in args: + # Ronald Rivest's RC5 algorithm + ciph=exerciseBlockCipher('RC5', verbose) + if (ciph!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for entry in Crypto.Util.testdata.rc5: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=ciph.new(key[4:], ciph.ECB, + version =ord(key[0]), + wordsize=ord(key[1]), + rounds =ord(key[2]) ) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('RC5 failed on entry '+`entry`) + for i in ciphertext: + if verbose: print hex(ord(i)), + if verbose: print + if verbose: print ' Completed' + + if 'skipjack' in args: + ciph=exerciseBlockCipher('Skipjack', verbose) # Skipjack + if (ciph!=None): + try: + import Crypto.Util.testdata + except ImportError: + if verbose: print ' Test suite data not available' + else: + if verbose: print ' Verifying against test suite...' + for entry in Crypto.Util.testdata.skipjack: + key,plain,cipher=entry + key=hex2str(key) + plain=hex2str(plain) + cipher=hex2str(cipher) + obj=ciph.new(key, ciph.ECB) + ciphertext=obj.encrypt(plain) + if (ciphertext!=cipher): + die('Skipjack failed on entry '+`entry`) + for i in ciphertext: + if verbose: print hex(ord(i)), + print + if verbose: print ' Completed' + + +def TestPKModules(args=['rsa', 'dsa', 'elgamal', 'qnew'], verbose=1): + # Set up a random pool; we won't bother to actually fill it with + # entropy from the keyboard + if verbose: print ' Initializing random pool' + from Crypto.Util.randpool import RandomPool + r=RandomPool(384) + r.stir() + randfunc=r.getBytes + + if 'rsa' in args: + print 'RSA:' + from Crypto.PublicKey import RSA + exercisePublicKey(randfunc, RSA, verbose) + r.stir() + + if 'dsa' in args: + print 'DSA:' + from Crypto.PublicKey import DSA + exercisePublicKey(randfunc, DSA, verbose) + r.stir() + + if 'elgamal' in args: + print 'ElGamal' + from Crypto.PublicKey import ElGamal + exercisePublicKey(randfunc, ElGamal, verbose) + r.stir() + + if 'qnew' in args: + print 'qNEW' + from Crypto.PublicKey import qNEW + exercisePublicKey(randfunc, qNEW, verbose) + r.stir() + + diff --git a/Util/testdata.py b/Util/testdata.py new file mode 100644 index 0000000..1ff7600 --- /dev/null +++ b/Util/testdata.py @@ -0,0 +1,860 @@ +# +# testdata.py : Test data for the various algorithms +# +# Part of the Python Cryptography Toolkit, version 1.1 +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +# Data for encryption algorithms is saved as (key, plaintext, +# ciphertext) tuples. Hashing algorithm test data is simply +# (text, hashvalue) pairs. + +# MD2 validation data + +md2= [ + ("", 0x8350e5a3e24c153df2275c9f80692773L), + ("a", 0x32ec01ec4a6dac72c0ab96fb34c0b5d1L), + ("abc", 0xda853b0d3f88d99b30283a69e6ded6bbL), + ("message digest", 0xab4f496bfb2a530b219ff33031fe06b0L), + ("abcdefghijklmnopqrstuvwxyz", 0x4e8ddff3650292ab5a4108c3aa47940bL), + ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + 0xda33def2a42df13975352846c30338cdL), + ("12345678901234567890123456789012345678901234567890123456789012345678901234567890", + 0xd5976f79d83d3a0dc9806c3c66f3efd8L) + ] + +# MD4 validation data + +md4= [ + ('', 0x31d6cfe0d16ae931b73c59d7e0c089c0L), + ("a", 0xbde52cb31de33e46245e05fbdbd6fb24L), + ("abc", 0xa448017aaf21d8525fc10ae87aa6729dL), + ("message digest", 0xd9130a8164549fe818874806e1c7014bL), + ("abcdefghijklmnopqrstuvwxyz", 0xd79e1c308aa5bbcdeea8ed63df412da9L), + ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + 0x043f8582f241db351ce627e153e7f0e4L), + ("12345678901234567890123456789012345678901234567890123456789012345678901234567890", + 0xe33b4ddc9c38f2199c3e7b164fcc0536L), + ] + +# MD5 validation data + +md5= [ + ('', 0xd41d8cd98f00b204e9800998ecf8427eL), + ('a', 0x0cc175b9c0f1b6a831c399e269772661L), + ('abc', 0x900150983cd24fb0d6963f7d28e17f72L), + ('message digest', 0xf96b697d7cb7938d525a2f31aaf161d0L), + ('abcdefghijklmnopqrstuvwxyz', 0xc3fcd3d76192e4007dfb496cca67e13bL), + ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + 0xd174ab98d277d9f5a5611c2c9f419d9fL), + ('12345678901234567890123456789012345678901234567890123456789' + '012345678901234567890', 0x57edf4a22be3c955ac49da2e2107b67aL) + ] + +# Test data for SHA, the Secure Hash Algorithm. + +sha = [ + ('abc', 0xA9993E364706816ABA3E25717850C26C9CD0D89DL), + ('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', + 0x84983E441C3BD26EBAAE4AA1F95129E5E54670F1L) + ] + +# Test data for HAVAL (passes, length, text, value) +haval = [(3, 128, '', '1BDC556B29AD02EC09AF8C66477F2A87'), + (3, 128, 'a', '24D2BC955A219E3E06462C91B555CFA1'), + (3, 128, 'HAVAL', '16C743E5EEFD3266ED50DEAC6C30313E'), + (3, 128, '0123456789', '82D163440F6E853229A97007EC4AF0E5'), + (3, 128, 'abcdefghijklmnopqrstuvwxyz', '92E8EC9AD7FD209D97E9CE21B50440E9'), + (3, 128, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', '4AE2F37CEF9275CCE0D73F6A1EB9CDD8'), + (3, 160, '', 'FE79D0A044FFB75D5354668D664E4F4B9CC33477'), + (3, 160, 'a', '5E1610FCED1D3ADB0BB18E92AC2B11F0BD99D8ED'), + (3, 160, 'HAVAL', '8E568AD6CCD58D17E0A11E92183232E0D1D2E9BF'), + (3, 160, '0123456789', '700D43A9B5E38300303FD4E25A6A326BEB4A2241'), + (3, 160, 'abcdefghijklmnopqrstuvwxyz', '1DD40AEAB9610585FCAE7492FF3B893C2A018F4E'), + (3, 160, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', '485ABB76ED2F5AC8BB86DDEB8CB4C54CF5BB077B'), + (3, 192, '', '5E05A29CA2DB5DABE05A36BEED1D9F23A62E24714E74E2FF'), + (3, 192, 'a', '53FD81208C51EBC807ED5A6596A2897BD8211975F10CC46E'), + (3, 192, 'HAVAL', '365F46AF04836A174C7F17E0A364E77FB613DC54990649D5'), + (3, 192, '0123456789', '241210926D574FAFB89CA187D51BDD78209DB46410660DF2'), + (3, 192, 'abcdefghijklmnopqrstuvwxyz', '91FF2A2BE448D2C4301E27AC664FC6BDE5B98BF275967DDC'), + (3, 192, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'F9658C8807D791F3A7F364E17B8771E85BE7BD539A1957A6'), + (3, 224, '', 'F3D390AF42356F1D32CBD3B6F26B16FAB3771AAE92E7B48D32DED172'), + (3, 224, 'a', 'C0ADA85221F054D8266370225C36765A4E337615C987703391D0A06F'), + (3, 224, 'HAVAL', 'A728355187983BBF110DC6EA263995F57D56B08A063FB2750D58648A'), + (3, 224, '0123456789', 'FD47F2202811C889A45C19E334249F52A23670843B6B4682636E5B14'), + (3, 224, 'abcdefghijklmnopqrstuvwxyz', '558832025C6B71DF1CA043905CE6199A031B4ED2DCAA820B2E27F122'), + (3, 224, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', '5311AADA23AF4F8993448B53C2994186F181E9D80D821A6A7F58386A'), + (3, 256, '', '839786AC4F89EE6AE87F2065AC681097D7276A9903B7E972E992566D0DE38E0C'), + (3, 256, 'a', '5BCB16472639B8E63CA1E2B347D9038F7A808D8B317CBE4F202DFB9C0529B0FD'), + (3, 256, 'HAVAL', '7208A60270D70EE998F942B03D5D3ACF5B3AA4A841F21360896948D0B78CACD2'), + (3, 256, '0123456789', 'CC2CE6F921AA5423E1EEAB178AE0FFD293972C2530DCE9C7FE8A7EA77C8ACEAC'), + (3, 256, 'abcdefghijklmnopqrstuvwxyz', 'DB9CEDB76576C2BFF4345F3D17C241CB1B4D29B0DBBE8F263E463BFFD260AC89'), + (3, 256, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', '8FB83BFDA5F8324DFBA078B3DB48A95B7C7C4023F8FB70045EF875425AF83868'), + (4, 128, '', 'A0E12BA547C3F7724B97BEB2B2466825'), + (4, 128, 'a', '9D576BED5A29B47100157FDBF7AF4B34'), + (4, 128, 'HAVAL', '3BF0CE4A6C375AD2C5FC954242B68219'), + (4, 128, '0123456789', '361F885D16277A4686558A0B694EE215'), + (4, 128, 'abcdefghijklmnopqrstuvwxyz', '198504C9CD40D1938396CE81F5F64BD0'), + (4, 128, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', '50F905D51FB69C2B8CA5D15F13C099CB'), + (4, 160, '', 'D4093169135AA22DAD562993D362F79BA23B5F10'), + (4, 160, 'a', 'F00666EFE778E269F149371FA9FABFC98C07B7BE'), + (4, 160, 'HAVAL', 'EB6E692476720867737523ECE547FF027839B84F'), + (4, 160, '0123456789', '0FB6575C829528FE5345E528E084AC814C1A88DA'), + (4, 160, 'abcdefghijklmnopqrstuvwxyz', '1BB37E16413FAE67FD39973C943A719C44F2855A'), + (4, 160, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', '1AC51AE99A8915BA8A651370C2B96037BC53496F'), + (4, 192, '', '51FA9E28C96865207ED6DAE2EAA1D8AF6E7DE2783EBEC4B4'), + (4, 192, 'a', 'A1446E6CEDB4B28BC6E13D4D1D2694E9CE4A3D942C73589E'), + (4, 192, 'HAVAL', '74AA31182FF09BCCE453A7F71B5A7C5E80872FA90CD93AE4'), + (4, 192, '0123456789', 'CA05546FFA4B69DAFA7C04424CC10802A2523EFCB8BEBB61'), + (4, 192, 'abcdefghijklmnopqrstuvwxyz', '5A238735D9E902E16CAD81229CC981A763508C73F4A52DD0'), + (4, 192, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'D51D73EB03B0D841C24F2007AA9159F0F70A971CBFBED33C'), + (4, 224, '', 'AACD8950B239B05E8A40A0419AFD3BBED206623913D8A6DFE71D174B'), + (4, 224, 'a', '54A26096C951725228D34A1B55C2DB5C28446E6B243FE2AE78623A4B'), + (4, 224, 'HAVAL', 'F9040EEBAE11709245501BEFFB5FB849F88A9086F24DF3A55A03A01A'), + (4, 224, '0123456789', + '144CB2DE11F05DF7C356282A3B485796DA653F6B702868C7DCF4AE76'), + (4, 224, 'abcdefghijklmnopqrstuvwxyz', 'FBB63F06592FB9AA4F59652B99BC53C1FF72675726C71326C682DABC'), + (4, 224, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', '1120B26105044DF0B4E5B904705F3B8CBBC14A52B73301C300BAFF8A'), + (4, 256, '', '75128F15B3DAADD15D3C1D85F88A8B7E39948BABA7B2F4EA3D7B8E9A75A710E7'), + (4, 256, 'a', 'D710C33A848E8138CCE2C3D8B4DE2252A55C75FD60B6B4E2D6C39EBBAB215EBF'), + (4, 256, 'HAVAL', '0D25D9903F37C840250179821DB66353CC490DF2B90A45A5E20060D5F2D67593'), + (4, 256, '0123456789', 'AEEF777C84664C3D07A939815E45DEF9C1750A6505DD24BF7FCEB88282A33DD2'), + (4, 256, 'abcdefghijklmnopqrstuvwxyz', 'CB8C4E5C74AD38DB97031A51C0125CDF466E396C16674479DDF57D5A7E5DB55B'), + (4, 256, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', '5A1910C4F800FF94B2D56403EF6371452EB50275896EFD57B4E2E716E955D6D1'), + (5, 128, '', '03FD808CAC90089E43B62B1C522953F8'), + (5, 128, 'a', '50FA56EAF75E634D63F2CDB6409125BA'), + (5, 128, 'HAVAL', 'C91CDFB768369566270A5E7F4B20E3B9'), + (5, 128, '0123456789', '29C0AAB1F6D03E11DFC1CF2B9E4DE91A'), + (5, 128, 'abcdefghijklmnopqrstuvwxyz', '6994C3F53914D42E736EA81427A408EC'), + (5, 128, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', '54E55ECCAC0BF7C7BBA47A49B3D27422'), + (5, 160, '', 'A8213CACD787CEBF23F416049540AAFB637D34F2'), + (5, 160, 'a', 'B960F55EADD6091ADEC094827455584131F3C62C'), + (5, 160, 'HAVAL', 'F9E24DD43FD5D3EA5B45D046529C7DDB93937F8B'), + (5, 160, '0123456789', 'A7C539EA79EAAF856089FA6DED81F60374E95A96'), + (5, 160, 'abcdefghijklmnopqrstuvwxyz', '40B8769D3C555E43DCF235C4F1E5E130EEE2CAC7'), + (5, 160, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'F4E246BC33D04DA73B0D0EDDF94CFA5824CF2063'), + (5, 192, '', 'A352EF5FC2004ECE93BACC1C7429B8DD66ED270FD7EE5B0D'), + (5, 192, 'a', 'FAF3158553F717992FFB29595FBC23E93AF632A5A2782C57'), + (5, 192, 'HAVAL', 'BED2A1EBD9CB2F89F65FE33254E460E5019275191589A739'), + (5, 192, '0123456789', '56048B23FAEEFBC90174B8238F1D8BB92FAEDAA6D73C8A93'), + (5, 192, 'abcdefghijklmnopqrstuvwxyz', 'D8D531F3872B42A23899A2057A503F15D8DCB19DB2361AE1'), + (5, 192, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', '20F3BA5DF1971041D4D2EC4724DCBC090A9BBB07151B9FE0'), + (5, 224, '', 'E84C8F723F431282E88B9D3F0AD44504769402D07E46F8F8B326438D'), + (5, 224, 'a', '23AD516F08E024A1C0976A25DDB161797E8EE2E4B657D725704216D6'), + (5, 224, 'HAVAL', 'A23B187B07B8F7A1B2DE0AE5408EF3CBA8830565355FF59D53CAB0E1'), + (5, 224, '0123456789', '9A050B4386E51F72E0F0088AF1917E737E66036F8561CFC25CDDB8DB'), + (5, 224, 'abcdefghijklmnopqrstuvwxyz', 'B917D5FB2637AEEF80EFC747CCD44E31B08726AA5890C20FD7757045'), + (5, 224, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'B70ADBC1504075689EBB71BF9B7D7A849805B77D37C5E1C209AA4E71'), + (5, 256, '', '5981D3F8CCE7F5674752595F4AD24C184BA1C738C986D4D2EDDF2BD86C3F8679'), + (5, 256, 'a', '166F2218E0994A78EBAD3FEAB0211B612B14E93E5CCEB60E6F143DF0FA166D39'), + (5, 256, 'HAVAL', '217BFDF84F5C775596C2F13CEEA7417CD4E198D53CA24902F9717585EC5789AC'), + (5, 256, '0123456789', 'A6828EEB82D5A9CBFC7C522AD4B3C38A42753DECEB20FB3A6FABC0DA8CCD6A1A'), + (5, 256, "abcdefghijklmnopqrstuvwxyz", + '1A1DC8099BDAA7F35B4DA4E805F1A28FEE909D8DEE920198185CBCAED8A10A8D'), + (5, 256, + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + 'C5647FC6C1877FFF96742F27E9266B6874894F41A08F5913033D9D532AEDDB39') + ] + +ripemd = [ ("", 0x9c1185a5c5e9fc54612808977ee8f548b2258d31L), + ("a", 0x0bdc9d2d256b3ee9daae347be6f4dc835a467ffeL), + ("abc", 0x8eb208f7e05d987a9b044a8e98c6b087f15a0bfcL), + ("message digest", 0x5d0689ef49d2fae572b881b123a85ffa21595f36L), + ("abcdefghijklmnopqrstuvwxyz", + 0xf71c27109c692c1b56bbdceb5b9d2865b3708dbcL), + ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 0x12a053384a9c0c88e405a06c27dcf49ada62eb2bL), + ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + 0xb0e20b6e3116640286ed3a87a5713079b21f5189L), + (8 *"1234567890", 0x9b752e45573d4b39f4dbd3323cab82bf63326bfbL) +] + + +# DES validation data + +# Key, IV, plaintext, ciphertext +des_cbc= [('0123456789abcdef', 'fedcba9876543210', + "7654321 Now is the time for \000\000\000\000", 'ccd173ffab2039f4acd8aefddfd8a1eb468e91157888ba681d269397f7fe62b4')] + +des = [ ('0000000000000000', '0000000000000000', '8CA64DE9C1B123A7'), + ('FFFFFFFFFFFFFFFF', 'FFFFFFFFFFFFFFFF', '7359B2163E4EDC58'), + ('3000000000000000', '1000000000000001', '958E6E627A05557B'), + ('1111111111111111', '1111111111111111', 'F40379AB9E0EC533'), + ('0123456789ABCDEF', '1111111111111111', '17668DFC7292532D'), + ('1111111111111111', '0123456789ABCDEF', '8A5AE1F81AB8F2DD'), + ('0000000000000000', '0000000000000000', '8CA64DE9C1B123A7'), + ('FEDCBA9876543210', '0123456789ABCDEF', 'ED39D950FA74BCC4'), + ('7CA110454A1A6E57', '01A1D6D039776742', '690F5B0D9A26939B'), + ('0131D9619DC1376E', '5CD54CA83DEF57DA', '7A389D10354BD271'), + ('07A1133E4A0B2686', '0248D43806F67172', '868EBB51CAB4599A'), + ('3849674C2602319E', '51454B582DDF440A', '7178876E01F19B2A'), + ('04B915BA43FEB5B6', '42FD443059577FA2', 'AF37FB421F8C4095'), + ('0113B970FD34F2CE', '059B5E0851CF143A', '86A560F10EC6D85B'), + ('0170F175468FB5E6', '0756D8E0774761D2', '0CD3DA020021DC09'), + ('43297FAD38E373FE', '762514B829BF486A', 'EA676B2CB7DB2B7A'), + ('07A7137045DA2A16', '3BDD119049372802', 'DFD64A815CAF1A0F'), + ('04689104C2FD3B2F', '26955F6835AF609A', '5C513C9C4886C088'), + ('37D06BB516CB7546', '164D5E404F275232', '0A2AEEAE3FF4AB77'), + ('1F08260D1AC2465E', '6B056E18759F5CCA', 'EF1BF03E5DFA575A'), + ('584023641ABA6176', '004BD6EF09176062', '88BF0DB6D70DEE56'), + ('025816164629B007', '480D39006EE762F2', 'A1F9915541020B56'), + ('49793EBC79B3258F', '437540C8698F3CFA', '6FBF1CAFCFFD0556'), + ('4FB05E1515AB73A7', '072D43A077075292', '2F22E49BAB7CA1AC'), + ('49E95D6D4CA229BF', '02FE55778117F12A', '5A6B612CC26CCE4A'), + ('018310DC409B26D6', '1D9D5C5018F728C2', '5F4C038ED12B2E41'), + ('1C587F1C13924FEF', '305532286D6F295A', '63FAC0D034D9F793'), + ('0101010101010101', '0123456789ABCDEF', '617B3A0CE8F07100'), + ('1F1F1F1F0E0E0E0E', '0123456789ABCDEF', 'DB958605F8C8C606'), + ('E0FEE0FEF1FEF1FE', '0123456789ABCDEF', 'EDBFD1C66C29CCC7'), + ('0000000000000000', 'FFFFFFFFFFFFFFFF', '355550B2150E2451'), + ('FFFFFFFFFFFFFFFF', '0000000000000000', 'CAAAAF4DEAF1DBAE'), + ('0123456789ABCDEF', '0000000000000000', 'D5D44FF720683D0D'), + ('FEDCBA9876543210', 'FFFFFFFFFFFFFFFF', '2A2BB008DF97C2F2'), + ('0101010101010101', '95F8A5E5DD31D900', '8000000000000000'), + ('0101010101010101', 'DD7F121CA5015619', '4000000000000000'), + ('0101010101010101', '2E8653104F3834EA', '2000000000000000'), + ('0101010101010101', '4BD388FF6CD81D4F', '1000000000000000'), + ('0101010101010101', '20B9E767B2FB1456', '0800000000000000'), + ('0101010101010101', '55579380D77138EF', '0400000000000000'), + ('0101010101010101', '6CC5DEFAAF04512F', '0200000000000000'), + ('0101010101010101', '0D9F279BA5D87260', '0100000000000000'), + ('0101010101010101', 'D9031B0271BD5A0A', '0080000000000000'), + ('0101010101010101', '424250B37C3DD951', '0040000000000000'), + ('0101010101010101', 'B8061B7ECD9A21E5', '0020000000000000'), + ('0101010101010101', 'F15D0F286B65BD28', '0010000000000000'), + ('0101010101010101', 'ADD0CC8D6E5DEBA1', '0008000000000000'), + ('0101010101010101', 'E6D5F82752AD63D1', '0004000000000000'), + ('0101010101010101', 'ECBFE3BD3F591A5E', '0002000000000000'), + ('0101010101010101', 'F356834379D165CD', '0001000000000000'), + ('0101010101010101', '2B9F982F20037FA9', '0000800000000000'), + ('0101010101010101', '889DE068A16F0BE6', '0000400000000000'), + ('0101010101010101', 'E19E275D846A1298', '0000200000000000'), + ('0101010101010101', '329A8ED523D71AEC', '0000100000000000'), + ('0101010101010101', 'E7FCE22557D23C97', '0000080000000000'), + ('0101010101010101', '12A9F5817FF2D65D', '0000040000000000'), + ('0101010101010101', 'A484C3AD38DC9C19', '0000020000000000'), + ('0101010101010101', 'FBE00A8A1EF8AD72', '0000010000000000'), + ('0101010101010101', '750D079407521363', '0000008000000000'), + ('0101010101010101', '64FEED9C724C2FAF', '0000004000000000'), + ('0101010101010101', 'F02B263B328E2B60', '0000002000000000'), + ('0101010101010101', '9D64555A9A10B852', '0000001000000000'), + ('0101010101010101', 'D106FF0BED5255D7', '0000000800000000'), + ('0101010101010101', 'E1652C6B138C64A5', '0000000400000000'), + ('0101010101010101', 'E428581186EC8F46', '0000000200000000'), + ('0101010101010101', 'AEB5F5EDE22D1A36', '0000000100000000'), + ('0101010101010101', 'E943D7568AEC0C5C', '0000000080000000'), + ('0101010101010101', 'DF98C8276F54B04B', '0000000040000000'), + ('0101010101010101', 'B160E4680F6C696F', '0000000020000000'), + ('0101010101010101', 'FA0752B07D9C4AB8', '0000000010000000'), + ('0101010101010101', 'CA3A2B036DBC8502', '0000000008000000'), + ('0101010101010101', '5E0905517BB59BCF', '0000000004000000'), + ('0101010101010101', '814EEB3B91D90726', '0000000002000000'), + ('0101010101010101', '4D49DB1532919C9F', '0000000001000000'), + ('0101010101010101', '25EB5FC3F8CF0621', '0000000000800000'), + ('0101010101010101', 'AB6A20C0620D1C6F', '0000000000400000'), + ('0101010101010101', '79E90DBC98F92CCA', '0000000000200000'), + ('0101010101010101', '866ECEDD8072BB0E', '0000000000100000'), + ('0101010101010101', '8B54536F2F3E64A8', '0000000000080000'), + ('0101010101010101', 'EA51D3975595B86B', '0000000000040000'), + ('0101010101010101', 'CAFFC6AC4542DE31', '0000000000020000'), + ('0101010101010101', '8DD45A2DDF90796C', '0000000000010000'), + ('0101010101010101', '1029D55E880EC2D0', '0000000000008000'), + ('0101010101010101', '5D86CB23639DBEA9', '0000000000004000'), + ('0101010101010101', '1D1CA853AE7C0C5F', '0000000000002000'), + ('0101010101010101', 'CE332329248F3228', '0000000000001000'), + ('0101010101010101', '8405D1ABE24FB942', '0000000000000800'), + ('0101010101010101', 'E643D78090CA4207', '0000000000000400'), + ('0101010101010101', '48221B9937748A23', '0000000000000200'), + ('0101010101010101', 'DD7C0BBD61FAFD54', '0000000000000100'), + ('0101010101010101', '2FBC291A570DB5C4', '0000000000000080'), + ('0101010101010101', 'E07C30D7E4E26E12', '0000000000000040'), + ('0101010101010101', '0953E2258E8E90A1', '0000000000000020'), + ('0101010101010101', '5B711BC4CEEBF2EE', '0000000000000010'), + ('0101010101010101', 'CC083F1E6D9E85F6', '0000000000000008'), + ('0101010101010101', 'D2FD8867D50D2DFE', '0000000000000004'), + ('0101010101010101', '06E7EA22CE92708F', '0000000000000002'), + ('0101010101010101', '166B40B44ABA4BD6', '0000000000000001'), + ('8001010101010101', '0000000000000000', '95A8D72813DAA94D'), + ('4001010101010101', '0000000000000000', '0EEC1487DD8C26D5'), + ('2001010101010101', '0000000000000000', '7AD16FFB79C45926'), + ('1001010101010101', '0000000000000000', 'D3746294CA6A6CF3'), + ('0801010101010101', '0000000000000000', '809F5F873C1FD761'), + ('0401010101010101', '0000000000000000', 'C02FAFFEC989D1FC'), + ('0201010101010101', '0000000000000000', '4615AA1D33E72F10'), + ('0180010101010101', '0000000000000000', '2055123350C00858'), + ('0140010101010101', '0000000000000000', 'DF3B99D6577397C8'), + ('0120010101010101', '0000000000000000', '31FE17369B5288C9'), + ('0110010101010101', '0000000000000000', 'DFDD3CC64DAE1642'), + ('0108010101010101', '0000000000000000', '178C83CE2B399D94'), + ('0104010101010101', '0000000000000000', '50F636324A9B7F80'), + ('0102010101010101', '0000000000000000', 'A8468EE3BC18F06D'), + ('0101800101010101', '0000000000000000', 'A2DC9E92FD3CDE92'), + ('0101400101010101', '0000000000000000', 'CAC09F797D031287'), + ('0101200101010101', '0000000000000000', '90BA680B22AEB525'), + ('0101100101010101', '0000000000000000', 'CE7A24F350E280B6'), + ('0101080101010101', '0000000000000000', '882BFF0AA01A0B87'), + ('0101040101010101', '0000000000000000', '25610288924511C2'), + ('0101020101010101', '0000000000000000', 'C71516C29C75D170'), + ('0101018001010101', '0000000000000000', '5199C29A52C9F059'), + ('0101014001010101', '0000000000000000', 'C22F0A294A71F29F'), + ('0101012001010101', '0000000000000000', 'EE371483714C02EA'), + ('0101011001010101', '0000000000000000', 'A81FBD448F9E522F'), + ('0101010801010101', '0000000000000000', '4F644C92E192DFED'), + ('0101010401010101', '0000000000000000', '1AFA9A66A6DF92AE'), + ('0101010201010101', '0000000000000000', 'B3C1CC715CB879D8'), + ('0101010180010101', '0000000000000000', '19D032E64AB0BD8B'), + ('0101010140010101', '0000000000000000', '3CFAA7A7DC8720DC'), + ('0101010120010101', '0000000000000000', 'B7265F7F447AC6F3'), + ('0101010110010101', '0000000000000000', '9DB73B3C0D163F54'), + ('0101010108010101', '0000000000000000', '8181B65BABF4A975'), + ('0101010104010101', '0000000000000000', '93C9B64042EAA240'), + ('0101010102010101', '0000000000000000', '5570530829705592'), + ('0101010101800101', '0000000000000000', '8638809E878787A0'), + ('0101010101400101', '0000000000000000', '41B9A79AF79AC208'), + ('0101010101200101', '0000000000000000', '7A9BE42F2009A892'), + ('0101010101100101', '0000000000000000', '29038D56BA6D2745'), + ('0101010101080101', '0000000000000000', '5495C6ABF1E5DF51'), + ('0101010101040101', '0000000000000000', 'AE13DBD561488933'), + ('0101010101020101', '0000000000000000', '024D1FFA8904E389'), + ('0101010101018001', '0000000000000000', 'D1399712F99BF02E'), + ('0101010101014001', '0000000000000000', '14C1D7C1CFFEC79E'), + ('0101010101012001', '0000000000000000', '1DE5279DAE3BED6F'), + ('0101010101011001', '0000000000000000', 'E941A33F85501303'), + ('0101010101010801', '0000000000000000', 'DA99DBBC9A03F379'), + ('0101010101010401', '0000000000000000', 'B7FC92F91D8E92E9'), + ('0101010101010201', '0000000000000000', 'AE8E5CAA3CA04E85'), + ('0101010101010180', '0000000000000000', '9CC62DF43B6EED74'), + ('0101010101010140', '0000000000000000', 'D863DBB5C59A91A0'), + ('0101010101010120', '0000000000000000', 'A1AB2190545B91D7'), + ('0101010101010110', '0000000000000000', '0875041E64C570F7'), + ('0101010101010108', '0000000000000000', '5A594528BEBEF1CC'), + ('0101010101010104', '0000000000000000', 'FCDB3291DE21F0C0'), + ('0101010101010102', '0000000000000000', '869EFD7F9F265A09'), + ('1046913489980131', '0000000000000000', '88D55E54F54C97B4'), + ('1007103489988020', '0000000000000000', '0C0CC00C83EA48FD'), + ('10071034C8980120', '0000000000000000', '83BC8EF3A6570183'), + ('1046103489988020', '0000000000000000', 'DF725DCAD94EA2E9'), + ('1086911519190101', '0000000000000000', 'E652B53B550BE8B0'), + ('1086911519580101', '0000000000000000', 'AF527120C485CBB0'), + ('5107B01519580101', '0000000000000000', '0F04CE393DB926D5'), + ('1007B01519190101', '0000000000000000', 'C9F00FFC74079067'), + ('3107915498080101', '0000000000000000', '7CFD82A593252B4E'), + ('3107919498080101', '0000000000000000', 'CB49A2F9E91363E3'), + ('10079115B9080140', '0000000000000000', '00B588BE70D23F56'), + ('3107911598080140', '0000000000000000', '406A9A6AB43399AE'), + ('1007D01589980101', '0000000000000000', '6CB773611DCA9ADA'), + ('9107911589980101', '0000000000000000', '67FD21C17DBB5D70'), + ('9107D01589190101', '0000000000000000', '9592CB4110430787'), + ('1007D01598980120', '0000000000000000', 'A6B7FF68A318DDD3'), + ('1007940498190101', '0000000000000000', '4D102196C914CA16'), + ('0107910491190401', '0000000000000000', '2DFA9F4573594965'), + ('0107910491190101', '0000000000000000', 'B46604816C0E0774'), + ('0107940491190401', '0000000000000000', '6E7E6221A4F34E87'), + ('19079210981A0101', '0000000000000000', 'AA85E74643233199'), + ('1007911998190801', '0000000000000000', '2E5A19DB4D1962D6'), + ('10079119981A0801', '0000000000000000', '23A866A809D30894'), + ('1007921098190101', '0000000000000000', 'D812D961F017D320'), + ('100791159819010B', '0000000000000000', '055605816E58608F'), + ('1004801598190101', '0000000000000000', 'ABD88E8B1B7716F1'), + ('1004801598190102', '0000000000000000', '537AC95BE69DA1E1'), + ('1004801598190108', '0000000000000000', 'AED0F6AE3C25CDD8'), + ('1002911598100104', '0000000000000000', 'B3E35A5EE53E7B8D'), + ('1002911598190104', '0000000000000000', '61C79C71921A2EF8'), + ('1002911598100201', '0000000000000000', 'E2F5728F0995013C'), + ('1002911698100101', '0000000000000000', '1AEAC39A61F0A464'), + ('7CA110454A1A6E57', '01A1D6D039776742', '690F5B0D9A26939B'), + ('0131D9619DC1376E', '5CD54CA83DEF57DA', '7A389D10354BD271'), + ('07A1133E4A0B2686', '0248D43806F67172', '868EBB51CAB4599A'), + ('3849674C2602319E', '51454B582DDF440A', '7178876E01F19B2A'), + ('04B915BA43FEB5B6', '42FD443059577FA2', 'AF37FB421F8C4095'), + ('0113B970FD34F2CE', '059B5E0851CF143A', '86A560F10EC6D85B'), + ('0170F175468FB5E6', '0756D8E0774761D2', '0CD3DA020021DC09'), + ('43297FAD38E373FE', '762514B829BF486A', 'EA676B2CB7DB2B7A'), + ('07A7137045DA2A16', '3BDD119049372802', 'DFD64A815CAF1A0F'), + ('04689104C2FD3B2F', '26955F6835AF609A', '5C513C9C4886C088'), + ('37D06BB516CB7546', '164D5E404F275232', '0A2AEEAE3FF4AB77'), + ('1F08260D1AC2465E', '6B056E18759F5CCA', 'EF1BF03E5DFA575A'), + ('584023641ABA6176', '004BD6EF09176062', '88BF0DB6D70DEE56'), + ('025816164629B007', '480D39006EE762F2', 'A1F9915541020B56'), + ('49793EBC79B3258F', '437540C8698F3CFA', '6FBF1CAFCFFD0556'), + ('4FB05E1515AB73A7', '072D43A077075292', '2F22E49BAB7CA1AC'), + ('49E95D6D4CA229BF', '02FE55778117F12A', '5A6B612CC26CCE4A'), + ('018310DC409B26D6', '1D9D5C5018F728C2', '5F4C038ED12B2E41'), + ('1C587F1C13924FEF', '305532286D6F295A', '63FAC0D034D9F793') ] + + +# LOKI91 Validation Data Suite +loki91 = [ +# List of weak and semi-weak keys +# +# Weak Keys +# +('0000000000000000', '0000000000000000', 'bd84a2085ef609c7'), +('0000000000000000', 'bd84a2085ef609c7', '0000000000000000'), +('ffffffffffffffff', '0000000000000000', '5c77e002d1991c4d'), +('ffffffffffffffff', '5c77e002d1991c4d', '0000000000000000'), +('55555555aaaaaaaa', '0000000000000000', '71fd6dc44bf4e881'), +('55555555aaaaaaaa', '71fd6dc44bf4e881', '0000000000000000'), +('aaaaaaaa55555555', '0000000000000000', '65b38fdc551b2576'), +('aaaaaaaa55555555', '65b38fdc551b2576', '0000000000000000'), +# +# Semi-Weak Keys +# +('0000000055555555', '0000000000000000', '85d84cad08342044'), +('0000000055555555', '8621ccb894705f8f', '0000000000000000'), +('aaaaaaaa00000000', '0000000000000000', '8621ccb894705f8f'), +('aaaaaaaa00000000', '85d84cad08342044', '0000000000000000'), +# +('00000000aaaaaaaa', '0000000000000000', '971fe23b8904399b'), +('00000000aaaaaaaa', 'e10c4efc9d4c9b52', '0000000000000000'), +('5555555500000000', '0000000000000000', 'e10c4efc9d4c9b52'), +('5555555500000000', '971fe23b8904399b', '0000000000000000'), +# +('00000000ffffffff', '0000000000000000', 'ca30a526e3bf6fe9'), +('00000000ffffffff', '9cc24ace4d477f39', '0000000000000000'), +('ffffffff00000000', '0000000000000000', '9cc24ace4d477f39'), +('ffffffff00000000', 'ca30a526e3bf6fe9', '0000000000000000'), +# +('5555555555555555', '0000000000000000', 'bc85de5e486d7ddb'), +('5555555555555555', '504d4c4fea439b63', '0000000000000000'), +('aaaaaaaaaaaaaaaa', '0000000000000000', '504d4c4fea439b63'), +('aaaaaaaaaaaaaaaa', 'bc85de5e486d7ddb', '0000000000000000'), +# +('55555555ffffffff', '0000000000000000', '0bef28021cd23fcb'), +('55555555ffffffff', 'baa5521bd8ff9f75', '0000000000000000'), +('ffffffffaaaaaaaa', '0000000000000000', 'baa5521bd8ff9f75'), +('ffffffffaaaaaaaa', '0bef28021cd23fcb', '0000000000000000'), +# +('aaaaaaaaffffffff', '0000000000000000', 'a78c6f84509c4d9a'), +('aaaaaaaaffffffff', '05dac4a04d83c0ab', '0000000000000000'), +('ffffffff55555555', '0000000000000000', '05dac4a04d83c0ab'), +('ffffffff55555555', 'a78c6f84509c4d9a', '0000000000000000'), +# +# Inversion of all the key and plaintext bits results +# in the inversion of all the ciphertext bits +# +('0123456789abcdef', '0000000000000000', 'd26de3321aaa29f6'), +('0123456789abcdef', 'fcf597b7da5a3a3d', '0000000000000000'), +('fedcba9876543210', 'ffffffffffffffff', '2d921ccde555d609'), +('fedcba9876543210', '030a684825a5c5c2', 'ffffffffffffffff'), +# +# Other assorted values +# +('0000000000000000', '0000000000000000', 'bd84a2085ef609c7'), +('0000000000000000', 'bd84a2085ef609c7', '0000000000000000'), +('0000000000000000', '355550b2150e2451', '3644dd20d5ba5d10'), +('0000000000000000', '3644dd20d5ba5d10', '355550b2150e2451'), +('0000000000000000', '35a7bae825c0d73b', '826ff28db7edf0a3'), +('0000000000000000', '826ff28db7edf0a3', '35a7bae825c0d73b'), +('0000000000000000', '8ca64de9c1b123a7', 'fb3038a35a93646b'), +('0000000000000000', 'fb3038a35a93646b', '8ca64de9c1b123a7'), +('0000000000000000', '8e2a251b94704c69', '4829a2faf34973e8'), +('0000000000000000', '4829a2faf34973e8', '8e2a251b94704c69'), +('0000000000000000', 'ffffffffffffffff', 'a3881ffd2e66e3b2'), +('0000000000000000', 'a3881ffd2e66e3b2', 'ffffffffffffffff'), +('0101010101010101', '0123456789abcdef', '7935b683eada00f4'), +('0101010101010101', 'ec5960c9d3bf1e1e', '0123456789abcdef'), +('0101010101010101', '617b3a0ce8f07100', 'a6090c5f651d84ff'), +('0101010101010101', '8eeb7798a291b0b3', '617b3a0ce8f07100'), +('0101010101010101', '9b38f6ce85aab9c3', '2165fc9b5a4430d4'), +('0101010101010101', '2143badadbd4f539', '9b38f6ce85aab9c3'), +('0113b970fd34f2ce', '059b5e0851cf143a', 'f1f6daa4368d32c0'), +('0113b970fd34f2ce', '313b8c589bcf2955', '059b5e0851cf143a'), +('0113b970fd34f2ce', '7514cdb961b6760d', 'a2f3f0e425f6cd30'), +('0113b970fd34f2ce', '31b8050defd959ce', '7514cdb961b6760d'), +('0113b970fd34f2ce', '86a560f10ec6d85b', 'a27506525cf66e5c'), +('0113b970fd34f2ce', '6566f294daf82c26', '86a560f10ec6d85b'), +('0123456789abcdef', '0000000000000000', 'd26de3321aaa29f6'), +('0123456789abcdef', 'fcf597b7da5a3a3d', '0000000000000000'), +('0123456789abcdef', '1111111111111111', 'e1dd3b59134f32ab'), +('0123456789abcdef', 'c7c0ea4a6a9d3360', '1111111111111111'), +('0123456789abcdef', '17668dfc7292532d', '7aab3b9a42feceb2'), +('0123456789abcdef', '0a9462fa8fee7f4a', '17668dfc7292532d'), +('0123456789abcdef', '23c086665917b8e1', '9fbc1a193dcee02d'), +('0123456789abcdef', '1cb728f0c1b26f75', '23c086665917b8e1'), +('0123456789abcdef', 'd5d44ff720683d0d', '621f20a1884677f3'), +('0123456789abcdef', 'c8c9a1ace8cf89dc', 'd5d44ff720683d0d'), +('0123456789abcdef', 'fce30226576320bd', '5da683da7e9fd159'), +('0123456789abcdef', '5edf1c8466ce6cca', 'fce30226576320bd'), +('0131d9619dc1376e', '5cd54ca83def57da', '018781deb9ea97cc'), +('0131d9619dc1376e', 'bd1084a4b6d1ab38', '5cd54ca83def57da'), +('0131d9619dc1376e', '65e160aed7b773a9', '57e3c651e72c6d99'), +('0131d9619dc1376e', 'aeab88168e9c0e3e', '65e160aed7b773a9'), +('0131d9619dc1376e', '7a389d10354bd271', '5520951007ac3123'), +('0131d9619dc1376e', '78eaddbfcb1ca8f2', '7a389d10354bd271'), +('0170f175468fb5e6', '0756d8e0774761d2', 'ab786040f925d13e'), +('0170f175468fb5e6', 'b7c1284ca7043dcb', '0756d8e0774761d2'), +('0170f175468fb5e6', '0cd3da020021dc09', '77ce52c63517b689'), +('0170f175468fb5e6', '802191063dd8ff06', '0cd3da020021dc09'), +('0170f175468fb5e6', '914c1806fccbce33', 'dd546f5263780633'), +('0170f175468fb5e6', '5356fadab9832b2c', '914c1806fccbce33'), +('018310dc409b26d6', '1d9d5c5018f728c2', '80d1106e854f7296'), +('018310dc409b26d6', '0eb06d867b7034b3', '1d9d5c5018f728c2'), +('018310dc409b26d6', '5a0bf934fd6009f8', 'f67c664125809846'), +('018310dc409b26d6', '9c1edf390e47f09a', '5a0bf934fd6009f8'), +('018310dc409b26d6', '5f4c038ed12b2e41', '20a61c3bc186b0b2'), +('018310dc409b26d6', 'e93ae52452b20098', '5f4c038ed12b2e41'), +('025816164629b007', '480d39006ee762f2', '8197cc96e066f971'), +('025816164629b007', '8e67efe514fa5985', '480d39006ee762f2'), +('025816164629b007', 'a1f9915541020b56', '0e1b15aa45daad23'), +('025816164629b007', '7b1bd88c59347ee4', 'a1f9915541020b56'), +('025816164629b007', 'ec92e65da168b46f', 'fd5d36553cc7cdad'), +('025816164629b007', '10f09ce8ec84f207', 'ec92e65da168b46f'), +('04689104c2fd3b2f', '26955f6835af609a', '082133fd6bfa1e88'), +('04689104c2fd3b2f', '36153da8e8e4a67a', '26955f6835af609a'), +('04689104c2fd3b2f', '5265227fe08a28ec', '1b03084901b20c14'), +('04689104c2fd3b2f', 'f2e7e1a4678d10a7', '5265227fe08a28ec'), +('04689104c2fd3b2f', '5c513c9c4886c088', 'db8e496a8aa4eae8'), +('04689104c2fd3b2f', '49a2827db6293c72', '5c513c9c4886c088'), +('04b915ba43feb5b6', '42fd443059577fa2', 'b794c8fec41d93fd'), +('04b915ba43feb5b6', '688762c5782c8d46', '42fd443059577fa2'), +('04b915ba43feb5b6', 'a483ea7ccf2e0e5a', '1319804d5e6477fb'), +('04b915ba43feb5b6', 'a4407673e0289704', 'a483ea7ccf2e0e5a'), +('04b915ba43feb5b6', 'af37fb421f8c4095', '5e1fbeca6d3dd240'), +('04b915ba43feb5b6', '76f8a9d3d646b45a', 'af37fb421f8c4095'), +('07a1133e4a0b2686', '0248d43806f67172', 'bdc50f07265f98bb'), +('07a1133e4a0b2686', '5352ea74d4f98d17', '0248d43806f67172'), +('07a1133e4a0b2686', '624f2e2dfa008142', '43966fba71697d9e'), +('07a1133e4a0b2686', '94e8f64562868d44', '624f2e2dfa008142'), +('07a1133e4a0b2686', '868ebb51cab4599a', '2bc4d7ac9f09e2cf'), +('07a1133e4a0b2686', '38df7d98c5841cb1', '868ebb51cab4599a'), +('07a7137045da2a16', '28e686668c3bd6d9', '5ddb3c7fc1c8ce22'), +('07a7137045da2a16', 'a725d943d567693c', '28e686668c3bd6d9'), +('07a7137045da2a16', '3bdd119049372802', '3f0db5539d36d6ff'), +('07a7137045da2a16', 'e617587ef5ec4992', '3bdd119049372802'), +('07a7137045da2a16', 'dfd64a815caf1a0f', 'a4af3b0773dad9be'), +('07a7137045da2a16', '3000a06acca1e95c', 'dfd64a815caf1a0f'), +('1111111111111111', '0123456789abcdef', '75f0c8bc2bafd309'), +('1111111111111111', '46b19d48d8569e55', '0123456789abcdef'), +('1111111111111111', '1111111111111111', 'cc8a79bccae0bb7e'), +('1111111111111111', 'd96e387165e701bc', '1111111111111111'), +('1111111111111111', '24900548c21a3567', '93f5dbd7f154b9ba'), +('1111111111111111', '66b41b7d36c436de', '24900548c21a3567'), +('1111111111111111', '8a5ae1f81ab8f2dd', '20f677bebabe1775'), +('1111111111111111', 'd96faf34f9f90454', '8a5ae1f81ab8f2dd'), +('1111111111111111', 'a273d7cb7d390531', '70ab8b1bc85a3535'), +('1111111111111111', '44260ee8c8eeb426', 'a273d7cb7d390531'), +('1111111111111111', 'f40379ab9e0ec533', '06f8080ae1393c2f'), +('1111111111111111', '9ae34de86300215f', 'f40379ab9e0ec533'), +('1c587f1c13924fef', '305532286d6f295a', '2fc181b0cb6b30b7'), +('1c587f1c13924fef', 'e9eef1eb68e8c676', '305532286d6f295a'), +('1c587f1c13924fef', '63fac0d034d9f793', 'e3f0d7afe60812b2'), +('1c587f1c13924fef', '7409d13ab01f25f6', '63fac0d034d9f793'), +('1c587f1c13924fef', 'f63de067f58c38ed', '3f4913280d2793fc'), +('1c587f1c13924fef', '0109eccd8a9c40dd', 'f63de067f58c38ed'), +('1f08260d1ac2465e', '2c0a241eb9f05999', 'e86f92ac2ec186f3'), +('1f08260d1ac2465e', '2dbafaaf1eb7b072', '2c0a241eb9f05999'), +('1f08260d1ac2465e', '6b056e18759f5cca', '02dd8970249255f1'), +('1f08260d1ac2465e', '377f235592b45ca9', '6b056e18759f5cca'), +('1f08260d1ac2465e', 'ef1bf03e5dfa575a', '98e1e5771c4a9455'), +('1f08260d1ac2465e', 'f489970bb6e8a94e', 'ef1bf03e5dfa575a'), +('1f1f1f1f0e0e0e0e', '0123456789abcdef', '1998d96b0bb0add6'), +('1f1f1f1f0e0e0e0e', '7454d8d7c56be016', '0123456789abcdef'), +('1f1f1f1f0e0e0e0e', '322f206b2d39d65d', '0b636eb981e987c0'), +('1f1f1f1f0e0e0e0e', '0b926c6049361bff', '322f206b2d39d65d'), +('1f1f1f1f0e0e0e0e', 'db958605f8c8c606', '5a153aea62bdd0cb'), +('1f1f1f1f0e0e0e0e', '2c7bf241aa0b2c97', 'db958605f8c8c606'), +('3000000000000000', '1000000000000001', '12f2d0471de69a16'), +('3000000000000000', 'b0ef21ad8b82dd0a', '1000000000000001'), +('3000000000000000', '958e6e627a05557b', '4be1b16ef2c9524a'), +('3000000000000000', '437b3ac6a2a027c8', '958e6e627a05557b'), +('3000000000000000', 'd3c4539579b96231', 'bbfcaf9cc8cd9e46'), +('3000000000000000', '7b8b6b2e19e13c69', 'd3c4539579b96231'), +('37d06bb516cb7546', '0a2aeeae3ff4ab77', '32a65f25341d17f0'), +('37d06bb516cb7546', '90d6dcee0f2f8d62', '0a2aeeae3ff4ab77'), +('37d06bb516cb7546', '164d5e404f275232', '782a69ec6256c642'), +('37d06bb516cb7546', '1dabf24c62f0fd89', '164d5e404f275232'), +('37d06bb516cb7546', '19acae3136c0bc7c', '9cd87a6a8ea4c562'), +('37d06bb516cb7546', 'f8bf4d34438681a2', '19acae3136c0bc7c'), +('3849674c2602319e', '126898d55e911500', 'c86caec1e3b7b17e'), +('3849674c2602319e', 'd8d28161851ed6d6', '126898d55e911500'), +('3849674c2602319e', '51454b582ddf440a', '8904c12a608bac74'), +('3849674c2602319e', '562de7a88b1f4877', '51454b582ddf440a'), +('3849674c2602319e', '7178876e01f19b2a', 'ba940cd1789e92fd'), +('3849674c2602319e', '295ece9867b10e95', '7178876e01f19b2a'), +('43297fad38e373fe', '4c974f1caa59f5d4', '71a64728851d6e14'), +('43297fad38e373fe', 'f386803c5a434216', '4c974f1caa59f5d4'), +('43297fad38e373fe', '762514b829bf486a', '6684fb847e1283c0'), +('43297fad38e373fe', 'bf8e6fbf6c48ec44', '762514b829bf486a'), +('43297fad38e373fe', 'ea676b2cb7db2b7a', '5a2d250d39eb8fc1'), +('43297fad38e373fe', '6d42d3b3b5a9abcc', 'ea676b2cb7db2b7a'), +('49793ebc79b3258f', '437540c8698f3cfa', '0b46e6cd65118775'), +('49793ebc79b3258f', 'b9d425d544c1ce71', '437540c8698f3cfa'), +('49793ebc79b3258f', '6fbf1cafcffd0556', '50bfa6e233cef0af'), +('49793ebc79b3258f', '000c45276715001d', '6fbf1cafcffd0556'), +('49793ebc79b3258f', 'a0cb2871752053f0', 'af27090a887c7e3e'), +('49793ebc79b3258f', '51fc08afec3c8f1a', 'a0cb2871752053f0'), +('49e95d6d4ca229bf', '00b0024eaac70ae3', '6f2421dc83df9ba2'), +('49e95d6d4ca229bf', '82537ce99e3476de', '00b0024eaac70ae3'), +('49e95d6d4ca229bf', '02fe55778117f12a', 'bd6bb416626be321'), +('49e95d6d4ca229bf', '417b8cdd1c64b3e7', '02fe55778117f12a'), +('49e95d6d4ca229bf', '5a6b612cc26cce4a', '73cb63595026b590'), +('49e95d6d4ca229bf', 'a90f566d8f4e3e41', '5a6b612cc26cce4a'), +('4fb05e1515ab73a7', '072d43a077075292', 'ac6174b535bcad69'), +('4fb05e1515ab73a7', '2d139d3801a19346', '072d43a077075292'), +('4fb05e1515ab73a7', '2f22e49bab7ca1ac', '86019dd993c46cbe'), +('4fb05e1515ab73a7', '44fef33e6971a69b', '2f22e49bab7ca1ac'), +('4fb05e1515ab73a7', 'ad87789bc00718c2', '733b12f600953a98'), +('4fb05e1515ab73a7', '8341990d8ba44544', 'ad87789bc00718c2'), +('584023641aba6176', '004bd6ef09176062', '6ceca0e2d5351cd1'), +('584023641aba6176', '9c6aedffd92b1cca', '004bd6ef09176062'), +('584023641aba6176', '88bf0db6d70dee56', 'b3a4719eca678ecc'), +('584023641aba6176', 'bb39bb8bdf9db594', '88bf0db6d70dee56'), +('584023641aba6176', 'c7d2845ea6d01c70', '2932e00da18db422'), +('584023641aba6176', '2217a63228e547c1', 'c7d2845ea6d01c70'), +('5b5a57676a56676e', '974affbf86022d1f', '6b6413098aaa7f2b'), +('5b5a57676a56676e', 'daabfab98dea5e78', '974affbf86022d1f'), +('7ca110454a1a6e57', '01a1d6d039776742', '0200c0b643578183'), +('7ca110454a1a6e57', '1405433e91265b1b', '01a1d6d039776742'), +('7ca110454a1a6e57', '690f5b0d9a26939b', 'd6173b3a2f754508'), +('7ca110454a1a6e57', '9f7c523c7bfe7586', '690f5b0d9a26939b'), +('7ca110454a1a6e57', 'ecd1c2f929f33ced', 'a5d38784ef7973ee'), +('7ca110454a1a6e57', 'c6642999c37b99b3', 'ecd1c2f929f33ced'), +('e0fee0fef1fef1fe', '0123456789abcdef', 'e09ca157c6dfe236'), +('e0fee0fef1fef1fe', '20bc4d004e45c048', '0123456789abcdef'), +('e0fee0fef1fef1fe', 'b7687facf9b1a656', 'eb2c523be4dedd8a'), +('e0fee0fef1fef1fe', 'c21c42ef608f5ae6', 'b7687facf9b1a656'), +('e0fee0fef1fef1fe', 'edbfd1c66c29ccc7', 'd7a84a5234f98995'), +('e0fee0fef1fef1fe', 'cb3f0e23d4ad3760', 'edbfd1c66c29ccc7'), +('fedcba9876543210', '0123456789abcdef', '480f0eac8d4c6cbc'), +('fedcba9876543210', '25d00012f501adaf', '0123456789abcdef'), +('fedcba9876543210', '2a2bb008df97c2f2', '9de0df5e77b9880c'), +('fedcba9876543210', '37365e5317307623', '2a2bb008df97c2f2'), +('fedcba9876543210', '7f46aa73f7fcde02', 'f6b3c639d4588687'), +('fedcba9876543210', '286334c90fc54d99', '7f46aa73f7fcde02'), +('fedcba9876543210', 'c44c1b3668a0f2cf', 'f1fbe0db72af2072'), +('fedcba9876543210', '4da710ea766dddb4', 'c44c1b3668a0f2cf'), +('fedcba9876543210', 'ed39d950fa74bcc4', '1529a5409e492728'), +('fedcba9876543210', 'ba2fcbdf566d6b40', 'ed39d950fa74bcc4'), +('fedcba9876543210', 'ffffffffffffffff', '2d921ccde555d609'), +('fedcba9876543210', '030a684825a5c5c2', 'ffffffffffffffff'), +('ffffffffffffffff', '0000000000000000', '5c77e002d1991c4d'), +('ffffffffffffffff', '5c77e002d1991c4d', '0000000000000000'), +('ffffffffffffffff', '16b15028f06a5ab8', 'c594d15528f44560'), +('ffffffffffffffff', 'c594d15528f44560', '16b15028f06a5ab8'), +('ffffffffffffffff', '3c7188775253884d', 'ac490aef39a87b61'), +('ffffffffffffffff', 'ac490aef39a87b61', '3c7188775253884d'), +('ffffffffffffffff', '7359b2163e4edc58', '04cfc75ca56c9b94'), +('ffffffffffffffff', '04cfc75ca56c9b94', '7359b2163e4edc58'), +('ffffffffffffffff', 'caaaaf4deaf1dbae', 'c9bb22df2a45a2ef'), +('ffffffffffffffff', 'c9bb22df2a45a2ef', 'caaaaf4deaf1dbae'), +('ffffffffffffffff', 'ffffffffffffffff', '427b5df7a109f638'), +('ffffffffffffffff', '427b5df7a109f638', 'ffffffffffffffff') ] + +# Test data for Alleged RC4 + +arc4 = [ ('0000000000000000', '0000000000000000', 'de188941a3375d3a'), + ('0123456789abcdef', '0123456789abcdef', '75b7878099e0c596'), + ('0123456789abcdef', '0000000000000000', '7494c2e7104b0879'), + ('ef012345', '00000000000000000000', 'd6a141a7ec3c38dfbd61') ] + +# Test data for Sapphire : Ciphertext of characters 0 through 255, with +# key "testSapphirekey" + +sapphire = ('0cf45580c5b2490830e0f90d5cc5970316e63d52f8cef4b88bace' + '96257aa39c3c5a9156c9cf2f3ee7fb9d659dd27928b8bc7fff555' + '593b0b27fb65180c21e2b1f1a121ecd2654ff274d6c5b1ed35eef' + 'f89f64b7e7dd55cdd5ba1a7f1516453fc648aa7926e359eb1ad26' + '341cb53074960d033887a7473724b743ef4f706c88c4f6ee17652' + '6e0c869089dc71225337b6c773c308f5b174e668b27b2f7c75cb3' + '169a985c85e29c6dfa9def1b461cb9684105c55dfd6ed8701b19f' + '1d05fe8ee8dad4062749ae87c4cb5f943607257024585af956171' + '9758b578aecbd9cd863b2ff50218650fecca0ca9f6aed52be36ce' + '39c4fa610b485dc3981ada313c0f02ba562' + ) + +# Test data for IDEA + +idea = [('00010002000300040005000600070008', '0000000100020003', '11fbed2b01986de5'), + ('00010002000300040005000600070008', '0102030405060708', '540E5FEA18C2F8B1'), + ('00010002000300040005000600070008', '0019324B647D96AF', '9F0A0AB6E10CED78'), + ('00010002000300040005000600070008', 'F5202D5B9C671B08', 'CF18FD7355E2C5C5'), + ('00010002000300040005000600070008', 'FAE6D2BEAA96826E', '85DF52005608193D'), + ('00010002000300040005000600070008', '0A141E28323C4650', '2F7DE750212FB734'), + ('00010002000300040005000600070008', '050A0F14191E2328', '7B7314925DE59C09'), + ('0005000A000F00140019001E00230028', '0102030405060708', '3EC04780BEFF6E20'), + ('3A984E2000195DB32EE501C8C47CEA60', '0102030405060708', '97BCD8200780DA86'), + ('006400C8012C019001F4025802BC0320', '05320A6414C819FA', '65BE87E7A2538AED'), + ('9D4075C103BC322AFB03E7BE6AB30006', '0808080808080808', 'F5DB1AC45E5EF9F9') + ]; + +# Diamond validation data + +diamond= [ +('0FE834FDB933C502923D92BC9E14368E70D41C66CBDF36155033A66E07E6CC6D8D', + '5A8D872D31EEDDE63FC46F6C36456D8E', '7345025F0802112D5575FC86C12DE34D'), +('0C07CFCB83B4BED44F6E253C5CFA36367A195E4D0D54843692856729A3542288', + '407D166413286727B6694F63F2E53517', 'DDC1BB703FFCF63936833A375745A481'), +('095310E1AAAD0E4BB0EC167B770239A1BAFB7BA009C0986CFF5DCED2B9A708', + '6AAABA305E9D60E437FADB7730883744', '4E8E8A32804C2DA248DD1621A2DF5A59'), +('06351601558986E3CB295E26D0B575E8A60226561CB098B91E9E48D0A421', + '621127720D127F14AB021095257F6C9D', '8099EC7AA94A31E7A602C59B73EE53C4'), +('0E29D0C2E84D8954AE30F96D8080BA260B5D5485D36A4E4DF7C3B48E85', + '23A6FCF704C04FBF19FDF983EDC39127', '7B86739B5F742871B53AD8916CBDC6B0'), +('0B2DF793534BE688C016711B05BF9DFFD4C74A1B33524FC494D3DA3E', + 'F1C964CDE2032DB0BC27B4BE0F3D9D0F', '1463F77F39BDC235EA5E8A5B081F9CE1'), +('089358CD27EF176094C635CACCD66234249E12137D65618533911A', + 'ECDA4CBE89F74DBF79EBE9B9F4F6CF2C', '4C333A87CDBC5F8EDD568A08ACD22D2E'), +('05031058F338D421B5EC460694B432ADD824708B6FBFC1E8E5BE', + '1D0D3F08D8487FB6B68E762973FD2152', 'F9BE7970B1A6689FBA1D1B83DBDC1346'), +('0DA5C3BE04CDD35306F442817EAB347B9344D7E5C68BF3AE0C', + '2ED35B01A830E3FCC46C1989C6F4D3DE', '1DA5D794C98F0937B32C355F9448B0A7'), +('0A4C748F1B820D90EF4E4487930184D83A975079258F3E4C', + '607CF038557E4F47BF698C167B57591E', '48065D489BC746D6A75DC5DCC6E4F50B'), +('0728C4C94EB27C184197F02B75630D65DF00E5F6ADE5FE', + 'D5C77CD3F9A7EDF2EDB958F4DAF45FF9', '33F7E9DF813EBE5C58244C8F3E85EBB4'), +('0FC973FE155E072F4D59E8DC50F75E731C32FAC65986', + '4574FF5B256C18928E2B9DF241825863', '687754F76CBDE753BAD5EDDF7A903715'), +('0C4AF410A88AC1E99CCB6E06588F53576D5E4F9D1A', + 'E4890AD4BD722A84A6C8BB3AB02C5FBD', '60291771D5810238C0459E0DE3A07725'), +('094F7102C1649E097A9259628C2F2711348FCBBD', + '6917AF1266EBC9C7E901753973A06E82', '5BB024216B9314BA550085190A1509BE'), +('069502CA455398A07296669DC55B67756EC554', + 'E292CAF38755D561CF7035DFBD554ADB', 'A13380C10591BEBD8DEBFF63E22D219E'), +('0E56BF070CEC71301616A85980DADC37FBAE', + '34F51EFDC20D06845853BAF2CE2727D9', '42B9F6F6A57E0F939FC40399FD3B3A4A'), +('0B1B81208A65F6DD6E9D75CA64B6DE9102', + '28D471B93D593959D0828FD1DA9B876D', '188C3C40455E49E1716FF2B00D3B4852'), +('084B00E05A8A31EDD4491705399571A5', + '8AF72EED4716980112A8EB3BF4A6BF0E', 'F197A1681AFD1F6B97EB46491722CDCD'), +('059CC4EB1BEE4A8C36D62AD1117D2C', + 'CB3DF13AA5C9A4B0CEA489C22D941127', 'CA635CB32EBA76EA6A263DC5B63E4462'), +('0DB3E4E9AAFEAA07657FE500DA49', + '840506BFFE845A9D573A7014D915389A', '34066FB8EC127674DEA47682F48ED6A0'), +('0A0E91DC60AE757379CC07078C', + 'C940AE7EC343183C01D9A435E1DA1675', '38E8C4B858CFF14B425CF6ADF9C4E05D'), +('0718DB219F364817F8EA38E9', + '4BDD132F3E7D215E5CAE53D033956989', '3FB1DDE1F10CA1813B71BDF51C9D0C4B'), +('0FB46C3A84C229322F38E0', + 'B05B9B1923A5154F901016264497FC10', '1A8F6CF4526045AFCC95AD377593797D'), +('0C5F7C6DE608F961490C', + 'EB4AEE30F3937A85D6DA4FE53E232333', '5005F10F2CDC39DA5D3A2B821E8AFAA7'), +('091C5D3EF1DAA3C5C8', + 'F329EF6A9492BCF8EA471B605DEE2718', '6C4C87C3C745FD514842A6CA979D26E3') + ] + +# Test data for RC5 + +rc5 = [('10200c1000000000000000000000000000000000', '0000000000000000', + '21A5DBEE154B8F6D'), + ('10200c10915F4619BE41B2516355A50110A9CE91', '21A5DBEE154B8F6D', + 'F7C013AC5B2B8952'), + ('10200c10783348E75AEB0F2FD7B169BB8DC16787', 'F7C013AC5B2B8952', + '2F42B3B70369FC92'), + ('10200c10DC49DB1375A5584F6485B413B5F12BAF', '2F42B3B70369FC92', + '65C178B284D197CC'), + ('10200c105269F149D41BA0152497574D7F153125', '65C178B284D197CC', + 'EB44E415DA319824') + ] + +# Test data for ARC2 +arc2 = [ +('5068696c6970476c617373', '0000000000000000', '624fb3e887419e48'), +('5068696c6970476c617373', 'ffffffffffffffff', '79cadef44c4a5a85'), +('5068696c6970476c617373', '0001020304050607', '90411525b34e4c2c'), +('5068696c6970476c617373', '0011223344556677', '078656aaba61cbfb'), +('ffffffffffffffff', '0000000000000000', 'd7bcc5dbb4d6e56a'), +('ffffffffffffffff', 'ffffffffffffffff', '7259018ec557b357'), +('ffffffffffffffff', '0001020304050607', '93d20a497f2ccb62'), +('ffffffffffffffff', '0011223344556677', 'cb15a7f819c0014d'), +('ffffffffffffffff5065746572477265656e6177617953e5ffe553', '0000000000000000', '63ac98cdf3843a7a'), +('ffffffffffffffff5065746572477265656e6177617953e5ffe553', 'ffffffffffffffff', '3fb49e2fa12371dd'), +('ffffffffffffffff5065746572477265656e6177617953e5ffe553', '0001020304050607', '46414781ab387d5f'), +('ffffffffffffffff5065746572477265656e6177617953e5ffe553', '0011223344556677', 'be09dc81feaca271'), +('53e5ffe553', '0000000000000000', 'e64221e608be30ab'), +('53e5ffe553', 'ffffffffffffffff', '862bc60fdcd4d9a9'), +('53e5ffe553', '0001020304050607', '6a34da50fa5e47de'), +('53e5ffe553', '0011223344556677', '584644c34503122c'), +] + +# Test data for Blowfish + +blowfish = [('6162636465666768696a6b6c6d6e6f707172737475767778797a', + '424c4f5746495348', '324ed0fef413a203'), + ('57686f206973204a6f686e2047616c743f', 'fedcba9876543210', + 'cc91732b8022f684') + ] + +# Test data for DES3 + +des3_cbc=[] + +des3= [('0123456789abcdeffedcba9876543210', '0123456789abcde7', + '7f1d0a77826b8aff') + ] + +# Test data for CAST + +_castkey = '0123456712345678234567893456789A' +_castdata = '0123456789ABCDEF' + +cast = [(_castkey, _castdata, '238B4FE5847E44B2'), + (_castkey[:10*2], _castdata, 'EB6A711A2C02271B'), + (_castkey[: 5*2], _castdata, '7AC816D16E9B302E'), + ] + +# Test data for Skipjack + +skipjack = [ ('00998877665544332211', '33221100ddccbbaa', + '2587cae27a12d300'), +('f8da02647722f7103adf', '1ddf39abf5cd711e', 'c92d22324c6b31ae'), +('82760ac137948821eee4', 'dd6c6cce8f83e69e', 'e32877c1d9527fff'), +('843c1687d3cdca5fc5c3', 'beaacf177fa41a11', '4745783f75b8861a'), +('ae870cd7ff33a995f7e5', 'c4c09f216c1bc60a', '5c101636b8a57a72'), +('5ccbd913ea8b73bd6391', 'd3f814b000245856', 'b4fc0f8e54728f91'), +('f65e74cd599c68a40cc7', '356ec7d93832329c', '93b750608f5701f8'), +('aa106e46d7087c4e93dc', '209ecf1c537ad56c', 'd823d45510099e61'), +('a93f9789a20c3cc34fea', '892eea9d64e17d66', '0959e231b275d95f'), +('88b163cbd61616888899', '991390fd760fc91b', 'e7700209886767ae'), +('fb6cd1ff70487561df10', 'daebc947ddca9c9e', 'e7cc49a56bd6a322'), +('5edc1ac0c4e7ef5f002c', '6419ddefe2cd8f2e', 'e48a05cf26e242fd'), +('8e3090c19aa32f94496a', '322998ecbd068112', '62c0e537b14df2c1'), +('b96e3fd46fa4263f9092', '3aae2aee20da93cc', '54d1e58a6b624d71'), +('9e6635baee28c5bce2bc', '14311112ca11f727', '5d0f235a9d221ce0'), +('04127ce16dc1b1726a66', '300e4313e7ad6796', '8e5b03522e68cbeb'), +('f0b89d75e979ccc9b172', '09cd1c1accbe7797', '572c9b4025a9134b'), +('f9bfc78798cbf1bcd4b5', '31b30ca354af3cd8', '8c959c904789fbda'), +('f43a51b4273bde27d2b0', '08c59b0db99ec267', 'b7d7f5fa342988fb'), +('cd51f0a75aa73c48edd2', '9784b1e3e7e60e60', '763aa8ee109397b3'), +('b3319a3f6622aa726bb3', 'f65216373d4b43c7', '0325600337b8ad3c'), +('493254c9596e993f5f9c', 'cba4c1215d5d36ce', '68e1c551c59108c0'), +('76150c2c3ced1c7ca021', '82294851288e75cb', '7eb6325d82a2096c'), +('7140d6c5486305872df6', 'c3a7b7e4a52e407b', '2483f385a42ee3c6'), +('3c2c3901f0ee9a3b2b0e', '1bfde32ab559e13a', 'd6fa9db8685fd88a'), +('606a8b4bdfaae8a0ba51', 'd205f7486c782838', '0330489170b85293'), +('7847a47a0fe79ab770ce', 'd96ff1f7c7fc60e0', '1f9b3301c9b2708c'), +('73b9ab0c36c99e91a891', '241d4bde19a75f8f', '2b86c57ffe168895'), +('a37f2ad5a85e170741f5', '7be1b8d58321c619', '5af7ceb3eed9dca1'), +('f7b0c2a8170e3c4e48b3', 'c9214ea01ec14948', '1b587736e116c04b'), +('a1fc67e44eacacf4a902', 'e2a3091feb581588', 'f3ecf0f1789a3923'), +('f14430affc5fe82a9ae9', '3cb466d301b60854', 'e8d114c20ffa1c79'), +('fd26df50486a4cd96d9b', 'b0684f8a5e63d935', '222903994d64fe3a'), +('a6d46d46ca287e1a332a', 'ba1f37e88edec55f', '91f2baad6fa0de55'), +('83c3f1cb8d06efc6196b', 'e9fed8501b7a6579', '83f9f08f89a854ee'), +('0edfa44c7d4a4ef0725e', 'eb5ca8b3fa1fcdbb', '2b1b6670a6be0324'), +('b8edcf167d99a711ccee', 'b8b525c6382af277', '211a695da473766f'), +('4f639e0d5a5d2ad7e9a4', '9162e781ff683853', 'eb2976370d22ef22'), +('37e006256a4ae6d320c4', 'c9f23a20a39ded11', 'dba4c0ef0ea098c0'), +('e41d0bd25f931ba1d85c', '5a6f12f32f7eefdf', '923daee8000709f9'), +('fdf65bbc5fe600f3cd68', 'cad5414c1c64f194', 'd5771e78b6f1fe1e'), +('1c269af2ff166acb27ef', '063a58a20b45378f', '634f7a3861af97a1'), +('1179f64acb6122ccf649', '08fbf42b4313347b', '3a803a4bd0e8c3e6'), +('078c87265eb8da323e90', '6d4ed0e9930532d1', 'f4fa372e7e1441a1'), +('2fff35f8eb774c843bb0', '40b699812345661d', '63a9197f7b75f53f'), +('09f77346a4393ce99856', '22ed54626a51e505', 'e91a050a7481b3dd'), +('5b878e0b22a705acf8fb', '0c489b66e2da531b', '6e9370a91b994878'), +('9d72c1ab2092c1b10877', 'c64b10f8b191bc2c', '5bdecded96d656c9'), +('72865f289725e1b55502', '91fdf7236f85bdd6', '1a5680e51736026f'), +('06e3c0e541f4aae6fe93', '40009f8a465a9feb', '0e7aace421bc79d8'), +('2ea09f1cc89e064f09bc', '543208b05bfa3858', 'a95d87fad12c3593') +] + +# Test data for XOR + +XOR = [] + diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..3d71101 --- /dev/null +++ b/__init__.py @@ -0,0 +1,3 @@ + +__all__ = ['Cipher', 'Hash', 'PublicKey', 'Util', 'Protocol', 'Winnow'] + diff --git a/_checkversion.py b/_checkversion.py new file mode 100644 index 0000000..a783341 --- /dev/null +++ b/_checkversion.py @@ -0,0 +1,16 @@ +"""This file (which is sourced, not imported) checks the version of the +"versioncheck" package. It is also an example of how to format your own +_checkversion.py file""" + +import pyversioncheck + +_PACKAGE="Crypto" +_VERSION="1.1a2" +_URL="http://starship.skyport.net/crew/amk/versions/pct.txt" + +try: + _myverbose=VERBOSE +except NameError: + _myverbose=1 + +pyversioncheck.versioncheck(_PACKAGE, _URL, _VERSION, verbose=_myverbose) diff --git a/block/ARC2.c b/block/ARC2.c new file mode 100644 index 0000000..77b59c9 --- /dev/null +++ b/block/ARC2.c @@ -0,0 +1,186 @@ + +/* + * rc2.c : Source code for the RC2 block cipher + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + + +typedef unsigned int U32; +typedef unsigned short U16; +typedef unsigned char U8; + +typedef struct +{ + PCTObject_HEAD + U16 xkey[64]; +} ARC2object; + +static inline void +ARC2encrypt(self, block) + ARC2object *self; + U8 *block; +{ + U16 x76, x54, x32, x10; + int i; + + x76 = (block[7] << 8) + block[6]; + x54 = (block[5] << 8) + block[4]; + x32 = (block[3] << 8) + block[2]; + x10 = (block[1] << 8) + block[0]; + + for (i = 0; i < 16; i++) + { + x10 += (x32 & ~x76) + (x54 & x76) + self->xkey[4*i+0]; + x10 = (x10 << 1) + (x10 >> 15 & 1); + + x32 += (x54 & ~x10) + (x76 & x10) + self->xkey[4*i+1]; + x32 = (x32 << 2) + (x32 >> 14 & 3); + + x54 += (x76 & ~x32) + (x10 & x32) + self->xkey[4*i+2]; + x54 = (x54 << 3) + (x54 >> 13 & 7); + + x76 += (x10 & ~x54) + (x32 & x54) + self->xkey[4*i+3]; + x76 = (x76 << 5) + (x76 >> 11 & 31); + + if (i == 4 || i == 10) { + x10 += self->xkey[x76 & 63]; + x32 += self->xkey[x10 & 63]; + x54 += self->xkey[x32 & 63]; + x76 += self->xkey[x54 & 63]; + } + } + + block[0] = (U8)x10; + block[1] = (U8)(x10 >> 8); + block[2] = (U8)x32; + block[3] = (U8)(x32 >> 8); + block[4] = (U8)x54; + block[5] = (U8)(x54 >> 8); + block[6] = (U8)x76; + block[7] = (U8)(x76 >> 8); +} + + +static inline void +ARC2decrypt(self, block) + ARC2object *self; + U8 *block; +{ + U16 x76, x54, x32, x10; + int i; + + x76 = (block[7] << 8) + block[6]; + x54 = (block[5] << 8) + block[4]; + x32 = (block[3] << 8) + block[2]; + x10 = (block[1] << 8) + block[0]; + + i = 15; + do { + x76 &= 65535; + x76 = (x76 << 11) + (x76 >> 5); + x76 -= (x10 & ~x54) + (x32 & x54) + self->xkey[4*i+3]; + + x54 &= 65535; + x54 = (x54 << 13) + (x54 >> 3); + x54 -= (x76 & ~x32) + (x10 & x32) + self->xkey[4*i+2]; + + x32 &= 65535; + x32 = (x32 << 14) + (x32 >> 2); + x32 -= (x54 & ~x10) + (x76 & x10) + self->xkey[4*i+1]; + + x10 &= 65535; + x10 = (x10 << 15) + (x10 >> 1); + x10 -= (x32 & ~x76) + (x54 & x76) + self->xkey[4*i+0]; + + if (i == 5 || i == 11) { + x76 -= self->xkey[x54 & 63]; + x54 -= self->xkey[x32 & 63]; + x32 -= self->xkey[x10 & 63]; + x10 -= self->xkey[x76 & 63]; + } + } while (i--); + + block[0] = (U8)x10; + block[1] = (U8)(x10 >> 8); + block[2] = (U8)x32; + block[3] = (U8)(x32 >> 8); + block[4] = (U8)x54; + block[5] = (U8)(x54 >> 8); + block[6] = (U8)x76; + block[7] = (U8)(x76 >> 8); +} + + +static inline void +ARC2init(self, key, keylength) + ARC2object *self; + U8 *key; + int keylength; +{ + U8 x; + U16 i; + /* 256-entry permutation table, probably derived somehow from pi */ + static const U8 permute[256] = { + 217,120,249,196, 25,221,181,237, 40,233,253,121, 74,160,216,157, + 198,126, 55,131, 43,118, 83,142, 98, 76,100,136, 68,139,251,162, + 23,154, 89,245,135,179, 79, 19, 97, 69,109,141, 9,129,125, 50, + 189,143, 64,235,134,183,123, 11,240,149, 33, 34, 92,107, 78,130, + 84,214,101,147,206, 96,178, 28,115, 86,192, 20,167,140,241,220, + 18,117,202, 31, 59,190,228,209, 66, 61,212, 48,163, 60,182, 38, + 111,191, 14,218, 70,105, 7, 87, 39,242, 29,155,188,148, 67, 3, + 248, 17,199,246,144,239, 62,231, 6,195,213, 47,200,102, 30,215, + 8,232,234,222,128, 82,238,247,132,170,114,172, 53, 77,106, 42, + 150, 26,210,113, 90, 21, 73,116, 75,159,208, 94, 4, 24,164,236, + 194,224, 65,110, 15, 81,203,204, 36,145,175, 80,161,244,112, 57, + 153,124, 58,133, 35,184,180,122,252, 2, 54, 91, 37, 85,151, 49, + 45, 93,250,152,227,138,146,174, 5,223, 41, 16,103,108,186,201, + 211, 0,230,207,225,158,168, 44, 99, 22, 1, 63, 88,226,137,169, + 13, 56, 52, 27,171, 51,255,176,187, 72, 12, 95,185,177,205, 46, + 197,243,219, 71,229,165,156,119, 10,166, 32,104,254,127,193,173 + }; + + /* The "bits" value may be some sort of export control weakening. + We'll hardwire it to 1024. */ +#define bits 1024 + + memcpy(self->xkey, key, keylength); + + /* Phase 1: Expand input key to 128 bytes */ + if (keylength < 128) { + i = 0; + x = ((U8 *)self->xkey)[keylength-1]; + do { + x = permute[(x + ((U8 *)self->xkey)[i++]) & 255]; + ((U8 *)self->xkey)[keylength++] = x; + } while (keylength < 128); + } + + /* Phase 2 - reduce effective key size to "bits" */ + keylength = (bits+7) >> 3; + i = 128-keylength; + x = permute[((U8 *)self->xkey)[i] & (255 >> + (7 & + ((bits %8 ) ? 8-(bits%8): 0)) + )]; + ((U8 *)self->xkey)[i] = x; + + while (i--) { + x = permute[ x ^ ((U8 *)self->xkey)[i+keylength] ]; + ((U8 *)self->xkey)[i] = x; + } + + /* Phase 3 - copy to self->xkey in little-endian order */ + i = 63; + do { + self->xkey[i] = ((U8 *)self->xkey)[2*i] + + (((U8 *)self->xkey)[2*i+1] << 8); + } while (i--); +} + +#undef bits diff --git a/block/Blowfish.c b/block/Blowfish.c new file mode 100644 index 0000000..a6840b5 --- /dev/null +++ b/block/Blowfish.c @@ -0,0 +1,506 @@ + +/* + * blowfish.c : Source code for the Blowfish block cipher + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +/*************************************************************************/ +/* File: bf.c + Blowfish cipher by Bruce Schneier, + Code by Bryan Olson, based partly on Schneier's. +*/ + +/* Define IntU32 to be an unsigned in 32 bits long */ +typedef unsigned int IntU32 ; +typedef unsigned char IntU8 ; +#define NROUNDS 16 + + + +/* Define IntP to be an integer which + is the same size as a pointer. */ +typedef unsigned long IntP ; + + +typedef struct +{ + IntU32 p[2][NROUNDS+2], + sbox[4][256] ; +} BFkey_type ; + +typedef struct +{ + PCTObject_HEAD + BFkey_type bfkey; +} Blowfishobject; + +/* File bfinit.h + Data to initialize P and S in BlowFish. +*/ + +static IntU32 p_init[NROUNDS+2] = +{ + 608135816UL, 2242054355UL, 320440878UL, 57701188UL, + 2752067618UL, 698298832UL, 137296536UL, 3964562569UL, + 1160258022UL, 953160567UL, 3193202383UL, 887688300UL, + 3232508343UL, 3380367581UL, 1065670069UL, 3041331479UL, + 2450970073UL, 2306472731UL +} ; + +static IntU32 s_init[4][256] = { + {3509652390UL, 2564797868UL, 805139163UL, 3491422135UL, + 3101798381UL, 1780907670UL, 3128725573UL, 4046225305UL, + 614570311UL, 3012652279UL, 134345442UL, 2240740374UL, + 1667834072UL, 1901547113UL, 2757295779UL, 4103290238UL, + 227898511UL, 1921955416UL, 1904987480UL, 2182433518UL, + 2069144605UL, 3260701109UL, 2620446009UL, 720527379UL, + 3318853667UL, 677414384UL, 3393288472UL, 3101374703UL, + 2390351024UL, 1614419982UL, 1822297739UL, 2954791486UL, + 3608508353UL, 3174124327UL, 2024746970UL, 1432378464UL, + 3864339955UL, 2857741204UL, 1464375394UL, 1676153920UL, + 1439316330UL, 715854006UL, 3033291828UL, 289532110UL, + 2706671279UL, 2087905683UL, 3018724369UL, 1668267050UL, + 732546397UL, 1947742710UL, 3462151702UL, 2609353502UL, + 2950085171UL, 1814351708UL, 2050118529UL, 680887927UL, + 999245976UL, 1800124847UL, 3300911131UL, 1713906067UL, + 1641548236UL, 4213287313UL, 1216130144UL, 1575780402UL, + 4018429277UL, 3917837745UL, 3693486850UL, 3949271944UL, + 596196993UL, 3549867205UL, 258830323UL, 2213823033UL, + 772490370UL, 2760122372UL, 1774776394UL, 2652871518UL, + 566650946UL, 4142492826UL, 1728879713UL, 2882767088UL, + 1783734482UL, 3629395816UL, 2517608232UL, 2874225571UL, + 1861159788UL, 326777828UL, 3124490320UL, 2130389656UL, + 2716951837UL, 967770486UL, 1724537150UL, 2185432712UL, + 2364442137UL, 1164943284UL, 2105845187UL, 998989502UL, + 3765401048UL, 2244026483UL, 1075463327UL, 1455516326UL, + 1322494562UL, 910128902UL, 469688178UL, 1117454909UL, + 936433444UL, 3490320968UL, 3675253459UL, 1240580251UL, + 122909385UL, 2157517691UL, 634681816UL, 4142456567UL, + 3825094682UL, 3061402683UL, 2540495037UL, 79693498UL, + 3249098678UL, 1084186820UL, 1583128258UL, 426386531UL, + 1761308591UL, 1047286709UL, 322548459UL, 995290223UL, + 1845252383UL, 2603652396UL, 3431023940UL, 2942221577UL, + 3202600964UL, 3727903485UL, 1712269319UL, 422464435UL, + 3234572375UL, 1170764815UL, 3523960633UL, 3117677531UL, + 1434042557UL, 442511882UL, 3600875718UL, 1076654713UL, + 1738483198UL, 4213154764UL, 2393238008UL, 3677496056UL, + 1014306527UL, 4251020053UL, 793779912UL, 2902807211UL, + 842905082UL, 4246964064UL, 1395751752UL, 1040244610UL, + 2656851899UL, 3396308128UL, 445077038UL, 3742853595UL, + 3577915638UL, 679411651UL, 2892444358UL, 2354009459UL, + 1767581616UL, 3150600392UL, 3791627101UL, 3102740896UL, + 284835224UL, 4246832056UL, 1258075500UL, 768725851UL, + 2589189241UL, 3069724005UL, 3532540348UL, 1274779536UL, + 3789419226UL, 2764799539UL, 1660621633UL, 3471099624UL, + 4011903706UL, 913787905UL, 3497959166UL, 737222580UL, + 2514213453UL, 2928710040UL, 3937242737UL, 1804850592UL, + 3499020752UL, 2949064160UL, 2386320175UL, 2390070455UL, + 2415321851UL, 4061277028UL, 2290661394UL, 2416832540UL, + 1336762016UL, 1754252060UL, 3520065937UL, 3014181293UL, + 791618072UL, 3188594551UL, 3933548030UL, 2332172193UL, + 3852520463UL, 3043980520UL, 413987798UL, 3465142937UL, + 3030929376UL, 4245938359UL, 2093235073UL, 3534596313UL, + 375366246UL, 2157278981UL, 2479649556UL, 555357303UL, + 3870105701UL, 2008414854UL, 3344188149UL, 4221384143UL, + 3956125452UL, 2067696032UL, 3594591187UL, 2921233993UL, + 2428461UL, 544322398UL, 577241275UL, 1471733935UL, + 610547355UL, 4027169054UL, 1432588573UL, 1507829418UL, + 2025931657UL, 3646575487UL, 545086370UL, 48609733UL, + 2200306550UL, 1653985193UL, 298326376UL, 1316178497UL, + 3007786442UL, 2064951626UL, 458293330UL, 2589141269UL, + 3591329599UL, 3164325604UL, 727753846UL, 2179363840UL, + 146436021UL, 1461446943UL, 4069977195UL, 705550613UL, + 3059967265UL, 3887724982UL, 4281599278UL, 3313849956UL, + 1404054877UL, 2845806497UL, 146425753UL, 1854211946UL}, + +{ 1266315497UL, 3048417604UL, 3681880366UL, 3289982499UL, + 2909710000UL, 1235738493UL, 2632868024UL, 2414719590UL, + 3970600049UL, 1771706367UL, 1449415276UL, 3266420449UL, + 422970021UL, 1963543593UL, 2690192192UL, 3826793022UL, + 1062508698UL, 1531092325UL, 1804592342UL, 2583117782UL, + 2714934279UL, 4024971509UL, 1294809318UL, 4028980673UL, + 1289560198UL, 2221992742UL, 1669523910UL, 35572830UL, + 157838143UL, 1052438473UL, 1016535060UL, 1802137761UL, + 1753167236UL, 1386275462UL, 3080475397UL, 2857371447UL, + 1040679964UL, 2145300060UL, 2390574316UL, 1461121720UL, + 2956646967UL, 4031777805UL, 4028374788UL, 33600511UL, + 2920084762UL, 1018524850UL, 629373528UL, 3691585981UL, + 3515945977UL, 2091462646UL, 2486323059UL, 586499841UL, + 988145025UL, 935516892UL, 3367335476UL, 2599673255UL, + 2839830854UL, 265290510UL, 3972581182UL, 2759138881UL, + 3795373465UL, 1005194799UL, 847297441UL, 406762289UL, + 1314163512UL, 1332590856UL, 1866599683UL, 4127851711UL, + 750260880UL, 613907577UL, 1450815602UL, 3165620655UL, + 3734664991UL, 3650291728UL, 3012275730UL, 3704569646UL, + 1427272223UL, 778793252UL, 1343938022UL, 2676280711UL, + 2052605720UL, 1946737175UL, 3164576444UL, 3914038668UL, + 3967478842UL, 3682934266UL, 1661551462UL, 3294938066UL, + 4011595847UL, 840292616UL, 3712170807UL, 616741398UL, + 312560963UL, 711312465UL, 1351876610UL, 322626781UL, + 1910503582UL, 271666773UL, 2175563734UL, 1594956187UL, + 70604529UL, 3617834859UL, 1007753275UL, 1495573769UL, + 4069517037UL, 2549218298UL, 2663038764UL, 504708206UL, + 2263041392UL, 3941167025UL, 2249088522UL, 1514023603UL, + 1998579484UL, 1312622330UL, 694541497UL, 2582060303UL, + 2151582166UL, 1382467621UL, 776784248UL, 2618340202UL, + 3323268794UL, 2497899128UL, 2784771155UL, 503983604UL, + 4076293799UL, 907881277UL, 423175695UL, 432175456UL, + 1378068232UL, 4145222326UL, 3954048622UL, 3938656102UL, + 3820766613UL, 2793130115UL, 2977904593UL, 26017576UL, + 3274890735UL, 3194772133UL, 1700274565UL, 1756076034UL, + 4006520079UL, 3677328699UL, 720338349UL, 1533947780UL, + 354530856UL, 688349552UL, 3973924725UL, 1637815568UL, + 332179504UL, 3949051286UL, 53804574UL, 2852348879UL, + 3044236432UL, 1282449977UL, 3583942155UL, 3416972820UL, + 4006381244UL, 1617046695UL, 2628476075UL, 3002303598UL, + 1686838959UL, 431878346UL, 2686675385UL, 1700445008UL, + 1080580658UL, 1009431731UL, 832498133UL, 3223435511UL, + 2605976345UL, 2271191193UL, 2516031870UL, 1648197032UL, + 4164389018UL, 2548247927UL, 300782431UL, 375919233UL, + 238389289UL, 3353747414UL, 2531188641UL, 2019080857UL, + 1475708069UL, 455242339UL, 2609103871UL, 448939670UL, + 3451063019UL, 1395535956UL, 2413381860UL, 1841049896UL, + 1491858159UL, 885456874UL, 4264095073UL, 4001119347UL, + 1565136089UL, 3898914787UL, 1108368660UL, 540939232UL, + 1173283510UL, 2745871338UL, 3681308437UL, 4207628240UL, + 3343053890UL, 4016749493UL, 1699691293UL, 1103962373UL, + 3625875870UL, 2256883143UL, 3830138730UL, 1031889488UL, + 3479347698UL, 1535977030UL, 4236805024UL, 3251091107UL, + 2132092099UL, 1774941330UL, 1199868427UL, 1452454533UL, + 157007616UL, 2904115357UL, 342012276UL, 595725824UL, + 1480756522UL, 206960106UL, 497939518UL, 591360097UL, + 863170706UL, 2375253569UL, 3596610801UL, 1814182875UL, + 2094937945UL, 3421402208UL, 1082520231UL, 3463918190UL, + 2785509508UL, 435703966UL, 3908032597UL, 1641649973UL, + 2842273706UL, 3305899714UL, 1510255612UL, 2148256476UL, + 2655287854UL, 3276092548UL, 4258621189UL, 236887753UL, + 3681803219UL, 274041037UL, 1734335097UL, 3815195456UL, + 3317970021UL, 1899903192UL, 1026095262UL, 4050517792UL, + 356393447UL, 2410691914UL, 3873677099UL, 3682840055UL}, + +{ 3913112168UL, 2491498743UL, 4132185628UL, 2489919796UL, + 1091903735UL, 1979897079UL, 3170134830UL, 3567386728UL, + 3557303409UL, 857797738UL, 1136121015UL, 1342202287UL, + 507115054UL, 2535736646UL, 337727348UL, 3213592640UL, + 1301675037UL, 2528481711UL, 1895095763UL, 1721773893UL, + 3216771564UL, 62756741UL, 2142006736UL, 835421444UL, + 2531993523UL, 1442658625UL, 3659876326UL, 2882144922UL, + 676362277UL, 1392781812UL, 170690266UL, 3921047035UL, + 1759253602UL, 3611846912UL, 1745797284UL, 664899054UL, + 1329594018UL, 3901205900UL, 3045908486UL, 2062866102UL, + 2865634940UL, 3543621612UL, 3464012697UL, 1080764994UL, + 553557557UL, 3656615353UL, 3996768171UL, 991055499UL, + 499776247UL, 1265440854UL, 648242737UL, 3940784050UL, + 980351604UL, 3713745714UL, 1749149687UL, 3396870395UL, + 4211799374UL, 3640570775UL, 1161844396UL, 3125318951UL, + 1431517754UL, 545492359UL, 4268468663UL, 3499529547UL, + 1437099964UL, 2702547544UL, 3433638243UL, 2581715763UL, + 2787789398UL, 1060185593UL, 1593081372UL, 2418618748UL, + 4260947970UL, 69676912UL, 2159744348UL, 86519011UL, + 2512459080UL, 3838209314UL, 1220612927UL, 3339683548UL, + 133810670UL, 1090789135UL, 1078426020UL, 1569222167UL, + 845107691UL, 3583754449UL, 4072456591UL, 1091646820UL, + 628848692UL, 1613405280UL, 3757631651UL, 526609435UL, + 236106946UL, 48312990UL, 2942717905UL, 3402727701UL, + 1797494240UL, 859738849UL, 992217954UL, 4005476642UL, + 2243076622UL, 3870952857UL, 3732016268UL, 765654824UL, + 3490871365UL, 2511836413UL, 1685915746UL, 3888969200UL, + 1414112111UL, 2273134842UL, 3281911079UL, 4080962846UL, + 172450625UL, 2569994100UL, 980381355UL, 4109958455UL, + 2819808352UL, 2716589560UL, 2568741196UL, 3681446669UL, + 3329971472UL, 1835478071UL, 660984891UL, 3704678404UL, + 4045999559UL, 3422617507UL, 3040415634UL, 1762651403UL, + 1719377915UL, 3470491036UL, 2693910283UL, 3642056355UL, + 3138596744UL, 1364962596UL, 2073328063UL, 1983633131UL, + 926494387UL, 3423689081UL, 2150032023UL, 4096667949UL, + 1749200295UL, 3328846651UL, 309677260UL, 2016342300UL, + 1779581495UL, 3079819751UL, 111262694UL, 1274766160UL, + 443224088UL, 298511866UL, 1025883608UL, 3806446537UL, + 1145181785UL, 168956806UL, 3641502830UL, 3584813610UL, + 1689216846UL, 3666258015UL, 3200248200UL, 1692713982UL, + 2646376535UL, 4042768518UL, 1618508792UL, 1610833997UL, + 3523052358UL, 4130873264UL, 2001055236UL, 3610705100UL, + 2202168115UL, 4028541809UL, 2961195399UL, 1006657119UL, + 2006996926UL, 3186142756UL, 1430667929UL, 3210227297UL, + 1314452623UL, 4074634658UL, 4101304120UL, 2273951170UL, + 1399257539UL, 3367210612UL, 3027628629UL, 1190975929UL, + 2062231137UL, 2333990788UL, 2221543033UL, 2438960610UL, + 1181637006UL, 548689776UL, 2362791313UL, 3372408396UL, + 3104550113UL, 3145860560UL, 296247880UL, 1970579870UL, + 3078560182UL, 3769228297UL, 1714227617UL, 3291629107UL, + 3898220290UL, 166772364UL, 1251581989UL, 493813264UL, + 448347421UL, 195405023UL, 2709975567UL, 677966185UL, + 3703036547UL, 1463355134UL, 2715995803UL, 1338867538UL, + 1343315457UL, 2802222074UL, 2684532164UL, 233230375UL, + 2599980071UL, 2000651841UL, 3277868038UL, 1638401717UL, + 4028070440UL, 3237316320UL, 6314154UL, 819756386UL, + 300326615UL, 590932579UL, 1405279636UL, 3267499572UL, + 3150704214UL, 2428286686UL, 3959192993UL, 3461946742UL, + 1862657033UL, 1266418056UL, 963775037UL, 2089974820UL, + 2263052895UL, 1917689273UL, 448879540UL, 3550394620UL, + 3981727096UL, 150775221UL, 3627908307UL, 1303187396UL, + 508620638UL, 2975983352UL, 2726630617UL, 1817252668UL, + 1876281319UL, 1457606340UL, 908771278UL, 3720792119UL, + 3617206836UL, 2455994898UL, 1729034894UL, 1080033504UL}, + +{ 976866871UL, 3556439503UL, 2881648439UL, 1522871579UL, + 1555064734UL, 1336096578UL, 3548522304UL, 2579274686UL, + 3574697629UL, 3205460757UL, 3593280638UL, 3338716283UL, + 3079412587UL, 564236357UL, 2993598910UL, 1781952180UL, + 1464380207UL, 3163844217UL, 3332601554UL, 1699332808UL, + 1393555694UL, 1183702653UL, 3581086237UL, 1288719814UL, + 691649499UL, 2847557200UL, 2895455976UL, 3193889540UL, + 2717570544UL, 1781354906UL, 1676643554UL, 2592534050UL, + 3230253752UL, 1126444790UL, 2770207658UL, 2633158820UL, + 2210423226UL, 2615765581UL, 2414155088UL, 3127139286UL, + 673620729UL, 2805611233UL, 1269405062UL, 4015350505UL, + 3341807571UL, 4149409754UL, 1057255273UL, 2012875353UL, + 2162469141UL, 2276492801UL, 2601117357UL, 993977747UL, + 3918593370UL, 2654263191UL, 753973209UL, 36408145UL, + 2530585658UL, 25011837UL, 3520020182UL, 2088578344UL, + 530523599UL, 2918365339UL, 1524020338UL, 1518925132UL, + 3760827505UL, 3759777254UL, 1202760957UL, 3985898139UL, + 3906192525UL, 674977740UL, 4174734889UL, 2031300136UL, + 2019492241UL, 3983892565UL, 4153806404UL, 3822280332UL, + 352677332UL, 2297720250UL, 60907813UL, 90501309UL, + 3286998549UL, 1016092578UL, 2535922412UL, 2839152426UL, + 457141659UL, 509813237UL, 4120667899UL, 652014361UL, + 1966332200UL, 2975202805UL, 55981186UL, 2327461051UL, + 676427537UL, 3255491064UL, 2882294119UL, 3433927263UL, + 1307055953UL, 942726286UL, 933058658UL, 2468411793UL, + 3933900994UL, 4215176142UL, 1361170020UL, 2001714738UL, + 2830558078UL, 3274259782UL, 1222529897UL, 1679025792UL, + 2729314320UL, 3714953764UL, 1770335741UL, 151462246UL, + 3013232138UL, 1682292957UL, 1483529935UL, 471910574UL, + 1539241949UL, 458788160UL, 3436315007UL, 1807016891UL, + 3718408830UL, 978976581UL, 1043663428UL, 3165965781UL, + 1927990952UL, 4200891579UL, 2372276910UL, 3208408903UL, + 3533431907UL, 1412390302UL, 2931980059UL, 4132332400UL, + 1947078029UL, 3881505623UL, 4168226417UL, 2941484381UL, + 1077988104UL, 1320477388UL, 886195818UL, 18198404UL, + 3786409000UL, 2509781533UL, 112762804UL, 3463356488UL, + 1866414978UL, 891333506UL, 18488651UL, 661792760UL, + 1628790961UL, 3885187036UL, 3141171499UL, 876946877UL, + 2693282273UL, 1372485963UL, 791857591UL, 2686433993UL, + 3759982718UL, 3167212022UL, 3472953795UL, 2716379847UL, + 445679433UL, 3561995674UL, 3504004811UL, 3574258232UL, + 54117162UL, 3331405415UL, 2381918588UL, 3769707343UL, + 4154350007UL, 1140177722UL, 4074052095UL, 668550556UL, + 3214352940UL, 367459370UL, 261225585UL, 2610173221UL, + 4209349473UL, 3468074219UL, 3265815641UL, 314222801UL, + 3066103646UL, 3808782860UL, 282218597UL, 3406013506UL, + 3773591054UL, 379116347UL, 1285071038UL, 846784868UL, + 2669647154UL, 3771962079UL, 3550491691UL, 2305946142UL, + 453669953UL, 1268987020UL, 3317592352UL, 3279303384UL, + 3744833421UL, 2610507566UL, 3859509063UL, 266596637UL, + 3847019092UL, 517658769UL, 3462560207UL, 3443424879UL, + 370717030UL, 4247526661UL, 2224018117UL, 4143653529UL, + 4112773975UL, 2788324899UL, 2477274417UL, 1456262402UL, + 2901442914UL, 1517677493UL, 1846949527UL, 2295493580UL, + 3734397586UL, 2176403920UL, 1280348187UL, 1908823572UL, + 3871786941UL, 846861322UL, 1172426758UL, 3287448474UL, + 3383383037UL, 1655181056UL, 3139813346UL, 901632758UL, + 1897031941UL, 2986607138UL, 3066810236UL, 3447102507UL, + 1393639104UL, 373351379UL, 950779232UL, 625454576UL, + 3124240540UL, 4148612726UL, 2007998917UL, 544563296UL, + 2244738638UL, 2330496472UL, 2058025392UL, 1291430526UL, + 424198748UL, 50039436UL, 29584100UL, 3605783033UL, + 2429876329UL, 2791104160UL, 1057563949UL, 3255363231UL, + 3075367218UL, 3463963227UL, 1469046755UL, 985887462UL} +} ; + +/* sLb(s,n) allows us to subsript s by byte offsets, which + allows us to avoid a subscript scaling. +*/ +#define sub(s,n) *((IntU32 *)((IntP)s+(n))) + +/* Below is one BlowFish round including the F function +*/ +#define round(l,r,n) \ + l ^= P[n]; \ + r ^= ( (sub(S[0],l>>22 & 0x3fc) + sub(S[1],l>>14 & 0x3fc)) \ + ^ sub(S[2],l>>6 & 0x3fc) ) +S[3][l & 0xff] + + + +/* This function requires the block to be two 32 bit integers, in +whatever endian form the machine uses. On little endian machines +use crypt_8bytes() on user data. make_bfkey should call crypt_block +on either endian machine. Pass direction 0 to encrypt, 1 to decrypt. +*/ +static void crypt_block(block, bfkey, direction) + IntU32 block[2]; + BFkey_type *bfkey; + short direction; +{ + register IntU32 left, right, + (*S)[256], + *P ; + + left = block[0] ; right = block[1] ; + + S = bfkey->sbox ; + P = bfkey->p[direction] ; + + round( left, right, 0 ) ; round( right, left, 1 ) ; + round( left, right, 2 ) ; round( right, left, 3 ) ; + round( left, right, 4 ) ; round( right, left, 5 ) ; + round( left, right, 6 ) ; round( right, left, 7 ) ; + round( left, right, 8 ) ; round( right, left, 9 ) ; + round( left, right, 10 ) ; round( right, left, 11 ) ; + round( left, right, 12 ) ; round( right, left, 13 ) ; + round( left, right, 14 ) ; round( right, left, 15 ) ; + + left = left ^ P[NROUNDS] ; + right = right ^ P[NROUNDS+1] ; + block[0] = right ; + block[1] = left ; +} + +/* The following should be allignment and endian independent. + I have not tested it on a little-endian machine. + It takes the input block from source, and puts the output + in dest. They can be the same. It takes the same direction + parameter as crypt_block(). +*/ +static void crypt_8bytes(source, dest, bfkey, direction) + IntU8 *source, *dest; + BFkey_type *bfkey; + short direction; +{ + IntU32 block[2] ; + + block[0] = source[3] | source[2]<<8 | source[1]<<16 | source[0]<<24 ; + block[1] = source[7] | source[6]<<8 | source[5]<<16 | source[4]<<24 ; + + crypt_block( block, bfkey, direction ) ; + + dest[0]= block[0]>>24 ; + dest[1]= block[0]>>16 & 0xff ; + dest[2]= block[0]>>8 & 0xff ; + dest[3]= block[0] & 0xff ; + dest[4]= block[1]>>24 ; + dest[5]= block[1]>>16 & 0xff ; + dest[6]= block[1]>> 8 & 0xff ; + dest[7]= block[1] & 0xff ; +} + +/* make_bfkey() takes the address of the key data as a char*, + and the length of the key in bytes. It generates and returns + a pointer to an object of BFkey_type, which can be passed + to the crypt functions. It does some simple testing of the + init data and crypt routine, and returns 0 on error. +*/ +static void make_bfkey(key_string, keylength, bfkey) + unsigned char *key_string; + int keylength; + BFkey_type *bfkey; +{ + int i, j, k ; + IntU32 dspace[2], + checksum=0 ; + + /* Copy constant initial data to P vector */ + for( i=0 ; i<NROUNDS+2 ; ++i ) + { + bfkey->p[0][i] = p_init[i] ; + bfkey->p[1][NROUNDS+1-i] = p_init[i] ; + checksum = (checksum<<1 | checksum>>31)+p_init[i] ; + } + + /* Copy constant initial data to sboxes */ + for( i=0 ; i<4 ; ++i ) + for( j=0 ; j<256 ; ++j ) + { + bfkey->sbox[i][j] = s_init[i][j] ; + checksum = ((checksum*13)<<11 | (checksum*13)>>21) + + s_init[i][j] ; + } + + /* Test init data. */ + if( checksum != 0x55861a61 ) + { + PyErr_SetString(PyExc_SystemError, + "Blowfish: Bad initialization data"); + return; + } + + dspace[0] = 0 ; + dspace[1] = 0 ; + + /* Test the crypt_block() routine. */ + for( i=0 ; i<10 ; ++i ) + crypt_block( dspace, bfkey, 0 ) ; + checksum = dspace[0] ; + for( i=0 ; i<10 ; ++i ) + crypt_block( dspace, bfkey, 1 ) ; + if( (checksum!=0xaafe4ebd) || dspace[0] || dspace[1] ) + { + PyErr_SetString(PyExc_SystemError, + "Blowfish: Error in crypt_block routine"); + return; + } + + + /* Xor key string into encryption key vector */ + j = 0 ; + for (i=0 ; i<NROUNDS+2 ; ++i) + { + IntU32 data; + data = 0 ; + for (k=0 ; k<4 ; ++k ) + data = (data << 8) | key_string[j++ % keylength]; + (bfkey->p)[0][i] ^= data; + } + + + for (i = 0 ; i<NROUNDS+2 ; i+=2) + { + crypt_block( dspace, bfkey, 0 ) ; + bfkey->p[0][i] = dspace[0] ; + bfkey->p[1][NROUNDS+1-i] = dspace[0] ; + bfkey->p[0][i+1] = dspace[1] ; + bfkey->p[1][NROUNDS-i] = dspace[1] ; + } + + for ( i=0 ; i<4 ; ++i ) + for ( j=0 ; j<256 ; j+=2 ) + { + crypt_block( dspace, bfkey, 0 ) ; + bfkey->sbox[i][j] = dspace[0] ; + bfkey->sbox[i][j+1] = dspace[1] ; + } +} + + +static inline void +Blowfishencrypt(self, block) + Blowfishobject *self; + unsigned char *block; +{ + crypt_8bytes(block, block, &(self->bfkey), 0); +} + + +static inline void +Blowfishdecrypt(self, block) + Blowfishobject *self; + unsigned char *block; +{ + crypt_8bytes(block, block, &(self->bfkey), 1); +} + +static inline void +Blowfishinit(self, key, keylength) + Blowfishobject *self; + unsigned char *key; + int keylength; +{ + make_bfkey(key, keylength, &(self->bfkey)); +} diff --git a/block/CAST.c b/block/CAST.c new file mode 100644 index 0000000..7062f3e --- /dev/null +++ b/block/CAST.c @@ -0,0 +1,466 @@ +/* + cast.c -- implementation of CAST-128 (aka CAST5) as described in RFC2144 + + compile -DPCT for use in the Python Cryptography Toolkit + (this should work automatically) + compile -DTEST to include main() which performs the tests + specified in RFC2144 + + Written by Wim Lewis <wiml@hhhh.org> based entirely on RFC2144. This code + is in the public domain. Consult your local laws for possible restrictions + on use, distribution, and import/export. RFC2144 states that this + algorithm "is available worldwide on a royalty-free basis for commercial + and non-commercial uses". + + This code is a pretty straightforward transliteration of the RFC into C. + It has not been optimized much at all: byte-order-independent arithmetic + operations are used where order-dependent pointer ops or unions might be + faster; the code could be rearranged to give the optimizer a better + chance to speed things up; etc. + + This code requires a vaguely ANSI-ish compiler. + + Tested with gcc 2.5.8 on i486, i586, i686, hp pa-risc, mc68040, sparc; + also with gcc 2.7.2 and (with minor changes) native Sun compiler on sparc + + History: + 21 Jul 1997: wiml : first working version & Python module +*/ + + +/* adjust these according to your compiler/platform. On some machines + uint32 will have to be a long. It's OK if uint32 is more than 32 bits. */ +typedef unsigned int uint32; +typedef unsigned char uint8; + +#ifdef PCTObject_HEAD +#define PCT +#endif + +#ifdef PCT +#define PCTstatic static +#else +#define PCTstatic +#endif + +/* this struct probably belongs in cast.h */ +typedef struct { + /* masking and rotate keys */ + uint32 Km[16]; + uint8 Kr[16]; + /* number of rounds (depends on original unpadded keylength) */ + int rounds; +} cast_keyschedule; + +/* these are the eight 32*256 S-boxes */ +#include "../block/cast5.c" + +/* fetch a uint32 from an array of uint8s (with a given offset) */ +#define fetch(ptr, base) (((((( ptr[base]<< 8 ) | ptr[base+1] )<< 8 ) | ptr[base+2] )<< 8 ) | ptr[base+3]) + +/* this is the round function f(D, Km, Kr) */ +static inline uint32 castfunc(D, Kmi, Kri, type) + uint32 D, Kmi; + uint8 Kri; + int type; +{ + uint32 I, f; + short Ia, Ib, Ic, Id; + + switch(type) { + case 0: + I = (Kmi + D) ; + break; + case 1: + I = (Kmi ^ D) ; + break; + default: + case 2: + I = (Kmi - D) ; + break; + } + + I &= 0xFFFFFFFF; + I = ( I << Kri ) | ( I >> ( 32-Kri ) ); + Ia = ( I >> 24 ) & 0xFF; + Ib = ( I >> 16 ) & 0xFF; + Ic = ( I >> 8 ) & 0xFF; + Id = ( I ) & 0xFF; + + switch(type) { + case 0: + f = ((S1[Ia] ^ S2[Ib]) - S3[Ic]) + S4[Id]; + break; + case 1: + f = ((S1[Ia] - S2[Ib]) + S3[Ic]) ^ S4[Id]; + break; + default: + case 2: + f = ((S1[Ia] + S2[Ib]) ^ S3[Ic]) - S4[Id]; + break; + } + + return f; +} + +/* encrypts/decrypts one block of data according to the key schedule + pointed to by `key'. Encrypts if decrypt=0, otherwise decrypts. */ +PCTstatic void castcrypt(key, block, decrypt) + cast_keyschedule *key; + uint8 *block; + int decrypt; +{ + uint32 L, R, tmp, f; + uint32 Kmi; + uint8 Kri; + short functype, round; + + L = fetch(block, 0); + R = fetch(block, 4); + +/* printf("L0 = %08x R0 = %08x\n", L, R); */ + + for(round = 0; round < key->rounds; round ++) { + + if (!decrypt) { + Kmi = key->Km[round]; + Kri = key->Kr[round]; + functype = round % 3; + } else { + Kmi = key->Km[(key->rounds) - round - 1]; + Kri = key->Kr[(key->rounds) - round - 1]; + functype = (((key->rounds) - round - 1) % 3); + } + + f = castfunc(R, Kmi, Kri, functype); + + tmp = L; + L = R; + R = tmp ^ f; + +/* printf("L%d = %08x R%d = %08x\n", round+1, L, round+1, R); */ + } + + block[0] = ( R & 0xFF000000 ) >> 24; + block[1] = ( R & 0x00FF0000 ) >> 16; + block[2] = ( R & 0x0000FF00 ) >> 8; + block[3] = ( R & 0x000000FF ); + block[4] = ( L & 0xFF000000 ) >> 24; + block[5] = ( L & 0x00FF0000 ) >> 16; + block[6] = ( L & 0x0000FF00 ) >> 8; + block[7] = ( L & 0x000000FF ); +} + +/* fetch a uint8 from an array of uint32s */ +#define b(a,n) (((a)[n/4] >> (24-((n&3)*8))) & 0xFF) + +/* key schedule round functions */ + +#define XZRound(T, F, ki1, ki2, ki3, ki4, \ + si11, si12, si13, si14, si15,\ + si25,\ + si35,\ + si45 ) \ + T[0] = F[ki1] ^ S5[si11 ] ^ S6[si12 ] ^ S7[si13 ] ^ S8[si14 ] ^ S7[si15];\ + T[1] = F[ki2] ^ S5[b(T, 0)] ^ S6[b(T,2)] ^ S7[b(T, 1)] ^ S8[b(T,3)] ^ S8[si25];\ + T[2] = F[ki3] ^ S5[b(T, 7)] ^ S6[b(T,6)] ^ S7[b(T, 5)] ^ S8[b(T,4)] ^ S5[si35];\ + T[3] = F[ki4] ^ S5[b(T,10)] ^ S6[b(T,9)] ^ S7[b(T,11)] ^ S8[b(T,8)] ^ S6[si45]; + +#define zxround() XZRound(z, x, 0, 2, 3, 1, \ + b(x,13), b(x,15), b(x,12), b(x,14),\ + b(x, 8), b(x,10), b(x, 9), b(x,11)) + +#define xzround() XZRound(x, z, 2, 0, 1, 3, \ + b(z,5), b(z,7), b(z,4), b(z,6), \ + b(z,0), b(z,2), b(z,1), b(z,3)) + +#define Kround(T, base, F,\ + i11, i12, i13, i14, i15,\ + i21, i22, i23, i24, i25,\ + i31, i32, i33, i34, i35,\ + i41, i42, i43, i44, i45)\ + T[base+0] = S5[b(F,i11)] ^ S6[b(F,i12)] ^ S7[b(F,i13)] ^ S8[b(F,i14)] ^ S5[b(F,i15)];\ + T[base+1] = S5[b(F,i21)] ^ S6[b(F,i22)] ^ S7[b(F,i23)] ^ S8[b(F,i24)] ^ S6[b(F,i25)];\ + T[base+2] = S5[b(F,i31)] ^ S6[b(F,i32)] ^ S7[b(F,i33)] ^ S8[b(F,i34)] ^ S7[b(F,i35)];\ + T[base+3] = S5[b(F,i41)] ^ S6[b(F,i42)] ^ S7[b(F,i43)] ^ S8[b(F,i44)] ^ S8[b(F,i45)]; + +/* generates sixteen 32-bit subkeys based on a 4x32-bit input key; + modifies the input key *in as well. */ +static void schedulekeys_half(in, keys) + uint32 *in; + uint32 *keys; +{ + uint32 x[4], z[4]; + + x[0] = in[0]; + x[1] = in[1]; + x[2] = in[2]; + x[3] = in[3]; + + zxround(); + Kround(keys, 0, z, + 8, 9, 7, 6, 2, + 10, 11, 5, 4, 6, + 12, 13, 3, 2, 9, + 14, 15, 1, 0, 12); + xzround(); + Kround(keys, 4, x, + 3, 2, 12, 13, 8, + 1, 0, 14, 15, 13, + 7, 6, 8, 9, 3, + 5, 4, 10, 11, 7); + zxround(); + Kround(keys, 8, z, + 3, 2, 12, 13, 9, + 1, 0, 14, 15, 12, + 7, 6, 8, 9, 2, + 5, 4, 10, 11, 6); + xzround(); + Kround(keys, 12, x, + 8, 9, 7, 6, 3, + 10, 11, 5, 4, 7, + 12, 13, 3, 2, 8, + 14, 15, 1, 0, 13); + + in[0] = x[0]; + in[1] = x[1]; + in[2] = x[2]; + in[3] = x[3]; +} + +/* generates a key schedule from an input key */ +PCTstatic void castschedulekeys(schedule, key, keybytes) + cast_keyschedule *schedule; + uint8 *key; + int keybytes; +{ + uint32 x[4]; + uint8 paddedkey[16]; + uint32 Kr_wide[16]; + int i; + + for(i = 0; i < keybytes; i++) + paddedkey[i] = key[i]; + for( ; i < 16 ; i++) + paddedkey[i] = 0; + + if (keybytes <= 10) + schedule->rounds = 12; + else + schedule->rounds = 16; + + x[0] = fetch(paddedkey, 0); + x[1] = fetch(paddedkey, 4); + x[2] = fetch(paddedkey, 8); + x[3] = fetch(paddedkey, 12); + + schedulekeys_half(x, schedule->Km); + schedulekeys_half(x, Kr_wide); + + for(i = 0; i < 16; i ++) { + /* The Kr[] subkeys are used for 32-bit circular shifts, + so we only need to keep them modulo 32 */ + schedule->Kr[i] = (uint8)(Kr_wide[i] & 0x1F); + } +} + +#ifdef TEST + +/* This performs a variety of encryptions and verifies that the results + match those specified in RFC2144 appendix B. Also verifies that + decryption restores the original data. */ + +#include <stdio.h> + +static cast_keyschedule sched; + +void encrypt(key, keylen, in, out) + uint8 *key; + int keylen; + uint8 *in, *out; +{ + int i; + uint8 k[16]; + + castschedulekeys(&sched, key, keylen); + + for(i = 0; i < 8; i++) + out[i] = in[i]; + castcrypt(&sched, out, 0); +} + +void tst(key, keylen, data, result) + uint8 *key; + int keylen; + uint8 *data, *result; +{ + uint8 d[8]; + int i; + + encrypt(key, keylen, data, d); + + for(i = 0; i < 8; i++) + if (d[i] != result[i]) + break; + + if (i == 8) { + printf("-- test ok (encrypt)\n"); + } else { + for(i = 0; i < 8; i++) + printf(" %02x", d[i]); + printf(" (computed)\n"); + for(i = 0; i < 8; i++) + printf(" %02x", result[i]); + printf(" (expected)\n"); + } + + /* uses key schedule already set up */ + castcrypt(&sched, d, 1); + if (bcmp(d, data, 8)) + printf(" test FAILED (decrypt)\n"); + else + printf(" test ok (decrypt)\n"); + +} + +uint8 key[16] = { 0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, + 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A }; +uint8 data[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; + +/* expected results of encrypting the above with 128, 80, and 40 + bits of key length */ +uint8 out1[8] = { 0x23, 0x8B, 0x4F, 0xE5, 0x84, 0x7E, 0x44, 0xB2 }; +uint8 out2[8] = { 0xEB, 0x6A, 0x71, 0x1A, 0x2C, 0x02, 0x27, 0x1B }; +uint8 out3[8] = { 0x7A, 0xC8, 0x16, 0xD1, 0x6E, 0x9B, 0x30, 0x2E }; + +/* expected results of the "full maintenance test" */ +uint8 afinal[16] = { 0xEE, 0xA9, 0xD0, 0xA2, 0x49, 0xFD, 0x3B, 0xA6, + 0xB3, 0x43, 0x6F, 0xB8, 0x9D, 0x6D, 0xCA, 0x92 }; +uint8 bfinal[16] = { 0xB2, 0xC9, 0x5E, 0xB0, 0x0C, 0x31, 0xAD, 0x71, + 0x80, 0xAC, 0x05, 0xB8, 0xE8, 0x3D, 0x69, 0x6E }; + +main() +{ + /* Appendix B.1 : Single Plaintext-Key-Ciphertext Sets */ + tst(key, 16, data, out1); + tst(key, 10, data, out2); + tst(key, 5, data, out3); + + /* Appendix B.2 : Full Maintenance Test */ + { + uint8 abuf[16]; + uint8 bbuf[16]; + int i; + + bcopy(key, abuf, 16); + bcopy(key, bbuf, 16); + + printf("\nrunning full maintenance test...\n"); + + for(i = 0; i < 1000000; i++) { + castschedulekeys(&sched, bbuf, 16); + castcrypt(&sched, abuf, 0); + castcrypt(&sched, abuf+8, 0); + + castschedulekeys(&sched, abuf, 16); + castcrypt(&sched, bbuf, 0); + castcrypt(&sched, bbuf+8, 0); + + if (!(i % 10000)) { + fprintf(stdout, "\r%d%% ", i / 10000); + fflush(stdout); + } + } + + printf("\r \r"); + + for(i = 0; i < 16; i ++) + if (abuf[i] != afinal[i] || bbuf[i] != bfinal[i]) + break; + + if(i == 16) { + printf("-- full maintenance test ok\n"); + } else { + for(i = 0; i < 16; i++) + printf(" %02x", abuf[i]); + printf("\n"); + for(i = 0; i < 16; i++) + printf(" %02x", bbuf[i]); + printf("\n"); + } + + printf("running maintenance test in reverse...\n"); + for(i = 0; i < 1000000; i++) { + castschedulekeys(&sched, abuf, 16); + castcrypt(&sched, bbuf+8, 1); + castcrypt(&sched, bbuf, 1); + + castschedulekeys(&sched, bbuf, 16); + castcrypt(&sched, abuf+8, 1); + castcrypt(&sched, abuf, 1); + + if (!(i % 10000)) { + fprintf(stdout, "\r%d%% ", i / 10000); + fflush(stdout); + } + } + + printf("\r \r"); + if (bcmp(abuf, key, 16) || bcmp(bbuf, key, 16)) + printf("-- reverse maintenance test FAILED\n"); + else + printf("-- reverse maintenance test ok\n"); + } +} + +#endif + +#ifdef PCT + +/* code to interface with the Python Cryptography Toolkit */ + +typedef struct +{ + PCTObject_HEAD + cast_keyschedule schedule; +} CASTobject; + +static void +CASTinit(self, key, keylength) + CASTobject *self; + unsigned char *key; + int keylength; +{ + /* presumably this will optimize out */ + if (sizeof(uint32) < 4 || sizeof(uint8) != 1) { + PyErr_SetString(PyExc_SystemError, + "CAST module compiled with bad typedefs!"); + } + + /* make sure the key length is within bounds */ + if (keylength < 5 || keylength > 16) { + PyErr_SetString(PyExc_ValueError, "CAST key must be " + "at least 5 bytes and no more than 16 bytes long"); + return; + } + + /* do the actual key schedule setup */ + castschedulekeys(&(self->schedule), key, keylength); +} + +static void +CASTencrypt(self, block) + CASTobject *self; + unsigned char *block; +{ + castcrypt(&(self->schedule), block, 0); +} + +static void CASTdecrypt(self, block) + CASTobject *self; + unsigned char *block; +{ + castcrypt(&(self->schedule), block, 1); +} + +#endif diff --git a/block/DES.c b/block/DES.c new file mode 100644 index 0000000..c79fa62 --- /dev/null +++ b/block/DES.c @@ -0,0 +1,678 @@ + +/* + * des.c : Source code for the DES block cipher + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +/* des.c */ +/* Copyright (C) 1993 Eric Young */ +/* Integrated into the PCT by A.M. Kuchling, November 1994 */ + +typedef unsigned char des_cblock[8]; + +/* ecb_enc.c */ +/* Copyright (C) 1993 Eric Young - see README for more details */ + +#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \ + l|=((unsigned long)(*((c)++)))<< 8, \ + l|=((unsigned long)(*((c)++)))<<16, \ + l|=((unsigned long)(*((c)++)))<<24) + +/* NOTE - c is not incremented as per c2l */ +#define c2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2|=((unsigned long)(*(--(c))))<<24; \ + case 7: l2|=((unsigned long)(*(--(c))))<<16; \ + case 6: l2|=((unsigned long)(*(--(c))))<< 8; \ + case 5: l2|=((unsigned long)(*(--(c)))); \ + case 4: l1|=((unsigned long)(*(--(c))))<<24; \ + case 3: l1|=((unsigned long)(*(--(c))))<<16; \ + case 2: l1|=((unsigned long)(*(--(c))))<< 8; \ + case 1: l1|=((unsigned long)(*(--(c)))); \ + } \ + } + +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff)) + +/* replacements for htonl and ntohl since I have no idea what to do + * when faced with machines with 8 byte longs. */ +#define HDRSIZE 4 + +#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24, \ + l|=((unsigned long)(*((c)++)))<<16, \ + l|=((unsigned long)(*((c)++)))<< 8, \ + l|=((unsigned long)(*((c)++)))) + +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +/* NOTE - c is not incremented as per l2c */ +#define l2cn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ + } \ + } + +#define D_ENCRYPT(L,R,S) \ + u=(R^s[S ]); \ + t=R^s[S+1]; \ + t=((t>>4)+(t<<28)); \ + L^= des_SPtrans[1][(t )&0x3f]| \ + des_SPtrans[3][(t>> 8)&0x3f]| \ + des_SPtrans[5][(t>>16)&0x3f]| \ + des_SPtrans[7][(t>>24)&0x3f]| \ + des_SPtrans[0][(u )&0x3f]| \ + des_SPtrans[2][(u>> 8)&0x3f]| \ + des_SPtrans[4][(u>>16)&0x3f]| \ + des_SPtrans[6][(u>>24)&0x3f]; + + /* IP and FP + * The problem is more of a geometric problem that random bit fiddling. + 0 1 2 3 4 5 6 7 62 54 46 38 30 22 14 6 + 8 9 10 11 12 13 14 15 60 52 44 36 28 20 12 4 + 16 17 18 19 20 21 22 23 58 50 42 34 26 18 10 2 + 24 25 26 27 28 29 30 31 to 56 48 40 32 24 16 8 0 + + 32 33 34 35 36 37 38 39 63 55 47 39 31 23 15 7 + 40 41 42 43 44 45 46 47 61 53 45 37 29 21 13 5 + 48 49 50 51 52 53 54 55 59 51 43 35 27 19 11 3 + 56 57 58 59 60 61 62 63 57 49 41 33 25 17 9 1 + + The output has been subject to swaps of the form + 0 1 -> 3 1 but the odd and even bits have been put into + 2 3 2 0 + different words. The main trick is to remember that + t=((l>>size)^r)&(mask); + r^=t; + l^=(t<<size); + can be used to swap and move bits between words. + + So l = 0 1 2 3 r = 16 17 18 19 + 4 5 6 7 20 21 22 23 + 8 9 10 11 24 25 26 27 + 12 13 14 15 28 29 30 31 + becomes (for size == 2 and mask == 0x3333) + t = 2^16 3^17 -- -- l = 0 1 16 17 r = 2 3 18 19 + 6^20 7^21 -- -- 4 5 20 21 6 7 22 23 + 10^24 11^25 -- -- 8 9 24 25 10 11 24 25 + 14^28 15^29 -- -- 12 13 28 29 14 15 28 29 + + Thanks for hints from Richard Outerbridge - he told me IP&FP + could be done in 15 xor, 10 shifts and 5 ands. + When I finally started to think of the problem in 2D + I first got ~42 operations without xors. When I remembered + how to use xors :-) I got it to its final state. + */ +#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\ + (b)^=(t),\ + (a)^=((t)<<(n))) + + + +/* spr.h */ +/* Copyright (C) 1993 Eric Young - see README for more details */ +static unsigned long des_SPtrans[8][64]={ +/* nibble 0 */ +{0x00820200, 0x00020000, 0x80800000, 0x80820200, +0x00800000, 0x80020200, 0x80020000, 0x80800000, +0x80020200, 0x00820200, 0x00820000, 0x80000200, +0x80800200, 0x00800000, 0x00000000, 0x80020000, +0x00020000, 0x80000000, 0x00800200, 0x00020200, +0x80820200, 0x00820000, 0x80000200, 0x00800200, +0x80000000, 0x00000200, 0x00020200, 0x80820000, +0x00000200, 0x80800200, 0x80820000, 0x00000000, +0x00000000, 0x80820200, 0x00800200, 0x80020000, +0x00820200, 0x00020000, 0x80000200, 0x00800200, +0x80820000, 0x00000200, 0x00020200, 0x80800000, +0x80020200, 0x80000000, 0x80800000, 0x00820000, +0x80820200, 0x00020200, 0x00820000, 0x80800200, +0x00800000, 0x80000200, 0x80020000, 0x00000000, +0x00020000, 0x00800000, 0x80800200, 0x00820200, +0x80000000, 0x80820000, 0x00000200, 0x80020200}, + +/* nibble 1 */ +{0x10042004, 0x00000000, 0x00042000, 0x10040000, +0x10000004, 0x00002004, 0x10002000, 0x00042000, +0x00002000, 0x10040004, 0x00000004, 0x10002000, +0x00040004, 0x10042000, 0x10040000, 0x00000004, +0x00040000, 0x10002004, 0x10040004, 0x00002000, +0x00042004, 0x10000000, 0x00000000, 0x00040004, +0x10002004, 0x00042004, 0x10042000, 0x10000004, +0x10000000, 0x00040000, 0x00002004, 0x10042004, +0x00040004, 0x10042000, 0x10002000, 0x00042004, +0x10042004, 0x00040004, 0x10000004, 0x00000000, +0x10000000, 0x00002004, 0x00040000, 0x10040004, +0x00002000, 0x10000000, 0x00042004, 0x10002004, +0x10042000, 0x00002000, 0x00000000, 0x10000004, +0x00000004, 0x10042004, 0x00042000, 0x10040000, +0x10040004, 0x00040000, 0x00002004, 0x10002000, +0x10002004, 0x00000004, 0x10040000, 0x00042000}, + +/* nibble 2 */ +{0x41000000, 0x01010040, 0x00000040, 0x41000040, +0x40010000, 0x01000000, 0x41000040, 0x00010040, +0x01000040, 0x00010000, 0x01010000, 0x40000000, +0x41010040, 0x40000040, 0x40000000, 0x41010000, +0x00000000, 0x40010000, 0x01010040, 0x00000040, +0x40000040, 0x41010040, 0x00010000, 0x41000000, +0x41010000, 0x01000040, 0x40010040, 0x01010000, +0x00010040, 0x00000000, 0x01000000, 0x40010040, +0x01010040, 0x00000040, 0x40000000, 0x00010000, +0x40000040, 0x40010000, 0x01010000, 0x41000040, +0x00000000, 0x01010040, 0x00010040, 0x41010000, +0x40010000, 0x01000000, 0x41010040, 0x40000000, +0x40010040, 0x41000000, 0x01000000, 0x41010040, +0x00010000, 0x01000040, 0x41000040, 0x00010040, +0x01000040, 0x00000000, 0x41010000, 0x40000040, +0x41000000, 0x40010040, 0x00000040, 0x01010000}, + +/* nibble 3 */ +{0x00100402, 0x04000400, 0x00000002, 0x04100402, +0x00000000, 0x04100000, 0x04000402, 0x00100002, +0x04100400, 0x04000002, 0x04000000, 0x00000402, +0x04000002, 0x00100402, 0x00100000, 0x04000000, +0x04100002, 0x00100400, 0x00000400, 0x00000002, +0x00100400, 0x04000402, 0x04100000, 0x00000400, +0x00000402, 0x00000000, 0x00100002, 0x04100400, +0x04000400, 0x04100002, 0x04100402, 0x00100000, +0x04100002, 0x00000402, 0x00100000, 0x04000002, +0x00100400, 0x04000400, 0x00000002, 0x04100000, +0x04000402, 0x00000000, 0x00000400, 0x00100002, +0x00000000, 0x04100002, 0x04100400, 0x00000400, +0x04000000, 0x04100402, 0x00100402, 0x00100000, +0x04100402, 0x00000002, 0x04000400, 0x00100402, +0x00100002, 0x00100400, 0x04100000, 0x04000402, +0x00000402, 0x04000000, 0x04000002, 0x04100400}, + +/* nibble 4 */ +{0x02000000, 0x00004000, 0x00000100, 0x02004108, +0x02004008, 0x02000100, 0x00004108, 0x02004000, +0x00004000, 0x00000008, 0x02000008, 0x00004100, +0x02000108, 0x02004008, 0x02004100, 0x00000000, +0x00004100, 0x02000000, 0x00004008, 0x00000108, +0x02000100, 0x00004108, 0x00000000, 0x02000008, +0x00000008, 0x02000108, 0x02004108, 0x00004008, +0x02004000, 0x00000100, 0x00000108, 0x02004100, +0x02004100, 0x02000108, 0x00004008, 0x02004000, +0x00004000, 0x00000008, 0x02000008, 0x02000100, +0x02000000, 0x00004100, 0x02004108, 0x00000000, +0x00004108, 0x02000000, 0x00000100, 0x00004008, +0x02000108, 0x00000100, 0x00000000, 0x02004108, +0x02004008, 0x02004100, 0x00000108, 0x00004000, +0x00004100, 0x02004008, 0x02000100, 0x00000108, +0x00000008, 0x00004108, 0x02004000, 0x02000008}, + +/* nibble 5 */ +{0x20000010, 0x00080010, 0x00000000, 0x20080800, +0x00080010, 0x00000800, 0x20000810, 0x00080000, +0x00000810, 0x20080810, 0x00080800, 0x20000000, +0x20000800, 0x20000010, 0x20080000, 0x00080810, +0x00080000, 0x20000810, 0x20080010, 0x00000000, +0x00000800, 0x00000010, 0x20080800, 0x20080010, +0x20080810, 0x20080000, 0x20000000, 0x00000810, +0x00000010, 0x00080800, 0x00080810, 0x20000800, +0x00000810, 0x20000000, 0x20000800, 0x00080810, +0x20080800, 0x00080010, 0x00000000, 0x20000800, +0x20000000, 0x00000800, 0x20080010, 0x00080000, +0x00080010, 0x20080810, 0x00080800, 0x00000010, +0x20080810, 0x00080800, 0x00080000, 0x20000810, +0x20000010, 0x20080000, 0x00080810, 0x00000000, +0x00000800, 0x20000010, 0x20000810, 0x20080800, +0x20080000, 0x00000810, 0x00000010, 0x20080010}, + +/* nibble 6 */ +{0x00001000, 0x00000080, 0x00400080, 0x00400001, +0x00401081, 0x00001001, 0x00001080, 0x00000000, +0x00400000, 0x00400081, 0x00000081, 0x00401000, +0x00000001, 0x00401080, 0x00401000, 0x00000081, +0x00400081, 0x00001000, 0x00001001, 0x00401081, +0x00000000, 0x00400080, 0x00400001, 0x00001080, +0x00401001, 0x00001081, 0x00401080, 0x00000001, +0x00001081, 0x00401001, 0x00000080, 0x00400000, +0x00001081, 0x00401000, 0x00401001, 0x00000081, +0x00001000, 0x00000080, 0x00400000, 0x00401001, +0x00400081, 0x00001081, 0x00001080, 0x00000000, +0x00000080, 0x00400001, 0x00000001, 0x00400080, +0x00000000, 0x00400081, 0x00400080, 0x00001080, +0x00000081, 0x00001000, 0x00401081, 0x00400000, +0x00401080, 0x00000001, 0x00001001, 0x00401081, +0x00400001, 0x00401080, 0x00401000, 0x00001001}, + +/* nibble 7 */ +{0x08200020, 0x08208000, 0x00008020, 0x00000000, +0x08008000, 0x00200020, 0x08200000, 0x08208020, +0x00000020, 0x08000000, 0x00208000, 0x00008020, +0x00208020, 0x08008020, 0x08000020, 0x08200000, +0x00008000, 0x00208020, 0x00200020, 0x08008000, +0x08208020, 0x08000020, 0x00000000, 0x00208000, +0x08000000, 0x00200000, 0x08008020, 0x08200020, +0x00200000, 0x00008000, 0x08208000, 0x00000020, +0x00200000, 0x00008000, 0x08000020, 0x08208020, +0x00008020, 0x08000000, 0x00000000, 0x00208000, +0x08200020, 0x08008020, 0x08008000, 0x00200020, +0x08208000, 0x00000020, 0x00200020, 0x08008000, +0x08208020, 0x00200000, 0x08200000, 0x08000020, +0x00208000, 0x00008020, 0x08008020, 0x08200000, +0x00000020, 0x08208000, 0x00208020, 0x00000000, +0x08000000, 0x08200020, 0x00008000, 0x00208020}}; + +static unsigned long des_skb[8][64]={ +/* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ +{0x00000000,0x00000010,0x20000000,0x20000010, +0x00010000,0x00010010,0x20010000,0x20010010, +0x00000800,0x00000810,0x20000800,0x20000810, +0x00010800,0x00010810,0x20010800,0x20010810, +0x00000020,0x00000030,0x20000020,0x20000030, +0x00010020,0x00010030,0x20010020,0x20010030, +0x00000820,0x00000830,0x20000820,0x20000830, +0x00010820,0x00010830,0x20010820,0x20010830, +0x00080000,0x00080010,0x20080000,0x20080010, +0x00090000,0x00090010,0x20090000,0x20090010, +0x00080800,0x00080810,0x20080800,0x20080810, +0x00090800,0x00090810,0x20090800,0x20090810, +0x00080020,0x00080030,0x20080020,0x20080030, +0x00090020,0x00090030,0x20090020,0x20090030, +0x00080820,0x00080830,0x20080820,0x20080830, +0x00090820,0x00090830,0x20090820,0x20090830}, +/* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ +{0x00000000,0x02000000,0x00002000,0x02002000, +0x00200000,0x02200000,0x00202000,0x02202000, +0x00000004,0x02000004,0x00002004,0x02002004, +0x00200004,0x02200004,0x00202004,0x02202004, +0x00000400,0x02000400,0x00002400,0x02002400, +0x00200400,0x02200400,0x00202400,0x02202400, +0x00000404,0x02000404,0x00002404,0x02002404, +0x00200404,0x02200404,0x00202404,0x02202404, +0x10000000,0x12000000,0x10002000,0x12002000, +0x10200000,0x12200000,0x10202000,0x12202000, +0x10000004,0x12000004,0x10002004,0x12002004, +0x10200004,0x12200004,0x10202004,0x12202004, +0x10000400,0x12000400,0x10002400,0x12002400, +0x10200400,0x12200400,0x10202400,0x12202400, +0x10000404,0x12000404,0x10002404,0x12002404, +0x10200404,0x12200404,0x10202404,0x12202404}, +/* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ +{0x00000000,0x00000001,0x00040000,0x00040001, +0x01000000,0x01000001,0x01040000,0x01040001, +0x00000002,0x00000003,0x00040002,0x00040003, +0x01000002,0x01000003,0x01040002,0x01040003, +0x00000200,0x00000201,0x00040200,0x00040201, +0x01000200,0x01000201,0x01040200,0x01040201, +0x00000202,0x00000203,0x00040202,0x00040203, +0x01000202,0x01000203,0x01040202,0x01040203, +0x08000000,0x08000001,0x08040000,0x08040001, +0x09000000,0x09000001,0x09040000,0x09040001, +0x08000002,0x08000003,0x08040002,0x08040003, +0x09000002,0x09000003,0x09040002,0x09040003, +0x08000200,0x08000201,0x08040200,0x08040201, +0x09000200,0x09000201,0x09040200,0x09040201, +0x08000202,0x08000203,0x08040202,0x08040203, +0x09000202,0x09000203,0x09040202,0x09040203}, +/* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ +{0x00000000,0x00100000,0x00000100,0x00100100, +0x00000008,0x00100008,0x00000108,0x00100108, +0x00001000,0x00101000,0x00001100,0x00101100, +0x00001008,0x00101008,0x00001108,0x00101108, +0x04000000,0x04100000,0x04000100,0x04100100, +0x04000008,0x04100008,0x04000108,0x04100108, +0x04001000,0x04101000,0x04001100,0x04101100, +0x04001008,0x04101008,0x04001108,0x04101108, +0x00020000,0x00120000,0x00020100,0x00120100, +0x00020008,0x00120008,0x00020108,0x00120108, +0x00021000,0x00121000,0x00021100,0x00121100, +0x00021008,0x00121008,0x00021108,0x00121108, +0x04020000,0x04120000,0x04020100,0x04120100, +0x04020008,0x04120008,0x04020108,0x04120108, +0x04021000,0x04121000,0x04021100,0x04121100, +0x04021008,0x04121008,0x04021108,0x04121108}, +/* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ +{0x00000000,0x10000000,0x00010000,0x10010000, +0x00000004,0x10000004,0x00010004,0x10010004, +0x20000000,0x30000000,0x20010000,0x30010000, +0x20000004,0x30000004,0x20010004,0x30010004, +0x00100000,0x10100000,0x00110000,0x10110000, +0x00100004,0x10100004,0x00110004,0x10110004, +0x20100000,0x30100000,0x20110000,0x30110000, +0x20100004,0x30100004,0x20110004,0x30110004, +0x00001000,0x10001000,0x00011000,0x10011000, +0x00001004,0x10001004,0x00011004,0x10011004, +0x20001000,0x30001000,0x20011000,0x30011000, +0x20001004,0x30001004,0x20011004,0x30011004, +0x00101000,0x10101000,0x00111000,0x10111000, +0x00101004,0x10101004,0x00111004,0x10111004, +0x20101000,0x30101000,0x20111000,0x30111000, +0x20101004,0x30101004,0x20111004,0x30111004}, +/* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ +{0x00000000,0x08000000,0x00000008,0x08000008, +0x00000400,0x08000400,0x00000408,0x08000408, +0x00020000,0x08020000,0x00020008,0x08020008, +0x00020400,0x08020400,0x00020408,0x08020408, +0x00000001,0x08000001,0x00000009,0x08000009, +0x00000401,0x08000401,0x00000409,0x08000409, +0x00020001,0x08020001,0x00020009,0x08020009, +0x00020401,0x08020401,0x00020409,0x08020409, +0x02000000,0x0A000000,0x02000008,0x0A000008, +0x02000400,0x0A000400,0x02000408,0x0A000408, +0x02020000,0x0A020000,0x02020008,0x0A020008, +0x02020400,0x0A020400,0x02020408,0x0A020408, +0x02000001,0x0A000001,0x02000009,0x0A000009, +0x02000401,0x0A000401,0x02000409,0x0A000409, +0x02020001,0x0A020001,0x02020009,0x0A020009, +0x02020401,0x0A020401,0x02020409,0x0A020409}, +/* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ +{0x00000000,0x00000100,0x00080000,0x00080100, +0x01000000,0x01000100,0x01080000,0x01080100, +0x00000010,0x00000110,0x00080010,0x00080110, +0x01000010,0x01000110,0x01080010,0x01080110, +0x00200000,0x00200100,0x00280000,0x00280100, +0x01200000,0x01200100,0x01280000,0x01280100, +0x00200010,0x00200110,0x00280010,0x00280110, +0x01200010,0x01200110,0x01280010,0x01280110, +0x00000200,0x00000300,0x00080200,0x00080300, +0x01000200,0x01000300,0x01080200,0x01080300, +0x00000210,0x00000310,0x00080210,0x00080310, +0x01000210,0x01000310,0x01080210,0x01080310, +0x00200200,0x00200300,0x00280200,0x00280300, +0x01200200,0x01200300,0x01280200,0x01280300, +0x00200210,0x00200310,0x00280210,0x00280310, +0x01200210,0x01200310,0x01280210,0x01280310}, +/* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ +{0x00000000,0x04000000,0x00040000,0x04040000, +0x00000002,0x04000002,0x00040002,0x04040002, +0x00002000,0x04002000,0x00042000,0x04042000, +0x00002002,0x04002002,0x00042002,0x04042002, +0x00000020,0x04000020,0x00040020,0x04040020, +0x00000022,0x04000022,0x00040022,0x04040022, +0x00002020,0x04002020,0x00042020,0x04042020, +0x00002022,0x04002022,0x00042022,0x04042022, +0x00000800,0x04000800,0x00040800,0x04040800, +0x00000802,0x04000802,0x00040802,0x04040802, +0x00002800,0x04002800,0x00042800,0x04042800, +0x00002802,0x04002802,0x00042802,0x04042802, +0x00000820,0x04000820,0x00040820,0x04040820, +0x00000822,0x04000822,0x00040822,0x04040822, +0x00002820,0x04002820,0x00042820,0x04042820, +0x00002822,0x04002822,0x00042822,0x04042822} +}; + +typedef struct des_ks_struct + { + union { + des_cblock _; + /* make sure things are correct size on machines with + * 8 byte longs */ + unsigned long pad[2]; + } ks; +#define _ ks._ + } des_key_schedule[16]; + +typedef struct +{ + PCTObject_HEAD + des_key_schedule KeySched; +} DESobject; + +static int des_encrypt(input,output,ks,encrypt) + unsigned long *input; + unsigned long *output; + des_key_schedule ks; + int encrypt; + { + register unsigned long l,r,t,u; + register int i; + register unsigned long *s; + + l=input[0]; + r=input[1]; + + /* do IP */ + PERM_OP(r,l,t, 4,0x0f0f0f0f); + PERM_OP(l,r,t,16,0x0000ffff); + PERM_OP(r,l,t, 2,0x33333333); + PERM_OP(l,r,t, 8,0x00ff00ff); + PERM_OP(r,l,t, 1,0x55555555); + /* r and l are reversed - remember that :-) - fix + * it in the next step */ + + /* Things have been modified so that the initial rotate is + * done outside the loop. This required the + * des_SPtrans values in sp.h to be rotated 1 bit to the right. + * One perl script later and things have a 5% speed up on a sparc2. + * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> + * for pointing this out. */ + t=(r<<1)|(r>>31); + r=(l<<1)|(l>>31); + l=t; + + /* clear the top bits on machines with 8byte longs */ + l&=0xffffffff; + r&=0xffffffff; + + s=(unsigned long *)ks; + /* I don't know if it is worth the effort of loop unrolling the + * inner loop */ + if (encrypt) + { + for (i=0; i<32; i+=4) + { + D_ENCRYPT(l,r,i+0); /* 1 */ + D_ENCRYPT(r,l,i+2); /* 2 */ + } + } + else + { + for (i=30; i>0; i-=4) + { + D_ENCRYPT(l,r,i-0); /* 16 */ + D_ENCRYPT(r,l,i-2); /* 15 */ + } + } + l=(l>>1)|(l<<31); + r=(r>>1)|(r<<31); + /* clear the top bits on machines with 8byte longs */ + l&=0xffffffff; + r&=0xffffffff; + + /* swap l and r + * we will not do the swap so just remember they are + * reversed for the rest of the subroutine + * luckily FP fixes this problem :-) */ + + PERM_OP(r,l,t, 1,0x55555555); + PERM_OP(l,r,t, 8,0x00ff00ff); + PERM_OP(r,l,t, 2,0x33333333); + PERM_OP(l,r,t,16,0x0000ffff); + PERM_OP(r,l,t, 4,0x0f0f0f0f); + + output[0]=l; + output[1]=r; + l=r=t=u=0; + return(0); + } + +static int des_ecb_encrypt(input,output,ks,encrypt) + des_cblock *input; + des_cblock *output; + des_key_schedule ks; + int encrypt; + { + register unsigned long l0,l1; + register unsigned char *in,*out; + unsigned long ll[2]; + + in=(unsigned char *)input; + out=(unsigned char *)output; + c2l(in,l0); + c2l(in,l1); + ll[0]=l0; + ll[1]=l1; + des_encrypt(ll,ll,ks,encrypt); + l0=ll[0]; + l1=ll[1]; + l2c(l0,out); + l2c(l1,out); + l0=l1=ll[0]=ll[1]=0; + return(0); + } + + + +static inline void DESdecrypt(self, block) + DESobject *self; + unsigned char *block; +{ + des_cblock output; + + des_ecb_encrypt(block, output, self->KeySched, 0); + memcpy(block, output, 8); +} + +static inline void DESencrypt(self, block) + DESobject *self; + unsigned char *block; +{ + des_cblock output; + + des_ecb_encrypt(block, output, self->KeySched, 1); + memcpy(block, output, 8); +} + +/* NOW DEFINED IN des_local.h + * See ecb_encrypt.c for a pseudo description of these macros. + * #define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\ + * (b)^=(t),\ + * (a)=((a)^((t)<<(n)))) + */ + +#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ + (a)=(a)^(t)^(t>>(16-(n)))) + +static char shifts2[16]={0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0}; + +/* return 0 if key parity is odd (correct), + * return -1 if key parity error, + * return -2 if illegal weak key. + */ +static int des_set_key(key,schedule) +des_cblock *key; +des_key_schedule schedule; + { + register unsigned long c,d,t,s; + register unsigned char *in; + register unsigned long *k; + register int i; + + k=(unsigned long *)schedule; + in=(unsigned char *)key; + + c2l(in,c); + c2l(in,d); + + /* do PC1 in 60 simple operations */ +/* PERM_OP(d,c,t,4,0x0f0f0f0f); + HPERM_OP(c,t,-2, 0xcccc0000); + HPERM_OP(c,t,-1, 0xaaaa0000); + HPERM_OP(c,t, 8, 0x00ff0000); + HPERM_OP(c,t,-1, 0xaaaa0000); + HPERM_OP(d,t,-8, 0xff000000); + HPERM_OP(d,t, 8, 0x00ff0000); + HPERM_OP(d,t, 2, 0x33330000); + d=((d&0x00aa00aa)<<7)|((d&0x55005500)>>7)|(d&0xaa55aa55); + d=(d>>8)|((c&0xf0000000)>>4); + c&=0x0fffffff; */ + + /* I now do it in 47 simple operations :-) + * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov) + * for the inspiration. :-) */ + PERM_OP (d,c,t,4,0x0f0f0f0f); + HPERM_OP(c,t,-2,0xcccc0000); + HPERM_OP(d,t,-2,0xcccc0000); + PERM_OP (d,c,t,1,0x55555555); + PERM_OP (c,d,t,8,0x00ff00ff); + PERM_OP (d,c,t,1,0x55555555); + d= (((d&0x000000ff)<<16)| (d&0x0000ff00) | + ((d&0x00ff0000)>>16)|((c&0xf0000000)>>4)); + c&=0x0fffffff; + + for (i=0; i<16; i++) + { + if (shifts2[i]) + { c=((c>>2)|(c<<26)); d=((d>>2)|(d<<26)); } + else + { c=((c>>1)|(c<<27)); d=((d>>1)|(d<<27)); } + c&=0x0fffffff; + d&=0x0fffffff; + /* could be a few less shifts but I am to lazy at this + * point in time to investigate */ + s= des_skb[0][ (c )&0x3f ]| + des_skb[1][((c>> 6)&0x03)|((c>> 7)&0x3c)]| + des_skb[2][((c>>13)&0x0f)|((c>>14)&0x30)]| + des_skb[3][((c>>20)&0x01)|((c>>21)&0x06) | + ((c>>22)&0x38)]; + t= des_skb[4][ (d )&0x3f ]| + des_skb[5][((d>> 7)&0x03)|((d>> 8)&0x3c)]| + des_skb[6][ (d>>15)&0x3f ]| + des_skb[7][((d>>21)&0x0f)|((d>>22)&0x30)]; + + /* table contained 0213 4657 */ + *(k++)=((t<<16)|(s&0x0000ffff))&0xffffffff; + s= ((s>>16)|(t&0xffff0000)); + + s=(s<<4)|(s>>28); + *(k++)=s&0xffffffff; + } + return(0); + } + +static const unsigned char odd_parity[256]={ + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, +112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, +128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, +145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, +161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, +176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, +193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, +208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, +224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, +241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254}; + +static inline void DESinit(self, key) + DESobject *self; + unsigned char *key; +{ + char oddkey[8]; + int i; + + for (i=0; i<8; i++) + { + oddkey[i]=odd_parity[ key[i] ]; + } + des_set_key(oddkey, self->KeySched); +} diff --git a/block/DES3.c b/block/DES3.c new file mode 100644 index 0000000..24c7651 --- /dev/null +++ b/block/DES3.c @@ -0,0 +1,690 @@ + +/* + * des.c : Source code for the DES block cipher + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +/* des.c */ +/* Copyright (C) 1993 Eric Young */ +/* Integrated into the PCT by A.M. Kuchling, November 1994 */ +/* Fully independent key mode added by Wim Lewis, July 1997 */ + +typedef unsigned char des_cblock[8]; + +/* ecb_enc.c */ +/* Copyright (C) 1993 Eric Young - see README for more details */ + +#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \ + l|=((unsigned long)(*((c)++)))<< 8, \ + l|=((unsigned long)(*((c)++)))<<16, \ + l|=((unsigned long)(*((c)++)))<<24) + +/* NOTE - c is not incremented as per c2l */ +#define c2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2|=((unsigned long)(*(--(c))))<<24; \ + case 7: l2|=((unsigned long)(*(--(c))))<<16; \ + case 6: l2|=((unsigned long)(*(--(c))))<< 8; \ + case 5: l2|=((unsigned long)(*(--(c)))); \ + case 4: l1|=((unsigned long)(*(--(c))))<<24; \ + case 3: l1|=((unsigned long)(*(--(c))))<<16; \ + case 2: l1|=((unsigned long)(*(--(c))))<< 8; \ + case 1: l1|=((unsigned long)(*(--(c)))); \ + } \ + } + +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff)) + +/* replacements for htonl and ntohl since I have no idea what to do + * when faced with machines with 8 byte longs. */ +#define HDRSIZE 4 + +#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24, \ + l|=((unsigned long)(*((c)++)))<<16, \ + l|=((unsigned long)(*((c)++)))<< 8, \ + l|=((unsigned long)(*((c)++)))) + +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +/* NOTE - c is not incremented as per l2c */ +#define l2cn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ + } \ + } + +#define D_ENCRYPT(L,R,S) \ + u=(R^s[S ]); \ + t=R^s[S+1]; \ + t=((t>>4)+(t<<28)); \ + L^= des_SPtrans[1][(t )&0x3f]| \ + des_SPtrans[3][(t>> 8)&0x3f]| \ + des_SPtrans[5][(t>>16)&0x3f]| \ + des_SPtrans[7][(t>>24)&0x3f]| \ + des_SPtrans[0][(u )&0x3f]| \ + des_SPtrans[2][(u>> 8)&0x3f]| \ + des_SPtrans[4][(u>>16)&0x3f]| \ + des_SPtrans[6][(u>>24)&0x3f]; + + /* IP and FP + * The problem is more of a geometric problem that random bit fiddling. + 0 1 2 3 4 5 6 7 62 54 46 38 30 22 14 6 + 8 9 10 11 12 13 14 15 60 52 44 36 28 20 12 4 + 16 17 18 19 20 21 22 23 58 50 42 34 26 18 10 2 + 24 25 26 27 28 29 30 31 to 56 48 40 32 24 16 8 0 + + 32 33 34 35 36 37 38 39 63 55 47 39 31 23 15 7 + 40 41 42 43 44 45 46 47 61 53 45 37 29 21 13 5 + 48 49 50 51 52 53 54 55 59 51 43 35 27 19 11 3 + 56 57 58 59 60 61 62 63 57 49 41 33 25 17 9 1 + + The output has been subject to swaps of the form + 0 1 -> 3 1 but the odd and even bits have been put into + 2 3 2 0 + different words. The main trick is to remember that + t=((l>>size)^r)&(mask); + r^=t; + l^=(t<<size); + can be used to swap and move bits between words. + + So l = 0 1 2 3 r = 16 17 18 19 + 4 5 6 7 20 21 22 23 + 8 9 10 11 24 25 26 27 + 12 13 14 15 28 29 30 31 + becomes (for size == 2 and mask == 0x3333) + t = 2^16 3^17 -- -- l = 0 1 16 17 r = 2 3 18 19 + 6^20 7^21 -- -- 4 5 20 21 6 7 22 23 + 10^24 11^25 -- -- 8 9 24 25 10 11 24 25 + 14^28 15^29 -- -- 12 13 28 29 14 15 28 29 + + Thanks for hints from Richard Outerbridge - he told me IP&FP + could be done in 15 xor, 10 shifts and 5 ands. + When I finally started to think of the problem in 2D + I first got ~42 operations without xors. When I remembered + how to use xors :-) I got it to its final state. + */ +#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\ + (b)^=(t),\ + (a)^=((t)<<(n))) + + + +/* spr.h */ +/* Copyright (C) 1993 Eric Young - see README for more details */ +static unsigned long des_SPtrans[8][64]={ +/* nibble 0 */ +{0x00820200, 0x00020000, 0x80800000, 0x80820200, +0x00800000, 0x80020200, 0x80020000, 0x80800000, +0x80020200, 0x00820200, 0x00820000, 0x80000200, +0x80800200, 0x00800000, 0x00000000, 0x80020000, +0x00020000, 0x80000000, 0x00800200, 0x00020200, +0x80820200, 0x00820000, 0x80000200, 0x00800200, +0x80000000, 0x00000200, 0x00020200, 0x80820000, +0x00000200, 0x80800200, 0x80820000, 0x00000000, +0x00000000, 0x80820200, 0x00800200, 0x80020000, +0x00820200, 0x00020000, 0x80000200, 0x00800200, +0x80820000, 0x00000200, 0x00020200, 0x80800000, +0x80020200, 0x80000000, 0x80800000, 0x00820000, +0x80820200, 0x00020200, 0x00820000, 0x80800200, +0x00800000, 0x80000200, 0x80020000, 0x00000000, +0x00020000, 0x00800000, 0x80800200, 0x00820200, +0x80000000, 0x80820000, 0x00000200, 0x80020200}, + +/* nibble 1 */ +{0x10042004, 0x00000000, 0x00042000, 0x10040000, +0x10000004, 0x00002004, 0x10002000, 0x00042000, +0x00002000, 0x10040004, 0x00000004, 0x10002000, +0x00040004, 0x10042000, 0x10040000, 0x00000004, +0x00040000, 0x10002004, 0x10040004, 0x00002000, +0x00042004, 0x10000000, 0x00000000, 0x00040004, +0x10002004, 0x00042004, 0x10042000, 0x10000004, +0x10000000, 0x00040000, 0x00002004, 0x10042004, +0x00040004, 0x10042000, 0x10002000, 0x00042004, +0x10042004, 0x00040004, 0x10000004, 0x00000000, +0x10000000, 0x00002004, 0x00040000, 0x10040004, +0x00002000, 0x10000000, 0x00042004, 0x10002004, +0x10042000, 0x00002000, 0x00000000, 0x10000004, +0x00000004, 0x10042004, 0x00042000, 0x10040000, +0x10040004, 0x00040000, 0x00002004, 0x10002000, +0x10002004, 0x00000004, 0x10040000, 0x00042000}, + +/* nibble 2 */ +{0x41000000, 0x01010040, 0x00000040, 0x41000040, +0x40010000, 0x01000000, 0x41000040, 0x00010040, +0x01000040, 0x00010000, 0x01010000, 0x40000000, +0x41010040, 0x40000040, 0x40000000, 0x41010000, +0x00000000, 0x40010000, 0x01010040, 0x00000040, +0x40000040, 0x41010040, 0x00010000, 0x41000000, +0x41010000, 0x01000040, 0x40010040, 0x01010000, +0x00010040, 0x00000000, 0x01000000, 0x40010040, +0x01010040, 0x00000040, 0x40000000, 0x00010000, +0x40000040, 0x40010000, 0x01010000, 0x41000040, +0x00000000, 0x01010040, 0x00010040, 0x41010000, +0x40010000, 0x01000000, 0x41010040, 0x40000000, +0x40010040, 0x41000000, 0x01000000, 0x41010040, +0x00010000, 0x01000040, 0x41000040, 0x00010040, +0x01000040, 0x00000000, 0x41010000, 0x40000040, +0x41000000, 0x40010040, 0x00000040, 0x01010000}, + +/* nibble 3 */ +{0x00100402, 0x04000400, 0x00000002, 0x04100402, +0x00000000, 0x04100000, 0x04000402, 0x00100002, +0x04100400, 0x04000002, 0x04000000, 0x00000402, +0x04000002, 0x00100402, 0x00100000, 0x04000000, +0x04100002, 0x00100400, 0x00000400, 0x00000002, +0x00100400, 0x04000402, 0x04100000, 0x00000400, +0x00000402, 0x00000000, 0x00100002, 0x04100400, +0x04000400, 0x04100002, 0x04100402, 0x00100000, +0x04100002, 0x00000402, 0x00100000, 0x04000002, +0x00100400, 0x04000400, 0x00000002, 0x04100000, +0x04000402, 0x00000000, 0x00000400, 0x00100002, +0x00000000, 0x04100002, 0x04100400, 0x00000400, +0x04000000, 0x04100402, 0x00100402, 0x00100000, +0x04100402, 0x00000002, 0x04000400, 0x00100402, +0x00100002, 0x00100400, 0x04100000, 0x04000402, +0x00000402, 0x04000000, 0x04000002, 0x04100400}, + +/* nibble 4 */ +{0x02000000, 0x00004000, 0x00000100, 0x02004108, +0x02004008, 0x02000100, 0x00004108, 0x02004000, +0x00004000, 0x00000008, 0x02000008, 0x00004100, +0x02000108, 0x02004008, 0x02004100, 0x00000000, +0x00004100, 0x02000000, 0x00004008, 0x00000108, +0x02000100, 0x00004108, 0x00000000, 0x02000008, +0x00000008, 0x02000108, 0x02004108, 0x00004008, +0x02004000, 0x00000100, 0x00000108, 0x02004100, +0x02004100, 0x02000108, 0x00004008, 0x02004000, +0x00004000, 0x00000008, 0x02000008, 0x02000100, +0x02000000, 0x00004100, 0x02004108, 0x00000000, +0x00004108, 0x02000000, 0x00000100, 0x00004008, +0x02000108, 0x00000100, 0x00000000, 0x02004108, +0x02004008, 0x02004100, 0x00000108, 0x00004000, +0x00004100, 0x02004008, 0x02000100, 0x00000108, +0x00000008, 0x00004108, 0x02004000, 0x02000008}, + +/* nibble 5 */ +{0x20000010, 0x00080010, 0x00000000, 0x20080800, +0x00080010, 0x00000800, 0x20000810, 0x00080000, +0x00000810, 0x20080810, 0x00080800, 0x20000000, +0x20000800, 0x20000010, 0x20080000, 0x00080810, +0x00080000, 0x20000810, 0x20080010, 0x00000000, +0x00000800, 0x00000010, 0x20080800, 0x20080010, +0x20080810, 0x20080000, 0x20000000, 0x00000810, +0x00000010, 0x00080800, 0x00080810, 0x20000800, +0x00000810, 0x20000000, 0x20000800, 0x00080810, +0x20080800, 0x00080010, 0x00000000, 0x20000800, +0x20000000, 0x00000800, 0x20080010, 0x00080000, +0x00080010, 0x20080810, 0x00080800, 0x00000010, +0x20080810, 0x00080800, 0x00080000, 0x20000810, +0x20000010, 0x20080000, 0x00080810, 0x00000000, +0x00000800, 0x20000010, 0x20000810, 0x20080800, +0x20080000, 0x00000810, 0x00000010, 0x20080010}, + +/* nibble 6 */ +{0x00001000, 0x00000080, 0x00400080, 0x00400001, +0x00401081, 0x00001001, 0x00001080, 0x00000000, +0x00400000, 0x00400081, 0x00000081, 0x00401000, +0x00000001, 0x00401080, 0x00401000, 0x00000081, +0x00400081, 0x00001000, 0x00001001, 0x00401081, +0x00000000, 0x00400080, 0x00400001, 0x00001080, +0x00401001, 0x00001081, 0x00401080, 0x00000001, +0x00001081, 0x00401001, 0x00000080, 0x00400000, +0x00001081, 0x00401000, 0x00401001, 0x00000081, +0x00001000, 0x00000080, 0x00400000, 0x00401001, +0x00400081, 0x00001081, 0x00001080, 0x00000000, +0x00000080, 0x00400001, 0x00000001, 0x00400080, +0x00000000, 0x00400081, 0x00400080, 0x00001080, +0x00000081, 0x00001000, 0x00401081, 0x00400000, +0x00401080, 0x00000001, 0x00001001, 0x00401081, +0x00400001, 0x00401080, 0x00401000, 0x00001001}, + +/* nibble 7 */ +{0x08200020, 0x08208000, 0x00008020, 0x00000000, +0x08008000, 0x00200020, 0x08200000, 0x08208020, +0x00000020, 0x08000000, 0x00208000, 0x00008020, +0x00208020, 0x08008020, 0x08000020, 0x08200000, +0x00008000, 0x00208020, 0x00200020, 0x08008000, +0x08208020, 0x08000020, 0x00000000, 0x00208000, +0x08000000, 0x00200000, 0x08008020, 0x08200020, +0x00200000, 0x00008000, 0x08208000, 0x00000020, +0x00200000, 0x00008000, 0x08000020, 0x08208020, +0x00008020, 0x08000000, 0x00000000, 0x00208000, +0x08200020, 0x08008020, 0x08008000, 0x00200020, +0x08208000, 0x00000020, 0x00200020, 0x08008000, +0x08208020, 0x00200000, 0x08200000, 0x08000020, +0x00208000, 0x00008020, 0x08008020, 0x08200000, +0x00000020, 0x08208000, 0x00208020, 0x00000000, +0x08000000, 0x08200020, 0x00008000, 0x00208020}}; + +static unsigned long des_skb[8][64]={ +/* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ +{0x00000000,0x00000010,0x20000000,0x20000010, +0x00010000,0x00010010,0x20010000,0x20010010, +0x00000800,0x00000810,0x20000800,0x20000810, +0x00010800,0x00010810,0x20010800,0x20010810, +0x00000020,0x00000030,0x20000020,0x20000030, +0x00010020,0x00010030,0x20010020,0x20010030, +0x00000820,0x00000830,0x20000820,0x20000830, +0x00010820,0x00010830,0x20010820,0x20010830, +0x00080000,0x00080010,0x20080000,0x20080010, +0x00090000,0x00090010,0x20090000,0x20090010, +0x00080800,0x00080810,0x20080800,0x20080810, +0x00090800,0x00090810,0x20090800,0x20090810, +0x00080020,0x00080030,0x20080020,0x20080030, +0x00090020,0x00090030,0x20090020,0x20090030, +0x00080820,0x00080830,0x20080820,0x20080830, +0x00090820,0x00090830,0x20090820,0x20090830}, +/* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ +{0x00000000,0x02000000,0x00002000,0x02002000, +0x00200000,0x02200000,0x00202000,0x02202000, +0x00000004,0x02000004,0x00002004,0x02002004, +0x00200004,0x02200004,0x00202004,0x02202004, +0x00000400,0x02000400,0x00002400,0x02002400, +0x00200400,0x02200400,0x00202400,0x02202400, +0x00000404,0x02000404,0x00002404,0x02002404, +0x00200404,0x02200404,0x00202404,0x02202404, +0x10000000,0x12000000,0x10002000,0x12002000, +0x10200000,0x12200000,0x10202000,0x12202000, +0x10000004,0x12000004,0x10002004,0x12002004, +0x10200004,0x12200004,0x10202004,0x12202004, +0x10000400,0x12000400,0x10002400,0x12002400, +0x10200400,0x12200400,0x10202400,0x12202400, +0x10000404,0x12000404,0x10002404,0x12002404, +0x10200404,0x12200404,0x10202404,0x12202404}, +/* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ +{0x00000000,0x00000001,0x00040000,0x00040001, +0x01000000,0x01000001,0x01040000,0x01040001, +0x00000002,0x00000003,0x00040002,0x00040003, +0x01000002,0x01000003,0x01040002,0x01040003, +0x00000200,0x00000201,0x00040200,0x00040201, +0x01000200,0x01000201,0x01040200,0x01040201, +0x00000202,0x00000203,0x00040202,0x00040203, +0x01000202,0x01000203,0x01040202,0x01040203, +0x08000000,0x08000001,0x08040000,0x08040001, +0x09000000,0x09000001,0x09040000,0x09040001, +0x08000002,0x08000003,0x08040002,0x08040003, +0x09000002,0x09000003,0x09040002,0x09040003, +0x08000200,0x08000201,0x08040200,0x08040201, +0x09000200,0x09000201,0x09040200,0x09040201, +0x08000202,0x08000203,0x08040202,0x08040203, +0x09000202,0x09000203,0x09040202,0x09040203}, +/* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ +{0x00000000,0x00100000,0x00000100,0x00100100, +0x00000008,0x00100008,0x00000108,0x00100108, +0x00001000,0x00101000,0x00001100,0x00101100, +0x00001008,0x00101008,0x00001108,0x00101108, +0x04000000,0x04100000,0x04000100,0x04100100, +0x04000008,0x04100008,0x04000108,0x04100108, +0x04001000,0x04101000,0x04001100,0x04101100, +0x04001008,0x04101008,0x04001108,0x04101108, +0x00020000,0x00120000,0x00020100,0x00120100, +0x00020008,0x00120008,0x00020108,0x00120108, +0x00021000,0x00121000,0x00021100,0x00121100, +0x00021008,0x00121008,0x00021108,0x00121108, +0x04020000,0x04120000,0x04020100,0x04120100, +0x04020008,0x04120008,0x04020108,0x04120108, +0x04021000,0x04121000,0x04021100,0x04121100, +0x04021008,0x04121008,0x04021108,0x04121108}, +/* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ +{0x00000000,0x10000000,0x00010000,0x10010000, +0x00000004,0x10000004,0x00010004,0x10010004, +0x20000000,0x30000000,0x20010000,0x30010000, +0x20000004,0x30000004,0x20010004,0x30010004, +0x00100000,0x10100000,0x00110000,0x10110000, +0x00100004,0x10100004,0x00110004,0x10110004, +0x20100000,0x30100000,0x20110000,0x30110000, +0x20100004,0x30100004,0x20110004,0x30110004, +0x00001000,0x10001000,0x00011000,0x10011000, +0x00001004,0x10001004,0x00011004,0x10011004, +0x20001000,0x30001000,0x20011000,0x30011000, +0x20001004,0x30001004,0x20011004,0x30011004, +0x00101000,0x10101000,0x00111000,0x10111000, +0x00101004,0x10101004,0x00111004,0x10111004, +0x20101000,0x30101000,0x20111000,0x30111000, +0x20101004,0x30101004,0x20111004,0x30111004}, +/* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ +{0x00000000,0x08000000,0x00000008,0x08000008, +0x00000400,0x08000400,0x00000408,0x08000408, +0x00020000,0x08020000,0x00020008,0x08020008, +0x00020400,0x08020400,0x00020408,0x08020408, +0x00000001,0x08000001,0x00000009,0x08000009, +0x00000401,0x08000401,0x00000409,0x08000409, +0x00020001,0x08020001,0x00020009,0x08020009, +0x00020401,0x08020401,0x00020409,0x08020409, +0x02000000,0x0A000000,0x02000008,0x0A000008, +0x02000400,0x0A000400,0x02000408,0x0A000408, +0x02020000,0x0A020000,0x02020008,0x0A020008, +0x02020400,0x0A020400,0x02020408,0x0A020408, +0x02000001,0x0A000001,0x02000009,0x0A000009, +0x02000401,0x0A000401,0x02000409,0x0A000409, +0x02020001,0x0A020001,0x02020009,0x0A020009, +0x02020401,0x0A020401,0x02020409,0x0A020409}, +/* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ +{0x00000000,0x00000100,0x00080000,0x00080100, +0x01000000,0x01000100,0x01080000,0x01080100, +0x00000010,0x00000110,0x00080010,0x00080110, +0x01000010,0x01000110,0x01080010,0x01080110, +0x00200000,0x00200100,0x00280000,0x00280100, +0x01200000,0x01200100,0x01280000,0x01280100, +0x00200010,0x00200110,0x00280010,0x00280110, +0x01200010,0x01200110,0x01280010,0x01280110, +0x00000200,0x00000300,0x00080200,0x00080300, +0x01000200,0x01000300,0x01080200,0x01080300, +0x00000210,0x00000310,0x00080210,0x00080310, +0x01000210,0x01000310,0x01080210,0x01080310, +0x00200200,0x00200300,0x00280200,0x00280300, +0x01200200,0x01200300,0x01280200,0x01280300, +0x00200210,0x00200310,0x00280210,0x00280310, +0x01200210,0x01200310,0x01280210,0x01280310}, +/* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ +{0x00000000,0x04000000,0x00040000,0x04040000, +0x00000002,0x04000002,0x00040002,0x04040002, +0x00002000,0x04002000,0x00042000,0x04042000, +0x00002002,0x04002002,0x00042002,0x04042002, +0x00000020,0x04000020,0x00040020,0x04040020, +0x00000022,0x04000022,0x00040022,0x04040022, +0x00002020,0x04002020,0x00042020,0x04042020, +0x00002022,0x04002022,0x00042022,0x04042022, +0x00000800,0x04000800,0x00040800,0x04040800, +0x00000802,0x04000802,0x00040802,0x04040802, +0x00002800,0x04002800,0x00042800,0x04042800, +0x00002802,0x04002802,0x00042802,0x04042802, +0x00000820,0x04000820,0x00040820,0x04040820, +0x00000822,0x04000822,0x00040822,0x04040822, +0x00002820,0x04002820,0x00042820,0x04042820, +0x00002822,0x04002822,0x00042822,0x04042822} +}; + +typedef struct des_ks_struct + { + union { + des_cblock _; + /* make sure things are correct size on machines with + * 8 byte longs */ + unsigned long pad[2]; + } ks; +#define _ ks._ + } des_key_schedule[16]; + +typedef struct +{ + PCTObject_HEAD + des_key_schedule KeySched1, KeySched2, KeySched3; +} DES3object; + +static int des_encrypt(input,output,ks,encrypt) + unsigned long *input; + unsigned long *output; + des_key_schedule ks; + int encrypt; + { + register unsigned long l,r,t,u; + register int i; + register unsigned long *s; + + l=input[0]; + r=input[1]; + + /* do IP */ + PERM_OP(r,l,t, 4,0x0f0f0f0f); + PERM_OP(l,r,t,16,0x0000ffff); + PERM_OP(r,l,t, 2,0x33333333); + PERM_OP(l,r,t, 8,0x00ff00ff); + PERM_OP(r,l,t, 1,0x55555555); + /* r and l are reversed - remember that :-) - fix + * it in the next step */ + + /* Things have been modified so that the initial rotate is + * done outside the loop. This required the + * des_SPtrans values in sp.h to be rotated 1 bit to the right. + * One perl script later and things have a 5% speed up on a sparc2. + * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> + * for pointing this out. */ + t=(r<<1)|(r>>31); + r=(l<<1)|(l>>31); + l=t; + + /* clear the top bits on machines with 8byte longs */ + l&=0xffffffff; + r&=0xffffffff; + + s=(unsigned long *)ks; + /* I don't know if it is worth the effort of loop unrolling the + * inner loop */ + if (encrypt) + { + for (i=0; i<32; i+=4) + { + D_ENCRYPT(l,r,i+0); /* 1 */ + D_ENCRYPT(r,l,i+2); /* 2 */ + } + } + else + { + for (i=30; i>0; i-=4) + { + D_ENCRYPT(l,r,i-0); /* 16 */ + D_ENCRYPT(r,l,i-2); /* 15 */ + } + } + l=(l>>1)|(l<<31); + r=(r>>1)|(r<<31); + /* clear the top bits on machines with 8byte longs */ + l&=0xffffffff; + r&=0xffffffff; + + /* swap l and r + * we will not do the swap so just remember they are + * reversed for the rest of the subroutine + * luckily FP fixes this problem :-) */ + + PERM_OP(r,l,t, 1,0x55555555); + PERM_OP(l,r,t, 8,0x00ff00ff); + PERM_OP(r,l,t, 2,0x33333333); + PERM_OP(l,r,t,16,0x0000ffff); + PERM_OP(r,l,t, 4,0x0f0f0f0f); + + output[0]=l; + output[1]=r; + l=r=t=u=0; + return(0); + } + +static int des_ecb_encrypt(input,output,ks,encrypt) + des_cblock *input; + des_cblock *output; + des_key_schedule ks; + int encrypt; + { + register unsigned long l0,l1; + register unsigned char *in,*out; + unsigned long ll[2]; + + in=(unsigned char *)input; + out=(unsigned char *)output; + c2l(in,l0); + c2l(in,l1); + ll[0]=l0; + ll[1]=l1; + des_encrypt(ll,ll,ks,encrypt); + l0=ll[0]; + l1=ll[1]; + l2c(l0,out); + l2c(l1,out); + l0=l1=ll[0]=ll[1]=0; + return(0); + } + + + +static inline void DES3decrypt(self, block) + DES3object *self; + unsigned char *block; +{ + des_cblock output, output2; + + des_ecb_encrypt(block, output, self->KeySched3, 0); + des_ecb_encrypt(output, output2, self->KeySched2, 1); + des_ecb_encrypt(output2, block, self->KeySched1, 0); +} + +static inline void DES3encrypt(self, block) + DES3object *self; + unsigned char *block; +{ + des_cblock output, output2; + + des_ecb_encrypt(block, output, self->KeySched1, 1); + des_ecb_encrypt(output, output2, self->KeySched2, 0); + des_ecb_encrypt(output2, block, self->KeySched3, 1); +} + +/* NOW DEFINED IN des_local.h + * See ecb_encrypt.c for a pseudo description of these macros. + * #define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\ + * (b)^=(t),\ + * (a)=((a)^((t)<<(n)))) + */ + +#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ + (a)=(a)^(t)^(t>>(16-(n)))) + +static char shifts2[16]={0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0}; + +static int des_set_key(key,schedule) +des_cblock *key; +des_key_schedule schedule; + { + register unsigned long c,d,t,s; + register unsigned char *in; + register unsigned long *k; + register int i; + + k=(unsigned long *)schedule; + in=(unsigned char *)key; + + c2l(in,c); + c2l(in,d); + + /* do PC1 in 60 simple operations */ +/* PERM_OP(d,c,t,4,0x0f0f0f0f); + HPERM_OP(c,t,-2, 0xcccc0000); + HPERM_OP(c,t,-1, 0xaaaa0000); + HPERM_OP(c,t, 8, 0x00ff0000); + HPERM_OP(c,t,-1, 0xaaaa0000); + HPERM_OP(d,t,-8, 0xff000000); + HPERM_OP(d,t, 8, 0x00ff0000); + HPERM_OP(d,t, 2, 0x33330000); + d=((d&0x00aa00aa)<<7)|((d&0x55005500)>>7)|(d&0xaa55aa55); + d=(d>>8)|((c&0xf0000000)>>4); + c&=0x0fffffff; */ + + /* I now do it in 47 simple operations :-) + * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov) + * for the inspiration. :-) */ + PERM_OP (d,c,t,4,0x0f0f0f0f); + HPERM_OP(c,t,-2,0xcccc0000); + HPERM_OP(d,t,-2,0xcccc0000); + PERM_OP (d,c,t,1,0x55555555); + PERM_OP (c,d,t,8,0x00ff00ff); + PERM_OP (d,c,t,1,0x55555555); + d= (((d&0x000000ff)<<16)| (d&0x0000ff00) | + ((d&0x00ff0000)>>16)|((c&0xf0000000)>>4)); + c&=0x0fffffff; + + for (i=0; i<16; i++) + { + if (shifts2[i]) + { c=((c>>2)|(c<<26)); d=((d>>2)|(d<<26)); } + else + { c=((c>>1)|(c<<27)); d=((d>>1)|(d<<27)); } + c&=0x0fffffff; + d&=0x0fffffff; + /* could be a few less shifts but I am to lazy at this + * point in time to investigate */ + s= des_skb[0][ (c )&0x3f ]| + des_skb[1][((c>> 6)&0x03)|((c>> 7)&0x3c)]| + des_skb[2][((c>>13)&0x0f)|((c>>14)&0x30)]| + des_skb[3][((c>>20)&0x01)|((c>>21)&0x06) | + ((c>>22)&0x38)]; + t= des_skb[4][ (d )&0x3f ]| + des_skb[5][((d>> 7)&0x03)|((d>> 8)&0x3c)]| + des_skb[6][ (d>>15)&0x3f ]| + des_skb[7][((d>>21)&0x0f)|((d>>22)&0x30)]; + + /* table contained 0213 4657 */ + *(k++)=((t<<16)|(s&0x0000ffff))&0xffffffff; + s= ((s>>16)|(t&0xffff0000)); + + s=(s<<4)|(s>>28); + *(k++)=s&0xffffffff; + } + return(0); + } + +static const unsigned char odd_parity[256]={ + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, +112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, +128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, +145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, +161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, +176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, +193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, +208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, +224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, +241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254}; + +static inline void DES3init(self, key, keylength) + DES3object *self; + unsigned char *key; + int keylength; +{ + char oddkey[16]; + int i; + + if (keylength != 16 && keylength != 24) { + PyErr_SetString(PyExc_ValueError, + "DES3 key must be either 16 or 24 bytes long"); + return; + } + + for (i=0; i<16; i++) + { + oddkey[i]=odd_parity[ key[i] ]; + } + des_set_key(oddkey+0, self->KeySched1); + des_set_key(oddkey+8, self->KeySched2); + if (keylength == 24) { + des_set_key(key+16, self->KeySched3); + } else { + memcpy(self->KeySched3, self->KeySched1, sizeof(self->KeySched3)); + } +} diff --git a/block/Diamond.c b/block/Diamond.c new file mode 100644 index 0000000..f199d83 --- /dev/null +++ b/block/Diamond.c @@ -0,0 +1,415 @@ + +/* + * diamond.c : Implementation of the Diamond block encryption algorithm + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + + +#define MAX_NUM_ROUNDS 16 + +typedef struct +{ + PCTObject_HEAD + unsigned char s[4096*MAX_NUM_ROUNDS], si[4096*MAX_NUM_ROUNDS]; + int keyindex, rounds; + unsigned long accum; +} Diamondobject; + + +/* Make sure that the following macro is called after BuildCRCTable(). */ + +#define crc32(crc, c)(((crc>>8)&0x00FFFFFFL)^(Ccitt32Table[(unsigned int)((unsigned int)crc^c)&0xFF])) + +/* crc.cpp -- contains table based CCITT 32 bit CRC function. + This file is in the Public Domain. + */ + +#define CRC_MASK 0xFFFFFFFFL +#define CRC32_POLYNOMIAL 0xEDB88320L + +static const unsigned int Ccitt32Table[256] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +#if 0 /* Not used, since Ccitt32Table is statically initialized */ +/****************************************************************************/ + +/* + * This routine simply builds the coefficient table used to calculate + * 32 bit CRC values throughout this program. The 256 long word table + * has to be set up once when the program starts. Alternatively, the + * values could be hard coded in, which would offer a miniscule improvement + * in overall performance of the program. + */ + +int BuildCRCTable() +{ + int i; + int j; + unsigned int value; + + for ( i = 0; i <= 255 ; i++ ) + { + value = i; + for ( j = 8 ; j > 0; j-- ) + { + if ( value & 1 ) + value = ( value >> 1 ) ^ CRC32_POLYNOMIAL; + else + value >>= 1; + } + Ccitt32Table[ i ] = value; + } + return 0; +} +#endif + + +/* diamond.c - Encryption designed to exceed DES in security. + This file and the Diamond and Diamond Lite Encryption Algorithms + described herein are hereby dedicated to the Public Domain by the + author and inventor, Michael Paul Johnson. Feel free to use these + for any purpose that is legally and morally right. The names + "Diamond Encryption Algorithm" and "Diamond Lite Encryption + Algorithm" should only be used to describe the algorithms described + in this file, to avoid confusion. + + Disclaimers: the following comes with no warranty, expressed or + implied. You, the user, must determine the suitability of this + information to your own uses. You must also find out what legal + requirements exist with respect to this data and programs using + it, and comply with whatever valid requirements exist. + */ + +int inline keyrand(self, max_value, key, keysize) + Diamondobject *self; + int max_value; + unsigned char *key; + int keysize; +{ /* value based on key[], sized keysize */ + int prandvalue, i; + unsigned long mask; + + if (!max_value) return 0; + mask = 0L; /* Create a mask to get the minimum */ + for (i=max_value; i > 0; i = i >> 1) /* number of bits to cover the */ + mask = (mask << 1) | 1L; /* range 0 to max_value. */ + i=0; + do + { + self->accum = crc32(self->accum, key[self->keyindex++]); + if (self->keyindex >= keysize) + { + self->keyindex = 0; /* Recycle thru the key */ + self->accum = crc32(self->accum, (keysize & 0xFF)); + self->accum = crc32(self->accum, ((keysize >> 8) & 0xFF)); + } + prandvalue = (int) (self->accum & mask); + if ((++i>97) && (prandvalue > max_value)) /* Don't loop forever. */ + prandvalue -= max_value; /* Introduce negligible bias. */ + } + while (prandvalue > max_value); /* Discard out of range values. */ + return prandvalue; +} + +static void inline makeonebox(self, i, j, key, keylen) + Diamondobject *self; + int i, j; + unsigned char *key; + int keylen; +{ + int n; + int pos, m, p; + int filled[256]; + + for (m = 0; m < 256; m++) /* The filled array is used to make + sure that */ + filled[m] = 0; /* each byte of the array is filled only once. */ + for (n = 255; n >= 0 ; n--) /* n counts the number of bytes left to fill */ + { + pos = keyrand(self, n, key, keylen); /* pos is the position among the UNFILLED */ + /* components of the s array that the */ + /* number n should be placed. */ + p=0; + while (filled[p]) p++; + for (m=0; m<pos; m++) + { + p++; + while (filled[p]) p++; + } + self->s[(4096*i) + (256*j) + p] = n; + filled[p] = 1; + } +} + +static inline void Diamondinit(self, key, keylen) + Diamondobject *self; + unsigned char *key; + int keylen; +{ + int i, j, k; +#if 0 + BuildCRCTable(); +#endif + self->keyindex = 0; + self->accum = 0xFFFFFFFFL; + + if (self->rounds<5 || MAX_NUM_ROUNDS<=self->rounds) + { + PyErr_SetString(PyExc_ValueError, "Number of rounds for Diamond must be " + "between 5 and 15."); + return; + } + for (i = 0; i < self->rounds; i++) + { + for (j = 0; j < 16; j++) + { + makeonebox(self, i, j, key, keylen); + } + } + for (i = 0; i < self->rounds; i++) + { + for (j = 0; j < 16; j++) + { + for (k = 0; k < 256; k++) + { + self->si[(4096 * i) + (256 * j) + self->s[(4096 * i) + (256 * j) + k]] = k; + } + } + } +} + +static void permute(self, x, y) /* x and y must be different. */ + Diamondobject *self; + unsigned char *x, *y; + { + y[0] = (x[0] & 1) | (x[1] & 2) | (x[2] & 4) | + (x[3] & 8) | (x[4] & 16) | (x[5] & 32) | + (x[6] & 64) | (x[7] & 128); + y[1] = (x[1] & 1) | (x[2] & 2) | (x[3] & 4) | + (x[4] & 8) | (x[5] & 16) | (x[6] & 32) | + (x[7] & 64) | (x[8] & 128); + y[2] = (x[2] & 1) | (x[3] & 2) | (x[4] & 4) | + (x[5] & 8) | (x[6] & 16) | (x[7] & 32) | + (x[8] & 64) | (x[9] & 128); + y[3] = (x[3] & 1) | (x[4] & 2) | (x[5] & 4) | + (x[6] & 8) | (x[7] & 16) | (x[8] & 32) | + (x[9] & 64) | (x[10] & 128); + y[4] = (x[4] & 1) | (x[5] & 2) | (x[6] & 4) | + (x[7] & 8) | (x[8] & 16) | (x[9] & 32) | + (x[10] & 64) | (x[11] & 128); + y[5] = (x[5] & 1) | (x[6] & 2) | (x[7] & 4) | + (x[8] & 8) | (x[9] & 16) | (x[10] & 32) | + (x[11] & 64) | (x[12] & 128); + y[6] = (x[6] & 1) | (x[7] & 2) | (x[8] & 4) | + (x[9] & 8) | (x[10] & 16) | (x[11] & 32) | + (x[12] & 64) | (x[13] & 128); + y[7] = (x[7] & 1) | (x[8] & 2) | (x[9] & 4) | + (x[10] & 8) | (x[11] & 16) | (x[12] & 32) | + (x[13] & 64) | (x[14] & 128); + y[8] = (x[8] & 1) | (x[9] & 2) | (x[10] & 4) | + (x[11] & 8) | (x[12] & 16) | (x[13] & 32) | + (x[14] & 64) | (x[15] & 128); + y[9] = (x[9] & 1) | (x[10] & 2) | (x[11] & 4) | + (x[12] & 8) | (x[13] & 16) | (x[14] & 32) | + (x[15] & 64) | (x[0] & 128); + y[10] = (x[10] & 1) | (x[11] & 2) | (x[12] & 4) | + (x[13] & 8) | (x[14] & 16) | (x[15] & 32) | + (x[0] & 64) | (x[1] & 128); + y[11] = (x[11] & 1) | (x[12] & 2) | (x[13] & 4) | + (x[14] & 8) | (x[15] & 16) | (x[0] & 32) | + (x[1] & 64) | (x[2] & 128); + y[12] = (x[12] & 1) | (x[13] & 2) | (x[14] & 4) | + (x[15] & 8) | (x[0] & 16) | (x[1] & 32) | + (x[2] & 64) | (x[3] & 128); + y[13] = (x[13] & 1) | (x[14] & 2) | (x[15] & 4) | + (x[0] & 8) | (x[1] & 16) | (x[2] & 32) | + (x[3] & 64) | (x[4] & 128); + y[14] = (x[14] & 1) | (x[15] & 2) | (x[0] & 4) | + (x[1] & 8) | (x[2] & 16) | (x[3] & 32) | + (x[4] & 64) | (x[5] & 128); + y[15] = (x[15] & 1) | (x[0] & 2) | (x[1] & 4) | + (x[2] & 8) | (x[3] & 16) | (x[4] & 32) | + (x[5] & 64) | (x[6] & 128); + } + +static void ipermute(self, x, y) /* x!=y */ + Diamondobject *self; + unsigned char *x, *y; + { + y[0] = (x[0] & 1) | (x[15] & 2) | (x[14] & 4) | + (x[13] & 8) | (x[12] & 16) | (x[11] & 32) | + (x[10] & 64) | (x[9] & 128); + y[1] = (x[1] & 1) | (x[0] & 2) | (x[15] & 4) | + (x[14] & 8) | (x[13] & 16) | (x[12] & 32) | + (x[11] & 64) | (x[10] & 128); + y[2] = (x[2] & 1) | (x[1] & 2) | (x[0] & 4) | + (x[15] & 8) | (x[14] & 16) | (x[13] & 32) | + (x[12] & 64) | (x[11] & 128); + y[3] = (x[3] & 1) | (x[2] & 2) | (x[1] & 4) | + (x[0] & 8) | (x[15] & 16) | (x[14] & 32) | + (x[13] & 64) | (x[12] & 128); + y[4] = (x[4] & 1) | (x[3] & 2) | (x[2] & 4) | + (x[1] & 8) | (x[0] & 16) | (x[15] & 32) | + (x[14] & 64) | (x[13] & 128); + y[5] = (x[5] & 1) | (x[4] & 2) | (x[3] & 4) | + (x[2] & 8) | (x[1] & 16) | (x[0] & 32) | + (x[15] & 64) | (x[14] & 128); + y[6] = (x[6] & 1) | (x[5] & 2) | (x[4] & 4) | + (x[3] & 8) | (x[2] & 16) | (x[1] & 32) | + (x[0] & 64) | (x[15] & 128); + y[7] = (x[7] & 1) | (x[6] & 2) | (x[5] & 4) | + (x[4] & 8) | (x[3] & 16) | (x[2] & 32) | + (x[1] & 64) | (x[0] & 128); + y[8] = (x[8] & 1) | (x[7] & 2) | (x[6] & 4) | + (x[5] & 8) | (x[4] & 16) | (x[3] & 32) | + (x[2] & 64) | (x[1] & 128); + y[9] = (x[9] & 1) | (x[8] & 2) | (x[7] & 4) | + (x[6] & 8) | (x[5] & 16) | (x[4] & 32) | + (x[3] & 64) | (x[2] & 128); + y[10] = (x[10] & 1) | (x[9] & 2) | (x[8] & 4) | + (x[7] & 8) | (x[6] & 16) | (x[5] & 32) | + (x[4] & 64) | (x[3] & 128); + y[11] = (x[11] & 1) | (x[10] & 2) | (x[9] & 4) | + (x[8] & 8) | (x[7] & 16) | (x[6] & 32) | + (x[5] & 64) | (x[4] & 128); + y[12] = (x[12] & 1) | (x[11] & 2) | (x[10] & 4) | + (x[9] & 8) | (x[8] & 16) | (x[7] & 32) | + (x[6] & 64) | (x[5] & 128); + y[13] = (x[13] & 1) | (x[12] & 2) | (x[11] & 4) | + (x[10] & 8) | (x[9] & 16) | (x[8] & 32) | + (x[7] & 64) | (x[6] & 128); + y[14] = (x[14] & 1) | (x[13] & 2) | (x[12] & 4) | + (x[11] & 8) | (x[10] & 16) | (x[9] & 32) | + (x[8] & 64) | (x[7] & 128); + y[15] = (x[15] & 1) | (x[14] & 2) | (x[13] & 4) | + (x[12] & 8) | (x[11] & 16) | (x[10] & 32) | + (x[9] & 64) | (x[8] & 128); + } + +static void inline substitute(self, round, x, y) + Diamondobject *self; + int round; + unsigned char *x, *y; +{ + int i; + + for (i = 0; i < 16; i++) + y[i] = self->s[(4096*round) + (256*i) + x[i]]; +} + +static void inline isubst(self, round, x, y) + Diamondobject *self; + int round; + unsigned char *x, *y; +{ + int i; + + for (i = 0; i < 16; i++) + y[i] = self->si[(4096*round) + (256*i) + x[i]]; +} + +void Diamondencrypt(self, block) + Diamondobject *self; + unsigned char *block; + { + int round; + unsigned char y[16], z[16]; + + substitute(self, 0, block, y); + for (round=1; round < self->rounds; round++) + { + permute(self, y, z); + substitute(self, round, z, y); + } + for(round=0; round<16; round++) block[round]=y[round]; + } + + void Diamonddecrypt(self, block) + Diamondobject *self; + unsigned char *block; + { + int round; + unsigned char y[16], z[16]; + + isubst(self, self->rounds-1, block, y); + for (round=self->rounds-2; round >= 0; round--) + { + ipermute(self, y, z); + isubst(self, round, z, y); + } + for(round=0; round<16; round++) block[round]=y[round]; + } + + + diff --git a/block/IDEA.c b/block/IDEA.c new file mode 100644 index 0000000..6db499e --- /dev/null +++ b/block/IDEA.c @@ -0,0 +1,214 @@ + +/* + * idea.c : Source code for the IDEA block cipher + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + + +#define low16(x) ((x)/* & 0xFFFF*/) +typedef unsigned short uint16; /* at LEAST 16 bits, maybe more */ +typedef unsigned short word16; +typedef unsigned long word32; +typedef unsigned char byte; + +#define MUL(x,y) (x = low16(x-1), t16 = low16((y)-1), \ + t32 = (word32)x*t16 + x + t16, x = low16(t32), \ + t16 = t32>>16, x = (x-t16) + (x<t16) + 1) + +#ifdef _GNUC_ +/* __const__ simply means there are no side effects for this function, + * which is useful info for the gcc optimizer + */ +#define CONST __const__ +#else +#define CONST +#endif + +typedef struct +{ + PCTObject_HEAD + word16 EK[6*8+4], DK[6*8+4]; + char Endianness; +} IDEAobject; + +CONST static uint16 +mulInv(x) + uint16 x; +{ + uint16 t0, t1; + uint16 q, y; + + if (x <= 1) + return x; /* 0 and 1 are self-inverse */ + t1 = 0x10001L / x; /* Since x >= 2, this fits into 16 bits */ + y = 0x10001L % x; + if (y == 1) + return low16(1 - t1); + t0 = 1; + do { + q = x / y; + x = x % y; + t0 += q * t1; + if (x == 1) + return t0; + q = y / x; + y = y % x; + t1 += q * t0; + } while (y != 1); + return low16(1 - t1); +} /* mukInv */ + +static inline void +IDEAinit(self, key, dummy) + IDEAobject *self; + unsigned char *key; + int dummy; +{ + int i, j; + uint16 t1, t2, t3; + word16 *DK, *EK; + + EK = self->EK; + TestEndianness(self->Endianness); + for (j = 0; j < 8; j++) { + EK[j] = (key[0] << 8) + key[1]; + key += 2; + } + for (i = 0; j < 6*8+4; j++) { + i++; + EK[i + 7] = (EK[i & 7] << 9) | (EK[(i + 1) & 7] >> 7); + EK += i & 8; + i &= 7; + } + EK = self->EK; + DK = self->DK+6*8+4; + t1 = mulInv(*EK++); + t2 = -*EK++; + t3 = -*EK++; + *--DK = mulInv(*EK++); + *--DK = t3; + *--DK = t2; + *--DK = t1; + + for (i = 0; i < 8 - 1; i++) { + t1 = *EK++; + *--DK = *EK++; + *--DK = t1; + + t1 = mulInv(*EK++); + t2 = -*EK++; + t3 = -*EK++; + *--DK = mulInv(*EK++); + *--DK = t2; + *--DK = t3; + *--DK = t1; + } + t1 = *EK++; + *--DK = *EK++; + *--DK = t1; + + t1 = mulInv(*EK++); + t2 = -*EK++; + t3 = -*EK++; + *--DK = mulInv(*EK++); + *--DK = t3; + *--DK = t2; + *--DK = t1; +} + +/* IDEA encryption/decryption algorithm */ +/* Note that in and out can be the same buffer */ +static void ideaCipher(self, block, key) + IDEAobject *self; + byte *block; + word16 const *key; +{ + register uint16 x1, x2, x3, x4, s2, s3; + word16 *in, *out; + register uint16 t16; /* Temporaries needed by MUL macro */ + register word32 t32; + int r = 8; + + in = (word16 *) block; + x1 = *in++; + x2 = *in++; + x3 = *in++; + x4 = *in; + if (self->Endianness==PCT_LITTLE_ENDIAN) + { + x1 = (x1 >> 8) | (x1 << 8); + x2 = (x2 >> 8) | (x2 << 8); + x3 = (x3 >> 8) | (x3 << 8); + x4 = (x4 >> 8) | (x4 << 8); + } + do { + MUL(x1, *key++); + x2 += *key++; + x3 += *key++; + MUL(x4, *key++); + + s3 = x3; + x3 ^= x1; + MUL(x3, *key++); + s2 = x2; + x2 ^= x4; + x2 += x3; + MUL(x2, *key++); + x3 += x2; + + x1 ^= x2; + x4 ^= x3; + + x2 ^= s3; + x3 ^= s2; + } while (--r); + MUL(x1, *key++); + x3 += *key++; + x2 += *key++; + MUL(x4, *key); + + out = (word16 *) block; + + if (self->Endianness==PCT_BIG_ENDIAN) + { + *out++ = x1; + *out++ = x3; + *out++ = x2; + *out = x4; + } /* !HIGHFIRST */ + else + { + x1 = low16(x1); + x2 = low16(x2); + x3 = low16(x3); + x4 = low16(x4); + + *out++ = (x1 >> 8) | (x1 << 8); + *out++ = (x3 >> 8) | (x3 << 8); + *out++ = (x2 >> 8) | (x2 << 8); + *out = (x4 >> 8) | (x4 << 8); + } +} /* ideaCipher */ + + +static inline void IDEAencrypt(self, block) + IDEAobject *self; + unsigned char *block; +{ + ideaCipher(self, block, self->EK); +} + +static inline void IDEAdecrypt(self, block) + IDEAobject *self; + unsigned char *block; +{ + ideaCipher(self, block, self->DK); +} + + diff --git a/block/RC5.c b/block/RC5.c new file mode 100644 index 0000000..2a1b983 --- /dev/null +++ b/block/RC5.c @@ -0,0 +1,233 @@ + +/* + * RC5.c : Implementation code for the RC5 block cipher + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +#define MAXTABLE 100 /* Maximum size of S-box table; changing this + affects the maximum number of rounds + possible. */ +typedef unsigned int U32; +#define LEFT(v,x,y,w,MASK) {U32 t1=(y) % (w), t2,t3=x;\ + t2=(w)-t1;\ + v= ( (t3 << t1) & MASK) | \ + ( (t3 >> t2) & MASK);} +#define RIGHT(v,x,y,w,MASK) {U32 t1=(y) % (w), t2,t3=x;\ + t2=(w)-t1;\ + v= ( (t3 >> t1) & MASK) | \ + ( (t3 << t2) & MASK);} + + + +typedef struct +{ + PCTObject_HEAD + int version; /* Version number of algorithm */ + int wordsize; /* Word size */ + int rounds; /* Number of rounds */ + U32 S[MAXTABLE]; + U32 mask; +} RC5object; + +static inline void +RC5init(self, key, keylen) + RC5object *self; + unsigned char *key; + int keylen; +{ + unsigned int P = 0, Q = 0; + int i; + + if (self->version!=0x10) + { + PyErr_SetString(PyExc_ValueError, + "RC5: Bad RC5 algorithm version"); + return; + } + if (self->wordsize!=16 && self->wordsize!=32) + { + PyErr_SetString(PyExc_ValueError, + "RC5: Unsupported word size"); + return; + } + if (self->rounds<0 || 255<self->rounds) + { + PyErr_SetString(PyExc_ValueError, + "RC5: rounds must be between 0 and 255"); + return; + } + switch(self->wordsize) + { + case(16): + P=0xb7e1; Q=0x9e37; self->mask=0xffff; + break; + case(32): + P=0xb7e15163; Q=0x9e3779b9; self->mask=0xffffffff; + break; + } + for(i=0; i<2*self->rounds+2; i++) self->S[i]=0; + { + unsigned int *L, A, B; + int u=self->wordsize/8, num; + int j, t=2*(self->rounds+1), c=(keylen-1)/u; + if ((keylen-1) % u) c++; + L=malloc(sizeof(unsigned int)*c); + if (L==NULL) + { + PyErr_SetString(PyExc_MemoryError, + "RC5: Can't allocate memory"); + } + for(i=0; i<c; i++) L[i]=0; + for(i=keylen-1; 0<=i; i--) L[i/u]=(L[i/u]<<8)+key[i]; + self->S[0]=P; + for(i=1; i<t; i++) self->S[i]=(self->S[i-1]+Q) & self->mask; + i=j=0; + A=B=0; + for(num = (t>c) ? 3*t : 3*c; 0<num; num--) + { + LEFT(A, self->S[i]+A+B, 3, self->wordsize, self->mask); + self->S[i]=A; + LEFT(B, L[j]+A+B, A+B, self->wordsize, self->mask); + L[j]=B; + i=(i+1)%t; + j=(j+1)%c; + } + free(L); + } +} + +static void RC5Encipher(self, Aptr, Bptr) + RC5object *self; + U32 *Aptr, *Bptr; +{ + int i; + register U32 A, B; + + A=(*Aptr+self->S[0]) & self->mask; + B=(*Bptr+self->S[1]) & self->mask; + + if (self->rounds) + for (i=2; i<=2*self->rounds; i+=2) + { + LEFT(A,A^B,B,self->wordsize,self->mask); + A += self->S[i]; + LEFT(B,A^B,A,self->wordsize,self->mask); + B += self->S[i+1]; + } + *Aptr=A; + *Bptr=B; +} + +static void RC5Decipher(self, Aptr, Bptr) + RC5object *self; + unsigned int *Aptr, *Bptr; +{ + int i; + U32 A, B; + + A=*Aptr; + B=*Bptr; + + if (self->rounds) + for (i=2*self->rounds; 2<=i; i-=2) + { + RIGHT(B,B-self->S[i+1],A,self->wordsize,self->mask); + B ^= A; + RIGHT(A,A-self->S[i],B,self->wordsize,self->mask); + A ^= B; + } + A = (A-self->S[0]) & self->mask; + B = (B-self->S[1]) & self->mask; + if (self->wordsize==32) + { + *Aptr=A; + *Bptr=B; + } + else /* self->wordsize==16 */ + { + *Aptr=A; + *Bptr=B; + } +} + +static inline void RC5encrypt(self, block) + RC5object *self; + unsigned char *block; +{ + U32 A,B; + + switch(self->wordsize) + { + case (32): + A=block[0] | block[1]<<8 | block[2]<<16 | block[3]<<24; + B=block[4] | block[5]<<8 | block[6]<<16 | block[7]<<24; + RC5Encipher(self, &A, &B); + block[0]=A & 255; A>>=8; + block[1]=A & 255; A>>=8; + block[2]=A & 255; A>>=8; + block[3]=A; + block[4]=B & 255; B>>=8; + block[5]=B & 255; B>>=8; + block[6]=B & 255; B>>=8; + block[7]=B; + break; + case (16): + A=block[0] + block[1]*256; + B=block[2] + block[3]*256; + RC5Encipher(self, &A, &B); + block[0] = A & 255; block[1] = A>>8; + block[2] = B & 255; block[3] = B>>8; + + A=block[4] + block[5]*256; + B=block[6] + block[7]*256; + RC5Encipher(self, &A, &B); + block[4] = A & 255; block[5] = A>>8; + block[6] = B & 255; block[7] = B>>8; + break; + } +} + +static inline void RC5decrypt(self, block) + RC5object *self; + unsigned char *block; +{ + U32 A,B; + + switch(self->wordsize) + { + case (32): + A=block[0] | block[1]<<8 | block[2]<<16 | block[3]<<24; + B=block[4] | block[5]<<8 | block[6]<<16 | block[7]<<24; + RC5Decipher(self, &A, &B); + block[0]=A & 255; A>>=8; + block[1]=A & 255; A>>=8; + block[2]=A & 255; A>>=8; + block[3]=A; + block[4]=B & 255; B>>=8; + block[5]=B & 255; B>>=8; + block[6]=B & 255; B>>=8; + block[7]=B; + break; + case (16): + A=block[0] + block[1]*256; + B=block[2] + block[3]*256; + RC5Decipher(self, &A, &B); + block[0] = A & 255; block[1] = A>>8; + block[2] = B & 255; block[3] = B>>8; + + A=block[4] + block[5]*256; + B=block[6] + block[7]*256; + RC5Decipher(self, &A, &B); + block[4] = A & 255; block[5] = A>>8; + block[6] = B & 255; block[7] = B>>8; + break; + } +} + + diff --git a/block/Skipjack.c b/block/Skipjack.c new file mode 100644 index 0000000..5f7aad9 --- /dev/null +++ b/block/Skipjack.c @@ -0,0 +1,139 @@ + +/* + * Skipjack.c : Implementation code for the Skipjack block cipher + * (recently declassified) + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +typedef struct +{ + PCTObject_HEAD + unsigned char key[12]; +} Skipjackobject; + +static unsigned char F[256] = +{ + 0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4, 0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9, + 0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e, 0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28, + 0x0a, 0xdf, 0x02, 0xa0, 0x17, 0xf1, 0x60, 0x68, 0x12, 0xb7, 0x7a, 0xc3, 0xe9, 0xfa, 0x3d, 0x53, + 0x96, 0x84, 0x6b, 0xba, 0xf2, 0x63, 0x9a, 0x19, 0x7c, 0xae, 0xe5, 0xf5, 0xf7, 0x16, 0x6a, 0xa2, + 0x39, 0xb6, 0x7b, 0x0f, 0xc1, 0x93, 0x81, 0x1b, 0xee, 0xb4, 0x1a, 0xea, 0xd0, 0x91, 0x2f, 0xb8, + 0x55, 0xb9, 0xda, 0x85, 0x3f, 0x41, 0xbf, 0xe0, 0x5a, 0x58, 0x80, 0x5f, 0x66, 0x0b, 0xd8, 0x90, + 0x35, 0xd5, 0xc0, 0xa7, 0x33, 0x06, 0x65, 0x69, 0x45, 0x00, 0x94, 0x56, 0x6d, 0x98, 0x9b, 0x76, + 0x97, 0xfc, 0xb2, 0xc2, 0xb0, 0xfe, 0xdb, 0x20, 0xe1, 0xeb, 0xd6, 0xe4, 0xdd, 0x47, 0x4a, 0x1d, + 0x42, 0xed, 0x9e, 0x6e, 0x49, 0x3c, 0xcd, 0x43, 0x27, 0xd2, 0x07, 0xd4, 0xde, 0xc7, 0x67, 0x18, + 0x89, 0xcb, 0x30, 0x1f, 0x8d, 0xc6, 0x8f, 0xaa, 0xc8, 0x74, 0xdc, 0xc9, 0x5d, 0x5c, 0x31, 0xa4, + 0x70, 0x88, 0x61, 0x2c, 0x9f, 0x0d, 0x2b, 0x87, 0x50, 0x82, 0x54, 0x64, 0x26, 0x7d, 0x03, 0x40, + 0x34, 0x4b, 0x1c, 0x73, 0xd1, 0xc4, 0xfd, 0x3b, 0xcc, 0xfb, 0x7f, 0xab, 0xe6, 0x3e, 0x5b, 0xa5, + 0xad, 0x04, 0x23, 0x9c, 0x14, 0x51, 0x22, 0xf0, 0x29, 0x79, 0x71, 0x7e, 0xff, 0x8c, 0x0e, 0xe2, + 0x0c, 0xef, 0xbc, 0x72, 0x75, 0x6f, 0x37, 0xa1, 0xec, 0xd3, 0x8e, 0x62, 0x8b, 0x86, 0x10, 0xe8, + 0x08, 0x77, 0x11, 0xbe, 0x92, 0x4f, 0x24, 0xc5, 0x32, 0x36, 0x9d, 0xcf, 0xf3, 0xa6, 0xbb, 0xac, + 0x5e, 0x6c, 0xa9, 0x13, 0x57, 0x25, 0xb5, 0xe3, 0xbd, 0xa8, 0x3a, 0x01, 0x05, 0x59, 0x2a, 0x46 +}; + +static inline void +Skipjackinit(self, key, keylen) + Skipjackobject *self; + unsigned char *key; + int keylen; +{ + /* The key is 10 bytes long, but each round uses 4 bytes from it, so + the key buffer will be 12 bytes long, with the first two bytes + copied into bytes 11 and 12. This lets each round use its key + bytes without any modulo operations. */ + memcpy(self->key, key, 10); + memcpy(self->key + 10, key, 2); +} + +static inline void Skipjackencrypt(self, block) + Skipjackobject *self; + unsigned char *block; +{ + int w1, w2, w3, w4; + int cv=0, i; + unsigned char *key = self->key; + + w1 = (block[0] << 8) + block[1]; + w2 = (block[2] << 8) + block[3]; + w3 = (block[4] << 8) + block[5]; + w4 = (block[6] << 8) + block[7]; + +#define G(result,w) {int g1 = w>>8, g2= w & 0xFF,g3;\ + g3 = F[g2 ^ key[cv]] ^ g1; \ + g1 = F[g3 ^ key[cv+1]] ^ g2; \ + g2 = F[g1 ^ key[cv+2]] ^ g3; \ + g3 = F[g2 ^ key[cv+3]] ^ g1; \ + result = (g2 << 8) + g3; cv = (cv+4) % 10;} + +#define ruleA(counter) {int t; G(t,w1); w1 = (counter+1) ^ t ^ w4; \ + w4 = w3; w3 = w2; w2 = t; \ + /*printf("%04x %04x %04x %04x\n", w1, w2, w3, w4);*/} +#define ruleB(counter) {int t; int t2 = w1; G(t,w1); \ + w1 = w4; w4 = w3; w3 = t2 ^ (counter+1) ^ w2; w2 = t;\ + /*printf("%04x %04x %04x %04x\n", w1, w2, w3, w4);*/} + + + /* 8 steps of rule A, 8 of rule B, 8 of A, 8 of B */ + for(i=0; i<8; i++) {ruleA(i);} + for(i=8; i<16; i++) {ruleB(i);} + for(i=16; i<24; i++) {ruleA(i);} + for(i=24; i<32; i++) {ruleB(i);} + +#undef ruleA +#undef ruleB +#undef G + + block[0] = w1 >> 8; block[1] = w1 & 255; + block[2] = w2 >> 8; block[3] = w2 & 255; + block[4] = w3 >> 8; block[5] = w3 & 255; + block[6] = w4 >> 8; block[7] = w4 & 255; +} + +static inline void Skipjackdecrypt(self, block) + Skipjackobject *self; + unsigned char *block; +{ + int w1, w2, w3, w4; + int cv = 4, i; + unsigned char *key = self->key; + + w1 = (block[0] << 8) + block[1]; + w2 = (block[2] << 8) + block[3]; + w3 = (block[4] << 8) + block[5]; + w4 = (block[6] << 8) + block[7]; + +#define Ginv(result,w) {int g2 = w>>8, g1= w & 0xFF,g3;\ + g3 = F[g2 ^ key[cv+3]] ^ g1; \ + g1 = F[g3 ^ key[cv+2]] ^ g2; \ + g2 = F[g1 ^ key[cv+1]] ^ g3; \ + g3 = F[g2 ^ key[cv+0]] ^ g1; \ + result = (g3 << 8) + g2; cv = (cv+10-4) % 10;} +#define ruleA(counter) {int t; int t2 = w2; Ginv(t,w2); w2 = w3; w3 = w4; \ + w4 = w1 ^ t2 ^ (counter+1); w1 = t; \ + /*printf("%04x %04x %04x %04x\n", w1, w2, w3, w4);*/} +#define ruleB(counter) {int t; Ginv(t, w2); \ + w2 = t ^ w3 ^ (counter+1); w3 = w4; w4 = w1; w1 = t;\ + /*printf("%04x %04x %04x %04x\n", w1, w2, w3, w4);*/} + + + /* 8 steps of rule Binv, 8 of rule Ainv, 8 of Binv, 8 of A */ + for(i=31; 24<=i; i--) {ruleB(i);} + for(i=23; 16<=i; i--) {ruleA(i);} + for(i=15; 8<=i; i--) {ruleB(i);} + for(i=7; 0<=i; i--) {ruleA(i);} + +#undef ruleA +#undef ruleB +#undef Ginv + + block[0] = w1 >> 8; block[1] = w1 & 255; + block[2] = w2 >> 8; block[3] = w2 & 255; + block[4] = w3 >> 8; block[5] = w3 & 255; + block[6] = w4 >> 8; block[7] = w4 & 255; +} diff --git a/block/cast5.c b/block/cast5.c new file mode 100644 index 0000000..0843b98 --- /dev/null +++ b/block/cast5.c @@ -0,0 +1,437 @@ +/* + These are the S-boxes for CAST5 as given in RFC 2144. +*/ + + +static const uint32 S1[256] = { +0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, +0x9c004dd3, 0x6003e540, 0xcf9fc949, 0xbfd4af27, 0x88bbbdb5, +0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, +0x22d4ff8e, 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, +0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, 0xa1c9e0d6, +0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, +0x22568e3a, 0xa2d341d0, 0x66db40c8, 0xa784392f, 0x004dff2f, +0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, +0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, +0x90ecf52e, 0x22b0c054, 0xbc8e5935, 0x4b6d2f7f, 0x50bb64a2, +0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, +0x4bff345d, 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, +0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, 0x882240f2, +0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, +0xb949e354, 0xb04669fe, 0xb1b6ab8a, 0xc71358dd, 0x6385c545, +0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, +0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, +0xf61b1891, 0xbb72275e, 0xaa508167, 0x38901091, 0xc6b505eb, +0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, +0xaa56d291, 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, +0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, 0x64459eab, +0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, +0x3fab0950, 0x325ff6c2, 0x81383f05, 0x6963c5c8, 0x76cb5ad6, +0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, +0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, +0x051ef495, 0xaa573b04, 0x4a805d8d, 0x548300d0, 0x00322a3c, +0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, +0x915a0bf5, 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, +0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, 0xcfa4bd3f, +0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, +0xe31231b2, 0x2ad5ad6c, 0x954329de, 0xadbe4528, 0xd8710f69, +0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, +0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, +0x032268d4, 0xc9600acc, 0xce387e6d, 0xbf6bb16c, 0x6a70fb78, +0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, +0xb347cc96, 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, +0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, 0x3f04442f, +0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, +0x2ad37c96, 0x0175cb9d, 0xc69dff09, 0xc75b65f0, 0xd9db40d8, +0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, +0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, +0x51c85f4d, 0x56907596, 0xa5bb15e6, 0x580304f0, 0xca042cf1, +0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, +0xbc306ed9, 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, +0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, 0xaf1fbda7, +0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, +0x26470db8, 0xf881814c, 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, +0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, +0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, +0xe1e696ff, 0xb141ab08, 0x7cca89b9, 0x1a69e783, 0x02cc4843, +0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, +0x5c8165bf }; + +static const uint32 S2[256] = { +0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, +0xeec5207a, 0x55889c94, 0x72fc0651, 0xada7ef79, 0x4e1d7235, +0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, +0xa1d6eff3, 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, +0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, 0xd1da4181, +0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, +0x25a1ff41, 0xe180f806, 0x1fc41080, 0x179bee7a, 0xd37ac6a9, +0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, +0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, +0x0d554b63, 0x5d681121, 0xc866c359, 0x3d63cf73, 0xcee234c0, +0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, +0xe4eb573b, 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, +0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, 0x10843094, +0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, +0xd9e0a227, 0x4ec73a34, 0xfc884f69, 0x3e4de8df, 0xef0e0088, +0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, +0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, +0x27e19ba5, 0xd5a6c252, 0xe49754bd, 0xc5d655dd, 0xeb667064, +0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, +0xe5d05860, 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, +0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, 0xeccf01db, +0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, +0x5ee22b95, 0x5f0e5304, 0x81ed6f61, 0x20e74364, 0xb45e1378, +0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, +0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, +0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, 0xa20c3005, 0x8871df63, +0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, +0x9f63293c, 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, +0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, 0x73f98417, +0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, +0x7cbad9a2, 0x2180036f, 0x50d99c08, 0xcb3f4861, 0xc26bd765, +0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, +0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, +0x846a3bae, 0x8ff77888, 0xee5d60f6, 0x7af75673, 0x2fdd5cdb, +0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, +0xd152de58, 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, +0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, 0xb8da230c, +0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, +0x61a3c9e8, 0xbca8f54d, 0xc72feffa, 0x22822e99, 0x82c570b4, +0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, +0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, +0xf7e19798, 0x7619b72f, 0x8f1c9ba4, 0xdc8637a0, 0x16a7d3b1, +0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, +0x520365d6, 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, +0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, 0x5483697b, +0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, +0x6c387e8a, 0x0ae6d249, 0xb284600c, 0xd835731d, 0xdcb1c647, +0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, +0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, +0xa345415e, 0x5c038323, 0x3e5d3bb9, 0x43d79572, 0x7e6dd07c, +0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, +0x4523ecf1 }; + +static const uint32 S3[256] = { +0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, +0x369fe44b, 0x8c1fc644, 0xaececa90, 0xbeb1f9bf, 0xeefbcaea, +0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, +0x927010d5, 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, +0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, 0x553fb2c0, +0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, +0x9255c5ed, 0x1257a240, 0x4e1a8302, 0xbae07fff, 0x528246e7, +0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, +0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, +0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, 0x99b03dbf, 0xb5dbc64b, +0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, +0xccc36f71, 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, +0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, 0xa747d2d0, +0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, +0x0a0fb402, 0x0f7fef82, 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, +0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, +0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, +0xe83ec305, 0x4f91751a, 0x925669c2, 0x23efe941, 0xa903f12e, +0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, +0x02778176, 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, +0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, 0xef303cab, +0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, +0x7d29dc96, 0x2756d3dc, 0x8b907cee, 0xb51fd240, 0xe7c07ce3, +0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, +0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, +0xbda8229c, 0x127dadaa, 0x438a074e, 0x1f97c090, 0x081bdb8a, +0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, +0x64380e51, 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, +0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, 0x4b39fffa, +0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, +0x27627545, 0x825cf47a, 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, +0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, +0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, +0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, 0x1f081fab, 0x108618ae, +0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, +0x2c3f8cc5, 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, +0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, 0x3a609437, +0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, +0x02717ef6, 0x4feb5536, 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, +0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, +0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, +0xabcc4f33, 0x7688c55d, 0x7b00a6b0, 0x947b0001, 0x570075d2, +0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, +0xee971b69, 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, +0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, 0x67214cb8, +0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, +0x606e6dc6, 0x60543a49, 0x5727c148, 0x2be98a1d, 0x8ab41738, +0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, +0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, +0x9c305a00, 0x52bce688, 0x1b03588a, 0xf7baefd5, 0x4142ed9c, +0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, +0xee353783 }; + +static const uint32 S4[256] = { +0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, +0x64ad8c57, 0x85510443, 0xfa020ed1, 0x7e287aff, 0xe60fb663, +0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, +0x241e4adf, 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, +0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, 0xee4d111a, +0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, +0x081b08ca, 0x05170121, 0x80530100, 0xe83e5efe, 0xac9af4f8, +0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, +0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, +0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, 0x2649abdf, 0xaea0c7f5, +0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, +0xf80eb2bb, 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, +0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, 0x4d351805, +0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, +0x9f46222f, 0x3991467d, 0xa5bf6d8e, 0x1143c44f, 0x43958302, +0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, +0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, +0x79098b02, 0xe4eabb81, 0x28123b23, 0x69dead38, 0x1574ca16, +0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, +0x09114003, 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, +0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, 0xe756bdff, +0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, +0x6b65811c, 0x5e146119, 0x6e85cb75, 0xbe07c002, 0xc2325577, +0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, +0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, +0xeca1d7c7, 0x041afa32, 0x1d16625a, 0x6701902c, 0x9b757a54, +0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, +0x56e55a79, 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, +0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, 0xb7747f9d, +0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, +0x213d42f6, 0x2c1c7c26, 0x61c2f50f, 0x6552daf9, 0xd2c231f8, +0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, +0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, +0x311170a7, 0x3e9b640c, 0xcc3e10d7, 0xd5cad3b6, 0x0caec388, +0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, +0xc1de8417, 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, +0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, 0x6f7de532, +0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, +0x001d7b95, 0x82e5e7d2, 0x109873f6, 0x00613096, 0xc32d9521, +0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, +0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, +0x0ce454a9, 0xd60acd86, 0x015f1919, 0x77079103, 0xdea03af6, +0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, +0xb8a5c3ef, 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, +0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, 0x39e4460c, +0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, +0x492fc295, 0x9266beab, 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, +0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, +0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, +0x932bcdf6, 0xb657c34d, 0x4edfd282, 0x7ae5290c, 0x3cb9536b, +0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, +0x0aef7ed2 }; + +static const uint32 S5[256] = { +0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, +0x1dd358f5, 0x44dd9d44, 0x1731167f, 0x08fbf1fa, 0xe7f511cc, +0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, +0x69befd7a, 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, +0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, 0x5f480a01, +0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, +0x8dba1cfe, 0x41a99b02, 0x1a550a04, 0xba8f65cb, 0x7251f4e7, +0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, +0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, +0x8709e6b0, 0xd7e07156, 0x4e29fea7, 0x6366e52d, 0x02d1c000, +0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, +0xd642a0c9, 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, +0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, 0x5c1ff900, +0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, +0x90c79505, 0xb0a8a774, 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, +0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, +0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, +0x524755f4, 0x03b63cc9, 0x0cc844b2, 0xbcf3f0aa, 0x87ac36e9, +0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, +0x01c94910, 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, +0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, 0x136e05db, +0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, +0xb6f589de, 0xec2941da, 0x26e46695, 0xb7566419, 0xf654efc5, +0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, +0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, +0x9e0885f9, 0x68cb3e47, 0x086c010f, 0xa21de820, 0xd18b69de, +0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, +0xb0d70eba, 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, +0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, 0x580a249f, +0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, +0x646c6bd7, 0x44904db3, 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, +0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, +0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, +0xc1092910, 0x8bc95fc6, 0x7d869cf4, 0x134f616f, 0x2e77118d, +0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, +0xaf462ba2, 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, +0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, 0x445f7382, +0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, +0x20936079, 0x459b80a5, 0xbe60e2db, 0xa9c23101, 0xeba5315c, +0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, +0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, +0x75922283, 0x784d6b17, 0x58ebb16e, 0x44094f85, 0x3f481d87, +0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, +0x2b092801, 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, +0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, 0x6cf6e479, +0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, +0xa09c7f70, 0x5346aba0, 0x5ce96c28, 0xe176eda3, 0x6bac307f, +0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, +0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, +0xeeb9491d, 0x34010718, 0xbb30cab8, 0xe822fe15, 0x88570983, +0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, +0xefe9e7d4 }; + +static const uint32 S6[256] = { +0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, +0x016843b4, 0xeced5cbc, 0x325553ac, 0xbf9f0960, 0xdfa1e2ed, +0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, +0x8989b138, 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, +0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, 0xa3149619, +0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, +0xa888614a, 0x2900af98, 0x01665991, 0xe1992863, 0xc8f30c60, +0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, +0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, +0x4c7f4448, 0xdab5d440, 0x6dba0ec3, 0x083919a7, 0x9fbaeed9, +0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, +0xba7dd9cd, 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, +0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, 0x284caf89, +0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, +0xefe8c36e, 0xf890cdd9, 0x80226dae, 0xc340a4a3, 0xdf7e9c09, +0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, +0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, +0xcf222ebf, 0x25ac6f48, 0xa9a99387, 0x53bddb65, 0xe76ffbe7, +0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, +0xc8087dfc, 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, +0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, 0x5f04456d, +0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, +0xe2220abe, 0xd2916ebf, 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, +0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, +0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, +0x692573e4, 0xe9a9d848, 0xf3160289, 0x3a62ef1d, 0xa787e238, +0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, +0x592af950, 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, +0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, 0x89dff0bb, +0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, +0xdc049441, 0xc8098f9b, 0x7dede786, 0xc39a3373, 0x42410005, +0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, +0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, +0x1f8fb214, 0xd372cf08, 0xcc3c4a13, 0x8cf63166, 0x061c87be, +0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, +0x3fc06976, 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, +0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, 0x3007cd3e, +0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, +0x8809286c, 0xf592d891, 0x08a930f6, 0x957ef305, 0xb7fbffbd, +0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, +0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, +0x257f0c3d, 0x9348af49, 0x361400bc, 0xe8816f4a, 0x3814f200, +0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, +0x54f4a084, 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, +0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, 0x653d7e6a, +0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, +0x0404a8c8, 0xb8e5a121, 0xb81a928a, 0x60ed5869, 0x97c55b96, +0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, +0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, +0xf544edeb, 0xb0e93524, 0xbebb8fbd, 0xa2d762cf, 0x49c92f54, +0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, +0xd675cf2f }; + +static const uint32 S7[256] = { +0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, +0xab9bc912, 0xde6008a1, 0x2028da1f, 0x0227bce7, 0x4d642916, +0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, +0xb28707de, 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, +0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, 0x4d495001, +0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, +0x1286becf, 0xb6eacb19, 0x2660c200, 0x7565bde4, 0x64241f7a, +0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, +0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, +0xeb12ff82, 0xe3486911, 0xd34d7516, 0x4e7b3aff, 0x5f43671b, +0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, +0xcb3a6c88, 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, +0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, 0x0a961288, +0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, +0xcf19df58, 0xbec3f756, 0xc06eba30, 0x07211b24, 0x45c28829, +0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, +0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, +0xaff60ff4, 0xea2c4e6d, 0x16e39264, 0x92544a8b, 0x009b4fc3, +0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, +0xbe838688, 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, +0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, 0xda6d0c74, +0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, +0xeed82b29, 0x1d382fe3, 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, +0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, +0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, +0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, 0xe7225308, 0x8b75cf77, +0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, +0x5dda0033, 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, +0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, 0x2711fd60, +0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, +0x488dcf25, 0x36c9d566, 0x28e74e41, 0xc2610aca, 0x3d49a9cf, +0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, +0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, +0x2b9f4fd5, 0x625aba82, 0x6a017962, 0x2ec01b9c, 0x15488aa9, +0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, +0x3453dc1e, 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, +0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, 0x66626c1c, +0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, +0x9ea294fb, 0x52cf564c, 0x9883fe66, 0x2ec40581, 0x763953c3, +0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, +0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, +0x3d321c5d, 0xc3f5e194, 0x4b269301, 0xc79f022f, 0x3c997e7e, +0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, +0xc61e45be, 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, +0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, 0x1814386b, +0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, +0x5479f8e6, 0x1cb8d647, 0x97fd61a9, 0xea7759f4, 0x2d57539d, +0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, +0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, +0x3d40f021, 0xc3c0bdae, 0x4958c24c, 0x518f36b2, 0x84b1d370, +0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, +0x954b8aa3 }; + +static const uint32 S8[256] = { +0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, +0xe6c1121b, 0x0e241600, 0x052ce8b5, 0x11a9cfb0, 0xe5952f11, +0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, +0x37ddddfc, 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, +0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, 0x0b15a15d, +0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, +0x72df191b, 0x7580330d, 0x94074251, 0x5c7dcdfa, 0xabbe6d63, +0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, +0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, +0xce949ad4, 0xb84769ad, 0x965bd862, 0x82f3d055, 0x66fb9767, +0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, +0x647a78fc, 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, +0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, 0xbbd35049, +0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, +0x58cb7e07, 0x3b74ef2e, 0x522fffb1, 0xd24708cc, 0x1c7e27cd, +0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, +0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, +0xc18910b1, 0xe11dbf7b, 0x06cd1af8, 0x7170c608, 0x2d5e3354, +0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, +0x77d51b42, 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, +0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, 0xe6459788, +0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, +0x24259fd7, 0xf8bef472, 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, +0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, +0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, +0xea7a6e98, 0x7cd16efc, 0x1436876c, 0xf1544107, 0xbedeee14, +0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, +0x151682eb, 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, +0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, 0xb6f2cf3b, +0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, +0xbae7dfdc, 0x42cbda70, 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, +0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, +0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, +0xc5c8b37e, 0x0d809ea2, 0x398feb7c, 0x132a4f94, 0x43b7950e, +0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, +0xacf3ebc3, 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, +0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, 0xe87b40e4, +0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, +0x38d7e5b2, 0x57720101, 0x730edebc, 0x5b643113, 0x94917e4f, +0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, +0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, +0xad1163ed, 0xea7b5965, 0x1a00726e, 0x11403092, 0x00da6d77, +0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, +0xcee7d28a, 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, +0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, 0xaa12e4f2, +0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, +0x67cdb156, 0x350d8384, 0x5938fa0f, 0x42399ef3, 0x36997b07, +0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, +0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, +0x0d2059d1, 0xa466bb1e, 0xf8da0a82, 0x04f19130, 0xba6e4ec0, +0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, +0xea8bf59e }; + diff --git a/buildkit b/buildkit new file mode 100755 index 0000000..54dc398 --- /dev/null +++ b/buildkit @@ -0,0 +1,144 @@ +#!/usr/bin/env python -- +# -*-Python-*- +# buildkit : Builds the object files +# +# Part of the Python Cryptography Toolkit, version 1.1 +# + +import sys, re, string +import os + +modules = [ +# Format: "hash" algorithm digestsize +('hash', 'MD2', 16), +('hash', 'MD4', 16), +('hash', 'MD5', 16), +('hash', 'SHA', 20), +('hash', 'RIPEMD', 20), +('hash', 'HAVAL', 32, [('rounds', 5), ('digestsize', 256)] ), + +# "block" algorithm blocksize keylength +('block', 'ARC2', 8, 0), +('block', 'Blowfish', 8, 0), +('block', 'CAST', 8, 0), +('block', 'DES', 8, 8), +('block', 'DES3', 8, 0), +('block', 'Diamond', 16, 0, [('rounds', 8)] ), +('block', 'IDEA', 8, 16), +('block', 'RC5', 8, 0, [('version', 0x10), + ('wordsize', 32), + ('rounds', 12) + ]), +('block', 'Skipjack', 8, 10), + +# Stream ciphers +('stream', 'ARC4', 1, 0), +('stream', 'Sapphire', 1, 0), +('stream', 'XOR', 1, 0), + +# "simple" filenamebase +#('simple', 'HAVAL') +] + +# This class neatly encapsulates files; comparisions examine the time of +# creation of the two files. + +class File: + def __init__(self, filename): + self.filename=filename + def __cmp__(self, other): + try: + t1=os.stat(self.filename)[8] + except os.error, (error, message): + if error==2: return(-1) + print error, message + raise os.error, (error,message) + try: + t2=os.stat(other.filename)[8] + except os.error, (error, message): + if error==2: return(+1) + print error, message + raise os.error, (error,message) + if (t1<t2): return(-1) + if (t1>t2): return(+1) + return 0 + +def execute(command): + print command + status=os.system(command) + if (status!=0): raise KeyboardInterrupt + +def BuildSourceFile(algorithm, moduletype, substlist): + sys.stderr.write(' Building C source code for '+algorithm+'\n') + input=open('framewks/'+moduletype+'.in', 'r') + output=open('src/'+algorithm+'module.c', 'w') + substlist.append( ('@@ALGORITHM@@', algorithm) ) + substlist.append( ('@@MODNAME@@', algorithm) ) + output.write('\n\n/* Do not modify this file; ' + +'it is automatically generated\n') + output.write(' from '+moduletype+'.in and '+algorithm+'.c\n */\n\n') + while (1): + l=input.readline() + if (l==''): break + for entry in substlist: + keyword, value=entry + l=re.sub(keyword, str(value), l) + + temp=l + l=re.sub('@@IMPLEMENTATION@@', '', l) + + # Check if the @@IMPLEMENTATION@@ keyword has been encountered + if (l!=temp): + newfile=open(moduletype+'/'+algorithm+'.c', 'r') + while (1): # Copy the implementation into the output file + l1=newfile.readline() + if (l1==''): break + output.write(l1) + output.write(l) + output.close() + input.close() + +for f in modules: + moduletype=f[0] + kw1=kw2=kw3=kw4="" + if type(f[-1]) == type([]): + # Handle any keyword arguments + for (kwarg, default) in f[-1]: + kw1 = kw1 + '"%s", ' % (kwarg); + kw2 = kw2 + 'new->%s = %s; ' % (kwarg, default) + kw3 = kw3 + ', &(new->%s)' % (kwarg) + kw4 = kw4 + 'i' + kw4 = '"' + kw4 + '"' + if (moduletype=='block' or moduletype=='stream'): + substlist = [('@@BLOCKSIZE@@', f[2]), + ('@@KEYSIZE@@', f[3]), + ('@@KEYWORDLIST@@', kw1), + ('@@KEYWORDDEFAULTS@@', kw2), + ('@@KEYWORDPTRS@@', kw3), + ('@@KEYWORDFMT@@', kw4)] + elif (moduletype=='hash'): + substlist=[('@@DIGESTSIZE@@', f[2]), + ('@@KEYWORDLIST@@', kw1), + ('@@KEYWORDDEFAULTS@@', kw2), + ('@@KEYWORDPTRS@@', kw3), + ('@@KEYWORDFMT@@', kw4)] + elif (moduletype=='simple'): + substlist=[] + else: + sys.stderr.write('Unknown keyword '+moduletype+'\n') + algorithm=f[1] + + framework=File('framewks/'+moduletype+'.in') + base=File(moduletype+'/'+algorithm+'.c') + source=File('src/'+algorithm+'module.c') + + if not os.path.exists(base.filename): + print algorithm, 'not present -- skipping' + else: + try: + if (source<base or source<framework): + BuildSourceFile(algorithm, moduletype, substlist) + except KeyboardInterrupt: + print 'Error occurred; deleting', source.filename + os.unlink(source.filename) + sys.exit(1) diff --git a/curiosa/README b/curiosa/README new file mode 100644 index 0000000..3e3747f --- /dev/null +++ b/curiosa/README @@ -0,0 +1,161 @@ + + The curiosa/ directory contains various cryptography-related +hacks that may of interest, but aren't really practical for real use. + + One amusement is to try to write the shortest possible +encryption program. Recently numerous people have started exporting +cryptographic systems in their .signatures (and on mailing labels, +T-shirts, ...). This is aimed at making the North American export +regulations look silly. The most common one seen in .sig files on +Usenet is RSA in 3 lines of Perl: + +#!/usr/local/bin/perl -s-- -export-a-crypto-system-sig -RSA-in-3-lines-PERL +($k,$n)=@ARGV;$m=unpack(H.$w,$m."\0"x$w),$_=`echo "16do$w 2+4Oi0$d*-^1[d2% +Sa2/d0<X+d*La1=z\U$n%0]SX$k"[$m*]\EszlXx++p|dc`,s/^.|\W//g,print pack('H*' +,$_)while read(STDIN,$m,($w=2*$d-1+length($n||die"$0 [-d] k n\n")&~1)/2) + + Usually we think of Python as a very clearly and spaciously +formatted language, because the program structure is indicated by +whitespace. However, it is possible to produce very compact code, as +the following programs demonstrate: + + * rsa.py (4 lines): Performs RSA public key +encryption/decryption. It requires two arguments, and can accept a +single option: '-d' for decryption (the default action is encryption). +The first argument must be the required exponent, expressed in +hexadecimal, and the second is the modulus, also in hex. You still +have to choose the correct exponent, whether the '-d' option is +present or not; '-d' simply changes the number of bytes read at a +single time. + + As an example: Let us assume the modulus is 6819722537, the +encryption exponent is 65537, and the decryption exponent is +2889233921. Then, after converting the numbers to hex, we can encrypt +and then decrypt by the following commands: + + echo 'Top secret message.' | rsa.py 10001 1967cb529 >ciphertext + cat ciphertext | rsa.py -d ac363601 1967cb529 + +#!/usr/local/bin/python +from sys import*;from string import*;a=argv;[s,p,q]=filter(lambda x:x[:1]!= +'-',a);d='-d'in a;e,n=atol(p,16),atol(q,16);l=(len(q)+1)/2;o,inb=l-d,l-1+d +while s:s=stdin.read(inb);s and map(stdout.write,map(lambda i,b=pow(reduce( +lambda x,y:(x<<8L)+y,map(ord,s)),e,n):chr(b>>8*i&255),range(o-1,-1,-1))) + + * arc4.py (5 lines): ARC4 is short for `Alleged RC4'. The +real RC4 algorithm is proprietary to RSA Data Security Inc. In +September of 1994, someone posted C code to both the Cypherpunks +mailing list and to the Usenet newsgroup @code{sci.crypt}, claiming +that it implemented the RC4 algorithm. This posted code is what I'm +calling Alleged RC4, or ARC4 for short. + + ARC4 is a private-key cipher; the same key is used to both +encrypt and decrypt. This is the script most likely to be of +practical use. It takes one argument, which is the key expressed in +hex bytes. The key can have any non-zero length. + + An example: To encrypt and then decrypt a message with the key +'foo', or, in hex, 0x66 0x6f 0x6f: + + cat 'Message.' | arc4.py 666f6f >ciphertext + cat ciphertext | arc4.py 666f6f + +#!/usr/local/bin/python +from sys import*;from string import *;t,x,y,j,s,a=range(256),0,0,0,1,argv[1] +k=(map(lambda b:atoi(a[b:b+2],16), range(0,len(a),2))*256)[:256] +for i in t[:]:j=(k[i]+t[i]+j)%256;t[i],t[j]=t[j],t[i] +while(s):s=stdin.read(1);l,x=len(s),(x+1)%256;y,c=(y+t[x])%256,l and ord(s);( +t[x],t[y])=t[y],t[x];stdout.write(chr(c^t[(t[x]+t[y])%256])[:l]) + + + * otp.py (2 lines): The only truly unbreakable encryption +method is the one-time pad, or OTP. In the OTP, the plaintext is +XORed with a stream of random data (the pad) to produce the +ciphertext. The recipient then XORs the ciphertext and gets the +plaintext again. This is secure because there is no way for an +eavesdropper to determine what the intended message is; the only thing +that can be learned is the length of the message. + + This sounds trivial; just get some random data, XOR it with +the message, and there you go! But the problem is with that word +"random"; how do you get truly random data? The rand() function in C +or your favorite programming language is almost certainly a +pseudo-random generator; a small amount of state is used to generate a +sequence of numbers that *looks* random to various statistical tests. +However, given a few numbers of the sequence, it may be possible to +derive the rest of the sequence in either the backward or forward +direction. This is obviously unsafe for a one-time pad; if you could +get just a few characters of the plaintext, you can compute the value +of the pad at that point and then derive the rest of the pad. + + Generating truly random numbers is difficult; usually some +physical phenomenon like keystroke timings, radioactive decay, or the +noise in a transistor is used. Then there's the problem of key +distribution; how does your correspondent get the random numbers? +Governments can afford to send a courier who carries the data in a +briefcase chained to his/her wrist; can you? + + In any event, let us assume you've magically obtained some +random numbers and placed them in a file called "pad". otp.py simply +XORs two files together, so to encrypt a file called "message": + + otp.py pad message >ciphertext + + Decryption is similar: + otp.py pad ciphertext >output + +#!/usr/local/bin/python +from sys import*;t=p=1;s=stdout;[i,j]=map(lambda f: open(f, 'r'), argv[1:3]) +while(t and p):t,p=i.read(1),j.read(1);t and p and s.write(chr(ord(t)^ord(p))) + + + Andrew Kuchling + akuchling@acm.org + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +-----BEGIN PGP SIGNATURE----- +Version: 2.6.2 + +iQCVAwUBMXIFKQRXhWZuGe+lAQEhlQP+Lz21PC5YjP+RpvFG51THBkDvB+8LVbib +2erNgCZGyVdIi0SduffwKcEHyhhYnsNxRgqkJslHr5wNkotMgamIWioE1yV2lEGf +iHktV7RewVWE/GhOZMRqULimuhvFfsyCCgTO1wbW5IKV22904VNli+fkTdgkSRLu +Kn1cD/HkX6k= +=hhTw +-----END PGP SIGNATURE----- diff --git a/curiosa/arc4.py b/curiosa/arc4.py new file mode 100644 index 0000000..f854450 --- /dev/null +++ b/curiosa/arc4.py @@ -0,0 +1,7 @@ +#!/usr/local/bin/python +from sys import*;from string import *;t,x,y,j,s,a=range(256),0,0,0,1,argv[1] +k=(map(lambda b:atoi(a[b:b+2],16), range(0,len(a),2))*256)[:256] +for i in t[:]:j=(k[i]+t[i]+j)%256;t[i],t[j]=t[j],t[i] +while(s):s=stdin.read(1);l,x=len(s),(x+1)%256;y,c=(y+t[x])%256,l and ord(s);( +t[x],t[y])=t[y],t[x];stdout.write(chr(c^t[(t[x]+t[y])%256])[:l]) + diff --git a/curiosa/gentest.py b/curiosa/gentest.py new file mode 100644 index 0000000..c20df97 --- /dev/null +++ b/curiosa/gentest.py @@ -0,0 +1,8 @@ +import string +hexstr='0123456789abcdef' +s='' +for i in range(0,16,2): s=s+chr(string.atol(hexstr[i:i+2], 16)) +f=open('data', 'w') +f.write(s) +f.close() + diff --git a/curiosa/otp.py b/curiosa/otp.py new file mode 100644 index 0000000..e27e017 --- /dev/null +++ b/curiosa/otp.py @@ -0,0 +1,3 @@ +#!/usr/local/bin/python +from sys import*;t=p=1;s=stdout;[i,j]=map(lambda f: open(f, 'r'), argv[1:3]) +while(t and p):t,p=i.read(1),j.read(1);t and p and s.write(chr(ord(t)^ord(p))) diff --git a/curiosa/rsa.py b/curiosa/rsa.py new file mode 100644 index 0000000..0506060 --- /dev/null +++ b/curiosa/rsa.py @@ -0,0 +1,5 @@ +#!/usr/local/bin/python +from sys import*;from string import*;a=argv;[s,p,q]=filter(lambda x:x[:1]!= +'-',a);d='-d'in a;e,n=atol(p,16),atol(q,16);l=(len(q)+1)/2;o,inb=l-d,l-1+d +while s:s=stdin.read(inb);s and map(stdout.write,map(lambda i,b=pow(reduce( +lambda x,y:(x<<8L)+y,map(ord,s)),e,n):chr(b>>8*i&255),range(o-1,-1,-1))) diff --git a/excludefiles b/excludefiles new file mode 100644 index 0000000..1c89e07 --- /dev/null +++ b/excludefiles @@ -0,0 +1,6 @@ +info +RCS +*~ +*,v +excludefiles +PGP diff --git a/framewks/block.in b/framewks/block.in new file mode 100644 index 0000000..19cd5b1 --- /dev/null +++ b/framewks/block.in @@ -0,0 +1,561 @@ +/* -*- C -*- */ +/* + * block.in : Generic framework for block encryption algorithms + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. This software is provided "as is" without + * warranty of fitness for use or suitability for any purpose, express + * or implied. Use at your own risk or not at all. + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _HAVE_STDC_HEADERS +#include <string.h> +#endif + +#include "Python.h" +#include "modsupport.h" + +#ifdef __GNUC__ +#define inline __inline__ +#else +#define inline +#endif + + + /* Cipher operation modes */ + +#define MODE_ECB 0 +#define MODE_CFB 1 +#define MODE_CBC 2 +#define MODE_OFB 3 +#define MODE_PGP 4 + + /* Endianness testing and definitions */ +#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ + if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} + +#define PCT_LITTLE_ENDIAN 1 +#define PCT_BIG_ENDIAN 0 + + /* + * + * Python interface + * + */ + +#define PCTObject_HEAD PyObject_HEAD int cipherMode, count; \ + unsigned char IV[@@BLOCKSIZE@@], oldCipher[@@BLOCKSIZE@@]; + +#define PCT_Str(x) #x +/* GetKeywordArg has been abandoned for PyArg_ParseTupleAndKeywords */ +#define GetKeywordArg(name, default) {\ + PyObject *arg; \ + if (kwdict==NULL || \ + (( arg=PyDict_GetItemString(kwdict, PCT_Str(name)))==NULL )) {new->name = default;} else { \ + if (!PyInt_Check(arg)) \ + { \ + PyErr_SetString(PyExc_TypeError, "Keyword argument must have integer value"); \ + Py_DECREF(new); \ + return NULL; \ + } \ + new->name = PyInt_AsLong(arg); \ + } \ + } + +@@IMPLEMENTATION@@ + + +staticforward PyTypeObject @@ALGORITHM@@type; + +#define is_@@ALGORITHM@@object(v) ((v)->ob_type == &@@ALGORITHM@@type) + +static @@ALGORITHM@@object * + new@@ALGORITHM@@object() +{ + @@ALGORITHM@@object * new; + new = PyObject_NEW(@@ALGORITHM@@object, &@@ALGORITHM@@type); + new->cipherMode = MODE_ECB; + return new; +} + +static void +@@ALGORITHM@@dealloc(ptr) +PyObject *ptr; +{ /* Overwrite the contents of the object, just in case... */ + int i; + @@ALGORITHM@@object *self=(@@ALGORITHM@@object *)ptr; + + for (i = 0; i < sizeof(@@ALGORITHM@@object); i++) + *((char *) self + i) = '\0'; + PyMem_DEL(self); +} + + +static char @@ALGORITHM@@new__doc__[] = +"Return a new @@ALGORITHM@@ encryption object."; + +static char *kwlist[] = {"key", "mode", "IV", @@KEYWORDLIST@@ + NULL}; + +static @@ALGORITHM@@object * +@@ALGORITHM@@new(self, args, kwdict) + PyObject *self; /* Not used */ + PyObject *args; + PyObject *kwdict; +{ + unsigned char *key, *IV; + + @@ALGORITHM@@object * new; + int i, keylen, IVlen=0, mode=MODE_ECB; + + new = new@@ALGORITHM@@object(); + /* Set default values */ + @@KEYWORDDEFAULTS@@ + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "s#|is#" @@KEYWORDFMT@@, kwlist, + &key, &keylen, &mode, &IV, &IVlen @@KEYWORDPTRS@@)) + { + Py_XDECREF(new); + return NULL; + } + + if (@@KEYSIZE@@!=0 && keylen!=@@KEYSIZE@@) + { + PyErr_SetString(PyExc_ValueError, "@@ALGORITHM@@ key must be " + "@@KEYSIZE@@ bytes long"); + return (NULL); + } + if (@@KEYSIZE@@==0 && keylen==0) + { + PyErr_SetString(PyExc_ValueError, "@@ALGORITHM@@ key cannot be " + "the null string (0 bytes long)"); + return (NULL); + } + if (IVlen != @@BLOCKSIZE@@ && IVlen != 0) + { + PyErr_SetString(PyExc_ValueError, "@@ALGORITHM@@ IV must be " + "@@BLOCKSIZE@@ bytes long"); + return (NULL); + } + if (mode<MODE_ECB || mode>MODE_PGP) + { + PyErr_SetString(PyExc_ValueError, "Unknown cipher feedback mode"); + return (NULL); + } + @@ALGORITHM@@init(new, key, keylen); + if (PyErr_Occurred()) + { + Py_DECREF(new); + return(NULL); + } + for (i = 0; i < @@BLOCKSIZE@@; i++) + { + new->IV[i] = 0; + new->oldCipher[i]=0; + } + for (i = 0; i < IVlen; i++) + { + new->IV[i] = IV[i]; + } + new->cipherMode = mode; + new->count=8; + return new; +} + +static char @@ALGORITHM@@_Encrypt__doc__[] = +"Decrypt the provided string of binary data."; + +static PyObject * +@@ALGORITHM@@_Encrypt(self, args) +@@ALGORITHM@@object * self; + PyObject *args; +{ + char *buffer, *str; + char temp[@@BLOCKSIZE@@]; + int i, j, len; + PyObject *result; + + if (!PyArg_Parse(args, "s#", &str, &len)) + return (NULL); + if (len==0) /* Handle empty string */ + { + return PyString_FromStringAndSize(NULL, 0); + } + if ( (len % @@BLOCKSIZE@@) !=0 && self->cipherMode!=MODE_CFB + && self->cipherMode!=MODE_PGP) + { + PyErr_SetString(PyExc_ValueError, "Strings for @@ALGORITHM@@ " + "must be a multiple of @@BLOCKSIZE@@ in length"); + return(NULL); + } + buffer=malloc(len); + if (buffer==NULL) + { + PyErr_SetString(PyExc_MemoryError, "No memory available in " + "@@ALGORITHM@@ encrypt"); + return(NULL); + } + switch(self->cipherMode) + { + case(MODE_ECB): + for(i=0; i<len; i+=@@BLOCKSIZE@@) + { + memcpy(buffer+i, str+i, @@BLOCKSIZE@@); + @@ALGORITHM@@encrypt(self, buffer+i); + } + break; + case(MODE_CBC): + for(i=0; i<len; i+=@@BLOCKSIZE@@) + { + for(j=0; j<@@BLOCKSIZE@@; j++) + { + temp[j]=str[i+j]^self->IV[j]; + } + @@ALGORITHM@@encrypt(self, temp); + memcpy(buffer+i, temp, @@BLOCKSIZE@@); + memcpy(self->IV, temp, @@BLOCKSIZE@@); + } + break; + case(MODE_CFB): + for(i=0; i<len; i++) + { + @@ALGORITHM@@encrypt(self, self->IV); + buffer[i]=str[i]^self->IV[0]; + memmove(self->IV, self->IV+1, @@BLOCKSIZE@@-1); + self->IV[@@BLOCKSIZE@@-1]=buffer[i]; + } + break; + case(MODE_PGP): + if (len<=@@BLOCKSIZE@@-self->count) + { /* If less than one block, XOR it in */ + for(i=0; i<len; i++) + buffer[i] = self->IV[self->count+i] ^= str[i]; + self->count += len; + } + else + { + int j; + for(i=0; i<@@BLOCKSIZE@@-self->count; i++) + buffer[i] = self->IV[self->count+i] ^= str[i]; + self->count=0; + for(; i<len-@@BLOCKSIZE@@; i+=@@BLOCKSIZE@@) + { + memcpy(self->oldCipher, self->IV, @@BLOCKSIZE@@); + @@ALGORITHM@@encrypt(self, self->IV); + for(j=0; j<@@BLOCKSIZE@@; j++) + buffer[i+j] = self->IV[j] ^= str[i+j]; + } + /* Do the remaining 1 to BLOCKSIZE bytes */ + memcpy(self->oldCipher, self->IV, @@BLOCKSIZE@@); + @@ALGORITHM@@encrypt(self, self->IV); + self->count=len-i; + for(j=0; j<len-i; j++) + { + buffer[i+j] = self->IV[j] ^= str[i+j]; + } + } + break; + default: + PyErr_SetString(PyExc_SystemError, "Unknown ciphertext feedback mode; " + "this shouldn't happen"); + return(NULL); + } + result=PyString_FromStringAndSize(buffer, len); + free(buffer); + return(result); +} + +static char @@ALGORITHM@@_Decrypt__doc__[] = +"Decrypt the provided string of binary data."; + + +static PyObject * +@@ALGORITHM@@_Decrypt(self, args) +@@ALGORITHM@@object * self; + PyObject *args; +{ + char *buffer, *str; + char temp[@@BLOCKSIZE@@]; + int i, j, len; + PyObject *result; + + if (!PyArg_Parse(args, "s#", &str, &len)) + return (NULL); + if (len==0) /* Handle empty string */ + { + return PyString_FromStringAndSize(NULL, 0); + } + if ( (len % @@BLOCKSIZE@@) !=0 && self->cipherMode!=MODE_CFB + && self->cipherMode!=MODE_PGP) + { + PyErr_SetString(PyExc_ValueError, "Strings for @@ALGORITHM@@ " + "must be a multiple of @@BLOCKSIZE@@ in length"); + return(NULL); + } + buffer=malloc(len); + if (buffer==NULL) + { + PyErr_SetString(PyExc_MemoryError, "No memory available in " + "@@ALGORITHM@@ decrypt"); + return(NULL); + } + switch(self->cipherMode) + { + case(MODE_ECB): + for(i=0; i<len; i+=@@BLOCKSIZE@@) + { + memcpy(buffer+i, str+i, @@BLOCKSIZE@@); + @@ALGORITHM@@decrypt(self, buffer+i); + } + break; + case(MODE_CBC): + for(i=0; i<len; i+=@@BLOCKSIZE@@) + { + memcpy(self->oldCipher, self->IV, @@BLOCKSIZE@@); + memcpy(temp, str+i, @@BLOCKSIZE@@); + @@ALGORITHM@@decrypt(self, temp); + for(j=0; j<@@BLOCKSIZE@@; j++) + { + buffer[i+j]=temp[j]^self->IV[j]; + self->IV[j]=str[i+j]; + } + } + break; + case(MODE_CFB): + for(i=0; i<len; i++) + { + @@ALGORITHM@@encrypt(self, self->IV); + buffer[i]=str[i]^self->IV[0]; + memmove(self->IV, self->IV+1, @@BLOCKSIZE@@-1); + self->IV[@@BLOCKSIZE@@-1]=str[i]; + } + break; + case(MODE_PGP): + if (len<=@@BLOCKSIZE@@-self->count) + { /* If less than one block, XOR it in */ + unsigned char t; + for(i=0; i<len; i++) + { + t=self->IV[self->count+i]; + buffer[i] = t ^ (self->IV[self->count+i] = str[i]); + } + self->count += len; + } + else + { + int j; + unsigned char t; + for(i=0; i<@@BLOCKSIZE@@-self->count; i++) + { + t=self->IV[self->count+i]; + buffer[i] = t ^ (self->IV[self->count+i] = str[i]); + } + self->count=0; + for(; i<len-@@BLOCKSIZE@@; i+=@@BLOCKSIZE@@) + { + memcpy(self->oldCipher, self->IV, @@BLOCKSIZE@@); + @@ALGORITHM@@encrypt(self, self->IV); + for(j=0; j<@@BLOCKSIZE@@; j++) + { + t=self->IV[j]; + buffer[i+j] = t ^ (self->IV[j] = str[i+j]); + } + } + /* Do the remaining 1 to BLOCKSIZE bytes */ + memcpy(self->oldCipher, self->IV, @@BLOCKSIZE@@); + @@ALGORITHM@@encrypt(self, self->IV); + self->count=len-i; + for(j=0; j<len-i; j++) + { + t=self->IV[j]; + buffer[i+j] = t ^ (self->IV[j] = str[i+j]); + } + } + break; + default: + PyErr_SetString(PyExc_SystemError, "Unknown ciphertext feedback mode; " + "this shouldn't happen"); + return(NULL); + } + result=PyString_FromStringAndSize(buffer, len); + free(buffer); + return(result); +} + +static char @@ALGORITHM@@_Sync__doc__[] = +"For objects using the PGP feedback mode, this method modifies the IV, " +"synchronizing it with the preceding ciphertext."; + +static PyObject * +@@ALGORITHM@@_Sync(self, args) +@@ALGORITHM@@object * self; + PyObject *args; +{ + if (self->cipherMode!=MODE_PGP) + { + PyErr_SetString(PyExc_SystemError, "sync() operation not defined for " + "this feedback mode"); + return(NULL); + } + + if (self->count!=8) + { + memmove(self->IV+@@BLOCKSIZE@@-self->count, self->IV, self->count); + memcpy(self->IV, self->oldCipher+self->count, @@BLOCKSIZE@@-self->count); + self->count=8; + } + Py_INCREF(Py_None); + return Py_None; +} + +#if 0 +void PrintState(self, msg) + @@ALGORITHM@@object *self; + char * msg; +{ + int count; + + printf("%sing: %i IV ", msg, (int)self->count); + for(count=0; count<8; count++) printf("%i ", self->IV[count]); + printf("\noldCipher:"); + for(count=0; count<8; count++) printf("%i ", self->oldCipher[count]); + printf("\n"); +} +#endif + + +/* @@ALGORITHM@@ object methods */ + +static PyMethodDef @@ALGORITHM@@methods[] = +{ + {"encrypt", (PyCFunction) @@ALGORITHM@@_Encrypt, 0, @@ALGORITHM@@_Encrypt__doc__}, + {"decrypt", (PyCFunction) @@ALGORITHM@@_Decrypt, 0, @@ALGORITHM@@_Decrypt__doc__}, + {"sync", (PyCFunction) @@ALGORITHM@@_Sync, 0, @@ALGORITHM@@_Sync__doc__}, + {NULL, NULL} /* sentinel */ +}; + + +static int +@@ALGORITHM@@setattr(ptr, name, v) + PyObject *ptr; + char *name; + PyObject *v; +{ + @@ALGORITHM@@object *self=(@@ALGORITHM@@object *)ptr; + if (strcmp(name, "IV") != 0) + { + PyErr_SetString(PyExc_AttributeError, + "non-existent block cipher object attribute"); + return -1; + } + if (v==NULL) + { + PyErr_SetString(PyExc_AttributeError, + "Can't delete IV attribute of block cipher object"); + return -1; + } + if (!PyString_Check(v)) + { + PyErr_SetString(PyExc_TypeError, + "IV attribute of block cipher object must be string"); + return -1; + } + if (PyString_Size(v)!=@@BLOCKSIZE@@) + { + PyErr_SetString(PyExc_ValueError, "@@ALGORITHM@@ IV must be " + "@@BLOCKSIZE@@ bytes long"); + return -1; + } + memcpy(self->IV, PyString_AsString(v), @@BLOCKSIZE@@); + return (0); +} + +static PyObject * +@@ALGORITHM@@getattr(s, name) + PyObject *s; + char *name; +{ + @@ALGORITHM@@object *self = (@@ALGORITHM@@object*)s; + if (strcmp(name, "IV") == 0) + { + return(PyString_FromStringAndSize(self->IV, @@BLOCKSIZE@@)); + } + if (strcmp(name, "mode") == 0) + { + return(PyInt_FromLong((long)(self->cipherMode))); + } + if (strcmp(name, "blocksize") == 0) + { + return PyInt_FromLong(@@BLOCKSIZE@@); + } + if (strcmp(name, "keysize") == 0) + { + return PyInt_FromLong(@@KEYSIZE@@); + } + return Py_FindMethod(@@ALGORITHM@@methods, (PyObject *) self, name); +} + +/* List of functions defined in the module */ + +static struct PyMethodDef modulemethods[] = +{ + {"new", (PyCFunction) @@ALGORITHM@@new, METH_VARARGS|METH_KEYWORDS, @@ALGORITHM@@new__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject @@ALGORITHM@@type = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "@@ALGORITHM@@", /*tp_name*/ + sizeof(@@ALGORITHM@@object), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + @@ALGORITHM@@dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + @@ALGORITHM@@getattr, /*tp_getattr*/ + @@ALGORITHM@@setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc) 0, /*tp_repr*/ + 0, /*tp_as_number*/ +}; + +/* Initialization function for the module (*must* be called initxx) */ + +#define insint(n,v) {PyObject *o=PyInt_FromLong(v); if (o!=NULL) {PyDict_SetItemString(d,n,o); Py_DECREF(o);}} + +void +init@@MODNAME@@() +{ + PyObject *m, *d, *x; + + @@ALGORITHM@@type.ob_type = &PyType_Type; + + /* Create the module and add the functions */ + m = Py_InitModule("@@MODNAME@@", modulemethods); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + x = PyString_FromString("@@MODNAME@@.error"); + PyDict_SetItemString(d, "error", x); + + insint("ECB", MODE_ECB); + insint("CFB", MODE_CFB); + insint("CBC", MODE_CBC); + insint("PGP", MODE_PGP); + insint("blocksize", @@BLOCKSIZE@@); + insint("keysize", @@KEYSIZE@@); + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module @@MODNAME@@"); +} + diff --git a/framewks/hash.in b/framewks/hash.in new file mode 100644 index 0000000..2a030eb --- /dev/null +++ b/framewks/hash.in @@ -0,0 +1,285 @@ +/* + * hash.in : Generic framework for hash function extension modules + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. This software is provided "as is" without + * warranty of fitness for use or suitability for any purpose, express + * or implied. Use at your own risk or not at all. + * + */ + +/* Basic object type */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef _HAVE_STDC_HEADERS +#include <string.h> +#endif + +#define Py_USE_NEW_NAMES +#include "Python.h" +#include "modsupport.h" + +/* Endianness testing and definitions */ +#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ + if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} + +#define PCT_LITTLE_ENDIAN 1 +#define PCT_BIG_ENDIAN 0 + +/* This is where the actual code for the hash function will go */ + +#define PCTObject_HEAD PyObject_HEAD +#define PCT_Str(x) #x +/* GetKeywordArg has been abandoned for PyArg_ParseTupleAndKeywords */ +#define GetKeywordArg(name, default) {\ + PyObject *arg; \ + if (kwdict==NULL || \ + (( arg=PyDict_GetItemString(kwdict, PCT_Str(name)))==NULL )) {new->name = default;} else { \ + if (!PyInt_Check(arg)) \ + { \ + PyErr_SetString(PyExc_TypeError, "Keyword argument must have integer value"); \ + Py_DECREF(new); \ + return NULL; \ + } \ + new->name = PyInt_AsLong(arg); \ + } \ + } + +@@IMPLEMENTATION@@ + +staticforward PyTypeObject @@ALGORITHM@@type; + +#define is_@@ALGORITHM@@object(v) ((v)->ob_type == &@@ALGORITHM@@type) + +static @@ALGORITHM@@object * +new@@ALGORITHM@@object() +{ + @@ALGORITHM@@object *new; + + new = PyObject_NEW(@@ALGORITHM@@object, &@@ALGORITHM@@type); + return new; +} + +/* Internal methods for a hashing object */ + +static void +@@ALGORITHM@@_dealloc(ptr) + PyObject *ptr; +{ + @@ALGORITHM@@object *@@ALGORITHM@@ptr=(@@ALGORITHM@@object *)ptr; + PyMem_DEL(@@ALGORITHM@@ptr); +} + + +/* External methods for a hashing object */ + +static char @@ALGORITHM@@_copy__doc__[] = +"Return a copy of the hashing object."; + +static PyObject * +@@ALGORITHM@@_copy(self, args) + @@ALGORITHM@@object *self; + PyObject *args; +{ + @@ALGORITHM@@object *newobj; + + if ( (newobj = new@@ALGORITHM@@object())==NULL) + return(NULL); + + if (!PyArg_NoArgs(args)) + {Py_DECREF(newobj); return(NULL);} + + @@ALGORITHM@@copy(self, newobj); + return((PyObject *)newobj); +} + +static char @@ALGORITHM@@_digest__doc__[] = +"Return the digest value as a string of binary data."; + +static PyObject * +@@ALGORITHM@@_digest(self, args) + @@ALGORITHM@@object *self; + PyObject *args; +{ + + if (!PyArg_NoArgs(args)) + return NULL; + + return (PyObject *)@@ALGORITHM@@digest(self); +} + +static char @@ALGORITHM@@_hexdigest__doc__[] = +"Return the digest value as a string of hexadecimal digits."; + +static PyObject * +@@ALGORITHM@@_hexdigest(self, args) + @@ALGORITHM@@object *self; + PyObject *args; +{ + PyObject *value, *retval; + unsigned char *raw_digest, *hex_digest; + int i, j, size; + + if (!PyArg_NoArgs(args)) + return NULL; + + /* Get the raw (binary) digest value */ + value = (PyObject *)@@ALGORITHM@@digest(self); + size = PyString_Size(value); + raw_digest = PyString_AsString(value); + + /* Create a new string */ + retval = PyString_FromStringAndSize(NULL, size * 2 ); + hex_digest = PyString_AsString(retval); + + /* Make hex version of the digest */ + for(i=j=0; i<size; i++) + { + char c; + c = raw_digest[i] / 16; c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + c = raw_digest[i] % 16; c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + } + Py_DECREF(value); + return retval; +} + +static char @@ALGORITHM@@_update__doc__[] = +"Update this hashing object's state with the provided string."; + +static PyObject * +@@ALGORITHM@@_update(self, args) + @@ALGORITHM@@object *self; + PyObject *args; +{ + unsigned char *cp; + int len; + + if (!PyArg_Parse(args, "s#", &cp, &len)) + return NULL; + + @@ALGORITHM@@update(self, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef @@ALGORITHM@@_methods[] = { + {"copy", (PyCFunction)@@ALGORITHM@@_copy, 0, @@ALGORITHM@@_copy__doc__}, + {"digest", (PyCFunction)@@ALGORITHM@@_digest, 0, @@ALGORITHM@@_digest__doc__}, + {"hexdigest", (PyCFunction)@@ALGORITHM@@_hexdigest, 0, @@ALGORITHM@@_hexdigest__doc__}, + {"update", (PyCFunction)@@ALGORITHM@@_update, 0, @@ALGORITHM@@_update__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +@@ALGORITHM@@_getattr(self, name) + @@ALGORITHM@@object *self; + char *name; +{ + if (strcmp(name, "blocksize")==0) + return PyInt_FromLong(1); + if (strcmp(name, "digestsize")==0) + return PyInt_FromLong(@@DIGESTSIZE@@); + + return Py_FindMethod(@@ALGORITHM@@_methods, (PyObject *)self, name); +} + +static PyTypeObject @@ALGORITHM@@type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "@@ALGORITHM@@", /*tp_name*/ + sizeof(@@ALGORITHM@@object), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + @@ALGORITHM@@_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + @@ALGORITHM@@_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ +}; + + +/* The single module-level function: new() */ + +static char @@ALGORITHM@@_new__doc__[] = + "Return a new @@ALGORITHM@@ hashing object. An optional string " + "argument may be provided; if present, this string will be " + " automatically hashed."; + +static char *kwlist[] = {"string", @@KEYWORDLIST@@ + NULL}; + +static PyObject * +@@ALGORITHM@@_new(self, args, kwdict) + PyObject *self; + PyObject *args; + PyObject *kwdict; +{ + @@ALGORITHM@@object *new; + unsigned char *cp = NULL; + int len; + + if ((new = new@@ALGORITHM@@object()) == NULL) + return NULL; + + @@KEYWORDDEFAULTS@@ + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#" @@KEYWORDFMT@@, kwlist, + &cp, &len @@KEYWORDPTRS@@)) { + Py_DECREF(new); + return NULL; + } + + @@ALGORITHM@@init(new); + + if (PyErr_Occurred()) {Py_DECREF(new); return NULL;} + if (cp) + @@ALGORITHM@@update(new, cp, len); + + return (PyObject *)new; +} + + +/* List of functions exported by this module */ + +static struct PyMethodDef @@ALGORITHM@@_functions[] = { + {"new", (PyCFunction)@@ALGORITHM@@_new, METH_VARARGS|METH_KEYWORDS, @@ALGORITHM@@_new__doc__}, + {"@@MODNAME@@", (PyCFunction)@@ALGORITHM@@_new, METH_VARARGS|METH_KEYWORDS, @@ALGORITHM@@_new__doc__}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +#define insint(n,v) {PyObject *o=PyInt_FromLong(v); if (o!=NULL) {PyDict_SetItemString(d,n,o); Py_DECREF(o);}} + +void +init@@MODNAME@@() +{ + PyObject *d, *m; + + @@ALGORITHM@@type.ob_type = &PyType_Type; + m = Py_InitModule("@@MODNAME@@", @@ALGORITHM@@_functions); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + insint("blocksize", 1); /* For future use, in case some hash + functions require an integral number of + blocks */ + insint("digestsize", @@DIGESTSIZE@@); + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module @@MODNAME@@"); +} + + diff --git a/framewks/simple.in b/framewks/simple.in new file mode 100644 index 0000000..6e9a9fd --- /dev/null +++ b/framewks/simple.in @@ -0,0 +1,3 @@ + + +@@IMPLEMENTATION@@ diff --git a/framewks/stream.in b/framewks/stream.in new file mode 100644 index 0000000..f196c10 --- /dev/null +++ b/framewks/stream.in @@ -0,0 +1,279 @@ +/* -*- C -*- */ + +/* + * stream.in : Generic framework for stream ciphers + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. This software is provided "as is" without + * warranty of fitness for use or suitability for any purpose, express + * or implied. Use at your own risk or not at all. + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _HAVE_STDC_HEADERS +#include <string.h> +#endif + +#include "Python.h" +#include "modsupport.h" + +#ifdef __GNUC__ +#define inline __inline__ +#else +#define inline +#endif + + + /* Endianness testing and definitions */ +#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ + if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} + +#define PCT_LITTLE_ENDIAN 1 +#define PCT_BIG_ENDIAN 0 + + + /* + * + * Python interface + * + */ + +#define PCTObject_HEAD PyObject_HEAD int dummy /* dummy is never used */ +#define PCT_Str(x) #x +/* GetKeywordArg has been abandoned for PyArg_ParseTupleAndKeywords */ +#define GetKeywordArg(name, default) {\ + PyObject *arg; \ + if (kwdict==NULL || \ + (( arg=PyDict_GetItemString(kwdict, PCT_Str(name)))==NULL )) {new->name = default;} else { \ + if (!PyInt_Check(arg)) \ + { \ + PyErr_SetString(PyExc_TypeError, "Keyword argument must have integer value"); \ + Py_DECREF(new); \ + return NULL; \ + } \ + new->name = PyInt_AsLong(arg); \ + } \ + } + +@@IMPLEMENTATION@@ + + +staticforward PyTypeObject @@ALGORITHM@@type; + +#define is_@@ALGORITHM@@object(v) ((v)->ob_type == &@@ALGORITHM@@type) + +static @@ALGORITHM@@object * + new@@ALGORITHM@@object() +{ + @@ALGORITHM@@object * new; + new = PyObject_NEW(@@ALGORITHM@@object, &@@ALGORITHM@@type); + return new; +} + +static void +@@ALGORITHM@@dealloc(self) +@@ALGORITHM@@object * self; +{ /* Overwrite the contents of the object, just in case... */ + int i; + + for (i = 0; i < sizeof(@@ALGORITHM@@object); i++) + *((char *) self + i) = '\0'; + PyMem_DEL(self); +} + +static char @@ALGORITHM@@new__doc__[] = +"Return a new @@ALGORITHM@@ encryption object."; + +static char *kwlist[] = {"key", @@KEYWORDLIST@@ + NULL}; + +static @@ALGORITHM@@object * +@@ALGORITHM@@new(self, args, kwdict) + PyObject *self; /* Not used */ + PyObject *args; + PyObject *kwdict; +{ + unsigned char *key; + @@ALGORITHM@@object * new; + int keylen; + + new = new@@ALGORITHM@@object(); + @@KEYWORDDEFAULTS@@ + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "s#" @@KEYWORDFMT@@, kwlist, + &key, &keylen @@KEYWORDPTRS@@)) + { + Py_DECREF(new); + return (NULL); + } + + if (@@KEYSIZE@@!=0 && keylen != @@KEYSIZE@@) + { + PyErr_SetString(PyExc_ValueError, "@@ALGORITHM@@ key must be " + "@@KEYSIZE@@ bytes long"); + return (NULL); + } + if (@@KEYSIZE@@== 0 && keylen == 0) + { + PyErr_SetString(PyExc_ValueError, "@@ALGORITHM@@ key cannot be " + "the null string (0 bytes long)"); + return (NULL); + } + @@ALGORITHM@@init(new, key, keylen); + if (PyErr_Occurred()) + { + Py_DECREF(new); + return (NULL); + } + return new; +} + +static char @@ALGORITHM@@_Encrypt__doc__[] = +"Decrypt the provided string of binary data."; + +static PyObject * +@@ALGORITHM@@_Encrypt(self, args) +@@ALGORITHM@@object * self; + PyObject *args; +{ + unsigned char *buffer, *str; + int len; + PyObject *result; + + if (!PyArg_Parse(args, "s#", &str, &len)) + return (NULL); + if (len == 0) /* Handle empty string */ + { + return PyString_FromStringAndSize(NULL, 0); + } + buffer = malloc(len); + if (buffer == NULL) + { + PyErr_SetString(PyExc_MemoryError, "No memory available in " + "@@ALGORITHM@@ encrypt"); + return (NULL); + } + memcpy(buffer, str, len); + @@ALGORITHM@@encrypt(self, buffer, len); + result = PyString_FromStringAndSize(buffer, len); + free(buffer); + return (result); +} + +static char @@ALGORITHM@@_Decrypt__doc__[] = +"Decrypt the provided string of binary data."; + +static PyObject * +@@ALGORITHM@@_Decrypt(self, args) +@@ALGORITHM@@object * self; + PyObject *args; +{ + char *buffer, *str; + int len; + PyObject *result; + + if (!PyArg_Parse(args, "s#", &str, &len)) + return (NULL); + if (len == 0) /* Handle empty string */ + { + return PyString_FromStringAndSize(NULL, 0); + } + buffer = malloc(len); + if (buffer == NULL) + { + PyErr_SetString(PyExc_MemoryError, "No memory available in " + "@@ALGORITHM@@ decrypt"); + return (NULL); + } + memcpy(buffer, str, len); + @@ALGORITHM@@decrypt(self, buffer, len); + result = PyString_FromStringAndSize(buffer, len); + free(buffer); + return (result); +} + +/* @@ALGORITHM@@object methods */ + +static PyMethodDef @@ALGORITHM@@methods[] = +{ + {"encrypt", (PyCFunction) @@ALGORITHM@@_Encrypt, 0, @@ALGORITHM@@_Encrypt__doc__}, + {"decrypt", (PyCFunction) @@ALGORITHM@@_Decrypt, 0, @@ALGORITHM@@_Decrypt__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +@@ALGORITHM@@getattr(self, name) +@@ALGORITHM@@object * self; + char *name; +{ + if (strcmp(name, "blocksize") == 0) + { + return PyInt_FromLong(@@BLOCKSIZE@@); + } + if (strcmp(name, "keysize") == 0) + { + return PyInt_FromLong(@@KEYSIZE@@); + } + return Py_FindMethod(@@ALGORITHM@@methods, (PyObject *) self, name); +} + + +/* List of functions defined in the module */ + +static struct PyMethodDef modulemethods[] = +{ + {"new", (PyCFunction) @@ALGORITHM@@new, METH_VARARGS|METH_KEYWORDS, @@ALGORITHM@@new__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject @@ALGORITHM@@type = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "@@ALGORITHM@@", /*tp_name*/ + sizeof(@@ALGORITHM@@object), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + @@ALGORITHM@@dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + @@ALGORITHM@@getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ +}; + +/* Initialization function for the module (*must* be called initxx) */ + +#define insint(n,v) {PyObject *o=PyInt_FromLong(v); if (o!=NULL) {PyDict_SetItemString(d,n,o); Py_DECREF(o);}} + +void + init@@MODNAME@@() +{ + PyObject *m, *d, *x; + + @@ALGORITHM@@type.ob_type = &PyType_Type; + /* Create the module and add the functions */ + m = Py_InitModule("@@MODNAME@@", modulemethods); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + x = PyString_FromString("@@MODNAME@@.error"); + PyDict_SetItemString(d, "error", x); + + insint("blocksize", @@BLOCKSIZE@@); + insint("keysize", @@KEYSIZE@@); + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module @@MODNAME@@"); +} + + diff --git a/info/architecture.txt b/info/architecture.txt new file mode 100644 index 0000000..d36b3ac --- /dev/null +++ b/info/architecture.txt @@ -0,0 +1,647 @@ +Network Working Group T. Ylonen +INTERNET-DRAFT T. Kivinen +draft-ietf-secsh-architecture-01.txt M. Saarinen +Expires in six months SSH + 7 November 1997 + + SSH Protocol Architecture + +Status of This memo + +This document is an Internet-Draft. Internet-Drafts are working +documents of the Internet Engineering Task Force (IETF), its areas, +and its working groups. Note that other groups may also distribute +working documents as Internet-Drafts. + +Internet-Drafts are draft documents valid for a maximum of six +months and may be updated, replaced, or obsoleted by other documents +at any time. It is inappropriate to use Internet-Drafts as reference +material or to cite them other than as ``work in progress.'' + +To learn the current status of any Internet-Draft, please check +the ``1id-abstracts.txt'' listing contained in the Internet-Drafts +Shadow Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe), +munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), +or ftp.isi.edu (US West Coast). + +Abstract + +SSH is a protocol for secure remote login and other secure network +services over an insecure network. + +This document describes the architecture of the SSH protocol, and the +notation and terminology used in SSH protocol documents. It also discusses +the SSH algorithm naming system that allows local extensions. + +The SSH protocol consists of three major components: Transport layer +protocol provides server authentication, confidentiality, and integrity +with perfect forward secrecy. User authentication protocol authenticates +the client to the server. Connection protocol multiplexes the encrypted +tunnel into several logical channels. Details of these protocols are +described in separate documents. + + + + + + + + + + + + + + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 1] + +INTERNET-DRAFT 7 November 1997 + +Table of Contents + +1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . 2 +2. Specification of Requirements . . . . . . . . . . . . . . . . . 2 +3. Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 3.1. Host Keys . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 3.2. Extensibility . . . . . . . . . . . . . . . . . . . . . . . 4 + 3.3. Policy Issues . . . . . . . . . . . . . . . . . . . . . . . 4 + 3.4. Security Properties . . . . . . . . . . . . . . . . . . . . 5 + 3.5. Packet Size and Overhead . . . . . . . . . . . . . . . . . . 5 + 3.6. Localization and Character Set Support . . . . . . . . . . . 6 +4. Data Type Representations Used in the SSH Protocols . . . . . . 7 + 4.1. Encoding of Network Addresses . . . . . . . . . . . . . . . 8 +5. Algorithm Naming . . . . . . . . . . . . . . . . . . . . . . . . 8 +6. Message Numbers . . . . . . . . . . . . . . . . . . . . . . . . 9 +7. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 9 +8. Security Considerations . . . . . . . . . . . . . . . . . . . . 10 +9. References . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 +10. Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . 11 + + + +1. Introduction + +SSH is a protocol for secure remote login and other secure network +services over an insecure network. It consists of three major +components: + +o Transport layer protocol [SSH-TRANS] provides server authentication, + confidentiality, and integrity. It may optionally also provide + compression. The transport layer will typically be run over a TCP/IP + connection, but might also be used on top of any other reliable data + stream. + +o User authentication protocol [SSH-USERAUTH] authenticates the client- + side user to the server. It runs over the transport layer protocol. + +o Connection protocol [SSH-CONN] multiplexes the encrypted tunnel into + several logical channels. It runs over the user authentication + protocol. + +The client sends a service request once a secure transport layer +connection has been established. A second service request is sent after +user authentication is complete. This allows new protocols to be defined +and coexist with the protocols listed above. + +The connection protocol provides channels that can be used for a wide +range of purposes. Standard methods are provided for setting up secure +interactive shell sessions and for forwarding ("tunneling") arbitrary +TCP/IP ports and X11 connections. + +2. Specification of Requirements + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 2] + +INTERNET-DRAFT 7 November 1997 + +All of the documents related to the SSH protocols shall use the keywords +"MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD +NOT", "RECOMMENDED, "MAY", and "OPTIONAL" to describe requirements. +They are to be interpreted as described in [RFC-2119]. + +3. Architecture + +3.1. Host Keys + +Each server host MUST have a host key. Hosts MAY have multiple host +keys using multiple different algorithms. Multiple hosts MAY share the +same host key. Every host MUST have at least one key using each REQUIRED +public key algorithm (currently DSS [FIPS-186]). + +The server host key is used during key exchange to verify that the +client is really talking to the correct server. For this to be possible, +the client must have a priori knowledge of the server's public host key. + +Two different trust models can be used: + +o The client has a local database that associates each host name (as + typed by the user) with the corresponding public host key. This + method requires no centrally administered infrastructure, and no + third-party coordination. The downside is that the database of name- + key associations may become burdensome to maintain. + +o The host name - key association is certified by some trusted + certification authority. The client only knows the CA root key, and + can verify the validity of all host keys certified by accepted CAs. + + The second alternative eases the maintenance problem, since ideally + only a single CA key needs to be securely stored on the client. On + the other hand, each host key must be appropriately certified by a + central authority before authorization is possible. Also, a lot of + trust is placed on the central infrastructure. + +The protocol provides the option that the server name - host key +association is not checked when connecting the host for the first time. +This allows communication without prior communication of host keys or +certification. The connection still provides protection against passive +listening; however, it becomes vulnerable to active man-in-the-middle +attacks. Implementations SHOULD NOT normally allow such connections by +default, as they pose a potential security problem. However, as there +is no widely deployed key infrastructure available on the Internet yet, +this option makes the protocol much more usable during the transition +time until such an infrastructure emerges, while still providing a much +higher level of security than that offered by older solutions (e.g. +telnet [RFC-854] and rlogin [RFC-1282]). +Implementations SHOULD try to make best effort to check host keys. An +example of a possible strategy is to only accept a host key without +checking the first time a host is connected, save the key in a local +database, and compare against that key on all future connections to that +host. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 3] + +INTERNET-DRAFT 7 November 1997 + +Implementations MAY provide additional methods for verifying the +correctness of host keys, e.g. a hexadecimal fingerprint derived from +the SHA-1 hash of the public key. Such fingerprints can easily be +verified by using telephone or other external communication channels. + +All implementations SHOULD provide an option to not accept host keys +that cannot be verified. + +We believe that ease of use is critical to end-user acceptance of +security solutions, and no improvement in security is gained if the new +solutions are not used. Thus, providing the option not to check the +the server host key is believed to improve overall security of the +Internet, even though it reduces the security of the protocol in +configurations where it is allowed. + +3.2. Extensibility + +We believe that the protocol will evolve over time, and some +organizations will want to use their own encryption, authentication +and/or key exchange methods. Central registration of all extensions is +cumbersome, especially for experimental or classified features. On the +other hand, having no central registration leads to conflicts in method +identifiers, making interoperability difficult. + +We have chosen to identify algorithms, methods, formats, and extension +protocols with textual names that are of a specific format. DNS names +are used to create local namespaces where experimental or classified +extensions can be defined without fear of conflicts with other +implementations. + +One design goal has been to keep the base protocol as simple as +possible, and to require as few algorithms as possible. However, all +implementations MUST support a minimal set of algorithms to ensure +interoperability (this does not imply that the local policy on all hosts +would necessary allow these algorithms). The mandatory algorithms are +specified in the relevant protocol documents. + +Additional algorithms, methods, formats, and extension protocols can be +defined in separate drafts. See Section ``Algorithm Naming'' for more +information. + +3.3. Policy Issues + +The protocol allows full negotiation of encryption, integrity, key +exchange, compression, and public key algorithms and formats. +Encryption, integrity, public key, and compression algorithms can be +different for each direction. + +The following policy issues SHOULD be addressed in the configuration +mechanisms of each implementation: + +o Encryption, integrity, and compression algorithms, separately for + each direction. The policy MUST specify which is the preferred + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 4] + +INTERNET-DRAFT 7 November 1997 + + algorithm (e.g. the first algorithm listed in each category). + +o Public key algorithms and key exchange method to be used for host + authentication. The existence of trusted host keys for different + public key algorithms also affects this choice. + +o The authentication methods that are to be required by the server for + each user. The server's policy MAY require multiple authentication + for some or all users. The required algorithms MAY depend on the + location from where the user is trying to log in from. + +o The operations that the user is allowed to perform using the + connection protocol. Some issues are related to security; for + example, the policy SHOULD NOT allow the server to start sessions or + run commands on the client machine, and MUST NOT allow connections to + the authentication agent unless forwarding it has been requested. + Other issues, such as which TCP/IP ports can be forwarded and by + whom, are clear local policy issues. Many of these issues may + involve traversing or bypassing firewalls, and are interrelated with + the local security policy. + +3.4. Security Properties + +The primary goal of the SSH protocols is improved security on the +Internet. It attempts to do this in a way that is easy to deploy, even +at the cost of absolute security. + +o All encryption, integrity, and public key algorithms used are well- + known, well-established algorithms. + +o All algorithms are used with cryptographically sound key sizes that + are believed to provide protection against even the strongest + cryptanalytic attacks for decades. + +o All algorithms are negotiated, and in case some algorithm is broken, + it is easy to switch to some other algorithm without modifying the + base protocol. + +Specific concessions were made to make wide-spread fast deployment +easier. The particular case where this comes up is verifying that the +server host key really belongs to the desired host; the protocol allows +the verification to be left out (but this is NOT RECOMMENDED). This is +believed to significantly improve usability in the short term, until +widespread Internet public key infrastructures emerge. + +3.5. Packet Size and Overhead + +Some readers will worry about the increase in packet size due to new +headers, padding, and MAC. The minimum packet size in the order of 28 +bytes (depending on negotiated algorithms). The increase is negligible +for large packets, but very significant for one-byte packets (telnet- +type sessions). There are, however, several factors that make this a +non-issue in almost all cases: + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 5] + +INTERNET-DRAFT 7 November 1997 + +o The minimum size of a TCP/IP header is 32 bytes. Thus, the increase + is actually from 33 to 51 bytes (roughly). + +o The minimum size of the data field of an ethernet packet is 46 bytes + [RFC-894]. Thus, the increase is by no more than 5 bytes. When + ethernet headers are considered, the increase is by less than 10 + percent. + +o The total fraction of telnet-type data in the Internet is negligible, + even with increased packet sizes. + +The only environment where the packet size increase is likely to have +significant effect is PPP [RFC-1134] over slow modem lines (PPP +compresses the TCP/IP headers, emphasizing the increase in packet size). +However, with modern modems, the time needed to transfer is on the order +of 2ms, which is a lot faster than people can type. + +There are also issues related to the maximum packet size. To minimize +delays in screen updates, one does not want excessively large packets +for interactive sessions. The maximum packet size is negotiated +separately for each channel. + +3.6. Localization and Character Set Support + +For the most part, the SSH protocols do not directly pass text that +would be displayed to the user. However, there are some places where +such data might be passed. When applicable, the character set for the +data MUST be explicitly specified. In most places, ISO 10646 with UTF-8 +encoding is used [RFC-2044]. When applicable, a field is also be +provided for a language tag [RFC-1766]. + +One big issue is the character set of the interactive session. There is +no clear solution, as different applications may display data in +different formats. Different types of terminal emulation may also be +employed in the client, and the character set to be used is effectively +determined by the terminal emulation. Thus, no place is provided for +specifying the character set or encoding for terminal session data +directly. However, the terminal emulation type (e.g. "vt100") is +transmitted to the remote site, and it implicitly specifies the +character set and encoding. Applications typically use the terminal +type to determine what character set they use, or the character set is +determined using some external means. The terminal emulation may also +allow configuring the default character set. In any case, character set +for the terminal session is considered primarily a client local issue. + +Internal names used to identify algorithms or protocols are normally +never displayed to users, and must be in US-ASCII. + +The client and server user names are inherently constrained by what the +server is prepared to accept. They might, however, occasionally be +displayed in logs, reports, etc. They MUST be encoded using ISO 10646 +UTF-8, but other encodings may be required in some cases. It is up to +the server to decide how to map user names to accepted user names. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 6] + +INTERNET-DRAFT 7 November 1997 + +Straight bit-wise binary comparison is RECOMMENDED. + +For localization purposes, the protocol attempts to minimize the number +of textual messages transmitted. When present, such messages typically +relate to errors, debugging information, or some externally configured +data. For data that is normally displayed, it SHOULD be possible to +fetch a localized message instead of the transmitted by using a numeric +code. The remaining messages SHOULD be configurable. + +4. Data Type Representations Used in the SSH Protocols + + byte + A byte represents an arbitrary 8-bit value (octet) [RFC1700]. + Fixed length data is sometimes represented as an array of bytes, + written byte[n], where n is the number of bytes in the array. + + boolean + A boolean value is stored as a single byte. The value 0 + represents false, and the value 1 represents true. All non-zero + values MUST be interpreted as true; however, applications MUST not + store values other than 0 and 1. + + uint32 + Represents a 32-bit unsigned integer. Stored as four bytes in the + order of decreasing significance (network byte order). + + For example, the value 699921578 (0x29b7f4aa) is stored as 29 b7 + f4 aa. + + string + Arbitrary length binary string. Strings are allowed to contain + arbitrary binary data, including null characters and 8-bit + characters. They are stored as a uint32 containing its length + (number of bytes that follow) and zero (= empty string) or more + bytes that are the value of the string. Terminating null + characters are not used. + + Strings are also used to store text. In that case, US-ASCII is + used for internal names, and ISO-10646 UTF-8 for text that might + be displayed to the user. Terminating null character SHOULD + normally not be stored in the string. + + For example, the US-ASCII string "testing" is represented as 00 00 + 00 07 t e s t i n g. The UTF8 mapping does not alter the encoding + of US-ASCII characters. + + mpint + Represents multiple precision integers in two's complement format, + stored as a string, 8 bits per byte, MSB first. Negative numbers + have one in the most significant bit of the first byte of the data + partition of. If the most significant bit would be set for a + positive number, the number MUST be preceded by a zero byte. + Unnecessary leading zero or 255 bytes MUST NOT be included. The + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 7] + +INTERNET-DRAFT 7 November 1997 + + value zero MUST be stored as a string with zero bytes of data. + + By convention, a number that is used in modular computations in + Z_n SHOULD be represented in the range 0 <= x < n. + + Examples: + + value (hex) representation (hex) + --------------------------------------------------------------- + 0 00 00 00 00 + 9a378f9b2e332a7 00 00 00 08 09 a3 78 f9 b2 e3 32 a7 + 80 00 00 00 02 00 80 + -1234 00 00 00 02 ed cc + -deadbeef 00 00 00 05 ff 21 52 41 11 + +4.1. Encoding of Network Addresses + +Network addresses are encoded as strings. DNS names MUST NOT be used, as +DNS is an insecure protocol. + +If an address contains a colon (':', ascii 58), it is interpreted as an +IPv6 address. The encoding of IPv6 addresses is described in RFC-1884. +IPv4 addresses are expressed in the standard dot-separated decimal +format (e.g. 127.0.0.1). + +5. Algorithm Naming + +The SSH protocols refer to particular hash, encryption, integrity, +compression, and key exchange algorithms or protocols by names. There +are some standard algorithms that all implementations MUST support. +There are also algorithms that are defined in the protocol specification +but are OPTIONAL. Furthermore, it is expected that some organizations +will want to use their own algorithms. + +In this protocol, all algorithm identifiers MUST be printable US-ASCII +strings no longer than 64 characters. Names MUST be case-sensitive. + +There are two formats for algorithm names: + +o Names that do not contain an at-sign (@) are reserved to be assigned + by IANA (Internet Assigned Numbers Authority). Examples include + `3des-cbc', `sha-1', `hmac-sha1', and `zlib' (the quotes are not part + of the name). Additional names of this format may be registered with + IANA; see Section ``IANA Considerations''. Names of this format MUST + NOT be used without first registering with IANA. Registered names + MUST NOT contain an at-sign (@) or a comma (,). + +o Anyone can define additional algorithms by using names in the format + name@domainname, e.g. "ourcipher-cbc@ssh.fi". The format of the part + preceding the at sign is not specified; it MUST consist of US-ASCII + characters except at-sign and comma. The part following the at-sign + MUST be a valid fully qualified internet domain name [RFC-1034] + controlled by the person or organization defining the name. It is up + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 8] + +INTERNET-DRAFT 7 November 1997 + + to each domain how it manages its local namespace. + +6. Message Numbers + +SSH packets have message numbers in the range 1-255. These numbers have +been allocated as follows: + + Transport layer protocol: + + 1-19 Transport layer generic (e.g. disconnect, ignore, debug, + etc) + 20-29 Algorithm negotiation + 30-49 Key exchange method specific (numbers can be reused for + different authentication methods) + + User authentication protocol: + + 50-59 User authentication generic + 60-79 User authentication method specific (numbers can be reused + for different authentication methods) + + Connection protocol: + + 80-89 Connection protocol generic + 90-127 Channel related messages + + Reserved for client protocols: + + 128-191 Reserved + + Local extensions: + + 192-255 Local extensions + +7. IANA Considerations + +Allocation of the following types of names in the SSH protocols is +assigned to IANA: + +o encryption algorithm names, + +o MAC algorithm names, + +o public key algorithm names (public key algorithm also implies + encoding and signature/encryption capability), + +o key exchange method names, and + +o protocol (service) names. + +The IANA-allocated names MUST be printable US-ASCII strings, and MUST +NOT contain the characters at-sign ('@'), comma (','), or whitespace or +control characters (ascii codes 32 or less). Names are case-sensitive, + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 9] + +INTERNET-DRAFT 7 November 1997 + +and MUST not be longer than 64 characters. + +Each category of names listed above has a separate namespace. However, +using the same name in multiple categories SHOULD be avoided to minimize +confusion. +8. Security Considerations + +Special care should be taken to ensure that all of the random numbers +are of good quality. The random numbers SHOULD be produced with safe +mechanisms discussed in [RFC1750]. + +When displaying text, such as error or debug messages to the user, the +client software SHOULD replace any control characters (except tab, +carriage return and newline) with safe sequences to avoid attacks by +sending terminal control characters. + +Not using MAC or encryption SHOULD be avoided. The user authentication +protocol is subject to man-in-the-middle attacks if the encryption is +disabled. The SSH protocol does not protect against message alteration +if no MAC is used. + +9. References + +[FIPS-186] Federal Information Processing Standards Publication (FIPS +PUB) 186, Digital Signature Standard, 18 May 1994. + +[RFC-854] Postel, J. and Reynolds, J., "Telnet Protocol Specification", +May 1983. + +[RFC-894] Hornig, C., "A Standard for the Transmission of IP Datagrams +over Ethernet Networks", April 1984. + +[RFC-1034] Mockapetris, P., "Domain Names - Concepts and Facilities", +November 1987. + +[RFC-1134] Perkins, D., "The Point-to-Point Protocol: A Proposal for +Multi-Protocol Transmission o Datagrams Over Point-to-Point Links", +November 1989. + +[RFC-1282] Kantor, B., "BSD Rlogin", December 1991. + +[RFC-1700] Reynolds, J. and Postel, J., "Assigned Numbers", October 1994 +(also STD 2). + +[RFC-1750] Eastlake, D., Crocker, S., and Schiller, J., "Randomness +Recommendations for Security", December 1994. + +[RFC-1766] Alvestrand, H., "Tags for the Identification of Languages", +March 1995. + +[RFC-2044] Yergeau, F., "UTF-8, a Transformation Format of Unicode and +ISO 10646", October 1996. + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 10] + +INTERNET-DRAFT 7 November 1997 + +[RFC-2119] Bradner, S., "Key words for use in RFCs to indicate +Requirement Levels", March 1997 + +[Schneier] Schneier, B., "Applied Cryptography Second Edition", John +Wiley & Sons, New York, NY, 1995. + +[SSH-TRANS] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH Transport +Layer Protocol", Internet Draft, draft-ietf-secsh-transport-02.txt + +[SSH-USERAUTH] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH +Authentication Protocol", Internet Draft, draft-ietf-secsh- +userauth-02.txt + +[SSH-CONNECT] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH Connection +Protocol", Internet Draft, draft-ietf-secsh-connect-02.txt + +10. Authors' Addresses + + Tatu Ylonen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: ylo@ssh.fi + + Tero Kivinen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: kivinen@ssh.fi + + Markku-Juhani O. Saarinen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: mjos@ssh.fi + + + + + + + + + + + + + + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 11] diff --git a/info/connect.txt b/info/connect.txt new file mode 100644 index 0000000..2cf1aea --- /dev/null +++ b/info/connect.txt @@ -0,0 +1,1001 @@ +Network Working Group T. Ylonen +INTERNET-DRAFT T. Kivinen +draft-ietf-secsh-connect-03.txt M. Saarinen +Expires in six months SSH + 7 November 1997 + + SSH Connection Protocol + +Status of This memo + +This document is an Internet-Draft. Internet-Drafts are working +documents of the Internet Engineering Task Force (IETF), its areas, +and its working groups. Note that other groups may also distribute +working documents as Internet-Drafts. + +Internet-Drafts are draft documents valid for a maximum of six +months and may be updated, replaced, or obsoleted by other documents +at any time. It is inappropriate to use Internet-Drafts as reference +material or to cite them other than as ``work in progress.'' + +To learn the current status of any Internet-Draft, please check +the ``1id-abstracts.txt'' listing contained in the Internet-Drafts +Shadow Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe), +munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), +or ftp.isi.edu (US West Coast). + +Abstract + +SSH is a protocol for secure remote login and other secure network +services over an insecure network. + +This document describes the SSH connection protocol. It provides +interactive login sessions, remote execution of commands, forwarded +TCP/IP connections, and forwarded X11 connections. All of these +channels are multiplexed into a single encrypted tunnel. + +The SSH Connection Protocol has been designed to run on top of +the SSH transport layer and user authentication protocols. + + + + + + + + + + + + + + + + + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 1] + +INTERNET-DRAFT 7 November 1997 + +Table of Contents + +1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . 2 +2. Global Requests . . . . . . . . . . . . . . . . . . . . . . . . 2 +3. Channel Mechanism . . . . . . . . . . . . . . . . . . . . . . . 3 + 3.1. Opening a Channel . . . . . . . . . . . . . . . . . . . . . 3 + 3.2. Data Transfer . . . . . . . . . . . . . . . . . . . . . . . 4 + 3.3. Closing a Channel . . . . . . . . . . . . . . . . . . . . . 5 + 3.4. Channel-Specific Requests . . . . . . . . . . . . . . . . . 6 +4. Interactive Sessions . . . . . . . . . . . . . . . . . . . . . . 6 + 4.1. Opening a Session . . . . . . . . . . . . . . . . . . . . . 6 + 4.2. Requesting a Pseudo-Terminal . . . . . . . . . . . . . . . . 7 + 4.3. X11 Forwarding . . . . . . . . . . . . . . . . . . . . . . . 7 + 4.3.1. Requesting X11 Forwarding . . . . . . . . . . . . . . . 7 + 4.3.2. X11 Channels . . . . . . . . . . . . . . . . . . . . . . 8 + 4.4. Authentication Agent Forwarding . . . . . . . . . . . . . . 8 + 4.4.1. Requesting Authentication Agent Forwarding . . . . . . . 8 + 4.4.2. Authentication Agent Channels . . . . . . . . . . . . . 8 + 4.5. Environment Variable Passing . . . . . . . . . . . . . . . . 9 + 4.6. Starting a Shell or a Command . . . . . . . . . . . . . . . 9 + 4.7. Session Data Transfer . . . . . . . . . . . . . . . . . . . 10 + 4.8. Window Dimension Change Message . . . . . . . . . . . . . . 10 + 4.9. Local Flow Control . . . . . . . . . . . . . . . . . . . . . 10 + 4.10. Signals . . . . . . . . . . . . . . . . . . . . . . . . . . 11 + 4.11. Returning Exit Status . . . . . . . . . . . . . . . . . . . 11 +5. TCP/IP Port Forwarding . . . . . . . . . . . . . . . . . . . . . 11 + 5.1. Requesting Port Forwarding . . . . . . . . . . . . . . . . . 12 + 5.2. TCP/IP Forwarding Channels . . . . . . . . . . . . . . . . . 12 +6. Encoding of Terminal Modes . . . . . . . . . . . . . . . . . . . 13 +7. Summary of Message Numbers . . . . . . . . . . . . . . . . . . . 15 +8. Security Considerations . . . . . . . . . . . . . . . . . . . . 15 +9. References . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 +10. Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . 16 + + + +1. Introduction + +The SSH Connection Protocol has been designed to run on top of the SSH +transport layer and user authentication protocols. It provides +interactive login sessions, remote execution of commands, forwarded +TCP/IP connections, and forwarded X11 connections. The service name for +this protocol (after user authentication) is "ssh-connection". + +This document should be read only after reading the SSH architecture +document [SSH-ARCH]. This document freely uses terminology and notation +from the architecture document without reference or further explanation. + +2. Global Requests + +There are several kinds of requests that affect the state of the remote +end "globally", independent of any channels. An example is a request to +start TCP/IP forwarding for a specific port. All such requests use the + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 2] + +INTERNET-DRAFT 7 November 1997 + +following format. + + byte SSH_MSG_GLOBAL_REQUEST + string request name (restricted to US-ASCII) + boolean want reply + ... request-specific data follows + +The recipient will respond to this message with SSH_MSG_REQUEST_SUCCESS, +SSH_MSG_REQUEST_FAILURE, or some request-specific continuation messages +if `want reply' is TRUE. + + byte SSH_MSG_REQUEST_SUCCESS + +If the recipient does not recognize or support the request, it simply +responds with SSH_MSG_REQUEST_FAILURE. + + byte SSH_MSG_REQUEST_FAILURE + +3. Channel Mechanism + +All terminal sessions, forwarded connections, etc. are channels. Either +side may open a channel. Multiple channels are multiplexed into a +single connection. + +Channels are identified by numbers at each end. The number referring to +a channel may be different on each side. Requests to open a channel +contain the sender's channel number. Any other channel-related messages +contain the recipient's channel number for the channel. + +Channels are flow-controlled. No data may be sent to a channel until a +message is received to indicate that window space is available. + +3.1. Opening a Channel + +When either side wishes to open a new channel, it allocates a local +number for the channel. It then sends the following message to the +other side, and includes the local channel number and initial window +size in the message. + + byte SSH_MSG_CHANNEL_OPEN + string channel type (restricted to US-ASCII) + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + ... channel type specific data follows + +The channel type is a name as described in the SSH architecture +document, with similar extension mechanisms. `sender channel' is a local +identifier for the channel used by the sender of this message. `initial +window size' specifies how many bytes of channel data can be sent to the +sender of this message without adjusting the window. `Maximum packet +size' specifies the maximum size of an individual data packet that can +be sent to the sender (for example, one might want to use smaller + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 3] + +INTERNET-DRAFT 7 November 1997 + +packets for interactive connections to get better interactive response +on slow links). + +The remote side then decides whether it can open the channel, and +responds with either + byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION + uint32 recipient channel + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + ... channel type specific data follows + +where `recipient channel' is the channel number given in the original +open request, and `sender channel' is the channel number allocated by +the other side, or + + byte SSH_MSG_CHANNEL_OPEN_FAILURE + uint32 recipient channel + uint32 reason code + string additional textual information (ISO-10646 UTF-8 + [[RFC-2044]]) + string language tag (as defined in [[RFC-1766]]) + +If the recipient of the SSH_MSG_CHANNEL_OPEN message does not support +the specified channel type, it simply responds with +SSH_MSG_CHANNEL_OPEN_FAILURE. The client MAY show the additional +information to the user. If this is done, the client software should +take the precautions discussed in [SSH-ARCH]. + +The following reason codes are defined: + +#define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED 1 +#define SSH_OPEN_CONNECT_FAILED 2 +#define SSH_OPEN_UNKNOWN_CHANNEL_TYPE 3 +#define SSH_OPEN_RESOURCE_SHORTAGE 4 + +3.2. Data Transfer + +The window size specifies how many bytes the other party can send before +it must wait for the window to be adjusted. Both parties use the +following message to adjust the window. + + byte SSH_MSG_CHANNEL_WINDOW_ADJUST + uint32 recipient channel + uint32 bytes to add + +After receiving this message, the recipient MAY send the given number of +bytes more that it was previously allowed to send; the window size is +incremented. + +Data transfer is done with messages of the following type. + + byte SSH_MSG_CHANNEL_DATA + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 4] + +INTERNET-DRAFT 7 November 1997 + + uint32 recipient channel + string data + +The maximum amount of data allowed is the current window size. The +window size is decremented by the amount of data sent. Both parties MAY +ignore all extra data sent after the allowed window is empty. + +Additionally, some channels can transfer several types of data. An +example of this is stderr data from interactive sessions. Such data can +be passed with SSH_MSG_CHANNEL_EXTENDED_DATA messages, where a separate +integer specifies the type of the data. The available types and their +interpretation depend on the type of the channel. + + byte SSH_MSG_CHANNEL_EXTENDED_DATA + uint32 recipient_channel + uint32 data_type_code + string data + +Data sent with these messages consumes the same window as ordinary data. + +Currently, only the following type is defined. + +#define SSH_EXTENDED_DATA_STDERR 1 + +3.3. Closing a Channel + +When a party will no longer send more data to a channel, it SHOULD send +SSH_MSG_CHANNEL_EOF. + + byte SSH_MSG_CHANNEL_EOF + uint32 recipient_channel + +No explicit response is sent to this message; however, the application +may send EOF to whatever is at the other end of the channel. Note that +the channel remains open after this message, and more data may still be +sent in the other direction. This message does not consume window space +and can be sent even if no window space is available. + +When either party wishes to terminate the channel, it sends +SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST send +back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this message for +the channel. The channel is considered closed for a party when it has +both sent and received SSH_MSG_CHANNEL_CLOSE, and the party may then +reuse the channel number. A party MAY send SSH_MSG_CHANNEL_CLOSE +without having sent or received SSH_MSG_CHANNEL_EOF. + byte SSH_MSG_CHANNEL_CLOSE + uint32 recipient_channel + +This message does not consume window space and can be sent even if no +window space is available. + +It is recommended that any data sent before this message is delivered to +the actual destination, if possible. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 5] + +INTERNET-DRAFT 7 November 1997 + +3.4. Channel-Specific Requests + +Many channel types have extensions that are specific to that particular +channel type. An example is requesting a pty (pseudo terminal) for an +interactive session. + +All channel-specific requests use the following format. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string request type (restricted to US-ASCII) + boolean want reply + ... type-specific data + +If want reply is FALSE, no response will be sent to the request. +Otherwise, the recipient responds with either SSH_MSG_CHANNEL_SUCCESS or +SSH_MSG_CHANNEL_FAILURE, or request-specific continuation messages. If +the request is not recognized or is not supported for the channel, +SSH_MSG_CHANNEL_FAILURE is returned. + +This message does not consume window space and can be sent even if no +window space is available. Request types are local to each channel type. + +The client is allowed to send further messages without waiting for the +response to the request. + + byte SSH_MSG_CHANNEL_SUCCESS + uint32 recipient_channel + + byte SSH_MSG_CHANNEL_FAILURE + uint32 recipient_channel + +These messages do not consume window space and can be sent even if no +window space is available. +4. Interactive Sessions + +A session is a remote execution of a program. The program may be a +shell, an application, a system command, or some built-in subsystem. It +may or may not have a tty, and may or may not involve X11 forwarding. +Multiple sessions can be active simultaneously. + +4.1. Opening a Session + +A session is started by sending the following message. + + byte SSH_MSG_CHANNEL_OPEN + string "session" + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + +Client implementations SHOULD reject any session channel open requests +to make it more difficult for a corrupt server to attack the client. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 6] + +INTERNET-DRAFT 7 November 1997 + +4.2. Requesting a Pseudo-Terminal + +A pseudo-terminal can be allocated for the session by sending the +following message. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient_channel + string "pty-req" + boolean want_reply + string TERM environment variable value (e.g., vt100) + uint32 terminal width, characters (e.g., 80) + uint32 terminal height, rows (e.g., 24) + uint32 terminal width, pixels (e.g., 480) + uint32 terminal height, pixels (e.g., 640) + string encoded terminal modes + +The encoding of terminal modes is described in Section ``Encoding of +Terminal Modes''. Zero dimension parameters MUST be ignored. The +character/row dimensions override the pixel dimensions (when nonzero). +Pixel dimensions refer to the drawable area of the window. + +The dimension parameters are only informational. + +The client SHOULD ignore pty requests. + +4.3. X11 Forwarding + +4.3.1. Requesting X11 Forwarding + +X11 forwarding may be requested for a session by sending + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "x11-req" + boolean want reply + boolean single connection + string x11 authentication protocol + string x11 authentication cookie + uint32 x11 screen number + +It is recommended that the authentication cookie that is sent be a fake, +random cookie, and that the cookie is checked and replaced by the real +cookie when a connection request is received. + +X11 connection forwarding should stop when the session channel is +closed; however, already opened forwardings should not be automatically +closed when the session channel is closed. + +If `single connection' is TRUE, only a single connection should be +forwarded. No more connections will be forwarded after the first, or +after the session channel has been closed. + +`X11 authentication protocol is the name of the X11 authentication + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 7] + +INTERNET-DRAFT 7 November 1997 + +method used, i.e. "MIT-MAGIC-COOKIE-1". + +4.3.2. X11 Channels + +X11 channels are opened with a channel open request. The resulting +channels are independent of the session, and closing the session channel +does not close the forwarded X11 channels. + + byte SSH_MSG_CHANNEL_OPEN + string "x11" + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + string originator address (e.g. "192.168.7.38") + uint32 originator port + +The recipient should respond with SSH_MSG_CHANNEL_OPEN_CONFIRMATION or +SSH_MSG_CHANNEL_OPEN_FAILURE. + +Implementations MUST reject any X11 channel open requests if they have +not requested X11 forwarding. + +4.4. Authentication Agent Forwarding + +It is RECOMMENDED that authentication agent forwarding is allowed even +when either or both parties do not support the SSH authentication agent +protocol [SSH-AGENT]. + +4.4.1. Requesting Authentication Agent Forwarding + +Authentication agent forwarding may be requested for a session by +sending + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "auth-agent-req" + boolean want reply + +The server responds with either SSH_MSG_CHANNEL_SUCCESS or +SSH_MSG_CHANNEL_FAILURE (if `want reply' is TRUE). The client MAY send +further messages without waiting for the response to this message. + +4.4.2. Authentication Agent Channels + +When an application requests a connection to the authentication agent, +the following message is sent to the originator of the session. + + byte SSH_MSG_CHANNEL_OPEN + string "auth-agent" + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 8] + +INTERNET-DRAFT 7 November 1997 + +The recipient should respond with open confirmation or open failure. + +Implementations MUST reject any agent channel open requests if they have +not requested agent forwarding. + +4.5. Environment Variable Passing + +Environment variables may be passed to the shell/command to be started +later. Typically, each machine will have a preconfigured set of +variables that it will allow. Since uncontrolled setting of environment +variables can be very dangerous, it is recommended that implementations +allow setting only variables whose names have been explicitly configured +to be allowed. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "env" + boolean want reply + string variable name + string variable value + +4.6. Starting a Shell or a Command + +Once the session has been set up, a program is started at the remote +end. Program can be a shell, an application program or a subsystem with +a host-independent name. Only one of these requests can succeed per +channel. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "shell" + boolean want reply + +This message will request the user's default shell (typically defined in +/etc/passwd in UNIX systems) to be started at the other end. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "exec" + boolean want reply + string command + +This message will request the server to start the execution of the given +command. The command string may contain a path. Normal precautions MUST +be taken to prevent the execution of unauthorized commands. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "subsystem" + boolean want reply + string subsystem name + +This last form executes a predefined subsystem. It expected that these + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 9] + +INTERNET-DRAFT 7 November 1997 + +will include a general file transfer mechanism, and possibly other +features. Implementations may also allow configuring more such +mechanisms. + +The server SHOULD not halt the execution of the protocol stack when +starting a shell or a program. All input and output from these SHOULD be +redirected the the channel or to the encrypted tunnel. + +It is RECOMMENDED to request and check the reply for these messages. The +client SHOULD ignore these messages. + +4.7. Session Data Transfer + +Data transfer for a session is done using SSH_MSG_CHANNEL_DATA and +SSH_MSG_CHANNEL_EXTENDED_DATA packets and the window mechanism. The +extended data type SSH_EXTENDED_DATA_STDERR has been defined for stderr +data. + +4.8. Window Dimension Change Message + +When the window (terminal) size changes on the client side, it MAY send +a message to the other side to inform it of the new dimensions. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient_channel + string "window-change" + boolean FALSE + uint32 terminal width, columns + uint32 terminal height, rows + uint32 terminal width, pixels + uint32 terminal height, pixels + +No response SHOULD be sent to this message. + +4.9. Local Flow Control + +On many systems it is possible to determine if a pseudo-terminal is +using control-S control-Q flow control. When flow control is allowed, +it is often desirable to do the flow control at the client end to speed +up responses to user requests. This is facilitated by the following +notification. Initially, the server is responsible for flow control. +(Here, again, client means the side originating the session, and server +the other side.) + +The message below is used by the server to inform the client when it can +or cannot perform flow control (control-S/control-Q processing). If +`client can do' is TRUE, the client is allowed to do flow control using +control-S and control-Q. The client MAY ignore this message. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "xon-xoff" + boolean FALSE + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 10] + +INTERNET-DRAFT 7 November 1997 + + boolean client can do + +No response is sent to this message. + +4.10. Signals + +A signal can be delivered to the remote process/service using the +following message. Some systems may not implement signals, in which +case they SHOULD ignore this message. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "signal" + boolean FALSE + uint32 signal number + +4.11. Returning Exit Status + +When the command running at the other end terminates, The following +message can be sent to return the exit status of the command. Returning +the status is RECOMMENDED. No acknowledgment is sent for this message. +The channel needs to be closed with SSH_MSG_CHANNEL_CLOSE after this +message. + +The client SHOULD ignore these messages. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient_channel + string "exit-status" + boolean FALSE + uint32 exit_status + +The remote command may also terminate violently due to a signal. Such a +condition can be indicated by the following message. A zero exit_status +usually means that the command terminated successfully. + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "exit-signal" + boolean FALSE + uint32 signal number + boolean core dumped + string error message (ISO-10646 UTF-8 [[RFC-2044]]) + string language tag (as defined in [[RFC-1766]]) + +The `error message' contains an additional explanation of the error +message. The message may consist of multiple lines. The client software +MAY display this message to the user. + + + + + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 11] + +INTERNET-DRAFT 7 November 1997 + +5. TCP/IP Port Forwarding + +5.1. Requesting Port Forwarding + +A party need not explicitly request forwardings from its own end to the +other direction. However, it if wishes to have connections to a port on +the other side be forwarded to the local side, it must explicitly +request this. + + byte SSH_MSG_GLOBAL_REQUEST + string "tcpip-forward" + boolean want reply + string address to bind (e.g. "0.0.0.0") + uint32 port number to bind + +`Address to bind' and `port number to bind' specify the IP address and +port to which the socket to be listened is bound. The address should be +"0.0.0.0" if connections are allowed from anywhere. (Note that the +client can still filter connections based on information passed in the +open request.) + +Implementations should only allow forwarding privileged ports if the +user has been authenticated as a privileged user. + +Client implementations SHOULD reject these messages; they are normally +only sent by the client. + +A port forwarding can be cancelled with the following message. Note +that channel open requests may be received until a reply to this message +is received. + + byte SSH_MSG_GLOBAL_REQUEST + string "cancel-tcpip-forward" + boolean want reply + string address_to_bind (e.g. "127.0.0.1") + uint32 port number to bind + +Client implementations SHOULD reject these messages; they are normally +only sent by the client. + +5.2. TCP/IP Forwarding Channels + +When a connection comes to a port for which remote forwarding has been +requested, a channel is opened to forward the port to the other side. + + byte SSH_MSG_CHANNEL_OPEN + string "forwarded-tcpip" + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + string address that was connected + uint32 port that was connected + string originator IP address + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 12] + +INTERNET-DRAFT 7 November 1997 + + uint32 originator port + +Implementations MUST reject these messages unless they have previously +requested a remote TCP/IP port forwarding with the given port number. + +When a connection comes to a locally forwarded TCP/IP port, the +following packet is sent to the other side. Note that these messages +MAY be sent also for ports for which no forwarding has been explicitly +requested. The receiving side must decide whether to allow the +forwarding. + + byte SSH_MSG_CHANNEL_OPEN + string "direct-tcpip" + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + string host to connect + uint32 port to connect + string originator IP address + uint32 originator port + +`Host to connect' and `port to connect' specify the TCP/IP host and port +where the recipient should connect the channel. `Host to connect' may +be either a domain name or a numeric IP address. + +`Originator IP address' is the numeric IP address of the machine where +the connection request comes from, and `originator port' is the port on +the originator host from where the connection came from. + +Forwarded TCP/IP channels are independent of any sessions, and closing a +session channel does not in any way imply that forwarded connections +should be closed. + +Client implementations SHOULD reject direct TCP/IP open requests for +security reasons. + +6. Encoding of Terminal Modes + +Terminal modes (as passed in a pty request) are encoded into a byte +stream. It is intended that the coding be portable across different +environments. + +The tty mode description is a stream of bytes. The stream consists of +opcode-argument pairs. It is terminated by opcode TTY_OP_END (0). +Opcodes 1-159 have a single uint32 argument. Opcodes 160-255 are not yet +defined, and cause parsing to stop (they should only be used after any +other data). + +The client SHOULD put in the stream any modes it knows about, and the +server MAY ignore any modes it does not know about. This allows some +degree of machine-independence, at least between systems that use a +POSIX-like tty interface. The protocol can support other systems as +well, but the client may need to fill reasonable values for a number of + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 13] + +INTERNET-DRAFT 7 November 1997 + +parameters so the server pty gets set to a reasonable mode (the server +leaves all unspecified mode bits in their default values, and only some +combinations make sense). + +The following opcodes have been defined. The naming of opcodes mostly +follows the POSIX terminal mode flags. + +0 TTY_OP_END Indicates end of options. +1 VINTR Interrupt character; 255 if none. Similarly for the + other characters. Not all of these characters are + supported on all systems. +2 VQUIT The quit character (sends SIGQUIT signal on POSIX + systems). +3 VERASE Erase the character to left of the cursor. +4 VKILL Kill the current input line. +5 VEOF End-of-file character (sends EOF from the terminal). +6 VEOL End-of-line character in addition to carriage return + and/or linefeed. +7 VEOL2 Additional end-of-line character. +8 VSTART Continues paused output (normally control-Q). +9 VSTOP Pauses output (normally control-S). +10 VSUSP Suspends the current program. +11 VDSUSP Another suspend character. +12 VREPRINT Reprints the current input line. +13 VWERASE Erases a word left of cursor. +14 VLNEXT Enter the next character typed literally, even if it + is a special character +15 VFLUSH Character to flush output. +16 VSWTCH Switch to a different shell layer. +17 VSTATUS Prints system status line (load, command, pid etc). +18 VDISCARD Toggles the flushing of terminal output. +30 IGNPAR The ignore parity flag. The parameter SHOULD be 0 if + this flag is FALSE set, and 1 if it is TRUE. +31 PARMRK Mark parity and framing errors. +32 INPCK Enable checking of parity errors. +33 ISTRIP Strip 8th bit off characters. +34 INLCR Map NL into CR on input. +35 IGNCR Ignore CR on input. +36 ICRNL Map CR to NL on input. +37 IUCLC Translate uppercase characters to lowercase. +38 IXON Enable output flow control. +39 IXANY Any char will restart after stop. +40 IXOFF Enable input flow control. +41 IMAXBEL Ring bell on input queue full. +50 ISIG Enable signals INTR, QUIT, [[D]]SUSP. +51 ICANON Canonicalize input lines. +52 XCASE Enable input and output of uppercase characters by + preceding their lowercase equivalents with `\'. +53 ECHO Enable echoing. +54 ECHOE Visually erase chars. +55 ECHOK Kill character discards current line. +56 ECHONL Echo NL even if ECHO is off. +57 NOFLSH Don't flush after interrupt. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 14] + +INTERNET-DRAFT 7 November 1997 + +58 TOSTOP Stop background jobs from output. +59 IEXTEN Enable extensions. +60 ECHOCTL Echo control characters as ^(Char). +61 ECHOKE Visual erase for line kill. +62 PENDIN Retype pending input. +70 OPOST Enable output processing. +71 OLCUC Convert lowercase to uppercase. +72 ONLCR Map NL to CR-NL. +73 OCRNL Translate carriage return to newline (output). +74 ONOCR Translate newline to carriage return-newline + (output). +75 ONLRET Newline performs a carriage return (output). +90 CS7 7 bit mode. +91 CS8 8 bit mode. +92 PARENB Parity enable. +93 PARODD Odd parity, else even. + +128 TTY_OP_ISPEED Specifies the input baud rate in bits per second. +129 TTY_OP_OSPEED Specifies the output baud rate in bits per second. + +7. Summary of Message Numbers + + #define SSH_MSG_GLOBAL_REQUEST 80 + #define SSH_MSG_REQUEST_SUCCESS 81 + #define SSH_MSG_REQUEST_FAILURE 82 + #define SSH_MSG_CHANNEL_OPEN 90 + #define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 91 + #define SSH_MSG_CHANNEL_OPEN_FAILURE 92 + #define SSH_MSG_CHANNEL_WINDOW_ADJUST 93 + #define SSH_MSG_CHANNEL_DATA 94 + #define SSH_MSG_CHANNEL_EXTENDED_DATA 95 + #define SSH_MSG_CHANNEL_EOF 96 + #define SSH_MSG_CHANNEL_CLOSE 97 + #define SSH_MSG_CHANNEL_REQUEST 98 + #define SSH_MSG_CHANNEL_SUCCESS 99 + #define SSH_MSG_CHANNEL_FAILURE 100 + +8. Security Considerations + +This protocol is assumed to run on top of a secure, authenticated +transport. User authentication and protection against network-level +attacks are assumed to be provided by the underlying protocols. + +This protocol can, however, be used to execute commands on remote +machines. The protocol also permits the server to run commands on the +client. Implementations may wish to disallow this to prevent an +attacker from coming from the server machine to the client machine. + +X11 forwarding provides major security improvements over normal cookie- +based X11 forwarding. The cookie never needs to be transmitted in the +clear, and traffic is encrypted and integrity-protected. No useful +authentication data will remain on the server machine after the +connection has been closed. On the other hand, in some situations a + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 15] + +INTERNET-DRAFT 7 November 1997 + +forwarded X11 connection might be used to get access to the local X +server across security perimeters. + +Port forwardings can potentially allow an intruder to cross security +perimeters such as firewalls. They do not offer anything fundamentally +new that a user couldn't do otherwise; however, they make opening +tunnels very easy. Implementations should allow policy control over +what can be forwarded. Administrators should be able to deny +forwardings where appropriate. + +Since this protocol normally runs inside an encrypted tunnel, firewalls +will not be able to examine the traffic. + +It is RECOMMENDED that implementations disable all of the potentially +dangerous features (e.g. agent forwarding, X11 forwarding, and TCP/IP +forwarding) if the host key has changed. + +9. References + +[RFC-1766] Alvestrand, H., "Tags for the Identification of Languages", +March 1995. + +[RFC-1884] Hinden, R., and Deering, S., "IP Version 6 Addressing +Architecture", December 1995 + +[RFC-2044] Yergeau, F., "UTF-8, a Transformation Format of Unicode and +ISO 10646", October 1996. + +[SSH-ARCH] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH Protocol +Architecture", Internet Draft, draft-ietf-secsh-architecture-00.txt + +[SSH-TRANS] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH Transport +Layer Protocol", Internet Draft, draft-ietf-secsh-transport-02.txt + +[SSH-USERAUTH] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH +Authentication Protocol", Internet Draft, draft-ietf-secsh- +userauth-02.txt + +10. Authors' Addresses + + Tatu Ylonen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: ylo@ssh.fi + + Tero Kivinen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: kivinen@ssh.fi + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 16] + +INTERNET-DRAFT 7 November 1997 + + Markku-Juhani O. Saarinen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: mjos@ssh.fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 17] diff --git a/info/digisign.py b/info/digisign.py new file mode 100644 index 0000000..8c7c363 --- /dev/null +++ b/info/digisign.py @@ -0,0 +1,340 @@ +#
+# digisign.py : Digital signature (using the q-NEW algorithm)
+#
+# Maintained by A.M. Kuchling (amk@magnet.com)
+# Date: 1997/09/05
+#
+# Distribute and use freely; there are no restrictions on further
+# dissemination and usage except those imposed by the laws of your
+# country of residence.
+#
+
+# TODO :
+# Add more comments and docstrings
+# Write documentation
+# Use mpz or gmp if available
+# Add assertions (later)
+
+import types, string, marshal
+
+# The code will try to use the SHA module, if present; otherwise
+# the MD5 module will be used. HASHBITS is the number of bits
+# in the hashing algorithm.
+
+try:
+ import sha ; hash = sha ; HASHBITS=160
+except ImportError:
+ import md5 ; hash = md5 ; HASHBITS=128
+
+class RandomPool:
+ """Cryptographically strong generation of random strings"""
+ def __init__(self, seed=None):
+ """Initialize the generator, using a provided string as the seed.
+ If the string is omitted, the class will try to do its best to get
+ some random data, and print a warning."""
+ if seed==None: seed=self.getseed()
+ S=range(256)
+ seed=map(ord, seed)
+ x = y = 0 ; i1 = i2 = 0
+ for i in range(256):
+ i2 = (seed[i1] + S[i] + i2) % 256
+ S[i], S[i2] = S[i2], S[i]
+ i1 = (i1 + 1) % len(seed)
+ self.S, self.x, self.y = S,x,y
+
+ def random(self, N):
+ "Return an N-byte string containing random bytes"
+ x,y, S = self.x, self.y, self.S
+ output=[None]*N
+ for i in range(N):
+ x = (x + 1) % 256
+ y = (y + S[x]) % 256
+ S[x], S[y] = S[y], S[x]
+ xorIndex = (S[x] + S[y]) % 256
+ output[i] = chr( S[xorIndex] )
+ self.x, self.y = x,y
+ return string.join(output, '')
+ def getseed(self):
+ """If no seed was provided by the user (bad user!), try to generate
+ a useful seed. This may print a warning."""
+ try:
+ # If possible, use /dev/urandom. On FreeBSD 2.1.5 or later,
+ # and Linux 1.3.X or later, /dev/urandom is maintained by the
+ # OS, and uses random things like disk interrupt timings and
+ # keyboard noise. It must be opened in unbuffered mode
+ # for some reason.
+ f=open('/dev/urandom', 'r', 0)
+ seed=f.read(256)
+ f.close()
+ return seed
+ except IOError:
+ # Presumably /dev/urandom isn't there... Use a lame
+ # time-based scheme that isn't very good, and print a warning
+ # to encourage the programmer to improve things.
+ import time, os, sys
+ print " (Using poor time-based scheme to initialize random-number generator)"
+ seed=str( 'dummy data' )
+ for i in range(0, 16):
+ rand='%s%s%s' % (time.time(), os.getpid(), sys.getrefcount(i) )
+ rand=hash.new( rand ).digest()
+ seed=seed+rand
+ return seed
+
+_randpool=None
+
+def RandomNumber(N, randfunc):
+ "Return an N-bit random number. N must be a multiple of 8."
+ if N % 8 != 0:
+ raise ValueError, "N must be a multiple of 8"
+ str=randfunc(N/8)
+ char=ord(str[0])|128
+ return Str2Int(chr(char)+str[1:])
+
+def Int2Str(n):
+ "Convert a long integer to a string form"
+ s=''
+ while n>0:
+ s=chr(n & 255)+s
+ n=n>>8
+ return s
+
+def Str2Int(s):
+ "Convert a string to a long integer"
+ if type(s)!=types.StringType: return s # Integers will be left alone
+ return reduce(lambda x,y : x*256+ord(y), s, 0L)
+
+sieve=[2,3,5,7,11,13,17,19,23,29,31,37,41]
+def isPrime(N):
+ """Test if a number N is prime, using a simple sieve check,
+ followed by a more elaborate XXX test."""
+ for i in sieve:
+ if (N % i)==0: return 0
+ N1=N - 1L ; n=1L
+ while (n<N): n=n<<1L # Compute number of bits in N
+ for j in sieve:
+ a=long(j) ; d=1L ; t=n
+ while (t): # Iterate over the bits in N1
+ x=(d*d) % N
+ if x==1L and d!=1L and d!=N1: return 0 # Square root of 1 found
+ if N1 & t: d=(x*a) % N
+ else: d=x
+ t=t>>1L
+ if d!=1L: return 0
+ return 1
+
+def getPrime(N, randfunc):
+ "Find a prime number measuring N bits"
+ number=RandomNumber(N, randfunc) | 1 # Ensure the number is odd
+ while (not isPrime(number)):
+ number=number+2
+ return number
+
+
+
+class Key:
+ """Key object (both public and private)
+
+ Methods
+ generate -- Generate a fresh public/private key pair
+ sign -- Sign a message, returning the signature
+ verify -- Verify a signature for the message
+ cansign -- Return TRUE if the key can sign messages
+ publickey -- Return a new key containing only the public data
+
+ Attributes:
+ size -- Size (in bits) of messages that can be processed.
+ """
+ def generate(self, bits=1024, randfunc=None, progress_func=None):
+ """Generate a private key with a modulus of the given size
+ bits -- modulus size of the generated key; the bigger it is,
+ the more secure the key (and the slower key generation
+ and signing is). For security, use a bit size of 768
+ or 1024; 512 or 384 is too small for comfort (though you may
+ use it in testing).
+ randfunc -- A function that generates random data.
+ (Optional, but recommended)
+ progress_func -- (Optional) As a key is generated, this function
+ will be used to output progress messages.
+ """
+ global _randpool
+ if randfunc==None:
+ _randpool=RandomPool()
+ randfunc=_randpool.random
+
+ if bits<384: raise ValueError, 'Key length <384 bits'
+
+ # Generate prime numbers p and q. q is a 160-bit prime
+ # number. p is another prime number (the modulus) whose bit
+ # size is chosen by the caller, and is generated so that p-1
+ # is a multiple of q.
+ #
+ # Note that only a single seed is used to
+ # generate p and q; if someone generates a key for you, you can
+ # use the seed to duplicate the key generation. This can
+ # protect you from someone generating values of p,q that have
+ # some special form that's easy to break.
+ if progress_func: progress_func('p,q\n')
+ while (1):
+ self.q = getPrime(160, randfunc)
+# assert pow(2, 159L)<self.q<pow(2, 160L)
+ self.seed = S = Int2Str(self.q)
+ C, N, V = 0, 2, {}
+ # Compute b and n such that bits-1 = b + n*HASHBITS
+ n= (bits-1) / HASHBITS
+ b= (bits-1) % HASHBITS ; powb=pow(long(2), b)
+ powL1=pow(long(2), bits-1)
+ while C<4096:
+ # The V array will contain (bits-1) bits of random
+ # data, that are assembled to produce a candidate
+ # value for p.
+ for k in range(0, n+1):
+ V[k]=Str2Int(hash.new(S+str(N)+str(k)).digest())
+ p = V[n] % powb
+ for k in range(n-1, -1, -1):
+ p= (p << long(HASHBITS) )+V[k]
+ p = p+powL1 # Ensure the high bit is set
+
+ # Ensure that p-1 is a multiple of q
+ p = p - (p % (2*self.q)-1)
+
+ # If p is still the right size, and it's prime, we're done!
+ if powL1<=p and isPrime(p): break
+
+ # Otherwise, increment the counter and try again
+ C, N = C+1, N+n+1
+ if C<4096: break # Ended early, so exit the while loop
+ if progress_func: progress_func('4096 values of p tried\n')
+
+ self.p = p
+ power=(p-1)/self.q
+
+ # Next parameter: g = h**((p-1)/q) mod p, such that h is any
+ # number <p-1, and g>1. g is kept; h can be discarded.
+ if progress_func: progress_func('h,g\n')
+ while (1):
+ h=Str2Int(randfunc(bits)) % (p-1)
+ g=pow(h, power, p)
+ if 1<h<p-1 and g>1: break
+ self.g=g
+
+ # x is the private key information, and is
+ # just a random number between 0 and q.
+ # y=g**x mod p, and is part of the public information.
+ if progress_func: progress_func('x,y\n')
+ while (1):
+ x=Str2Int(randfunc(20))
+ if 0<x<self.q: break
+ self.x, self.y=x, pow(g, x, p)
+
+ self.size = 160
+
+ def cansign(self):
+ """Return a Boolean denoting whether the object contains
+ private components, and hence can sign messages."""
+ if hasattr(self, 'x'): return 1
+ else: return 0
+ def canencrypt(self):
+ return 0
+
+ def publickey(self):
+ new=Key()
+ new.p = self.p
+ new.q = self.q
+ new.g = self.g
+ new.y = self.y
+ # x isn't copied, of course, since it's the private key.
+# assert not (hasattr(new, 'x') or hasattr(new, 'seed') )
+ return new
+
+ def _sign(self, M, K):
+ if (self.q<=K):
+ raise ValueError, 'K is greater than q'
+ if M<0:
+ raise ValueError, 'Illegal value of M (<0)'
+ if M>=pow(2,161L):
+ raise ValueError, 'Illegal value of M (too large)'
+ r=pow(self.g, K, self.p) % self.q
+ s=(K- (r*M*self.x % self.q)) % self.q
+ return marshal.dumps( (r,s) )
+ def _verify(self, M, sig):
+ r, s = marshal.loads(sig)
+ if r<=0 or r>=self.q or s<=0 or s>=self.q: return 0
+ if M<0:
+ raise ValueError, 'Illegal value of M (<0)'
+ if M>=pow(2,161L):
+ raise ValueError, 'Illegal value of M (too large)'
+ if M<=0 or M>=pow(2,161L): return 0
+ v1=pow(self.g, s, self.p)
+ v2=pow(self.y, M*r, self.p)
+ v=((v1*v2) % self.p)
+ v=v % self.q
+ if v==r: return 1
+ return 0
+
+ def sign(self, M, randfunc=None, K=None):
+ if (not self.cansign()):
+ raise TypeError, 'Not a private key object'
+ if type(M)==types.StringType: M=Str2Int(M)
+
+ if K==None:
+ # Generate a random value of K, that must be <q
+ global _randpool
+ if randfunc==None:
+ if _randpool==None:
+ _randpool=RandomPool()
+ randfunc=_randpool.random
+ K=RandomNumber(160, randfunc) >> 1
+# assert K<self.q
+ else:
+ if type(K)==types.StringType: K=Str2Int(K)
+ return self._sign(M, K)
+ def verify(self, M, signature):
+ if type(M)==types.StringType: M=Str2Int(M)
+ return self._verify(M, signature)
+ validate=verify
+
+new=Key
+
+if __name__=='__main__':
+ import sys, string
+ BITS=512
+ if len(sys.argv)>1:
+ BITS=string.atoi(sys.argv[1])
+ print ' Generating', BITS, 'bit key'
+ key=new()
+ key.generate(BITS, progress_func=sys.stdout.write)
+ print ' Key data: (the private key is x)'
+ for i in 'xygqp': print '\t', i, ':', hex(getattr(key, i))
+ message="This is a contract."
+
+ if key.cansign():
+ print ' Signature test'
+ print "Message:", message
+ signature=key.sign(message)
+ print "Signature:", repr(signature)
+ result=key.verify(message, signature)
+ if not result:
+ print " Sig. verification failed when it should have succeeded"
+ else: print 'Signature verified'
+
+ # Test on a mangled message
+ result=key.verify(message[:-1], signature)
+ if result:
+ print " Sig. verification succeeded when it should have failed"
+
+ # Change a single bit in the message
+ badtext=message[:-3]+chr( 1 ^ ord(message[-3]) )+message[-3:]
+ result=key.verify(badtext, signature)
+ if result:
+ print " Sig. verification succeeded when it should have failed"
+
+ import pickle
+ print 'Removing private key data'
+ pubonly=key.publickey()
+ pickledata=pickle.dumps(pubonly)
+ pubonly=pickle.loads(pickledata)
+ result=pubonly.verify(message, signature)
+ if not result:
+ print " Sig. verification failed when it should have succeeded"
+ else:
+ print 'Signature verified'
diff --git a/info/libdsign.tex b/info/libdsign.tex new file mode 100644 index 0000000..8a31698 --- /dev/null +++ b/info/libdsign.tex @@ -0,0 +1,146 @@ +\section{Standard Module \sectcode{digisign}}
+\label{module-digisign}
+
+This module provides a simple class to create keys for generating and
+verifying \dfn{digital signatures}. These digital signatures act like
+handwritten signatures in the real world; only one key can sign a
+message, but anyone can use the key to verify that the signature
+corresponds to the message. If even a single bit of the message is
+changed, verifying the signature will fail.
+
+The algorithm used is Nyberg and Ruppel's q-NEW. The \code{Key} class
+contains a single q-NEW key. A newly generated key is a \dfn{private
+key}. It includes private information, and can both sign messages, or
+verify a previously generated signature for a given message. It's
+possible to create a copy of the key without the private information,
+using the \code{publickey} method; this public key can only verify
+signatures produced with the private key, and thus doesn't have to be
+kept secret.
+
+For example, you could generate a private key, create the matching
+public key, and send the public key to all your correspondents. Then,
+when you wish to send a message, you can sign it using the private
+key, and send the signature along with the message; your
+correspondents can then verify the signature using the public key,
+assuring them that the message wasn't changed while being transmitted.
+
+
+\stmodindex{digisign}
+\renewcommand{\indexsubitem}{(in module digisign)}
+
+\subsection{The {\tt Key} class}
+
+The \code{Key} class encapsulates a single key. To create a new key
+object, call either \code{digisign.new()} or \code{digisign.Key()},
+which will return an empty \code{Key} instance, and then call the
+\code{generate()} method of the empty instance to create a random
+private key. To obtain a \code{Key} instance containing only the
+public key, call the \code{publickey} method. Both private and public
+\code{Key} instances can be pickled.
+
+The \code{generate} and \code{sign} methods require random data. Both
+those functions have a keyword parameter named \code{randfunc}; this
+parameter must be a function (or a bound method) which will take a
+single integer parameter N and return N bytes of random data.
+
+It is critical that the random number generator be
+\dfn{cryptographically strong}; consult Schneier's book for the
+details. You may wish to use the \code{RandomPool} class described
+below, which is such a generator. Be sure you know what you're doing
+before writing your own generator, since keys produced with a poor
+generator will offer little or no security. Simple linear generators,
+like the one in the
+\code{whrandom} module, are not strong, and should never be used with
+this module.
+
+\begin{funcdesc}{cansign}{}
+Return true if this key contains the private information required to
+generate new signatures. Return false if the key can only be used to
+verify signatures.
+\end{funcdesc}
+
+\begin{funcdesc}{publickey}{}
+Return a new \code{Key} instance containing only the public key
+information. This new instance cannot generate signatures, though it
+can verify them.
+\end{funcdesc}
+
+\begin{funcdesc}{generate}{bits\, randfunc\, progress_func}
+Generate a new private key that can sign messages and verify its own
+signatures. \var{bits} is the size of the key in bits, and has a
+minimum value of 384; as this value increases, the key becomes harder
+to break, and signing and verifying gets slower. A value of 768 or
+1024 is recommended for security; key sizes of 384 or 512 bits may be
+used in testing for the sake of speed, but they're easily breakable in
+1996.
+
+\var{randfunc} is a function that generates random strings, as described above.
+The function \var{progress_func} will be called at various points
+during key generation, and passed a string explaining the algorithm's
+progress. Key generation is a lengthy process, so this can be used
+to keep the user informed while it's going on.
+\end{funcdesc}
+
+\begin{funcdesc}{sign}{M \optional{\, randfunc\, K} }
+Generate and return a string containing a signature for the short
+message string \var{M}. \var{M} is limited in size to 20 bytes or
+less. If you wish to sign a larger piece of data, like a 1K email
+message, the usual procedure is to take the hash of the message and
+use that as \var{M}. This can be done using the \code{md5} module.
+
+The optional parameter \var{K} is a random
+value required to produce a signature; it can be a string of no more
+than 20 bytes in length. Alternatively (and more easily), the
+\var{randfunc} parameter can be provided, and a value of \var{K} will
+be automatically generated.
+
+{\bf Important:} The same value of \var{K} must \emph{never} be used
+twice, because that would allow a third party to determine the private
+key data. An attacker can save every message and signature produced
+by a key, and try all possible pairings in case a value of \var{K} was
+reused. Once an attacker has the private key data, they can sign
+messages as they like. (If automatically generated, \var{K} is a
+159-bit number, so the odds of the same number being used twice are
+infinitesimal as long as a good random number generator is used.)
+\end{funcdesc}
+
+\begin{funcdesc}{verify}{M\, signature}
+Return true if \var{signature} matches the message \var{M}. If they
+don't match, return false; this may happen because \var{M} or
+\var{signature} was modified, or because the signature was generated
+with a different key.
+\end{funcdesc}
+
+\subsection{The {\tt RandomPool} class}
+
+The \code{RandomPool} class provides cryptographically strong
+generation of random data.
+
+\begin{funcdesc}{RandomPool}{\optional{seed}}
+Return a \code{RandomPool} instance, initialized using the string
+\var{seed}. The instance can then be used to get random data.
+The more randomness that can be in the \var{seed} string, the more
+secure the resulting random data will be.
+
+If \var{seed} is omitted, the class will try to get random data on its
+own, reading from \file{/dev/urandom} on those systems that support
+it, and otherwise using the current system time. The system time
+isn't a very good source of randomness, so a warning will be printed
+to \code{sys.stdout}.
+\end{funcdesc}
+
+\begin{funcdesc}{random}{N}
+Return an \var{N}-byte string containing random data.
+
+When bound to an instance, this method can be passed as the
+\var{randfunc} parameters to \code{Key}'s \code{generate} and
+\code{sign} methods. Simply create a \code{RandomPool} object
+\var{R}, and pass \code{\var{R}.random} as the parameter.
+\end{funcdesc}
+
+
+\begin{seealso}
+\seetext{XXX Nyberg and Ruppel's paper},
+\seetext{Schneier's book}
+\end{seealso}
+
diff --git a/info/mail b/info/mail new file mode 100644 index 0000000..a62055a --- /dev/null +++ b/info/mail @@ -0,0 +1,1867 @@ +BABYL OPTIONS: -*- rmail -*- +Version: 5 +Labels: +Note: This is the header of an rmail file. +Note: If you are seeing it in rmail, +Note: it means the file has no messages in it. + +1, answered,, +Summary-line: 18-Sep pobox@tiac.net [87] #Re: Digital signature module +Mail-from: From python-list-request@cwi.nl Wed Sep 17 23:01:39 1997 +Return-Path: python-list-request@cwi.nl +Received: from grommit.magnet.com (grommit-int.magnet.com [208.192.176.4]) by lemur.magnet.com with SMTP id XAA17092; Wed, 17 Sep 1997 23:01:38 -0400 (EDT) +Received: by grommit.magnet.com (951211.SGI.8.6.12.PATCH1042/Magnet) + id XAA09314; Wed, 17 Sep 1997 23:01:37 -0400 +Received: from hera.cwi.nl(192.16.191.1) by grommit.magnet.com via smap (3.1) + id xma009311; Wed, 17 Sep 97 23:01:27 -0400 +Received: by hera.cwi.nl + id EAA17446; Thu, 18 Sep 1997 04:50:03 +0200 (MET DST) +To: python-list@cwi.nl +Sender: python-list-request@cwi.nl +Errors-To: python-list-request@cwi.nl +From: pobox@tiac.net (Jeff Macdonald) +Newsgroups: comp.lang.python +Subject: Re: Digital signature module +Date: Thu, 18 Sep 1997 02:31:28 GMT +X-Organization: slightly +Message-ID: <34208e46.8913966@news.tiac.net> +References: <199709171152.HAA27968@dolphin.automatrix.com> + <199709171526.LAA20637@lemur.magnet.com> +Reply-To: pobox@tiac.net +X-Newsreader: Forte Free Agent 1.11/32.235 + +*** EOOH *** +Return-Path: python-list-request@cwi.nl +To: python-list@cwi.nl +Sender: python-list-request@cwi.nl +Errors-To: python-list-request@cwi.nl +From: pobox@tiac.net (Jeff Macdonald) +Newsgroups: comp.lang.python +Subject: Re: Digital signature module +Date: Thu, 18 Sep 1997 02:31:28 GMT +X-Organization: slightly +References: <199709171152.HAA27968@dolphin.automatrix.com> + <199709171526.LAA20637@lemur.magnet.com> +Reply-To: pobox@tiac.net +X-Newsreader: Forte Free Agent 1.11/32.235 + +Andrew Kuchling <amk@magnet.com> wrote: + +.. stuff deleted + +> What exactly are you thinking of doing with Diffie-Hellman, +>though? It only lets two sides agree on some shared secret data, +>which is only really useful with some additional encryption algorithm. + +or with a secure hash, to do some MAC (?) type authorization. +Basically, once both sides have the shared secret, they hash that with +some plain text data (and probably time stamp stuff too). + +>It's not hard to implement D-H with the things already in the digisign +>module: + +I'll pull that over... + +> +> 1) Generate a large prime p with the PrimeNumber function, and +>a primitive root a (2 will do). + +Do you have any pointers to the generation of the primitive number? +I've seen posts that say 2 is good, but I would like to know why. + +... rest of Diffie-Hellman removed + +>Skip Montanaro <skip@calendar.com> wrote: +>>Python already supports arbitrarily long integers. Is that not enough? +> +> Depends on the application; the implementation of Python's +>long integers is in pure C, and the algorithms used are all fairly +>straightforward. By way of contrast, in GMP 2.0.2 the algorithms are +>more elaborate, different ones are used for operands of different +>sizes, and there's optimized inline assembly code for x86, AXP, +>PowerPC, and a dozen other processors. +> +> The most recent version of gmp module can be used as a module, +>or it can be dropped on top of Objects/longobject.c, making Python use +>GMP long integers by default. Unfortunately that also alters the +>format in which long integers are marshalled, which means marshal +>isn't compatible with everyone else when long integers are in use, and +>you have to regenerate all your *.pyc files. +> +> +> Andrew Kuchling +> amk@magnet.com +> http://starship.skyport.net/crew/amk/ + +I missed that line about the arbitrarly long ints, and just looked it +up in Mark Lutz's book. + +And on a completely different subject, I got Tom Christiansen's +<tchrist@jhereg.perl.com> 'Uneeded mime' message to my non-mime post, +to which I replied : +Tom, +me thinks your perl script has gone amuck... perhaps you should be +using python... :) + +and his reply was: + +yup, you're right. + +Which hopefully means that he's working on a Python book..... :) + + +1, answered,, +Summary-line: 19-Sep Jeff_Macdonald@VirtualBui [116] #Re: Digital signature module +Mail-from: From Jeff_Macdonald@VirtualBuilder.com Fri Sep 19 19:57:15 1997 +Return-Path: Jeff_Macdonald@VirtualBuilder.com +Received: from grommit.magnet.com (grommit-int.magnet.com [208.192.176.4]) by lemur.magnet.com with SMTP id TAA14038 for <amk@magnet.com>; Fri, 19 Sep 1997 19:57:14 -0400 (EDT) +Received: by grommit.magnet.com (951211.SGI.8.6.12.PATCH1042/Magnet) + for <amk@magnet.com> id TAA03445; Fri, 19 Sep 1997 19:57:08 -0400 +Received: from maildeliver0.tiac.net(199.0.65.19) by grommit.magnet.com via smap (3.1) + id xma003443; Fri, 19 Sep 97 19:56:57 -0400 +Received: from mailrelay.tiac.net (mailrelay.tiac.net [199.0.65.237]) by maildeliver0.tiac.net (8.8.0/8.8) with ESMTP id TAA30586 for <amk@magnet.com>; Fri, 19 Sep 1997 19:56:28 -0400 (EDT) +Received: from jeffmacd (pobox.tiac.net [199.3.136.237]) + by mailrelay.tiac.net (8.8.7/8.8.7) with SMTP id TAA04019 + for <amk@magnet.com>; Fri, 19 Sep 1997 19:57:47 -0400 (EDT) +Message-Id: <3.0.3.32.19970919195427.007d6870@pop.tiac.net> +X-Sender: pobox@pop.tiac.net +X-Mailer: QUALCOMM Windows Eudora Pro Version 3.0.3 (32) +Date: Fri, 19 Sep 1997 19:54:27 -0400 +To: Andrew Kuchling <amk@magnet.com> +From: Jeff Macdonald <Jeff_Macdonald@VirtualBuilder.com> +Subject: Re: Digital signature module +In-Reply-To: <199709191907.PAA05854@lemur.magnet.com> +References: <34208e46.8913966@news.tiac.net> +Mime-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" + +*** EOOH *** +Return-Path: Jeff_Macdonald@VirtualBuilder.com +X-Sender: pobox@pop.tiac.net +X-Mailer: QUALCOMM Windows Eudora Pro Version 3.0.3 (32) +Date: Fri, 19 Sep 1997 19:54:27 -0400 +To: Andrew Kuchling <amk@magnet.com> +From: Jeff Macdonald <Jeff_Macdonald@VirtualBuilder.com> +Subject: Re: Digital signature module +In-Reply-To: <199709191907.PAA05854@lemur.magnet.com> +References: <34208e46.8913966@news.tiac.net> +Mime-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" + +-----BEGIN PGP SIGNED MESSAGE----- + +At 03:07 PM 9/19/97 -0400, you wrote: +> +> BTW, according to Schneier's _Applied Cryptography_, 2nd ed., +>(p-1)/2 should also be prime for security. So p should be generated +>like this: +> +>while (1): +> p2=digisign.PrimeNumber(< desired bit size - 1 >) +> p=p2*2+1 +> if digisign.isPrime(p): break +> +>Also according to Schneier, you can check if a number g is a +>generator, by taking the prime factors of p-1; if pow(g, (p-1)/factor, +>p)!=1 for all the factors, it's a generator. Following on the above +>example, there are only two factors, 2 and p2: +> +>while (1): +> g=<some random number> +> if not (pow(g, 2, p)==1 or +> pow(g, p2, p)==1): +> break +> + +Your talent for transforming algorithms to code is very impressive. It's hard +to believe I was a math major. :) I've been trying to code this for weeks. + +>Hmm... it doesn't look too difficult to produce a Diffie-Hellman class +>after all, though I can't imagine what its interface should look like. +>Do you have any ideas about that? +> + +I've looked into several crypto libs, and the interface that I think would +make +most sense is to follow Bruce's book. Here's from Alice's point of view. + +(n,g)=dh.generatePrimes(bitsizeOfN) # set dh.n +# send bob n,g +X=dh.generateX(RandomFunction) # set dh.x +# send bob X +# get Bob's Y +k=dh.generate(Y) + +Bob would need to use n and g, so we also need a + +dh.setPrimes(n,g) + +to be able to generate a Y. So from Bob's view: + +# get n and g +dh.setPrimes(n,g) +# generate Y +Y=dh.generateX(RandomFunction) +# send Alice Y +# get Alice X +k=dh.generate(X) + +A more general interface may look like this: + +dh.setPrimes(n,g) +dh.generatePrimes(bitSize) +dh.generatePublic(RandomFuction) +dh.generatePrivate(publicKey) + +In either Peter G. or Eric Y.'s lib, there are values of n and g that where +pre-computed for 512, 768, 1024, 2048 and 4096 bits. I believe these values +were suggest by NIST. This should speed things up, the only change to the +interface would be: + +dh.setPrimes(bitSize) # sh.setPrimes(n,g) should still exist + +That would use the pre-computed values of n and g. Both parties would +have to agree on the bit size. + +This looks very clean to me, what do you think? + +I do have Bruce's book, and will be picking up 'Handbook of Applied +Crytpography' +this weekend. This book supposedly goes into more detail with the algorithms. + + + + +-----BEGIN PGP SIGNATURE----- +Version: PGP for Personal Privacy 5.0 +Charset: noconv + +iQCVAwUBNCMQsRxijdIqz8FNAQHqJwP/fF5vHyPlrzdihlj3BmnToFvrtz8W8l0p +7ZCcNCHIMwXC1a0Oi4TOi/Rx1banAs8C1bu/x42dwZbkBuMO4gYuh8pnImxLrYp6 +BIUV13usq3eciAkq7Jvl/0h3K8ns6MVWiUjw+RAukb+yqRO3hObxonoTR5PTZum0 +MaPYKsjtFMc= +=vQPL +-----END PGP SIGNATURE----- + +---------------------------------------------- +| Internet solutions for small companies | +| | +| email: Jeff_Macdonald@VirtualBuilder.com | +| Web: www.VirtualBuilder.com | +| PGP: www.VirtualBuilder.com/pgpkey.html | +---------------------------------------------- + + + +1, answered,, +Summary-line: 20-Sep Jeff_Macdonald@VirtualBui [161] #Diffie-Hellman +Mail-from: From Jeff_Macdonald@VirtualBuilder.com Sat Sep 20 01:22:02 1997 +Return-Path: Jeff_Macdonald@VirtualBuilder.com +Received: from grommit.magnet.com (grommit-int.magnet.com [208.192.176.4]) by lemur.magnet.com with SMTP id BAA17238 for <amk@magnet.com>; Sat, 20 Sep 1997 01:22:02 -0400 (EDT) +Received: by grommit.magnet.com (951211.SGI.8.6.12.PATCH1042/Magnet) + for <amk@magnet.com> id BAA05386; Sat, 20 Sep 1997 01:22:02 -0400 +Received: from maildeliver0.tiac.net(199.0.65.19) by grommit.magnet.com via smap (3.1) + id xma005384; Sat, 20 Sep 97 01:21:55 -0400 +Received: from mailrelay.tiac.net (mailrelay.tiac.net [199.0.65.237]) by maildeliver0.tiac.net (8.8.0/8.8) with ESMTP id BAA28643 for <amk@magnet.com>; Sat, 20 Sep 1997 01:21:54 -0400 (EDT) +Received: from jeffmacd (pobox.tiac.net [199.3.136.237]) + by mailrelay.tiac.net (8.8.7/8.8.7) with SMTP id BAA20887 + for <amk@magnet.com>; Sat, 20 Sep 1997 01:23:17 -0400 (EDT) +Message-Id: <3.0.3.32.19970920011955.007da980@pop.tiac.net> +X-Sender: pobox@pop.tiac.net +X-Mailer: QUALCOMM Windows Eudora Pro Version 3.0.3 (32) +Date: Sat, 20 Sep 1997 01:19:55 -0400 +To: Andrew Kuchling <amk@magnet.com> +From: Jeff Macdonald <Jeff_Macdonald@VirtualBuilder.com> +Subject: Diffie-Hellman +In-Reply-To: <199709191907.PAA05854@lemur.magnet.com> +References: <34208e46.8913966@news.tiac.net> +Mime-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" + +*** EOOH *** +Return-Path: Jeff_Macdonald@VirtualBuilder.com +X-Sender: pobox@pop.tiac.net +X-Mailer: QUALCOMM Windows Eudora Pro Version 3.0.3 (32) +Date: Sat, 20 Sep 1997 01:19:55 -0400 +To: Andrew Kuchling <amk@magnet.com> +From: Jeff Macdonald <Jeff_Macdonald@VirtualBuilder.com> +Subject: Diffie-Hellman +In-Reply-To: <199709191907.PAA05854@lemur.magnet.com> +References: <34208e46.8913966@news.tiac.net> +Mime-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" + +-----BEGIN PGP SIGNED MESSAGE----- + +> +> BTW, according to Schneier's _Applied Cryptography_, 2nd ed., +>(p-1)/2 should also be prime for security. So p should be generated +>like this: +> +>while (1): +> p2=digisign.PrimeNumber(< desired bit size - 1 >) +> p=p2*2+1 +> if digisign.isPrime(p): break +> + +Andrew, I tried the above algo, but your getPrime only take bits that are a +multiple +of 8 (besides, digisign doesn't have PrimeNumber!). + +>Also according to Schneier, you can check if a number g is a +>generator, by taking the prime factors of p-1; if pow(g, (p-1)/factor, +>p)!=1 for all the factors, it's a generator. Following on the above +>example, there are only two factors, 2 and p2: +> + +not comment below... + +>while (1): +> g=<some random number> +> if not (pow(g, 2, p)==1 or <<--- this should be _and_, no? +> pow(g, p2, p)==1): +> break +> + +also, my books don't show a pow(a,b,c) but a pow(a,b). Shouldn't this be +modpow(a,b,c)? +I also can't find a modpow(a,b,c) either. Wait, is that part of mpz? + +I really think a BigInteger class would help alot. Look at java's +BigIntger. You can +generate primes, and do all that fancy math stuff with it. + +Below I've implemented the primes n and g in java. Am I interpeting your +code the +right way? I'm using Java-Linux JDK 1.1.3.. + +import java.math.BigInteger; +import java.util.Random; +import java.lang.Integer; + +public class dh +{ + + public BigInteger n; + public BigInteger g; + + public dh(int bits) + { + BigInteger one=new BigInteger("1"); + BigInteger two=new BigInteger("2"); + BigInteger prime; + BigInteger sub_prime; + BigInteger generator; + + while(true) { + sub_prime=new BigInteger(bits-1,1,new Random()); // this generates a prime + prime=sub_prime.multiply(two); + prime=prime.add(one); + + if(prime.isProbablePrime(1)) { + break; + } + } + + n=prime; + + while (true) { + generator=new BigInteger(8,1,new Random()); // this generates a +prime with 8 bits + + if(!(generator.modPow(two,prime).equals(one)) && + !(generator.modPow(sub_prime,prime).equals(one))) { + break; + } + } + + + g=generator; + + } + + + static public void main(String[] args) { + + Integer bits=new Integer(args[0]); + + dh myDH=new dh(bits.intValue()); + + System.out.print("n is: "); + System.out.println(myDH.n); + System.out.println("n has " + myDH.n.bitLength() + " bits."); + System.out.print("g is: "); + System.out.println(myDH.g); + System.out.println("g has " + myDH.g.bitLength() + " bits."); + + System.exit(0); + } +} + + +>Hmm... it doesn't look too difficult to produce a Diffie-Hellman class +>after all, though I can't imagine what its interface should look like. +>Do you have any ideas about that? +> +> +> Andrew Kuchling +> amk@magnet.com +> http://starship.skyport.net/crew/amk/ +> +> +-----BEGIN PGP SIGNATURE----- +Version: PGP for Personal Privacy 5.0 +Charset: noconv + +iQCVAwUBNCNc+RxijdIqz8FNAQEUmQP+JAKEGpjo8n+wjiAlFY7ohopAbGetubz1 +6QJyft9w4dZYbYAGV66bjIrrZu8oIVAwvCbKlCmThW14u90iMIndaMlJH29SPZ9m +mUyUJYRal8NH3QWBtpv6n7619r88ImnU1NxaSA2EQ1F6ZZwfIIb6YQsxFeR2w1id +3T+8Q0sWRn0= +=f6RN +-----END PGP SIGNATURE----- + +---------------------------------------------- +| Internet solutions for small companies | +| | +| email: Jeff_Macdonald@VirtualBuilder.com | +| Web: www.VirtualBuilder.com | +| PGP: www.VirtualBuilder.com/pgpkey.html | +---------------------------------------------- + + + +1,, +Summary-line: 18-Aug wiml@omnigroup.com [229] #PCT patches +Mail-from: From wiml@omnigroup.com Mon Aug 18 19:15:33 1997 +Return-Path: wiml@omnigroup.com +Received: from grommit.magnet.com (grommit-int.magnet.com [208.192.176.4]) by lemur.magnet.com with SMTP id TAA01155 for <amk@magnet.com>; Mon, 18 Aug 1997 19:15:32 -0400 (EDT) +Received: by grommit.magnet.com (951211.SGI.8.6.12.PATCH1042/Magnet) + for <amk@magnet.com> id TAA00517; Mon, 18 Aug 1997 19:00:44 -0400 +Received: from ignem.omnigroup.com(198.151.161.40) by grommit.magnet.com via smap (3.1) + id xma000511; Mon, 18 Aug 97 19:00:41 -0400 +Received: from reason.omnigroup.com (reason [198.151.161.25]) + by ignem.omnigroup.com (8.8.5/8.8.5) with SMTP id QAA02952; + Mon, 18 Aug 1997 16:00:39 -0700 (GMT-0700) +From: William Lewis <wiml@omnigroup.com> +Message-Id: <199708182300.QAA02952@ignem.omnigroup.com> +Received: by reason.omnigroup.com (NX5.67g/NX3.0X) + id AA25820; Mon, 18 Aug 97 16:00:46 -0700 +Date: Mon, 18 Aug 97 16:00:46 -0700 +To: amk@magnet.com +Cc: wiml@hhhh.org +Subject: PCT patches + +*** EOOH *** +Return-Path: wiml@omnigroup.com +From: William Lewis <wiml@omnigroup.com> +Date: Mon, 18 Aug 97 16:00:46 -0700 +To: amk@magnet.com +Cc: wiml@hhhh.org +Subject: PCT patches + +-----BEGIN PGP SIGNED MESSAGE----- + +Here's a patch against PCT version 1.0.1. It allows DES3 to use +independent keys, fixes some bugs in the RSA module and the block +cipher framework, and adds a "mode" attribute to block cipher objects. + +diff -rc pycrypt101.orig/Extensions/Crypto/Lib/RSA.py pycrypt101/Extensions/Crypto/Lib/RSA.py +*** pycrypt101.orig/Extensions/Crypto/Lib/RSA.py Sun Apr 14 19:14:27 1996 +- --- pycrypt101/Extensions/Crypto/Lib/RSA.py Fri Aug 1 12:43:41 1997 +*************** +*** 12,18 **** + import pubkey + + error = 'RSA module' +! keydata=['d', 'e', 'n'] + + # Generate an RSA key with N bits + def generate(N, randfunc, verbose=None): +- --- 12,18 ---- + import pubkey + + error = 'RSA module' +! keydata=['d', 'e', 'n', 'p', 'q'] + + # Generate an RSA key with N bits + def generate(N, randfunc, verbose=None): +*************** +*** 42,48 **** + if len(tuple) in [3,5] : + obj.d=tuple[2] + if len(tuple)==5: +! obj.p=tuple[4] ; obj.q=tuple[5] + return obj + + class RSAobj(pubkey.pubkey): +- --- 42,48 ---- + if len(tuple) in [3,5] : + obj.d=tuple[2] + if len(tuple)==5: +! obj.p=tuple[3] ; obj.q=tuple[4] + return obj + + class RSAobj(pubkey.pubkey): +Only in pycrypt101/Extensions/Crypto/Lib: RSA.py~ +Only in pycrypt101/Extensions/Crypto/block: cast.c +Only in pycrypt101/Extensions/Crypto/block: cast5.c +Only in pycrypt101/Extensions/Crypto/block: casttest.py +diff -rc pycrypt101.orig/Extensions/Crypto/block/des3.c pycrypt101/Extensions/Crypto/block/des3.c +*** pycrypt101.orig/Extensions/Crypto/block/des3.c Thu Dec 21 16:29:35 1995 +- --- pycrypt101/Extensions/Crypto/block/des3.c Mon Aug 18 15:56:37 1997 +*************** +*** 13,18 **** +- --- 13,19 ---- + /* des.c */ + /* Copyright (C) 1993 Eric Young */ + /* Integrated into the PCT by A.M. Kuchling, November 1994 */ ++ /* Fully independet key mode added by Wim Lewis, July 1997 */ + + typedef unsigned char des_cblock[8]; + +*************** +*** 430,436 **** + typedef struct + { + PCTObject_HEAD +! des_key_schedule KeySched1, KeySched2; + } DES3object; + + static int des_encrypt(input,output,ks,encrypt) +- --- 431,437 ---- + typedef struct + { + PCTObject_HEAD +! des_key_schedule KeySched1, KeySched2, KeySched3; + } DES3object; + + static int des_encrypt(input,output,ks,encrypt) +*************** +*** 544,550 **** + { + des_cblock output, output2; + +! des_ecb_encrypt(block, output, self->KeySched1, 0); + des_ecb_encrypt(output, output2, self->KeySched2, 1); + des_ecb_encrypt(output2, block, self->KeySched1, 0); + } +- --- 545,551 ---- + { + des_cblock output, output2; + +! des_ecb_encrypt(block, output, self->KeySched3, 0); + des_ecb_encrypt(output, output2, self->KeySched2, 1); + des_ecb_encrypt(output2, block, self->KeySched1, 0); + } +*************** +*** 557,563 **** + + des_ecb_encrypt(block, output, self->KeySched1, 1); + des_ecb_encrypt(output, output2, self->KeySched2, 0); +! des_ecb_encrypt(output2, block, self->KeySched1, 1); + } + + /* NOW DEFINED IN des_local.h +- --- 558,564 ---- + + des_ecb_encrypt(block, output, self->KeySched1, 1); + des_ecb_encrypt(output, output2, self->KeySched2, 0); +! des_ecb_encrypt(output2, block, self->KeySched3, 1); + } + + /* NOW DEFINED IN des_local.h +*************** +*** 643,652 **** + return(0); + } + +! static inline void DES3init(self, key) + DES3object *self; + unsigned char *key; + { + des_set_key(key+0, self->KeySched1); + des_set_key(key+8, self->KeySched2); + } +- --- 644,665 ---- + return(0); + } + +! static inline void DES3init(self, key, keylength) + DES3object *self; + unsigned char *key; ++ int keylength; + { ++ if (keylength != 16 && keylength != 24) { ++ PyErr_SetString(PyExc_ValueError, ++ "DES3 key must be either 16 or 24 bytes long"); ++ return; ++ } ++ + des_set_key(key+0, self->KeySched1); + des_set_key(key+8, self->KeySched2); ++ if (keylength == 24) { ++ des_set_key(key+16, self->KeySched3); ++ } else { ++ memcpy(self->KeySched3, self->KeySched1, sizeof(self->KeySched3)); ++ } + } +Only in pycrypt101/Extensions/Crypto/block: des3.c~ +diff -rc pycrypt101.orig/Extensions/Crypto/config.pct pycrypt101/Extensions/Crypto/config.pct +*** pycrypt101.orig/Extensions/Crypto/config.pct Tue Jan 30 17:24:24 1996 +- --- pycrypt101/Extensions/Crypto/config.pct Fri Aug 1 13:45:52 1997 +*************** +*** 15,24 **** + block ARC2 8 0 + block Blowfish 8 0 + block DES 8 8 +! block DES3 8 16 + block IDEA 8 16 + block RC5 8 0 + block Diamond 16 0 + + # Stream ciphers + stream ARC4 1 0 +- --- 15,25 ---- + block ARC2 8 0 + block Blowfish 8 0 + block DES 8 8 +! block DES3 8 0 + block IDEA 8 16 + block RC5 8 0 + block Diamond 16 0 + + # Stream ciphers + stream ARC4 1 0 +Only in pycrypt101/Extensions/Crypto: config.pct~ +diff -rc pycrypt101.orig/Extensions/Crypto/framewks/block.in pycrypt101/Extensions/Crypto/framewks/block.in +*** pycrypt101.orig/Extensions/Crypto/framewks/block.in Thu Dec 21 16:28:55 1995 +- --- pycrypt101/Extensions/Crypto/framewks/block.in Mon Aug 18 15:45:59 1997 +*************** +*** 441,447 **** + "@@BLOCKSIZE@@ bytes long"); + return -1; + } +! memcpy(self->IV, PyString_AsString, @@BLOCKSIZE@@); + return (0); + } + +- --- 441,447 ---- + "@@BLOCKSIZE@@ bytes long"); + return -1; + } +! memcpy(self->IV, PyString_AsString(v), @@BLOCKSIZE@@); + return (0); + } + +*************** +*** 454,459 **** +- --- 454,463 ---- + if (strcmp(name, "IV") == 0) + { + return(PyString_FromStringAndSize(self->IV, @@BLOCKSIZE@@)); ++ } ++ if (strcmp(name, "mode") == 0) ++ { ++ return(PyInt_FromLong((long)(self->cipherMode))); + } + return Py_FindMethod(@@ALGORITHM@@methods, (PyObject *) self, name); + } +Only in pycrypt101/Extensions/Crypto/framewks: block.in~ + + + +-----BEGIN PGP SIGNATURE----- +Version: 2.6.2 +Comment: Processed by Mailcrypt 3.4, an Emacs/PGP interface + +iQCVAwUBM/jT1F8UnN8n93LBAQGm1wP/Zqo3wKLiN1wa7eIUXSspn27UKM83dt1S +oJzl3HeHR+JNkOLTUdz37rkX4NbHVhr3wyuE/Hhi7C0LVG/kDfV3cpw2VYCyQgVK +b91ILoS5C8AgGwUm+mXKtEd73wt7hdOZTDsx0uboNb9c0PqEIzOB5SroNIEl9GLk +7D+C7KyyuYM= +=VPS6 +-----END PGP SIGNATURE----- + + +1, answered,, +Summary-line: 15-Sep JeffBauer@Bigfoot.com [57] #pycrypt101 Makefile.pre.in diffs +Mail-from: From JeffBauer@Bigfoot.com Mon Sep 15 17:07:06 1997 +Return-Path: JeffBauer@Bigfoot.com +Received: from grommit.magnet.com (grommit-int.magnet.com [208.192.176.4]) by lemur.magnet.com with SMTP id RAA01368 for <amk@magnet.com>; Mon, 15 Sep 1997 17:07:05 -0400 (EDT) +Received: by grommit.magnet.com (951211.SGI.8.6.12.PATCH1042/Magnet) + for <amk@magnet.com> id RAA07289; Mon, 15 Sep 1997 17:07:04 -0400 +Received: from sasha.pov.net(207.152.31.2) by grommit.magnet.com via smap (3.1) + id xma007284; Mon, 15 Sep 97 17:06:41 -0400 +Received: from jbauer.remote.pov.net (jbauer.remote.pov.net [207.152.31.187]) by sasha.pov.net (8.6.12/8.6.12) with SMTP id QAA08178 for <amk@magnet.com>; Mon, 15 Sep 1997 16:11:23 -0500 +Received: by jbauer.remote.pov.net with Microsoft Mail + id <01BCC1F1.8909C810@jbauer.remote.pov.net>; Mon, 15 Sep 1997 16:07:58 -0500 +Message-ID: <01BCC1F1.8909C810@jbauer.remote.pov.net> +From: Jeff Bauer <JeffBauer@Bigfoot.com> +To: "'amk@magnet.com'" <amk@magnet.com> +Subject: pycrypt101 Makefile.pre.in diffs +Date: Mon, 15 Sep 1997 16:07:21 -0500 +MIME-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: 7bit + +*** EOOH *** +Return-Path: JeffBauer@Bigfoot.com +From: Jeff Bauer <JeffBauer@Bigfoot.com> +To: "'amk@magnet.com'" <amk@magnet.com> +Subject: pycrypt101 Makefile.pre.in diffs +Date: Mon, 15 Sep 1997 16:07:21 -0500 +MIME-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: 7bit + +Hi Andrew. + +I finally got around to getting pycrypt101 to compile on my +Solaris 2.5.1 machine for Python 1.4. FWIW, I've included +my Makefile.pre.in diffs. Is there a precompiled version of +pycrypt/python available for NT? I could look into doing the +project myself, but U.S. export policies would make it an +pointless exercise in wish fulfillment :( <<-- meaning that I +couldn't share my efforts with anyone. Maybe I should +plan a vacation to Australia and take my laptop along ... + +Are you going to the 6th Python Workshop? + +Best regards, + +Jeff Bauer + +_______________________________________________ + +diff Makefile.pre.in.origin Makefile.pre.in.sunos5 +43a44,45 +> VERSION= 1.4 +> +96,97c98,99 +< CC= @CC@ -I/usr/local/include +< SYSLIBS= -L/usr/local/lib $(LIBM) $(LIBC) +--- +> CC= @CC@ +> SYSLIBS= $(LIBM) $(LIBC) +113,115c115,117 +< INCLUDEPY= $(installdir)/include/Py +< LIBP= $(exec_installdir)/lib/python +< LIBPL= $(LIBP)/lib +--- +> INCLUDEPY= $(prefix)/include/python$(VERSION) +> LIBP= $(exec_prefix)/lib/python$(VERSION) +> LIBPL= $(LIBP)/config + + +1,, +Summary-line: 15-Sep JeffBauer@Bigfoot.com [44] #RE: pycrypt101 Makefile.pre.in diffs +Mail-from: From JeffBauer@Bigfoot.com Mon Sep 15 20:44:15 1997 +Return-Path: JeffBauer@Bigfoot.com +Received: from grommit.magnet.com (grommit-int.magnet.com [208.192.176.4]) by lemur.magnet.com with SMTP id UAA05975 for <amk@magnet.com>; Mon, 15 Sep 1997 20:44:14 -0400 (EDT) +Received: by grommit.magnet.com (951211.SGI.8.6.12.PATCH1042/Magnet) + for <amk@magnet.com> id UAA09777; Mon, 15 Sep 1997 20:44:13 -0400 +Received: from sasha.pov.net(207.152.31.2) by grommit.magnet.com via smap (3.1) + id xma009773; Mon, 15 Sep 97 20:44:06 -0400 +Received: from jbauer.remote.pov.net (jbauer.remote.pov.net [207.152.31.187]) by sasha.pov.net (8.6.12/8.6.12) with SMTP id TAA17127 for <amk@magnet.com>; Mon, 15 Sep 1997 19:49:01 -0500 +Received: by jbauer.remote.pov.net with Microsoft Mail + id <01BCC20F.D99CA130@jbauer.remote.pov.net>; Mon, 15 Sep 1997 19:44:59 -0500 +Message-ID: <01BCC20F.D99CA130@jbauer.remote.pov.net> +From: Jeff Bauer <JeffBauer@Bigfoot.com> +To: "'Andrew Kuchling'" <amk@magnet.com> +Subject: RE: pycrypt101 Makefile.pre.in diffs +Date: Mon, 15 Sep 1997 19:42:16 -0500 +MIME-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: 7bit + +*** EOOH *** +Return-Path: JeffBauer@Bigfoot.com +From: Jeff Bauer <JeffBauer@Bigfoot.com> +To: "'Andrew Kuchling'" <amk@magnet.com> +Subject: RE: pycrypt101 Makefile.pre.in diffs +Date: Mon, 15 Sep 1997 19:42:16 -0500 +MIME-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: 7bit + + +>>my Makefile.pre.in diffs. Is there a precompiled version of +>>pycrypt/python available for NT? I could look into doing the +>>project myself, but U.S. export policies would make it an + + >Not as far as I know. + +>>Are you going to the 6th Python Workshop? + + >Yes; you too? + +Yep. + +> Thanks for the patches; I'll look at them later. Actually, I +>think it's about time I returned to working on pycrypt, and would like +>to hear suggestions for new features. Various people have sent me +>bugfixes which I haven't gotten around to examining yet, and someone +>submitted a CAST128 implementation (a block cipher algorithm, +>specified in an informational RFC somewhere). Do you have any +>suggestions for new features? + +Not really, except for NT support. I know Mark Hammond is too busy +to consider it, but his locale would be ideal from a distribution angle. +I have a Newton clinical application that will encrypt the data with IDEA +and ship it to my Sun box via an internal modem connected to any ISP. +I presently have the C code working for IDEA, but it would be nice to +have everything work natively in Python. Interoperability with NT +would widen the circle of available systems, which is not so important +for this project, as it is for me to commit to this kind of security +framework. + +Best regards, + +Jeff Bauer + + +1,, +Summary-line: 28-Jul wiml@hhhh.org[1035] #Re: Python Cryptography Toolkit +Mail-from: From wiml@omnigroup.com Mon Jul 28 18:55:29 1997 +Return-Path: wiml@omnigroup.com +Received: from grommit.magnet.com (grommit-int.magnet.com [208.192.176.4]) by lemur.magnet.com with SMTP id SAA02128 for <amk@magnet.com>; Mon, 28 Jul 1997 18:55:29 -0400 (EDT) +Received: by grommit.magnet.com (951211.SGI.8.6.12.PATCH1042/Magnet) + for <amk@magnet.com> id SAA23549; Mon, 28 Jul 1997 18:55:28 -0400 +Received: from ignem.omnigroup.com(198.151.161.40) by grommit.magnet.com via smap (3.1) + id xma023544; Mon, 28 Jul 97 18:55:05 -0400 +Received: from reason.omnigroup.com (reason [198.151.161.25]) + by ignem.omnigroup.com (8.8.5/8.8.5) with SMTP id PAA19358 + for <amk@magnet.com>; Mon, 28 Jul 1997 15:55:08 -0700 (GMT-0700) +Message-Id: <199707282255.PAA19358@ignem.omnigroup.com> +Received: by reason.omnigroup.com (NX5.67g/NX3.0X) + id AA08245; Mon, 28 Jul 97 15:55:16 -0700 +Date: Mon, 28 Jul 97 15:55:16 -0700 +To: amk@magnet.com +Subject: Re: Python Cryptography Toolkit +From: wiml@hhhh.org +Reply-To: wiml@hhhh.org + +*** EOOH *** +Return-Path: wiml@omnigroup.com +Date: Mon, 28 Jul 97 15:55:16 -0700 +To: amk@magnet.com +Subject: Re: Python Cryptography Toolkit +From: wiml@hhhh.org +Reply-To: wiml@hhhh.org + +-----BEGIN PGP SIGNED MESSAGE----- + +You write: +> Unfortunately, I couldn't read your e-mail, since I retired that PGP +> key long ago; sorry! + +It did look a little old :-) Mostly I just wanted to protect the code +from any evil whitespace-gobbling mailers it might encounter. + +What follows is an implementation of CAST (CAST-128, CAST5) as described +in rfc2144. It's not the fastest code ever written but it works. +There are some test vectors in casttest.py; if compiled with +- -DTEST the C code will perform the much more elaborate "Full Maintenance +Test". It should be reasonably portable. + +I also wanted to say thanks for the PCT; it's a nicely designed and +useful adjunct to one of my favorite programming languages. + +#! /bin/sh +# This is a shell archive, meaning: +# 1. Remove everything above the #! /bin/sh line. +# 2. Save the resulting text in a file. +# 3. Execute the file with /bin/sh (not csh) to create the files: +# cast.c +# cast5.c +# casttest.py +# This archive created: Tue Jul 22 22:15:02 1997 +export PATH; PATH=/bin:$PATH +echo shar: extracting "'cast.c'" '(11926 characters)' +if test -f 'cast.c' +then + echo shar: will not over-write existing file "'cast.c'" +else +sed 's/^X//' << \SHAR_EOF > 'cast.c' +X/* +X cast.c -- implementation of CAST-128 (aka CAST5) as described in RFC2144 +X +X compile -DPCT for use in the Python Cryptography Toolkit +X (this should work automatically) +X compile -DTEST to include main() which performs the tests +X specified in RFC2144 +X +X Written by Wim Lewis <wiml@hhhh.org> based entirely on RFC2144. This code +X is in the public domain. Consult your local laws for possible restrictions +X on use, distribution, and import/export. RFC2144 states that this +X algorithm "is available worldwide on a royalty-free basis for commercial +X and non-commercial uses". +X +X This code is a pretty straightforward transliteration of the RFC into C. +X It has not been optimized much at all: byte-order-independent arithmetic +X operations are used where order-dependent pointer ops or unions might be +X faster; the code could be rearranged to give the optimizer a better +X chance to speed things up; etc. +X +X This code requires a vaguely ANSI-ish compiler. +X +X Tested with gcc 2.5.8 on i486, i586, i686, hp pa-risc, mc68040, sparc; +X also with gcc 2.7.2 and (with minor changes) native Sun compiler on sparc +X +X History: +X 21 Jul 1997: wiml : first working version & Python module +X*/ +X +X +X/* adjust these according to your compiler/platform. On some machines +X uint32 will have to be a long. It's OK if uint32 is more than 32 bits. */ +Xtypedef unsigned int uint32; +Xtypedef unsigned char uint8; +X +X#ifdef PCTObject_HEAD +X#define PCT +X#endif +X +X#ifdef PCT +X#define PCTstatic static +X#else +X#define PCTstatic +X#endif +X +X/* this struct probably belongs in cast.h */ +Xtypedef struct { +X /* masking and rotate keys */ +X uint32 Km[16]; +X uint8 Kr[16]; +X /* number of rounds (depends on original unpadded keylength) */ +X int rounds; +X} cast_keyschedule; +X +X/* these are the eight 32*256 S-boxes */ +X#include "../block/cast5.c" +X +X/* fetch a uint32 from an array of uint8s (with a given offset) */ +X#define fetch(ptr, base) (((((( ptr[base]<< 8 ) | ptr[base+1] )<< 8 ) | ptr[base+2] )<< 8 ) | ptr[base+3]) +X +X/* this is the round function f(D, Km, Kr) */ +Xstatic inline uint32 castfunc(D, Kmi, Kri, type) +X uint32 D, Kmi; +X uint8 Kri; +X int type; +X{ +X uint32 I, f; +X short Ia, Ib, Ic, Id; +X +X switch(type) { +X case 0: +X I = (Kmi + D) ; +X break; +X case 1: +X I = (Kmi ^ D) ; +X break; +X default: +X case 2: +X I = (Kmi - D) ; +X break; +X } +X +X I &= 0xFFFFFFFF; +X I = ( I << Kri ) | ( I >> ( 32-Kri ) ); +X Ia = ( I >> 24 ) & 0xFF; +X Ib = ( I >> 16 ) & 0xFF; +X Ic = ( I >> 8 ) & 0xFF; +X Id = ( I ) & 0xFF; +X +X switch(type) { +X case 0: +X f = ((S1[Ia] ^ S2[Ib]) - S3[Ic]) + S4[Id]; +X break; +X case 1: +X f = ((S1[Ia] - S2[Ib]) + S3[Ic]) ^ S4[Id]; +X break; +X default: +X case 2: +X f = ((S1[Ia] + S2[Ib]) ^ S3[Ic]) - S4[Id]; +X break; +X } +X +X return f; +X} +X +X/* encrypts/decrypts one block of data according to the key schedule +X pointed to by `key'. Encrypts if decrypt=0, otherwise decrypts. */ +XPCTstatic void castcrypt(key, block, decrypt) +X cast_keyschedule *key; +X uint8 *block; +X int decrypt; +X{ +X uint32 L, R, tmp, f; +X uint32 Kmi; +X uint8 Kri; +X short functype, round; +X +X L = fetch(block, 0); +X R = fetch(block, 4); +X +X/* printf("L0 = %08x R0 = %08x\n", L, R); */ +X +X for(round = 0; round < key->rounds; round ++) { +X +X if (!decrypt) { +X Kmi = key->Km[round]; +X Kri = key->Kr[round]; +X functype = round % 3; +X } else { +X Kmi = key->Km[(key->rounds) - round - 1]; +X Kri = key->Kr[(key->rounds) - round - 1]; +X functype = (((key->rounds) - round - 1) % 3); +X } +X +X f = castfunc(R, Kmi, Kri, functype); +X +X tmp = L; +X L = R; +X R = tmp ^ f; +X +X/* printf("L%d = %08x R%d = %08x\n", round+1, L, round+1, R); */ +X } +X +X block[0] = ( R & 0xFF000000 ) >> 24; +X block[1] = ( R & 0x00FF0000 ) >> 16; +X block[2] = ( R & 0x0000FF00 ) >> 8; +X block[3] = ( R & 0x000000FF ); +X block[4] = ( L & 0xFF000000 ) >> 24; +X block[5] = ( L & 0x00FF0000 ) >> 16; +X block[6] = ( L & 0x0000FF00 ) >> 8; +X block[7] = ( L & 0x000000FF ); +X} +X +X/* fetch a uint8 from an array of uint32s */ +X#define b(a,n) (((a)[n/4] >> (24-((n&3)*8))) & 0xFF) +X +X/* key schedule round functions */ +X +X#define XZRound(T, F, ki1, ki2, ki3, ki4, \ +X si11, si12, si13, si14, si15,\ +X si25,\ +X si35,\ +X si45 ) \ +X T[0] = F[ki1] ^ S5[si11 ] ^ S6[si12 ] ^ S7[si13 ] ^ S8[si14 ] ^ S7[si15];\ +X T[1] = F[ki2] ^ S5[b(T, 0)] ^ S6[b(T,2)] ^ S7[b(T, 1)] ^ S8[b(T,3)] ^ S8[si25];\ +X T[2] = F[ki3] ^ S5[b(T, 7)] ^ S6[b(T,6)] ^ S7[b(T, 5)] ^ S8[b(T,4)] ^ S5[si35];\ +X T[3] = F[ki4] ^ S5[b(T,10)] ^ S6[b(T,9)] ^ S7[b(T,11)] ^ S8[b(T,8)] ^ S6[si45]; +X +X#define zxround() XZRound(z, x, 0, 2, 3, 1, \ +X b(x,13), b(x,15), b(x,12), b(x,14),\ +X b(x, 8), b(x,10), b(x, 9), b(x,11)) +X +X#define xzround() XZRound(x, z, 2, 0, 1, 3, \ +X b(z,5), b(z,7), b(z,4), b(z,6), \ +X b(z,0), b(z,2), b(z,1), b(z,3)) +X +X#define Kround(T, base, F,\ +X i11, i12, i13, i14, i15,\ +X i21, i22, i23, i24, i25,\ +X i31, i32, i33, i34, i35,\ +X i41, i42, i43, i44, i45)\ +X T[base+0] = S5[b(F,i11)] ^ S6[b(F,i12)] ^ S7[b(F,i13)] ^ S8[b(F,i14)] ^ S5[b(F,i15)];\ +X T[base+1] = S5[b(F,i21)] ^ S6[b(F,i22)] ^ S7[b(F,i23)] ^ S8[b(F,i24)] ^ S6[b(F,i25)];\ +X T[base+2] = S5[b(F,i31)] ^ S6[b(F,i32)] ^ S7[b(F,i33)] ^ S8[b(F,i34)] ^ S7[b(F,i35)];\ +X T[base+3] = S5[b(F,i41)] ^ S6[b(F,i42)] ^ S7[b(F,i43)] ^ S8[b(F,i44)] ^ S8[b(F,i45)]; +X +X/* generates sixteen 32-bit subkeys based on a 4x32-bit input key; +X modifies the input key *in as well. */ +Xstatic void schedulekeys_half(in, keys) +X uint32 *in; +X uint32 *keys; +X{ +X uint32 x[4], z[4]; +X +X x[0] = in[0]; +X x[1] = in[1]; +X x[2] = in[2]; +X x[3] = in[3]; +X +X zxround(); +X Kround(keys, 0, z, +X 8, 9, 7, 6, 2, +X 10, 11, 5, 4, 6, +X 12, 13, 3, 2, 9, +X 14, 15, 1, 0, 12); +X xzround(); +X Kround(keys, 4, x, +X 3, 2, 12, 13, 8, +X 1, 0, 14, 15, 13, +X 7, 6, 8, 9, 3, +X 5, 4, 10, 11, 7); +X zxround(); +X Kround(keys, 8, z, +X 3, 2, 12, 13, 9, +X 1, 0, 14, 15, 12, +X 7, 6, 8, 9, 2, +X 5, 4, 10, 11, 6); +X xzround(); +X Kround(keys, 12, x, +X 8, 9, 7, 6, 3, +X 10, 11, 5, 4, 7, +X 12, 13, 3, 2, 8, +X 14, 15, 1, 0, 13); +X +X in[0] = x[0]; +X in[1] = x[1]; +X in[2] = x[2]; +X in[3] = x[3]; +X} +X +X/* generates a key schedule from an input key */ +XPCTstatic void castschedulekeys(schedule, key, keybytes) +X cast_keyschedule *schedule; +X uint8 *key; +X int keybytes; +X{ +X uint32 x[4]; +X uint8 paddedkey[16]; +X uint32 Kr_wide[16]; +X int i; +X +X for(i = 0; i < keybytes; i++) +X paddedkey[i] = key[i]; +X for( ; i < 16 ; i++) +X paddedkey[i] = 0; +X +X if (keybytes <= 10) +X schedule->rounds = 12; +X else +X schedule->rounds = 16; +X +X x[0] = fetch(paddedkey, 0); +X x[1] = fetch(paddedkey, 4); +X x[2] = fetch(paddedkey, 8); +X x[3] = fetch(paddedkey, 12); +X +X schedulekeys_half(x, schedule->Km); +X schedulekeys_half(x, Kr_wide); +X +X for(i = 0; i < 16; i ++) { +X /* The Kr[] subkeys are used for 32-bit circular shifts, +X so we only need to keep them modulo 32 */ +X schedule->Kr[i] = (uint8)(Kr_wide[i] & 0x1F); +X } +X} +X +X#ifdef TEST +X +X/* This performs a variety of encryptions and verifies that the results +X match those specified in RFC2144 appendix B. Also verifies that +X decryption restores the original data. */ +X +X#include <stdio.h> +X +Xstatic cast_keyschedule sched; +X +Xvoid encrypt(key, keylen, in, out) +X uint8 *key; +X int keylen; +X uint8 *in, *out; +X{ +X int i; +X uint8 k[16]; +X +X castschedulekeys(&sched, key, keylen); +X +X for(i = 0; i < 8; i++) +X out[i] = in[i]; +X castcrypt(&sched, out, 0); +X} +X +Xvoid tst(key, keylen, data, result) +X uint8 *key; +X int keylen; +X uint8 *data, *result; +X{ +X uint8 d[8]; +X int i; +X +X encrypt(key, keylen, data, d); +X +X for(i = 0; i < 8; i++) +X if (d[i] != result[i]) +X break; +X +X if (i == 8) { +X printf("-- test ok (encrypt)\n"); +X } else { +X for(i = 0; i < 8; i++) +X printf(" %02x", d[i]); +X printf(" (computed)\n"); +X for(i = 0; i < 8; i++) +X printf(" %02x", result[i]); +X printf(" (expected)\n"); +X } +X +X /* uses key schedule already set up */ +X castcrypt(&sched, d, 1); +X if (bcmp(d, data, 8)) +X printf(" test FAILED (decrypt)\n"); +X else +X printf(" test ok (decrypt)\n"); +X +X} +X +Xuint8 key[16] = { 0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, +X 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A }; +Xuint8 data[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; +X +X/* expected results of encrypting the above with 128, 80, and 40 +X bits of key length */ +Xuint8 out1[8] = { 0x23, 0x8B, 0x4F, 0xE5, 0x84, 0x7E, 0x44, 0xB2 }; +Xuint8 out2[8] = { 0xEB, 0x6A, 0x71, 0x1A, 0x2C, 0x02, 0x27, 0x1B }; +Xuint8 out3[8] = { 0x7A, 0xC8, 0x16, 0xD1, 0x6E, 0x9B, 0x30, 0x2E }; +X +X/* expected results of the "full maintenance test" */ +Xuint8 afinal[16] = { 0xEE, 0xA9, 0xD0, 0xA2, 0x49, 0xFD, 0x3B, 0xA6, +X 0xB3, 0x43, 0x6F, 0xB8, 0x9D, 0x6D, 0xCA, 0x92 }; +Xuint8 bfinal[16] = { 0xB2, 0xC9, 0x5E, 0xB0, 0x0C, 0x31, 0xAD, 0x71, +X 0x80, 0xAC, 0x05, 0xB8, 0xE8, 0x3D, 0x69, 0x6E }; +X +Xmain() +X{ +X /* Appendix B.1 : Single Plaintext-Key-Ciphertext Sets */ +X tst(key, 16, data, out1); +X tst(key, 10, data, out2); +X tst(key, 5, data, out3); +X +X /* Appendix B.2 : Full Maintenance Test */ +X { +X uint8 abuf[16]; +X uint8 bbuf[16]; +X int i; +X +X bcopy(key, abuf, 16); +X bcopy(key, bbuf, 16); +X +X printf("\nrunning full maintenance test...\n"); +X +X for(i = 0; i < 1000000; i++) { +X castschedulekeys(&sched, bbuf, 16); +X castcrypt(&sched, abuf, 0); +X castcrypt(&sched, abuf+8, 0); +X +X castschedulekeys(&sched, abuf, 16); +X castcrypt(&sched, bbuf, 0); +X castcrypt(&sched, bbuf+8, 0); +X +X if (!(i % 10000)) { +X fprintf(stdout, "\r%d%% ", i / 10000); +X fflush(stdout); +X } +X } +X +X printf("\r \r"); +X +X for(i = 0; i < 16; i ++) +X if (abuf[i] != afinal[i] || bbuf[i] != bfinal[i]) +X break; +X +X if(i == 16) { +X printf("-- full maintenance test ok\n"); +X } else { +X for(i = 0; i < 16; i++) +X printf(" %02x", abuf[i]); +X printf("\n"); +X for(i = 0; i < 16; i++) +X printf(" %02x", bbuf[i]); +X printf("\n"); +X } +X +X printf("running maintenance test in reverse...\n"); +X for(i = 0; i < 1000000; i++) { +X castschedulekeys(&sched, abuf, 16); +X castcrypt(&sched, bbuf+8, 1); +X castcrypt(&sched, bbuf, 1); +X +X castschedulekeys(&sched, bbuf, 16); +X castcrypt(&sched, abuf+8, 1); +X castcrypt(&sched, abuf, 1); +X +X if (!(i % 10000)) { +X fprintf(stdout, "\r%d%% ", i / 10000); +X fflush(stdout); +X } +X } +X +X printf("\r \r"); +X if (bcmp(abuf, key, 16) || bcmp(bbuf, key, 16)) +X printf("-- reverse maintenance test FAILED\n"); +X else +X printf("-- reverse maintenance test ok\n"); +X } +X} +X +X#endif +X +X#ifdef PCT +X +X/* code to interface with the Python Cryptography Toolkit */ +X +Xtypedef struct +X{ +X PCTObject_HEAD +X cast_keyschedule schedule; +X} CASTobject; +X +Xstatic void +XCASTinit(self, key, keylength) +X CASTobject *self; +X unsigned char *key; +X int keylength; +X{ +X /* presumably this will optimize out */ +X if (sizeof(uint32) < 4 || sizeof(uint8) != 1) { +X PyErr_SetString(PyExc_SystemError, +X "CAST module compiled with bad typedefs!"); +X } +X +X /* make sure the key length is within bounds */ +X if (keylength < 5 || keylength > 16) { +X PyErr_SetString(PyExc_ValueError, "CAST key must be " +X "at least 5 bytes and no more than 16 bytes long"); +X return; +X } +X +X /* do the actual key schedule setup */ +X castschedulekeys(&(self->schedule), key, keylength); +X} +X +Xstatic void +XCASTencrypt(self, block) +X CASTobject *self; +X unsigned char *block; +X{ +X castcrypt(&(self->schedule), block, 0); +X} +X +Xstatic void CASTdecrypt(self, block) +X CASTobject *self; +X unsigned char *block; +X{ +X castcrypt(&(self->schedule), block, 1); +X} +X +X#endif +SHAR_EOF +if test 11926 -ne "`wc -c < 'cast.c'`" +then + echo shar: error transmitting "'cast.c'" '(should have been 11926 characters)' +fi +fi # end of overwriting check +echo shar: extracting "'cast5.c'" '(24921 characters)' +if test -f 'cast5.c' +then + echo shar: will not over-write existing file "'cast5.c'" +else +sed 's/^X//' << \SHAR_EOF > 'cast5.c' +X/* +X These are the S-boxes for CAST5 as given in RFC 2144. +X*/ +X +X +Xstatic const uint32 S1[256] = { +X0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, +X0x9c004dd3, 0x6003e540, 0xcf9fc949, 0xbfd4af27, 0x88bbbdb5, +X0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, +X0x22d4ff8e, 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, +X0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, 0xa1c9e0d6, +X0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, +X0x22568e3a, 0xa2d341d0, 0x66db40c8, 0xa784392f, 0x004dff2f, +X0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, +X0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, +X0x90ecf52e, 0x22b0c054, 0xbc8e5935, 0x4b6d2f7f, 0x50bb64a2, +X0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, +X0x4bff345d, 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, +X0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, 0x882240f2, +X0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, +X0xb949e354, 0xb04669fe, 0xb1b6ab8a, 0xc71358dd, 0x6385c545, +X0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, +X0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, +X0xf61b1891, 0xbb72275e, 0xaa508167, 0x38901091, 0xc6b505eb, +X0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, +X0xaa56d291, 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, +X0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, 0x64459eab, +X0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, +X0x3fab0950, 0x325ff6c2, 0x81383f05, 0x6963c5c8, 0x76cb5ad6, +X0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, +X0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, +X0x051ef495, 0xaa573b04, 0x4a805d8d, 0x548300d0, 0x00322a3c, +X0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, +X0x915a0bf5, 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, +X0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, 0xcfa4bd3f, +X0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, +X0xe31231b2, 0x2ad5ad6c, 0x954329de, 0xadbe4528, 0xd8710f69, +X0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, +X0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, +X0x032268d4, 0xc9600acc, 0xce387e6d, 0xbf6bb16c, 0x6a70fb78, +X0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, +X0xb347cc96, 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, +X0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, 0x3f04442f, +X0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, +X0x2ad37c96, 0x0175cb9d, 0xc69dff09, 0xc75b65f0, 0xd9db40d8, +X0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, +X0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, +X0x51c85f4d, 0x56907596, 0xa5bb15e6, 0x580304f0, 0xca042cf1, +X0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, +X0xbc306ed9, 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, +X0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, 0xaf1fbda7, +X0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, +X0x26470db8, 0xf881814c, 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, +X0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, +X0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, +X0xe1e696ff, 0xb141ab08, 0x7cca89b9, 0x1a69e783, 0x02cc4843, +X0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, +X0x5c8165bf }; +X +Xstatic const uint32 S2[256] = { +X0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, +X0xeec5207a, 0x55889c94, 0x72fc0651, 0xada7ef79, 0x4e1d7235, +X0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, +X0xa1d6eff3, 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, +X0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, 0xd1da4181, +X0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, +X0x25a1ff41, 0xe180f806, 0x1fc41080, 0x179bee7a, 0xd37ac6a9, +X0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, +X0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, +X0x0d554b63, 0x5d681121, 0xc866c359, 0x3d63cf73, 0xcee234c0, +X0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, +X0xe4eb573b, 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, +X0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, 0x10843094, +X0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, +X0xd9e0a227, 0x4ec73a34, 0xfc884f69, 0x3e4de8df, 0xef0e0088, +X0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, +X0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, +X0x27e19ba5, 0xd5a6c252, 0xe49754bd, 0xc5d655dd, 0xeb667064, +X0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, +X0xe5d05860, 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, +X0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, 0xeccf01db, +X0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, +X0x5ee22b95, 0x5f0e5304, 0x81ed6f61, 0x20e74364, 0xb45e1378, +X0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, +X0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, +X0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, 0xa20c3005, 0x8871df63, +X0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, +X0x9f63293c, 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, +X0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, 0x73f98417, +X0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, +X0x7cbad9a2, 0x2180036f, 0x50d99c08, 0xcb3f4861, 0xc26bd765, +X0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, +X0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, +X0x846a3bae, 0x8ff77888, 0xee5d60f6, 0x7af75673, 0x2fdd5cdb, +X0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, +X0xd152de58, 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, +X0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, 0xb8da230c, +X0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, +X0x61a3c9e8, 0xbca8f54d, 0xc72feffa, 0x22822e99, 0x82c570b4, +X0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, +X0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, +X0xf7e19798, 0x7619b72f, 0x8f1c9ba4, 0xdc8637a0, 0x16a7d3b1, +X0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, +X0x520365d6, 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, +X0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, 0x5483697b, +X0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, +X0x6c387e8a, 0x0ae6d249, 0xb284600c, 0xd835731d, 0xdcb1c647, +X0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, +X0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, +X0xa345415e, 0x5c038323, 0x3e5d3bb9, 0x43d79572, 0x7e6dd07c, +X0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, +X0x4523ecf1 }; +X +Xstatic const uint32 S3[256] = { +X0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, +X0x369fe44b, 0x8c1fc644, 0xaececa90, 0xbeb1f9bf, 0xeefbcaea, +X0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, +X0x927010d5, 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, +X0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, 0x553fb2c0, +X0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, +X0x9255c5ed, 0x1257a240, 0x4e1a8302, 0xbae07fff, 0x528246e7, +X0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, +X0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, +X0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, 0x99b03dbf, 0xb5dbc64b, +X0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, +X0xccc36f71, 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, +X0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, 0xa747d2d0, +X0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, +X0x0a0fb402, 0x0f7fef82, 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, +X0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, +X0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, +X0xe83ec305, 0x4f91751a, 0x925669c2, 0x23efe941, 0xa903f12e, +X0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, +X0x02778176, 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, +X0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, 0xef303cab, +X0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, +X0x7d29dc96, 0x2756d3dc, 0x8b907cee, 0xb51fd240, 0xe7c07ce3, +X0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, +X0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, +X0xbda8229c, 0x127dadaa, 0x438a074e, 0x1f97c090, 0x081bdb8a, +X0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, +X0x64380e51, 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, +X0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, 0x4b39fffa, +X0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, +X0x27627545, 0x825cf47a, 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, +X0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, +X0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, +X0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, 0x1f081fab, 0x108618ae, +X0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, +X0x2c3f8cc5, 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, +X0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, 0x3a609437, +X0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, +X0x02717ef6, 0x4feb5536, 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, +X0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, +X0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, +X0xabcc4f33, 0x7688c55d, 0x7b00a6b0, 0x947b0001, 0x570075d2, +X0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, +X0xee971b69, 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, +X0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, 0x67214cb8, +X0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, +X0x606e6dc6, 0x60543a49, 0x5727c148, 0x2be98a1d, 0x8ab41738, +X0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, +X0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, +X0x9c305a00, 0x52bce688, 0x1b03588a, 0xf7baefd5, 0x4142ed9c, +X0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, +X0xee353783 }; +X +Xstatic const uint32 S4[256] = { +X0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, +X0x64ad8c57, 0x85510443, 0xfa020ed1, 0x7e287aff, 0xe60fb663, +X0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, +X0x241e4adf, 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, +X0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, 0xee4d111a, +X0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, +X0x081b08ca, 0x05170121, 0x80530100, 0xe83e5efe, 0xac9af4f8, +X0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, +X0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, +X0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, 0x2649abdf, 0xaea0c7f5, +X0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, +X0xf80eb2bb, 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, +X0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, 0x4d351805, +X0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, +X0x9f46222f, 0x3991467d, 0xa5bf6d8e, 0x1143c44f, 0x43958302, +X0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, +X0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, +X0x79098b02, 0xe4eabb81, 0x28123b23, 0x69dead38, 0x1574ca16, +X0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, +X0x09114003, 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, +X0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, 0xe756bdff, +X0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, +X0x6b65811c, 0x5e146119, 0x6e85cb75, 0xbe07c002, 0xc2325577, +X0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, +X0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, +X0xeca1d7c7, 0x041afa32, 0x1d16625a, 0x6701902c, 0x9b757a54, +X0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, +X0x56e55a79, 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, +X0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, 0xb7747f9d, +X0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, +X0x213d42f6, 0x2c1c7c26, 0x61c2f50f, 0x6552daf9, 0xd2c231f8, +X0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, +X0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, +X0x311170a7, 0x3e9b640c, 0xcc3e10d7, 0xd5cad3b6, 0x0caec388, +X0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, +X0xc1de8417, 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, +X0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, 0x6f7de532, +X0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, +X0x001d7b95, 0x82e5e7d2, 0x109873f6, 0x00613096, 0xc32d9521, +X0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, +X0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, +X0x0ce454a9, 0xd60acd86, 0x015f1919, 0x77079103, 0xdea03af6, +X0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, +X0xb8a5c3ef, 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, +X0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, 0x39e4460c, +X0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, +X0x492fc295, 0x9266beab, 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, +X0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, +X0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, +X0x932bcdf6, 0xb657c34d, 0x4edfd282, 0x7ae5290c, 0x3cb9536b, +X0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, +X0x0aef7ed2 }; +X +Xstatic const uint32 S5[256] = { +X0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, +X0x1dd358f5, 0x44dd9d44, 0x1731167f, 0x08fbf1fa, 0xe7f511cc, +X0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, +X0x69befd7a, 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, +X0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, 0x5f480a01, +X0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, +X0x8dba1cfe, 0x41a99b02, 0x1a550a04, 0xba8f65cb, 0x7251f4e7, +X0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, +X0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, +X0x8709e6b0, 0xd7e07156, 0x4e29fea7, 0x6366e52d, 0x02d1c000, +X0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, +X0xd642a0c9, 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, +X0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, 0x5c1ff900, +X0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, +X0x90c79505, 0xb0a8a774, 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, +X0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, +X0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, +X0x524755f4, 0x03b63cc9, 0x0cc844b2, 0xbcf3f0aa, 0x87ac36e9, +X0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, +X0x01c94910, 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, +X0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, 0x136e05db, +X0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, +X0xb6f589de, 0xec2941da, 0x26e46695, 0xb7566419, 0xf654efc5, +X0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, +X0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, +X0x9e0885f9, 0x68cb3e47, 0x086c010f, 0xa21de820, 0xd18b69de, +X0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, +X0xb0d70eba, 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, +X0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, 0x580a249f, +X0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, +X0x646c6bd7, 0x44904db3, 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, +X0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, +X0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, +X0xc1092910, 0x8bc95fc6, 0x7d869cf4, 0x134f616f, 0x2e77118d, +X0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, +X0xaf462ba2, 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, +X0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, 0x445f7382, +X0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, +X0x20936079, 0x459b80a5, 0xbe60e2db, 0xa9c23101, 0xeba5315c, +X0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, +X0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, +X0x75922283, 0x784d6b17, 0x58ebb16e, 0x44094f85, 0x3f481d87, +X0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, +X0x2b092801, 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, +X0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, 0x6cf6e479, +X0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, +X0xa09c7f70, 0x5346aba0, 0x5ce96c28, 0xe176eda3, 0x6bac307f, +X0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, +X0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, +X0xeeb9491d, 0x34010718, 0xbb30cab8, 0xe822fe15, 0x88570983, +X0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, +X0xefe9e7d4 }; +X +Xstatic const uint32 S6[256] = { +X0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, +X0x016843b4, 0xeced5cbc, 0x325553ac, 0xbf9f0960, 0xdfa1e2ed, +X0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, +X0x8989b138, 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, +X0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, 0xa3149619, +X0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, +X0xa888614a, 0x2900af98, 0x01665991, 0xe1992863, 0xc8f30c60, +X0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, +X0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, +X0x4c7f4448, 0xdab5d440, 0x6dba0ec3, 0x083919a7, 0x9fbaeed9, +X0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, +X0xba7dd9cd, 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, +X0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, 0x284caf89, +X0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, +X0xefe8c36e, 0xf890cdd9, 0x80226dae, 0xc340a4a3, 0xdf7e9c09, +X0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, +X0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, +X0xcf222ebf, 0x25ac6f48, 0xa9a99387, 0x53bddb65, 0xe76ffbe7, +X0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, +X0xc8087dfc, 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, +X0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, 0x5f04456d, +X0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, +X0xe2220abe, 0xd2916ebf, 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, +X0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, +X0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, +X0x692573e4, 0xe9a9d848, 0xf3160289, 0x3a62ef1d, 0xa787e238, +X0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, +X0x592af950, 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, +X0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, 0x89dff0bb, +X0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, +X0xdc049441, 0xc8098f9b, 0x7dede786, 0xc39a3373, 0x42410005, +X0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, +X0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, +X0x1f8fb214, 0xd372cf08, 0xcc3c4a13, 0x8cf63166, 0x061c87be, +X0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, +X0x3fc06976, 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, +X0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, 0x3007cd3e, +X0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, +X0x8809286c, 0xf592d891, 0x08a930f6, 0x957ef305, 0xb7fbffbd, +X0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, +X0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, +X0x257f0c3d, 0x9348af49, 0x361400bc, 0xe8816f4a, 0x3814f200, +X0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, +X0x54f4a084, 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, +X0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, 0x653d7e6a, +X0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, +X0x0404a8c8, 0xb8e5a121, 0xb81a928a, 0x60ed5869, 0x97c55b96, +X0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, +X0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, +X0xf544edeb, 0xb0e93524, 0xbebb8fbd, 0xa2d762cf, 0x49c92f54, +X0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, +X0xd675cf2f }; +X +Xstatic const uint32 S7[256] = { +X0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, +X0xab9bc912, 0xde6008a1, 0x2028da1f, 0x0227bce7, 0x4d642916, +X0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, +X0xb28707de, 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, +X0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, 0x4d495001, +X0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, +X0x1286becf, 0xb6eacb19, 0x2660c200, 0x7565bde4, 0x64241f7a, +X0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, +X0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, +X0xeb12ff82, 0xe3486911, 0xd34d7516, 0x4e7b3aff, 0x5f43671b, +X0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, +X0xcb3a6c88, 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, +X0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, 0x0a961288, +X0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, +X0xcf19df58, 0xbec3f756, 0xc06eba30, 0x07211b24, 0x45c28829, +X0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, +X0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, +X0xaff60ff4, 0xea2c4e6d, 0x16e39264, 0x92544a8b, 0x009b4fc3, +X0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, +X0xbe838688, 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, +X0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, 0xda6d0c74, +X0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, +X0xeed82b29, 0x1d382fe3, 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, +X0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, +X0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, +X0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, 0xe7225308, 0x8b75cf77, +X0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, +X0x5dda0033, 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, +X0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, 0x2711fd60, +X0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, +X0x488dcf25, 0x36c9d566, 0x28e74e41, 0xc2610aca, 0x3d49a9cf, +X0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, +X0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, +X0x2b9f4fd5, 0x625aba82, 0x6a017962, 0x2ec01b9c, 0x15488aa9, +X0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, +X0x3453dc1e, 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, +X0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, 0x66626c1c, +X0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, +X0x9ea294fb, 0x52cf564c, 0x9883fe66, 0x2ec40581, 0x763953c3, +X0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, +X0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, +X0x3d321c5d, 0xc3f5e194, 0x4b269301, 0xc79f022f, 0x3c997e7e, +X0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, +X0xc61e45be, 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, +X0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, 0x1814386b, +X0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, +X0x5479f8e6, 0x1cb8d647, 0x97fd61a9, 0xea7759f4, 0x2d57539d, +X0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, +X0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, +X0x3d40f021, 0xc3c0bdae, 0x4958c24c, 0x518f36b2, 0x84b1d370, +X0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, +X0x954b8aa3 }; +X +Xstatic const uint32 S8[256] = { +X0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, +X0xe6c1121b, 0x0e241600, 0x052ce8b5, 0x11a9cfb0, 0xe5952f11, +X0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, +X0x37ddddfc, 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, +X0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, 0x0b15a15d, +X0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, +X0x72df191b, 0x7580330d, 0x94074251, 0x5c7dcdfa, 0xabbe6d63, +X0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, +X0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, +X0xce949ad4, 0xb84769ad, 0x965bd862, 0x82f3d055, 0x66fb9767, +X0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, +X0x647a78fc, 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, +X0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, 0xbbd35049, +X0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, +X0x58cb7e07, 0x3b74ef2e, 0x522fffb1, 0xd24708cc, 0x1c7e27cd, +X0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, +X0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, +X0xc18910b1, 0xe11dbf7b, 0x06cd1af8, 0x7170c608, 0x2d5e3354, +X0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, +X0x77d51b42, 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, +X0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, 0xe6459788, +X0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, +X0x24259fd7, 0xf8bef472, 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, +X0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, +X0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, +X0xea7a6e98, 0x7cd16efc, 0x1436876c, 0xf1544107, 0xbedeee14, +X0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, +X0x151682eb, 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, +X0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, 0xb6f2cf3b, +X0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, +X0xbae7dfdc, 0x42cbda70, 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, +X0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, +X0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, +X0xc5c8b37e, 0x0d809ea2, 0x398feb7c, 0x132a4f94, 0x43b7950e, +X0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, +X0xacf3ebc3, 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, +X0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, 0xe87b40e4, +X0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, +X0x38d7e5b2, 0x57720101, 0x730edebc, 0x5b643113, 0x94917e4f, +X0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, +X0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, +X0xad1163ed, 0xea7b5965, 0x1a00726e, 0x11403092, 0x00da6d77, +X0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, +X0xcee7d28a, 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, +X0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, 0xaa12e4f2, +X0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, +X0x67cdb156, 0x350d8384, 0x5938fa0f, 0x42399ef3, 0x36997b07, +X0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, +X0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, +X0x0d2059d1, 0xa466bb1e, 0xf8da0a82, 0x04f19130, 0xba6e4ec0, +X0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, +X0xea8bf59e }; +X +SHAR_EOF +if test 24921 -ne "`wc -c < 'cast5.c'`" +then + echo shar: error transmitting "'cast5.c'" '(should have been 24921 characters)' +fi +fi # end of overwriting check +echo shar: extracting "'casttest.py'" '(675 characters)' +if test -f 'casttest.py' +then + echo shar: will not over-write existing file "'casttest.py'" +else +sed 's/^X//' << \SHAR_EOF > 'casttest.py' +X +Ximport cast +X +Xtestkey = '\x01\x23\x45\x67\x12\x34\x56\x78\x23\x45\x67\x89\x34\x56\x78\x9A' +Xtestdata = '\x01\x23\x45\x67\x89\xAB\xCD\xEF' +X +Xcipher128 = '\x23\x8B\x4F\xE5\x84\x7E\x44\xB2' +Xcipher80 = '\xEB\x6A\x71\x1A\x2C\x02\x27\x1B' +Xcipher40 = '\x7A\xC8\x16\xD1\x6E\x9B\x30\x2E' +X +Xprint 'cast-128:', +Xk = cast.new(testkey, cast.ECB) +Xif k.encrypt(testdata) == cipher128: +X print 'ok' +Xelse: +X print 'not ok' +X +Xprint 'cast5-80:', +Xk = cast.new(testkey[:10], cast.ECB) +Xif k.encrypt(testdata) == cipher80: +X print 'ok' +Xelse: +X print 'not ok' +X +Xprint 'cast5-40:', +Xk = cast.new(testkey[:5], cast.ECB) +Xif k.encrypt(testdata) == cipher40: +X print 'ok' +Xelse: +X print 'not ok' +X +X +SHAR_EOF +if test 675 -ne "`wc -c < 'casttest.py'`" +then + echo shar: error transmitting "'casttest.py'" '(should have been 675 characters)' +fi +fi # end of overwriting check +# End of shell archive +exit 0 + + + + +-----BEGIN PGP SIGNATURE----- +Version: 2.6.2 +Comment: Processed by Mailcrypt 3.4, an Emacs/PGP interface + +iQCVAwUBM90jSV8UnN8n93LBAQFiFgQAqoqtg7jvvYdJpfsVu3Qg5FbbBI9EH1An +NtRKtUTU44v/cF1Bs/J7WBXtWrEC7nqb4LApFSL8RcKrWDZRcjFhb4nHyFkJpAg8 +hSHXi3LTZLMh1D/o0cm5YA/BCp1RnAQCPpnSJvfPc/H6m2KpjmdeZcp1dYBF77EH +vpa7UKaJuDg= +=i+5W +-----END PGP SIGNATURE----- + +
\ No newline at end of file diff --git a/info/rfc2104.html b/info/rfc2104.html new file mode 100644 index 0000000..6eab319 --- /dev/null +++ b/info/rfc2104.html @@ -0,0 +1,625 @@ +<!-- X-URL: http://www.pmg.lcs.mit.edu/cgi-bin/rfc/view-plain?number=2104 --> +<BASE HREF="http://www.pmg.lcs.mit.edu/cgi-bin/rfc/view-plain?number=2104"> + +<title>RFC 2104</title> +<pre width=80> + + + + + + +Network Working Group H. Krawczyk +Request for Comments: <a href="/cgi-bin/rfc/view?2104">2104</a> IBM +Category: Informational M. Bellare + UCSD + R. Canetti + IBM + February <a href="/cgi-bin/rfc/view?1997">1997</a> + + + HMAC: Keyed-Hashing for Message Authentication + +Status of This Memo + + This memo provides information for the Internet community. This memo + does not specify an Internet standard of any kind. Distribution of + this memo is unlimited. + +Abstract + + This document describes HMAC, a mechanism for message authentication + using cryptographic hash functions. HMAC can be used with any + iterative cryptographic hash function, e.g., MD5, SHA-1, in + combination with a secret shared key. The cryptographic strength of + HMAC depends on the properties of the underlying hash function. + +1. Introduction + + Providing a way to check the integrity of information transmitted + over or stored in an unreliable medium is a prime necessity in the + world of open computing and communications. Mechanisms that provide + such integrity check based on a secret key are usually called + "message authentication codes" (MAC). Typically, message + authentication codes are used between two parties that share a secret + key in order to validate information transmitted between these + parties. In this document we present such a MAC mechanism based on + cryptographic hash functions. This mechanism, called HMAC, is based + on work by the authors [BCK1] where the construction is presented and + cryptographically analyzed. We refer to that work for the details on + the rationale and security analysis of HMAC, and its comparison to + other keyed-hash methods. + + + + + + + + + + + +Krawczyk, et. al. Informational [Page 1] + +RFC <a href="/cgi-bin/rfc/view?2104">2104</a> HMAC February <a href="/cgi-bin/rfc/view?1997">1997</a> + + + HMAC can be used in combination with any iterated cryptographic hash + function. MD5 and SHA-1 are examples of such hash functions. HMAC + also uses a secret key for calculation and verification of the + message authentication values. The main goals behind this + construction are + + * To use, without modifications, available hash functions. + In particular, hash functions that perform well in software, + and for which code is freely and widely available. + + * To preserve the original performance of the hash function without + incurring a significant degradation. + + * To use and handle keys in a simple way. + + * To have a well understood cryptographic analysis of the strength of + the authentication mechanism based on reasonable assumptions on the + underlying hash function. + + * To allow for easy replaceability of the underlying hash function in + case that faster or more secure hash functions are found or + required. + + This document specifies HMAC using a generic cryptographic hash + function (denoted by H). Specific instantiations of HMAC need to + define a particular hash function. Current candidates for such hash + functions include SHA-1 [SHA], MD5 [MD5], RIPEMD-<a href="/cgi-bin/rfc/view?128">128</a>/<a href="/cgi-bin/rfc/view?160">160</a> [RIPEMD]. + These different realizations of HMAC will be denoted by HMAC-SHA1, + HMAC-MD5, HMAC-RIPEMD, etc. + + Note: To the date of writing of this document MD5 and SHA-1 are the + most widely used cryptographic hash functions. MD5 has been recently + shown to be vulnerable to collision search attacks [Dobb]. This + attack and other currently known weaknesses of MD5 do not compromise + the use of MD5 within HMAC as specified in this document (see + [Dobb]); however, SHA-1 appears to be a cryptographically stronger + function. To this date, MD5 can be considered for use in HMAC for + applications where the superior performance of MD5 is critical. In + any case, implementers and users need to be aware of possible + cryptanalytic developments regarding any of these cryptographic hash + functions, and the eventual need to replace the underlying hash + function. (See section 6 for more information on the security of + HMAC.) + + + + + + + + +Krawczyk, et. al. Informational [Page 2] + +RFC <a href="/cgi-bin/rfc/view?2104">2104</a> HMAC February <a href="/cgi-bin/rfc/view?1997">1997</a> + + +2. Definition of HMAC + + The definition of HMAC requires a cryptographic hash function, which + we denote by H, and a secret key K. We assume H to be a cryptographic + hash function where data is hashed by iterating a basic compression + function on blocks of data. We denote by B the byte-length of such + blocks (B=64 for all the above mentioned examples of hash functions), + and by L the byte-length of hash outputs (L=16 for MD5, L=20 for + SHA-1). The authentication key K can be of any length up to B, the + block length of the hash function. Applications that use keys longer + than B bytes will first hash the key using H and then use the + resultant L byte string as the actual key to HMAC. In any case the + minimal recommended length for K is L bytes (as the hash output + length). See section 3 for more information on keys. + + We define two fixed and different strings ipad and opad as follows + (the 'i' and 'o' are mnemonics for inner and outer): + + ipad = the byte 0x36 repeated B times + opad = the byte 0x5C repeated B times. + + To compute HMAC over the data `text' we perform + + H(K XOR opad, H(K XOR ipad, text)) + + Namely, + + (1) append zeros to the end of K to create a B byte string + (e.g., if K is of length 20 bytes and B=64, then K will be + appended with 44 zero bytes 0x00) + (2) XOR (bitwise exclusive-OR) the B byte string computed in step + (1) with ipad + (3) append the stream of data 'text' to the B byte string resulting + from step (2) + (4) apply H to the stream generated in step (3) + (5) XOR (bitwise exclusive-OR) the B byte string computed in + step (1) with opad + (6) append the H result from step (4) to the B byte string + resulting from step (5) + (7) apply H to the stream generated in step (6) and output + the result + + For illustration purposes, sample code based on MD5 is provided as an + appendix. + + + + + + + +Krawczyk, et. al. Informational [Page 3] + +RFC <a href="/cgi-bin/rfc/view?2104">2104</a> HMAC February <a href="/cgi-bin/rfc/view?1997">1997</a> + + +3. Keys + + The key for HMAC can be of any length (keys longer than B bytes are + first hashed using H). However, less than L bytes is strongly + discouraged as it would decrease the security strength of the + function. Keys longer than L bytes are acceptable but the extra + length would not significantly increase the function strength. (A + longer key may be advisable if the randomness of the key is + considered weak.) + + Keys need to be chosen at random (or using a cryptographically strong + pseudo-random generator seeded with a random seed), and periodically + refreshed. (Current attacks do not indicate a specific recommended + frequency for key changes as these attacks are practically + infeasible. However, periodic key refreshment is a fundamental + security practice that helps against potential weaknesses of the + function and keys, and limits the damage of an exposed key.) + +4. Implementation Note + + HMAC is defined in such a way that the underlying hash function H can + be used with no modification to its code. In particular, it uses the + function H with the pre-defined initial value IV (a fixed value + specified by each iterative hash function to initialize its + compression function). However, if desired, a performance + improvement can be achieved at the cost of (possibly) modifying the + code of H to support variable IVs. + + The idea is that the intermediate results of the compression function + on the B-byte blocks (K XOR ipad) and (K XOR opad) can be precomputed + only once at the time of generation of the key K, or before its first + use. These intermediate results are stored and then used to + initialize the IV of H each time that a message needs to be + authenticated. This method saves, for each authenticated message, + the application of the compression function of H on two B-byte blocks + (i.e., on (K XOR ipad) and (K XOR opad)). Such a savings may be + significant when authenticating short streams of data. We stress + that the stored intermediate values need to be treated and protected + the same as secret keys. + + Choosing to implement HMAC in the above way is a decision of the + local implementation and has no effect on inter-operability. + + + + + + + + + +Krawczyk, et. al. Informational [Page 4] + +RFC <a href="/cgi-bin/rfc/view?2104">2104</a> HMAC February <a href="/cgi-bin/rfc/view?1997">1997</a> + + +5. Truncated output + + A well-known practice with message authentication codes is to + truncate the output of the MAC and output only part of the bits + (e.g., [MM, ANSI]). Preneel and van Oorschot [PV] show some + analytical advantages of truncating the output of hash-based MAC + functions. The results in this area are not absolute as for the + overall security advantages of truncation. It has advantages (less + information on the hash result available to an attacker) and + disadvantages (less bits to predict for the attacker). Applications + of HMAC can choose to truncate the output of HMAC by outputting the t + leftmost bits of the HMAC computation for some parameter t (namely, + the computation is carried in the normal way as defined in section 2 + above but the end result is truncated to t bits). We recommend that + the output length t be not less than half the length of the hash + output (to match the birthday attack bound) and not less than 80 bits + (a suitable lower bound on the number of bits that need to be + predicted by an attacker). We propose denoting a realization of HMAC + that uses a hash function H with t bits of output as HMAC-H-t. For + example, HMAC-SHA1-80 denotes HMAC computed using the SHA-1 function + and with the output truncated to 80 bits. (If the parameter t is not + specified, e.g. HMAC-MD5, then it is assumed that all the bits of the + hash are output.) + +6. Security + + The security of the message authentication mechanism presented here + depends on cryptographic properties of the hash function H: the + resistance to collision finding (limited to the case where the + initial value is secret and random, and where the output of the + function is not explicitly available to the attacker), and the + message authentication property of the compression function of H when + applied to single blocks (in HMAC these blocks are partially unknown + to an attacker as they contain the result of the inner H computation + and, in particular, cannot be fully chosen by the attacker). + + These properties, and actually stronger ones, are commonly assumed + for hash functions of the kind used with HMAC. In particular, a hash + function for which the above properties do not hold would become + unsuitable for most (probably, all) cryptographic applications, + including alternative message authentication schemes based on such + functions. (For a complete analysis and rationale of the HMAC + function the reader is referred to [BCK1].) + + + + + + + + +Krawczyk, et. al. Informational [Page 5] + +RFC <a href="/cgi-bin/rfc/view?2104">2104</a> HMAC February <a href="/cgi-bin/rfc/view?1997">1997</a> + + + Given the limited confidence gained so far as for the cryptographic + strength of candidate hash functions, it is important to observe the + following two properties of the HMAC construction and its secure use + for message authentication: + + 1. The construction is independent of the details of the particular + hash function H in use and then the latter can be replaced by any + other secure (iterative) cryptographic hash function. + + 2. Message authentication, as opposed to encryption, has a + "transient" effect. A published breaking of a message authentication + scheme would lead to the replacement of that scheme, but would have + no adversarial effect on information authenticated in the past. This + is in sharp contrast with encryption, where information encrypted + today may suffer from exposure in the future if, and when, the + encryption algorithm is broken. + + The strongest attack known against HMAC is based on the frequency of + collisions for the hash function H ("birthday attack") [PV,BCK2], and + is totally impractical for minimally reasonable hash functions. + + As an example, if we consider a hash function like MD5 where the + output length equals L=16 bytes (<a href="/cgi-bin/rfc/view?128">128</a> bits) the attacker needs to + acquire the correct message authentication tags computed (with the + _same_ secret key K!) on about 2**64 known plaintexts. This would + require the processing of at least 2**64 blocks under H, an + impossible task in any realistic scenario (for a block length of 64 + bytes this would take <a href="/cgi-bin/rfc/view?250">250</a>,000 years in a continuous 1Gbps link, and + without changing the secret key K during all this time). This attack + could become realistic only if serious flaws in the collision + behavior of the function H are discovered (e.g. collisions found + after 2**30 messages). Such a discovery would determine the immediate + replacement of the function H (the effects of such failure would be + far more severe for the traditional uses of H in the context of + digital signatures, public key certificates, etc.). + + Note: this attack needs to be strongly contrasted with regular + collision attacks on cryptographic hash functions where no secret key + is involved and where 2**64 off-line parallelizable (!) operations + suffice to find collisions. The latter attack is approaching + feasibility [VW] while the birthday attack on HMAC is totally + impractical. (In the above examples, if one uses a hash function + with, say, <a href="/cgi-bin/rfc/view?160">160</a> bit of output then 2**64 should be replaced by 2**80.) + + + + + + + + +Krawczyk, et. al. Informational [Page 6] + +RFC <a href="/cgi-bin/rfc/view?2104">2104</a> HMAC February <a href="/cgi-bin/rfc/view?1997">1997</a> + + + A correct implementation of the above construction, the choice of + random (or cryptographically pseudorandom) keys, a secure key + exchange mechanism, frequent key refreshments, and good secrecy + protection of keys are all essential ingredients for the security of + the integrity verification mechanism provided by HMAC. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Krawczyk, et. al. Informational [Page 7] + +RFC <a href="/cgi-bin/rfc/view?2104">2104</a> HMAC February <a href="/cgi-bin/rfc/view?1997">1997</a> + + +Appendix -- Sample Code + + For the sake of illustration we provide the following sample code for + the implementation of HMAC-MD5 as well as some corresponding test + vectors (the code is based on MD5 code as described in [MD5]). + +/* +** Function: hmac_md5 +*/ + +void +hmac_md5(text, text_len, key, key_len, digest) +unsigned char* text; /* pointer to data stream */ +int text_len; /* length of data stream */ +unsigned char* key; /* pointer to authentication key */ +int key_len; /* length of authentication key */ +caddr_t digest; /* caller digest to be filled in */ + +{ + MD5_CTX context; + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + + + +Krawczyk, et. al. Informational [Page 8] + +RFC <a href="/cgi-bin/rfc/view?2104">2104</a> HMAC February <a href="/cgi-bin/rfc/view?1997">1997</a> + + + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + bzero( k_ipad, sizeof k_ipad); + bzero( k_opad, sizeof k_opad); + bcopy( key, k_ipad, key_len); + bcopy( key, k_opad, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + /* + * perform inner MD5 + */ + MD5Init(&context); /* init context for 1st + * pass */ + MD5Update(&context, k_ipad, 64) /* start with inner pad */ + MD5Update(&context, text, text_len); /* then text of datagram */ + MD5Final(digest, &context); /* finish up 1st pass */ + /* + * perform outer MD5 + */ + MD5Init(&context); /* init context for 2nd + * pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, digest, 16); /* then results of 1st + * hash */ + MD5Final(digest, &context); /* finish up 2nd pass */ +} + +Test Vectors (Trailing '\0' of a character string not included in test): + + key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + key_len = 16 bytes + data = "Hi There" + data_len = 8 bytes + digest = 0x<a href="/cgi-bin/rfc/view?9294727">9294727</a>a<a href="/cgi-bin/rfc/view?3638">3638</a>bb1c13f48ef<a href="/cgi-bin/rfc/view?8158">8158</a>bfc9d + + key = "Jefe" + data = "what do ya want for nothing?" + data_len = 28 bytes + digest = 0x<a href="/cgi-bin/rfc/view?750">750</a>c<a href="/cgi-bin/rfc/view?783">783</a>e6ab0b<a href="/cgi-bin/rfc/view?503">503</a>eaa86e<a href="/cgi-bin/rfc/view?310">310</a>a5db<a href="/cgi-bin/rfc/view?738">738</a> + + key = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + + +Krawczyk, et. al. Informational [Page 9] + +RFC <a href="/cgi-bin/rfc/view?2104">2104</a> HMAC February <a href="/cgi-bin/rfc/view?1997">1997</a> + + + key_len 16 bytes + data = 0xDDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD + data_len = 50 bytes + digest = 0x56be<a href="/cgi-bin/rfc/view?34521">34521</a>d<a href="/cgi-bin/rfc/view?144">144</a>c88dbb8c<a href="/cgi-bin/rfc/view?733">733</a>f0e8b3f6 + +Acknowledgments + + Pau-Chen Cheng, Jeff Kraemer, and Michael Oehler, have provided + useful comments on early drafts, and ran the first interoperability + tests of this specification. Jeff and Pau-Chen kindly provided the + sample code and test vectors that appear in the appendix. Burt + Kaliski, Bart Preneel, Matt Robshaw, Adi Shamir, and Paul van + Oorschot have provided useful comments and suggestions during the + investigation of the HMAC construction. + +References + + [ANSI] ANSI X9.9, "American National Standard for Financial + Institution Message Authentication (Wholesale)," American + Bankers Association, <a href="/cgi-bin/rfc/view?1981">1981</a>. Revised <a href="/cgi-bin/rfc/view?1986">1986</a>. + + [Atk] Atkinson, R., "IP Authentication Header", RFC <a href="/cgi-bin/rfc/view?1826">1826</a>, August + <a href="/cgi-bin/rfc/view?1995">1995</a>. + + [BCK1] M. Bellare, R. Canetti, and H. Krawczyk, + "Keyed Hash Functions and Message Authentication", + Proceedings of Crypto'96, LNCS <a href="/cgi-bin/rfc/view?1109">1109</a>, pp. 1-15. + (http://www.research.ibm.com/security/keyed-md5.html) + + [BCK2] M. Bellare, R. Canetti, and H. Krawczyk, + "Pseudorandom Functions Revisited: The Cascade Construction", + Proceedings of FOCS'96. + + [Dobb] H. Dobbertin, "The Status of MD5 After a Recent Attack", + RSA Labs' CryptoBytes, Vol. 2 No. 2, Summer <a href="/cgi-bin/rfc/view?1996">1996</a>. + http://www.rsa.com/rsalabs/pubs/cryptobytes.html + + [PV] B. Preneel and P. van Oorschot, "Building fast MACs from hash + functions", Advances in Cryptology -- CRYPTO'95 Proceedings, + Lecture Notes in Computer Science, Springer-Verlag Vol.<a href="/cgi-bin/rfc/view?963">963</a>, + <a href="/cgi-bin/rfc/view?1995">1995</a>, pp. 1-14. + + [MD5] Rivest, R., "The MD5 Message-Digest Algorithm", + RFC <a href="/cgi-bin/rfc/view?1321">1321</a>, April <a href="/cgi-bin/rfc/view?1992">1992</a>. + + + +Krawczyk, et. al. Informational [Page 10] + +RFC <a href="/cgi-bin/rfc/view?2104">2104</a> HMAC February <a href="/cgi-bin/rfc/view?1997">1997</a> + + + [MM] Meyer, S. and Matyas, S.M., Cryptography, New York Wiley, + <a href="/cgi-bin/rfc/view?1982">1982</a>. + + [RIPEMD] H. Dobbertin, A. Bosselaers, and B. Preneel, "RIPEMD-<a href="/cgi-bin/rfc/view?160">160</a>: A + strengthened version of RIPEMD", Fast Software Encryption, + LNCS Vol <a href="/cgi-bin/rfc/view?1039">1039</a>, pp. 71-82. + ftp://ftp.esat.kuleuven.ac.be/pub/COSIC/bosselae/ripemd/. + + [SHA] NIST, FIPS PUB <a href="/cgi-bin/rfc/view?180">180</a>-1: Secure Hash Standard, April <a href="/cgi-bin/rfc/view?1995">1995</a>. + + [Tsu] G. Tsudik, "Message authentication with one-way hash + functions", In Proceedings of Infocom'92, May <a href="/cgi-bin/rfc/view?1992">1992</a>. + (Also in "Access Control and Policy Enforcement in + Internetworks", Ph.D. Dissertation, Computer Science + Department, University of Southern California, April <a href="/cgi-bin/rfc/view?1991">1991</a>.) + + [VW] P. van Oorschot and M. Wiener, "Parallel Collision + Search with Applications to Hash Functions and Discrete + Logarithms", Proceedings of the 2nd ACM Conf. Computer and + Communications Security, Fairfax, VA, November <a href="/cgi-bin/rfc/view?1994">1994</a>. + +Authors' Addresses + + Hugo Krawczyk + IBM T.J. Watson Research Center + P.O.Box <a href="/cgi-bin/rfc/view?704">704</a> + Yorktown Heights, NY <a href="/cgi-bin/rfc/view?10598">10598</a> + + EMail: hugo@watson.ibm.com + + Mihir Bellare + Dept of Computer Science and Engineering + Mail Code 0<a href="/cgi-bin/rfc/view?114">114</a> + University of California at San Diego + <a href="/cgi-bin/rfc/view?9500">9500</a> Gilman Drive + La Jolla, CA <a href="/cgi-bin/rfc/view?92093">92093</a> + + EMail: mihir@cs.ucsd.edu + + Ran Canetti + IBM T.J. Watson Research Center + P.O.Box <a href="/cgi-bin/rfc/view?704">704</a> + Yorktown Heights, NY <a href="/cgi-bin/rfc/view?10598">10598</a> + + EMail: canetti@watson.ibm.com + + + + + + +Krawczyk, et. al. Informational [Page 11] + +</pre> diff --git a/info/rfc2104.txt b/info/rfc2104.txt new file mode 100644 index 0000000..0291fe4 --- /dev/null +++ b/info/rfc2104.txt @@ -0,0 +1,619 @@ + + + + + + + +Network Working Group H. Krawczyk +Request for Comments: 2104 IBM +Category: Informational M. Bellare + UCSD + R. Canetti + IBM + February 1997 + + + HMAC: Keyed-Hashing for Message Authentication + +Status of This Memo + + This memo provides information for the Internet community. This memo + does not specify an Internet standard of any kind. Distribution of + this memo is unlimited. + +Abstract + + This document describes HMAC, a mechanism for message authentication + using cryptographic hash functions. HMAC can be used with any + iterative cryptographic hash function, e.g., MD5, SHA-1, in + combination with a secret shared key. The cryptographic strength of + HMAC depends on the properties of the underlying hash function. + +1. Introduction + + Providing a way to check the integrity of information transmitted + over or stored in an unreliable medium is a prime necessity in the + world of open computing and communications. Mechanisms that provide + such integrity check based on a secret key are usually called + "message authentication codes" (MAC). Typically, message + authentication codes are used between two parties that share a secret + key in order to validate information transmitted between these + parties. In this document we present such a MAC mechanism based on + cryptographic hash functions. This mechanism, called HMAC, is based + on work by the authors [BCK1] where the construction is presented and + cryptographically analyzed. We refer to that work for the details on + the rationale and security analysis of HMAC, and its comparison to + other keyed-hash methods. + + + + + + + + + + + +Krawczyk, et. al. Informational [Page 1] + +RFC 2104 HMAC February 1997 + + + HMAC can be used in combination with any iterated cryptographic hash + function. MD5 and SHA-1 are examples of such hash functions. HMAC + also uses a secret key for calculation and verification of the + message authentication values. The main goals behind this + construction are + + * To use, without modifications, available hash functions. + In particular, hash functions that perform well in software, + and for which code is freely and widely available. + + * To preserve the original performance of the hash function without + incurring a significant degradation. + + * To use and handle keys in a simple way. + + * To have a well understood cryptographic analysis of the strength of + the authentication mechanism based on reasonable assumptions on the + underlying hash function. + + * To allow for easy replaceability of the underlying hash function in + case that faster or more secure hash functions are found or + required. + + This document specifies HMAC using a generic cryptographic hash + function (denoted by H). Specific instantiations of HMAC need to + define a particular hash function. Current candidates for such hash + functions include SHA-1 [SHA], MD5 [MD5], RIPEMD-128/160 [RIPEMD]. + These different realizations of HMAC will be denoted by HMAC-SHA1, + HMAC-MD5, HMAC-RIPEMD, etc. + + Note: To the date of writing of this document MD5 and SHA-1 are the + most widely used cryptographic hash functions. MD5 has been recently + shown to be vulnerable to collision search attacks [Dobb]. This + attack and other currently known weaknesses of MD5 do not compromise + the use of MD5 within HMAC as specified in this document (see + [Dobb]); however, SHA-1 appears to be a cryptographically stronger + function. To this date, MD5 can be considered for use in HMAC for + applications where the superior performance of MD5 is critical. In + any case, implementers and users need to be aware of possible + cryptanalytic developments regarding any of these cryptographic hash + functions, and the eventual need to replace the underlying hash + function. (See section 6 for more information on the security of + HMAC.) + + + + + + + + +Krawczyk, et. al. Informational [Page 2] + +RFC 2104 HMAC February 1997 + + +2. Definition of HMAC + + The definition of HMAC requires a cryptographic hash function, which + we denote by H, and a secret key K. We assume H to be a cryptographic + hash function where data is hashed by iterating a basic compression + function on blocks of data. We denote by B the byte-length of such + blocks (B=64 for all the above mentioned examples of hash functions), + and by L the byte-length of hash outputs (L=16 for MD5, L=20 for + SHA-1). The authentication key K can be of any length up to B, the + block length of the hash function. Applications that use keys longer + than B bytes will first hash the key using H and then use the + resultant L byte string as the actual key to HMAC. In any case the + minimal recommended length for K is L bytes (as the hash output + length). See section 3 for more information on keys. + + We define two fixed and different strings ipad and opad as follows + (the 'i' and 'o' are mnemonics for inner and outer): + + ipad = the byte 0x36 repeated B times + opad = the byte 0x5C repeated B times. + + To compute HMAC over the data `text' we perform + + H(K XOR opad, H(K XOR ipad, text)) + + Namely, + + (1) append zeros to the end of K to create a B byte string + (e.g., if K is of length 20 bytes and B=64, then K will be + appended with 44 zero bytes 0x00) + (2) XOR (bitwise exclusive-OR) the B byte string computed in step + (1) with ipad + (3) append the stream of data 'text' to the B byte string resulting + from step (2) + (4) apply H to the stream generated in step (3) + (5) XOR (bitwise exclusive-OR) the B byte string computed in + step (1) with opad + (6) append the H result from step (4) to the B byte string + resulting from step (5) + (7) apply H to the stream generated in step (6) and output + the result + + For illustration purposes, sample code based on MD5 is provided as an + appendix. + + + + + + + +Krawczyk, et. al. Informational [Page 3] + +RFC 2104 HMAC February 1997 + + +3. Keys + + The key for HMAC can be of any length (keys longer than B bytes are + first hashed using H). However, less than L bytes is strongly + discouraged as it would decrease the security strength of the + function. Keys longer than L bytes are acceptable but the extra + length would not significantly increase the function strength. (A + longer key may be advisable if the randomness of the key is + considered weak.) + + Keys need to be chosen at random (or using a cryptographically strong + pseudo-random generator seeded with a random seed), and periodically + refreshed. (Current attacks do not indicate a specific recommended + frequency for key changes as these attacks are practically + infeasible. However, periodic key refreshment is a fundamental + security practice that helps against potential weaknesses of the + function and keys, and limits the damage of an exposed key.) + +4. Implementation Note + + HMAC is defined in such a way that the underlying hash function H can + be used with no modification to its code. In particular, it uses the + function H with the pre-defined initial value IV (a fixed value + specified by each iterative hash function to initialize its + compression function). However, if desired, a performance + improvement can be achieved at the cost of (possibly) modifying the + code of H to support variable IVs. + + The idea is that the intermediate results of the compression function + on the B-byte blocks (K XOR ipad) and (K XOR opad) can be precomputed + only once at the time of generation of the key K, or before its first + use. These intermediate results are stored and then used to + initialize the IV of H each time that a message needs to be + authenticated. This method saves, for each authenticated message, + the application of the compression function of H on two B-byte blocks + (i.e., on (K XOR ipad) and (K XOR opad)). Such a savings may be + significant when authenticating short streams of data. We stress + that the stored intermediate values need to be treated and protected + the same as secret keys. + + Choosing to implement HMAC in the above way is a decision of the + local implementation and has no effect on inter-operability. + + + + + + + + + +Krawczyk, et. al. Informational [Page 4] + +RFC 2104 HMAC February 1997 + + +5. Truncated output + + A well-known practice with message authentication codes is to + truncate the output of the MAC and output only part of the bits + (e.g., [MM, ANSI]). Preneel and van Oorschot [PV] show some + analytical advantages of truncating the output of hash-based MAC + functions. The results in this area are not absolute as for the + overall security advantages of truncation. It has advantages (less + information on the hash result available to an attacker) and + disadvantages (less bits to predict for the attacker). Applications + of HMAC can choose to truncate the output of HMAC by outputting the t + leftmost bits of the HMAC computation for some parameter t (namely, + the computation is carried in the normal way as defined in section 2 + above but the end result is truncated to t bits). We recommend that + the output length t be not less than half the length of the hash + output (to match the birthday attack bound) and not less than 80 bits + (a suitable lower bound on the number of bits that need to be + predicted by an attacker). We propose denoting a realization of HMAC + that uses a hash function H with t bits of output as HMAC-H-t. For + example, HMAC-SHA1-80 denotes HMAC computed using the SHA-1 function + and with the output truncated to 80 bits. (If the parameter t is not + specified, e.g. HMAC-MD5, then it is assumed that all the bits of the + hash are output.) + +6. Security + + The security of the message authentication mechanism presented here + depends on cryptographic properties of the hash function H: the + resistance to collision finding (limited to the case where the + initial value is secret and random, and where the output of the + function is not explicitly available to the attacker), and the + message authentication property of the compression function of H when + applied to single blocks (in HMAC these blocks are partially unknown + to an attacker as they contain the result of the inner H computation + and, in particular, cannot be fully chosen by the attacker). + + These properties, and actually stronger ones, are commonly assumed + for hash functions of the kind used with HMAC. In particular, a hash + function for which the above properties do not hold would become + unsuitable for most (probably, all) cryptographic applications, + including alternative message authentication schemes based on such + functions. (For a complete analysis and rationale of the HMAC + function the reader is referred to [BCK1].) + + + + + + + + +Krawczyk, et. al. Informational [Page 5] + +RFC 2104 HMAC February 1997 + + + Given the limited confidence gained so far as for the cryptographic + strength of candidate hash functions, it is important to observe the + following two properties of the HMAC construction and its secure use + for message authentication: + + 1. The construction is independent of the details of the particular + hash function H in use and then the latter can be replaced by any + other secure (iterative) cryptographic hash function. + + 2. Message authentication, as opposed to encryption, has a + "transient" effect. A published breaking of a message authentication + scheme would lead to the replacement of that scheme, but would have + no adversarial effect on information authenticated in the past. This + is in sharp contrast with encryption, where information encrypted + today may suffer from exposure in the future if, and when, the + encryption algorithm is broken. + + The strongest attack known against HMAC is based on the frequency of + collisions for the hash function H ("birthday attack") [PV,BCK2], and + is totally impractical for minimally reasonable hash functions. + + As an example, if we consider a hash function like MD5 where the + output length equals L=16 bytes (128 bits) the attacker needs to + acquire the correct message authentication tags computed (with the + _same_ secret key K!) on about 2**64 known plaintexts. This would + require the processing of at least 2**64 blocks under H, an + impossible task in any realistic scenario (for a block length of 64 + bytes this would take 250,000 years in a continuous 1Gbps link, and + without changing the secret key K during all this time). This attack + could become realistic only if serious flaws in the collision + behavior of the function H are discovered (e.g. collisions found + after 2**30 messages). Such a discovery would determine the immediate + replacement of the function H (the effects of such failure would be + far more severe for the traditional uses of H in the context of + digital signatures, public key certificates, etc.). + + Note: this attack needs to be strongly contrasted with regular + collision attacks on cryptographic hash functions where no secret key + is involved and where 2**64 off-line parallelizable (!) operations + suffice to find collisions. The latter attack is approaching + feasibility [VW] while the birthday attack on HMAC is totally + impractical. (In the above examples, if one uses a hash function + with, say, 160 bit of output then 2**64 should be replaced by 2**80.) + + + + + + + + +Krawczyk, et. al. Informational [Page 6] + +RFC 2104 HMAC February 1997 + + + A correct implementation of the above construction, the choice of + random (or cryptographically pseudorandom) keys, a secure key + exchange mechanism, frequent key refreshments, and good secrecy + protection of keys are all essential ingredients for the security of + the integrity verification mechanism provided by HMAC. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Krawczyk, et. al. Informational [Page 7] + +RFC 2104 HMAC February 1997 + + +Appendix -- Sample Code + + For the sake of illustration we provide the following sample code for + the implementation of HMAC-MD5 as well as some corresponding test + vectors (the code is based on MD5 code as described in [MD5]). + +/* +** Function: hmac_md5 +*/ + +void +hmac_md5(text, text_len, key, key_len, digest) +unsigned char* text; /* pointer to data stream */ +int text_len; /* length of data stream */ +unsigned char* key; /* pointer to authentication key */ +int key_len; /* length of authentication key */ +caddr_t digest; /* caller digest to be filled in */ + +{ + MD5_CTX context; + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + + + +Krawczyk, et. al. Informational [Page 8] + +RFC 2104 HMAC February 1997 + + + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + bzero( k_ipad, sizeof k_ipad); + bzero( k_opad, sizeof k_opad); + bcopy( key, k_ipad, key_len); + bcopy( key, k_opad, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + /* + * perform inner MD5 + */ + MD5Init(&context); /* init context for 1st + * pass */ + MD5Update(&context, k_ipad, 64) /* start with inner pad */ + MD5Update(&context, text, text_len); /* then text of datagram */ + MD5Final(digest, &context); /* finish up 1st pass */ + /* + * perform outer MD5 + */ + MD5Init(&context); /* init context for 2nd + * pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, digest, 16); /* then results of 1st + * hash */ + MD5Final(digest, &context); /* finish up 2nd pass */ +} + +Test Vectors (Trailing '\0' of a character string not included in test): + + key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + key_len = 16 bytes + data = "Hi There" + data_len = 8 bytes + digest = 0x9294727a3638bb1c13f48ef8158bfc9d + + key = "Jefe" + data = "what do ya want for nothing?" + data_len = 28 bytes + digest = 0x750c783e6ab0b503eaa86e310a5db738 + + key = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + + +Krawczyk, et. al. Informational [Page 9] + +RFC 2104 HMAC February 1997 + + + key_len 16 bytes + data = 0xDDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD + data_len = 50 bytes + digest = 0x56be34521d144c88dbb8c733f0e8b3f6 + +Acknowledgments + + Pau-Chen Cheng, Jeff Kraemer, and Michael Oehler, have provided + useful comments on early drafts, and ran the first interoperability + tests of this specification. Jeff and Pau-Chen kindly provided the + sample code and test vectors that appear in the appendix. Burt + Kaliski, Bart Preneel, Matt Robshaw, Adi Shamir, and Paul van + Oorschot have provided useful comments and suggestions during the + investigation of the HMAC construction. + +References + + [ANSI] ANSI X9.9, "American National Standard for Financial + Institution Message Authentication (Wholesale)," American + Bankers Association, 1981. Revised 1986. + + [Atk] Atkinson, R., "IP Authentication Header", RFC 1826, August + 1995. + + [BCK1] M. Bellare, R. Canetti, and H. Krawczyk, + "Keyed Hash Functions and Message Authentication", + Proceedings of Crypto'96, LNCS 1109, pp. 1-15. + (http://www.research.ibm.com/security/keyed-md5.html) + + [BCK2] M. Bellare, R. Canetti, and H. Krawczyk, + "Pseudorandom Functions Revisited: The Cascade Construction", + Proceedings of FOCS'96. + + [Dobb] H. Dobbertin, "The Status of MD5 After a Recent Attack", + RSA Labs' CryptoBytes, Vol. 2 No. 2, Summer 1996. + http://www.rsa.com/rsalabs/pubs/cryptobytes.html + + [PV] B. Preneel and P. van Oorschot, "Building fast MACs from hash + functions", Advances in Cryptology -- CRYPTO'95 Proceedings, + Lecture Notes in Computer Science, Springer-Verlag Vol.963, + 1995, pp. 1-14. + + [MD5] Rivest, R., "The MD5 Message-Digest Algorithm", + RFC 1321, April 1992. + + + +Krawczyk, et. al. Informational [Page 10] + +RFC 2104 HMAC February 1997 + + + [MM] Meyer, S. and Matyas, S.M., Cryptography, New York Wiley, + 1982. + + [RIPEMD] H. Dobbertin, A. Bosselaers, and B. Preneel, "RIPEMD-160: A + strengthened version of RIPEMD", Fast Software Encryption, + LNCS Vol 1039, pp. 71-82. + ftp://ftp.esat.kuleuven.ac.be/pub/COSIC/bosselae/ripemd/. + + [SHA] NIST, FIPS PUB 180-1: Secure Hash Standard, April 1995. + + [Tsu] G. Tsudik, "Message authentication with one-way hash + functions", In Proceedings of Infocom'92, May 1992. + (Also in "Access Control and Policy Enforcement in + Internetworks", Ph.D. Dissertation, Computer Science + Department, University of Southern California, April 1991.) + + [VW] P. van Oorschot and M. Wiener, "Parallel Collision + Search with Applications to Hash Functions and Discrete + Logarithms", Proceedings of the 2nd ACM Conf. Computer and + Communications Security, Fairfax, VA, November 1994. + +Authors' Addresses + + Hugo Krawczyk + IBM T.J. Watson Research Center + P.O.Box 704 + Yorktown Heights, NY 10598 + + EMail: hugo@watson.ibm.com + + Mihir Bellare + Dept of Computer Science and Engineering + Mail Code 0114 + University of California at San Diego + 9500 Gilman Drive + La Jolla, CA 92093 + + EMail: mihir@cs.ucsd.edu + + Ran Canetti + IBM T.J. Watson Research Center + P.O.Box 704 + Yorktown Heights, NY 10598 + + EMail: canetti@watson.ibm.com + + + + + + +Krawczyk, et. al. Informational [Page 11] diff --git a/info/transport.txt b/info/transport.txt new file mode 100644 index 0000000..ad10cf9 --- /dev/null +++ b/info/transport.txt @@ -0,0 +1,1178 @@ +Network Working Group T. Ylonen +INTERNET-DRAFT T. Kivinen +draft-ietf-secsh-transport-03.txt M. Saarinen +Expires in six months SSH + 7 November 1997 + + + SSH Transport Layer Protocol + +Status of This memo + +This document is an Internet-Draft. Internet-Drafts are working +documents of the Internet Engineering Task Force (IETF), its areas, +and its working groups. Note that other groups may also distribute +working documents as Internet-Drafts. + +Internet-Drafts are draft documents valid for a maximum of six +months and may be updated, replaced, or obsoleted by other documents +at any time. It is inappropriate to use Internet-Drafts as reference +material or to cite them other than as ``work in progress.'' + +To learn the current status of any Internet-Draft, please check +the ``1id-abstracts.txt'' listing contained in the Internet-Drafts +Shadow Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe), +munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), +or ftp.isi.edu (US West Coast). + +Abstract + +SSH is a protocol for secure remote login and other secure network +services over an insecure network. + +This document describes the SSH transport layer protocol which +typically runs on top of TCP/IP. The protocol can be used as a basis +for a number of secure network services. It provides strong +encryption, server authentication, and integrity protection. It may +also provide compression. + +Key exchange method, public key algorithm, symmetric encryption +algorithm, message authentication algorithm, and hash algorithm are +all negotiated. + +This document also describes the Diffie-Hellman key exchange method +and the minimal set of algorithms that are needed to implement the +SSH transport layer protocol. + + + + + + + + + + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 1] + +INTERNET-DRAFT 7 November 1997 + +Table of Contents + +1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . 2 +2. Conventions Used in This Document . . . . . . . . . . . . . . . 3 +3. Connection Setup . . . . . . . . . . . . . . . . . . . . . . . . 3 + 3.1. Use over TCP/IP . . . . . . . . . . . . . . . . . . . . . . 3 + 3.2. Protocol Version Exchange . . . . . . . . . . . . . . . . . 3 + 3.3. Compatibility with Old SSH Versions . . . . . . . . . . . . 4 + 3.3.1. Old Client, New Server . . . . . . . . . . . . . . . . . 4 + 3.3.2. New Client, Old Server . . . . . . . . . . . . . . . . . 4 +4. Binary Packet Protocol . . . . . . . . . . . . . . . . . . . . . 4 + 4.1. Maximum Packet Length . . . . . . . . . . . . . . . . . . . 5 + 4.2. Compression . . . . . . . . . . . . . . . . . . . . . . . . 5 + 4.3. Encryption . . . . . . . . . . . . . . . . . . . . . . . . . 6 + 4.4. Data Integrity . . . . . . . . . . . . . . . . . . . . . . . 7 + 4.5. Key Exchange Methods . . . . . . . . . . . . . . . . . . . . 8 + 4.6. Public Key Algorithms . . . . . . . . . . . . . . . . . . . 8 +5. Key Exchange . . . . . . . . . . . . . . . . . . . . . . . . . . 9 + 5.1. Algorithm Negotiation . . . . . . . . . . . . . . . . . . . 10 + 5.2. Output from Key Exchange . . . . . . . . . . . . . . . . . . 12 + 5.3. Taking Keys into Use . . . . . . . . . . . . . . . . . . . . 13 +6. Diffie-Hellman Key Exchange . . . . . . . . . . . . . . . . . . 13 + 6.1. diffie-hellman-group1-sha1 . . . . . . . . . . . . . . . . . 15 +7. Key Re-Exchange . . . . . . . . . . . . . . . . . . . . . . . . 15 +8. Service Request . . . . . . . . . . . . . . . . . . . . . . . . 16 +9. Additional Messages . . . . . . . . . . . . . . . . . . . . . . 16 + 9.1. Disconnection Message . . . . . . . . . . . . . . . . . . . 17 + 9.2. Ignored Data Message . . . . . . . . . . . . . . . . . . . . 17 + 9.3. Debug Message . . . . . . . . . . . . . . . . . . . . . . . 17 + 9.4. Reserved Messages . . . . . . . . . . . . . . . . . . . . . 18 +10. Summary of Message Numbers . . . . . . . . . . . . . . . . . . 18 +11. Security Considerations . . . . . . . . . . . . . . . . . . . . 18 +12. References . . . . . . . . . . . . . . . . . . . . . . . . . . 19 +13. Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . 20 + + + +1. Introduction + +The SSH transport layer is a secure low level transport protocol. It +provides strong encryption, cryptographic host authentication, and +integrity protection. + +Authentication in this protocol level is host-based; this protocol does +not perform user authentication. A higher level protocol for user +authentication can be designed on top of this protocol. + +The protocol has been designed to be simple, flexible, to allow +parameter negotiation, and to minimize the number of round-trips. Key +exchange method, public key algorithm, symmetric encryption algorithm, +message authentication algorithm, and hash algorithm are all negotiated. +It is expected that in most environments, only 2 round-trips will be +needed for full key exchange, server authentication, service request, + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 2] + +INTERNET-DRAFT 7 November 1997 + +and acceptance notification of service request. The worst case is 3 +round-trips. + +2. Conventions Used in This Document + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", and +"MAY" that appear in this document are to be interpreted as described in +[RFC-2119]. + +The used data types and terminology are specified in the architecture +document [SSH-ARCH]. + +The architecture document also discusses the algorithm naming +conventions that MUST be used with the SSH protocols. +3. Connection Setup + +SSH works over any 8-bit clean, binary-transparent transport. The +underlying transport SHOULD protect against transmission errors as such +errors cause the SSH connection to terminate. + +The client initiates the connection. + +3.1. Use over TCP/IP + +When used over TCP/IP, the server normally listens for connections on +port 22. This port number has been registered with the IANA, and has +been officially assigned for SSH. + +3.2. Protocol Version Exchange + +When the connection has been established, both sides MUST send an +identification string of the form "SSH-protoversion-softwareversion +comments", followed by carriage return and newline characters (ascii 13 +and 10, respectively). No null character is sent. The maximum length +of the string is 255 characters, including the carriage return and +newline. + +The part of the identification string preceding carriage return and +newline is used in the Diffie-Hellman key exchange (Section ``Diffie- +Hellman Key Exchange''). + +The server MAY send other lines of data before sending the version +string. Each line SHOULD be terminated by a carriage return and +newline. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded +in ISO-10646 UTF-8 [RFC-2044] (language is not specified). Clients MUST +be able to process such lines; they MAY be silently ignored, or MAY be +displayed to the client user; if they are displayed, control character +filtering discussed in [SSH-ARCH] SHOULD be used. The primary use of +this feature is to allow TCP-wrappers to display an error message before +disconnecting. + +Version strings MUST consist of printable US-ASCII characters, not +including whitespaces or a minus sign (-). The version string is + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 3] + +INTERNET-DRAFT 7 November 1997 + +primarily used to trigger compatible extensions and to indicate the +capabilities of an implementation. The comment string should contain +additional information that might be useful in solving user problems. + +The protocol version described in this document is 2.0. + +Key exchange will begin immediately after sending this identifier. All +packets following the identification string SHALL use the binary packet +protocol, to be described below. + +3.3. Compatibility with Old SSH Versions + +During a transition period, it is important to be able to work +compatibly with installed SSH clients and servers using an older version +of the protocol. Information in this section is only relevant for +implementations supporting compatibility with SSH versions 1.x. + +3.3.1. Old Client, New Server + +Server implementations MAY support a configurable "compatibility" flag +that enables compatibility with old versions. When this flag is on, the +server SHOULD not send any further data after its initialization string +until it has received an identification string from the client. The +server can then determine whether the client is using an old protocol, +and can revert to the old protocol if desired. + +When compatibility with old clients is not needed, the server MAY send +its initial key exchange data immediately after the identification +string. + +3.3.2. New Client, Old Server + +Since the new client MAY immediately send additional data after its +identification string (before receiving server's identification), the +old protocol may already have been corrupted when the client learns that +the server is old. When this happens, the client SHOULD close the +connection to the server, and reconnect using the old protocol. + +4. Binary Packet Protocol + +Each packet is of the following format. + + uint32 packet_length + byte padding_length + byte[n1] payload; n1 = packet_length - padding_length - 1 + byte[n2] random padding; n2 = padding_length + byte[m] mac (message authentication code); m = mac_length + + packet_length + The length of the packet (bytes), not including MAC or the + packet_length field itself. + + padding_length + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 4] + +INTERNET-DRAFT 7 November 1997 + + Length of padding (bytes). + + payload + The useful contents of the packet. If compression has been + negotiated, this field is compressed. Initially, compression MUST + be "none". + + Padding + Arbitrary-length padding, such that the total length of + (packet_length || padding_length || payload || padding) is a + multiple of the cipher block size or 8, whichever is larger. + There MUST be at least four bytes of padding. The padding SHOULD + consist of random bytes. The maximum amount of padding is 255 + bytes. + + MAC + Message authentication code. If message authentication has been + negotiated, this field contains the MAC bytes. Initially, the MAC + algorithm MUST be "none". + +Note that length of the concatenation of packet length, padding length, +payload, and padding MUST be a multiple of the cipher block size or 8, +whichever is larger. This constraint MUST be enforced even when using +stream ciphers. Note that the packet length field is also encrypted, +and processing it requires special care when sending or receiving +packets. + +The minimum size of a packet is 16 (or the cipher block size, whichever +is larger) bytes (plus MAC); implementations SHOULD decrypt the length +after receiving the first 8 (or cipher block size, whichever is larger) +bytes of a packet. + +4.1. Maximum Packet Length + +All implementations MUST be able to process packets with uncompressed +payload length of 32768 bytes or less and total packet size of 35000 +bytes or less (including length, padding length, payload, padding, and +MAC). Implementations SHOULD support longer packets, where they might +be needed e.g. if an implementation wants to send a very large number of +certificates. Such packets MAY be sent if the version string indicates +that the other party is able to process them. However, implementations +SHOULD check that the packet length is reasonable for the implementation +to avoid denial-of-service and/or buffer overflow attacks. + +4.2. Compression + +If compression has been negotiated, the payload field (and only it) will +be compressed using the negotiated algorithm. The length field and MAC +will be computed from the compressed payload. + +Compression MAY be stateful, depending on the method. Compression MUST +be independent for each direction, and implementations MUST allow +independently choosing the algorithm for each direction. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 5] + +INTERNET-DRAFT 7 November 1997 + +The following compression methods are currently defined: + + none REQUIRED no compression + zlib OPTIONAL GNU ZLIB (LZ77) compression + +The "zlib" compression is described in [RFC-1950] and in [RFC-1951]. +The compression context is initialized after each key exchange, and is +passed from one packet to the next with only a partial flush being +performed at the end of each packet. A partial flush means that all +data will be output, but the next packet will continue using compression +tables from the end of the previous packet. + +Additional methods may be defined as specified in [SSH-ARCH]. + +4.3. Encryption + +An encryption algorithm and a key will be negotiated during the key +exchange. When encryption is in effect, the packet length, padding +length, payload and padding fields of each packet MUST be encrypted with +the given algorithm. + +The encrypted data in all packets sent in one direction SHOULD be +considered a single data stream. For example, initialization vectors +SHOULD be passed from the end of one packet to the beginning of the next +packet. All ciphers SHOULD use keys with an effective key length of 128 +bits or more. + +The ciphers in each direction MUST run independently of each other, and +implementations MUST allow independently choosing the algorithm for each +direction (if multiple algorithms are allowed by local policy). + +The following ciphers are currently defined: + + 3des-cbc REQUIRED three-key 3DES in CBC mode + blowfish-cbc RECOMMENDED Blowfish in CBC mode + arcfour OPTIONAL the ARCFOUR stream cipher + idea-cbc OPTIONAL IDEA in CBC mode + cast128-cbc OPTIONAL CAST-128 in CBC mode + none OPTIONAL no encryption; NOT RECOMMENDED + +The "3des-cbc" cipher is three-key triple-DES (encrypt-decrypt-encrypt), +where the first 8 bytes of the key are used for the first encryption, +the next 8 bytes for the decryption, and the following 8 bytes for the +final encryption. This requires 24 bytes of key data (of which 168 bits +are actually used). To implement CBC mode, outer chaining MUST be used +(i.e., there is only one initialization vector). This is a block cipher +with 8 byte blocks. This algorithm is defined in [Schneier]. + +The "blowfish-cbc" cipher is Blowfish in CBC mode, with 128 bit keys +[Schneier]. This is a block cipher with 8 byte blocks. + +The "arcfour" is the Arcfour stream cipher with 128 bit keys. The +Arcfour cipher is believed to be compatible with the RC4 cipher + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 6] + +INTERNET-DRAFT 7 November 1997 + +[Schneier]. RC4 is a registered trademark of RSA Data Security Inc. + +The "idea-cbc" cipher is the IDEA cipher in CBC mode [Schneier]. IDEA +is patented by Ascom AG. + +The "cast128-cbc" cipher is the CAST-128 cipher in CBC mode [RFC-2144]. + +The "none" algorithm specifies that no encryption is to be done. Note +that this method provides no confidentiality protection, and it is not +recommended. Some functionality (e.g. password authentication) may be +disabled for security reasons if this cipher is chosen. + +Additional methods may be defined as specified in [SSH-ARCH]. + +4.4. Data Integrity + +Data integrity is protected by including with each packet a message +authentication code (MAC) that is computed from a shared secret, packet +sequence number, and the contents of the packet. + +The message authentication algorithm and key are negotiated during key +exchange. Initially, no MAC will be in effect, and its length MUST be +zero. After key exchange, the selected MAC will be computed before +encryption from the concatenation of packet data: + + mac = MAC(key, sequence_number || unencrypted_packet) + +where unencrypted_packet is the entire packet without MAC (the length +fields, payload and padding), and sequence_number is an implicit packet +sequence number represented as uint32. The sequence number is initial- +ized to zero for the first packet, and is incremented after every packet +(regardless of whether encryption or MAC is in use). It is never reset, +even if keys/algorithms are renegotiated later. It wraps around to zero +after every 2^32 packets. The packet sequence number itself is not +included in the packet sent over the wire. + +The MAC algorithms for each direction MUST run independently, and +implementations MUST allow choosing the algorithm independently for both +directions. + +The MAC bytes resulting from the MAC algorithm MUST be transmitted +without encryption as the last part of the packet. The number of MAC +bytes depends on the algorithm chosen. + +The following MAC algorithms are currently defined: + + hmac-sha1 REQUIRED HMAC-SHA1 (length = 20) + hmac-sha-96 RECOMMENDED first 96 bits of HMAC-SHA1 (length = 12) + hmac-md5 OPTIONAL HMAC-MD5 (length = 16) + hmac-md5-96 OPTIONAL first 96 bits of HMAC-MD5 (length = 12) + none OPTIONAL no MAC; NOT RECOMMENDED + +The "hmac-*" algorithms are described in [RFC-2104]. The "*-n" MACs use + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 7] + +INTERNET-DRAFT 7 November 1997 + +only the first n bits of the resulting value. + +The hash algorithms are described in [Schneier]. + +The "none" method is NOT RECOMMENDED. An active attacker may be able to +modify transmitted data if this is used. + +Additional methods may be defined as specified in [SSH-ARCH]. + +4.5. Key Exchange Methods + +The key exchange method specifies how one-time session keys are +generated for encryption and for authentication, and how the server +authentication is done. + +Only one REQUIRED key exchange method has been defined: + + diffie-hellman-group1-sha1 REQUIRED + +This method is described later in this document. + +Additional methods may be defined as specified in [SSH-ARCH]. + +4.6. Public Key Algorithms + +This protocol has been designed to be able to operate with almost any +public key format, encoding, and algorithm (signature and/or +encryption). + +There are several aspects that define a public key type: + +o Key format: how is the key encoded and how are certificates + represented. The key blobs in this protocol MAY contain certificates + in addition to keys. + +o Signature and/or encryption algorithms. Some key types may not + support both signing and encryption. Key usage may also be + restricted by policy statements in e.g. certificates. In this case, + different key types SHOULD be defined for the different policy + alternatives. + +o Encoding of signatures and/or encrypted data. This includes but is + not limited to padding, byte order, and data formats. + +The following public key and/or certificate formats are currently +defined: + +ssh-dss REQUIRED sign Simple DSS +x509v3 RECOMMENDED sign X.509 certificates +spki OPTIONAL sign SPKI certificates +pgp OPTIONAL sign OpenPGP certificates + +Additional key types may be defined as specified in [SSH-ARCH]. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 8] + +INTERNET-DRAFT 7 November 1997 + +The key type MUST always be explicitly known (from algorithm negotiation +or some other source). It is not normally included in the key blob. + +Certificates and public keys are encoded as: + + uint32 the combined length of the format identifier and associated data + string certificate or public key format identifier + byte[n] key/certificate data + +The certificate part may have be a zero length string, but a public key +is required. This is the public key that will be used for +authentication; the certificate sequence contained in the certificate +blob can be used to provide authorization. + +The "ssh-dss" key format has the following specific encoding: + + uint32 length + string "ssh-dss" + mpint p + mpint q + mpint g + mpint y + +Here the "p", "q", "g", and "y" parameters form the signature key blob. + +Signing and verifying using this key format are done according to the +Digital Signature Standard [FIPS-186] using the SHA-1 hash. A +description can also be found in [Schneier]. + +The resulting signature is encoded as: + + uint32 length + string "ssh-dss" + mpint r + mpint s + +The "x509v3" method indicates that the certificates, the public key, and +the resulting signature are in X.509v3 compatible DER-encoded format. +The formats used in X.509v3 is described in [PKIX-Part1]. + +The "spki" method indicates that the certificate blob contains a +sequence of SPKI certificates. The format of SPKI certificates is +described in [SPKI]. + +The "pgp" method indicates the the certificates, the public key, and the +signature are in OpenPGP compatible binary format. [PGP] + +5. Key Exchange + +Key exchange begins by each side sending lists of supported algorithms. +Each side has a preferred algorithm in each category, and it is assumed +that most implementations at any given time will use the same preferred +algorithm. Each side MAY guess which algorithm the other side is using, + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 9] + +INTERNET-DRAFT 7 November 1997 + +and MAY send an initial key exchange packet according to the algorithm +if appropriate for the preferred method. If all algorithms were guessed +right, the optimistically sent packet MUST be handled as the first key +exchange packet. However, if the guess was wrong, and a packet was +optimistically sent by one or both parties, such packets MUST be ignored +(even if the error in the guess would not affect the contents of the +initial packet(s)), and the appropriate side MUST send the correct +initial packet. + +Server authentication in the key exchange MAY be implicit. After a key +exchange with implicit server authentication, the client MUST wait for +response to its service request message before sending any further data. + +5.1. Algorithm Negotiation + +Key exchange begins by each side sending the following packet: + + byte SSH_MSG_KEXINIT + byte[16] cookie (random bytes) + string kex_algorithms + string server_host_key_algorithms + string encryption_algorithms_client_to_server + string encryption_algorithms_server_to_client + string mac_algorithms_client_to_server + string mac_algorithms_server_to_client + string compression_algorithms_client_to_server + string compression_algorithms_server_to_client + string languages_client_to_server + string languages_server_to_client + boolean first_kex_packet_follows + uint32 0 (reserved for future extension) + +Each of the algorithm strings MUST be a comma-separated list of +algorithm names (see ``Algorithm Naming'' in [SSH-ARCH]). Each +supported (allowed) algorithm MUST be listed, in order of preference. + +The first algorithm in each list MUST be the preferred (guessed) +algorithm. Each string MUST contain at least one algorithm name. + + cookie + The cookie MUST be a random value generated by the sender. Its + purpose is to make it impossible for either side to fully + determine the keys and the session identifier. + + kex_algorithms + Key exchange algorithms were defined above. The first algorithm + MUST be the preferred (and guessed) algorithm. If both sides make + the same guess, that algorithm MUST used. Otherwise, the + following algorithm MUST be used to choose a key exchange method: + iterate over client's kex algorithms, one at a time. Choose the + first algorithm that satisfies the following conditions: + + o the server also supports the algorithm, + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 10] + +INTERNET-DRAFT 7 November 1997 + + o if the algorithm requires an encryption-capable host key, there is + an encryption-capable algorithm on the server's + server_host_key_algorithms that is also supported by the client, + and + + o if the algorithm requires a signature-capable host key, there is a + signature-capable algorithm on the server's + server_host_key_algorithms that is also supported by the client. + + If no algorithm satisfying all these conditions can be found, the + connection fails, and both sides MUST disconnect. + + server_host_key_algorithms + List of the algorithms supported for the server host key. The + server lists the algorithms for which it has host keys; the client + lists the algorithms that it is willing to accept. (There MAY be + multiple host keys for a host, possibly with different + algorithms.) + + Some host keys may not support both signatures and encryption + (this can be determined from the algorithm), and thus not all host + keys are valid for all key exchange methods. + + Algorithm selection depends on whether the chosen key exchange + algorithm requires a signature- or encryption capable host key. + It MUST be possible to determine this from the public key + algorithm name. The first algorithm on the client's list that + satisfies the requirements and is also supported by the server + MUST be chosen. If there is no such algorithm, both sides MUST + disconnect. + + encryption_algorithms + Lists the acceptable symmetric encryption algorithms in order of + preference. The chosen encryption algorithm to each direction + MUST be the first algorithm on the client's list that is also on + the server's list. If there is no such algorithm, both sides MUST + disconnect. + + Note that "none" must be explicitly listed if it is to be + acceptable. The defined algorithm names are listed in Section + ``Encryption''. + + mac_algorithms + Lists the acceptable MAC algorithms in order of preference. The + chosen MAC algorithm MUST be the first algorithm on the client's + list that is also on the server's list. If there is no such + algorithm, both sides MUST disconnect. + + Note that "none" must be explicitly listed if it is to be + acceptable. The MAC algorithm names are listed in Section ``Data + Integrity''. + + compression_algorithms + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 11] + +INTERNET-DRAFT 7 November 1997 + + Lists the acceptable compression algorithms in order of + preference. The chosen compression algorithm MUST be the first + algorithm on the client's list that is also on the server's list. + If there is no such algorithm, both sides MUST disconnect. + + Note that "none" must be explicitly listed if it is to be + acceptable. The compression algorithm names are listed in Section + ``Compression''. + + languages + This is a comma-separated list of language tags in order of + preference [RFC-1766]. Both parties MAY ignore this list. If + there are no language preferences, this list SHOULD be empty. + + first_kex_packet_follows + Indicates whether a guessed key exchange packet follows. If a + guessed packet will be sent, this MUST be true. If no guessed + packet will be sent, this MUST be false. + + After receiving the SSH_MSG_KEXINIT packet from the other side, + each party will know whether their guess was right. If the other + party's guess was wrong, and this field was true, the next packet + MUST be silently ignored, and both sides MUST then act as + determined by the negotiated key exchange method. If the guess + was right, key exchange MUST continue using the guessed packet. + +After the KEXINIT packet exchange, the key exchange algorithm is run. +It may involve several packet exchanges, as specified by the key +exchange method. + +5.2. Output from Key Exchange + +The key exchange produces two values: a shared secret K, and an exchange +hash H. Encryption and authentication keys are derived from these. The +exchange hash H from the first key exchange is additionally used as the +session identifier, which is a unique identifier for this connection. +It is used by authentication methods as a part of the data that is +signed as a proof of possession of a private key. Once computed, the +session identifier is not changed, even if keys are later re-exchanged. + +Each key exchange method specifies a hash function that is used in the +key exchange. The same hash algorithm MUST be used in key derivation. +Here, we'll call it HASH. + +Encryption keys MUST be computed as HASH of a known value and K as +follows: + +o Initial IV client to server: HASH(K || "A" || session_id) ("A" means + the single character A, ascii 65). + +o Initial IV server to client: HASH(K || "B" || session_id) + +o Encryption key client to server: HASH(K || "C" || session_id) + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 12] + +INTERNET-DRAFT 7 November 1997 + +o Encryption key server to client: HASH(K || "D" || session_id) + +o Integrity key client to server: HASH(K || "E" || session_id) + +o Integrity key server to client: HASH(K || "F" || session_id) + +Key data MUST be taken from the beginning of the hash output. 128 bits +(16 bytes) SHOULD be used for algorithms with variable-length keys. For +other algorithms, as many bytes as are needed are taken from the +beginning of the hash value. If the key length in longer than the output +of the HASH, the key is extended by computing HASH of the concatenation +of K and the entire key so far, and appending the resulting bytes (as +many as HASH generates) to the key. This process is repeated until +enough key material is available; the key is taken from the beginning of +this value. In other words, + + K1 = HASH(K || X || session_id) (X is e.g. "A") + K2 = HASH(K || K1) + K3 = HASH(K || K1 || K2) + ... + key = K1 || K2 || K3 || ... + +5.3. Taking Keys into Use + +Key exchange ends by each side sending an SSH_MSG_NEWKEYS message. This +message is sent with the old keys and algorithms. All messages sent +after this message MUST use the new keys and algorithms. + +When this message is received, the new keys and algorithms MUST be taken +into use for receiving. + +This message is the only valid message after key exchange, in addition +to SSH_MSG_DEBUG, SSH_MSG_DISCONNECT and SSH_MSG_IGNORE messages. The +purpose of this message is to ensure that a party is able to respond +with a disconnect message that the other party can understand if +something goes wrong with the key exchange. Implementations MUST NOT +accept any other messages after key exchange before receiving +SSH_MSG_NEWKEYS. + + byte SSH_MSG_NEWKEYS + +6. Diffie-Hellman Key Exchange + +The Diffie-Hellman key exchange provides a shared secret that can not be +determined by either party alone. The key exchange is combined with a +signature with the host key to provide host authentication. + +In the following description (C is the client, S is the server; p is a +large safe prime, g is a generator for a subgroup of GF(p), and q is the +order of the subgroup; V_S is S's version string; V_C is C's version +string; K_S is S's public host key; I_C is C's KEXINIT message and I_S +S's KEXINIT message which have been exchanged before this part begins): +1. C generates a random number x (1 < x < q) and computes e = g^x mod p. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 13] + +INTERNET-DRAFT 7 November 1997 + + C sends "e" to S. + +2. S generates a random number y (0 < y < q) and computes f = g^y mod p. + S receives "e". It computes K = e^y mod p, H = hash(V_C || V_S || + I_C || I_S || K_S || e || f || K), and signature s on H with its + private host key. S sends "K_S || f || s" to C. The signing + operation may involve a second hashing operation. + +3. C verifies that K_S really is the host key for S (e.g. using + certificates or a local database). C is also allowed to accept the + key without verification; however, doing so will render the protocol + insecure against active attacks (but may be desirable for practical + reasons in the short term in many environments). C then computes K = + f^x mod p, H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K), + and verifies the signature s on H. + + Either side MUST NOT send or accept e or f values that are not in the + range [1, p-1]. If this condition is violated, the key exchange + fails. + +This is implemented with the following messages. The hash algorithm for +computing the exchange hash is defined by the method name, and is called +HASH. The public key algorithm for signing is negotiated with the +KEXINIT messages. + +First, the client sends: + + byte SSH_MSG_KEXDH_INIT + mpint e + +The server responds with: + + byte SSH_MSG_KEXDH_REPLY + string server public host key and certificates (K_S) + mpint f + string signature of H + +The hash H is computed as the HASH hash of the concatenation of the +following: + + string V_C, the client's version string (CR and NL excluded) + string V_S, the server's version string (CR and NL excluded) + string I_C, the payload of the client's SSH_MSG_KEXINIT + string I_S, the payload of the server's SSH_MSG_KEXINIT + string K_S, the host key + mpint e, exchange value sent by the client + mpint f, exchange value sent by the server + mpint K, the shared secret + +This value is called the exchange hash, and it is used to authenticate +the key exchange. + +The signature algorithm MUST be applied over H, not the original data. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 14] + +INTERNET-DRAFT 7 November 1997 + +Most signature algorithms include hashing and additional padding. For +example, a "ssh-dss" specifies SHA-1 hashing; in that case, the data is +first hashed with HASH to compute H, and H is then hashed with SHA-1 as +part of the signing operation. + +6.1. diffie-hellman-group1-sha1 + +The "diffie-hellman-group1-sha1" method specifies Diffie-Hellman key +exchange with SHA-1 as HASH, and the following group: + +The prime p is equal to 2^1024 - 2^960 - 1 + 2^64 * floor( 2^894 Pi + +129093 ). Its hexadecimal value is + + FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 + 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD + EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 + E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED + EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381 + FFFFFFFF FFFFFFFF. + +In decimal, this value is + + 179769313486231590770839156793787453197860296048756011706444 + 423684197180216158519368947833795864925541502180565485980503 + 646440548199239100050792877003355816639229553136239076508735 + 759914822574862575007425302077447712589550957937778424442426 + 617334727629299387668709205606050270810842907692932019128194 + 467627007. + +The generator used with this prime is g = 2. The group order q is (p - +1) / 2. + +This group was taken from the ISAKMP/Oakley specification, and was +originally generated by Richard Schroeppel at the University of Arizona. +Properties of this prime are described in [Orm96]. + +7. Key Re-Exchange + +Key re-exchange is started by sending a SSH_MSG_KEXINIT packet when not +already doing a key exchange (as described in Section ``Algorithm +Negotiation''). When this message is received, a party MUST respond +with its own SSH_MSG_KEXINIT message except when the received +SSH_MSG_KEXINIT already was a reply. Either party MAY initiate the re- +exchange, but roles MUST NOT be changed (i.e., the server remains the +server, and the client remains the client). + +Key re-exchange is performed using whatever encryption was in effect +when the exchange was started. Encryption, compression, and MAC methods +are not changed before a new SSH_MSG_NEWKEYS is sent after the key +exchange (as in the initial key exchange). Re-exchange is processed +identically to the initial key exchange, except for the session +identifier that will remain unchanged. It is permissible to change some +or all of the algorithms during the re-exchange. Host keys can also + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 15] + +INTERNET-DRAFT 7 November 1997 + +change. All keys and initialization vectors are recomputed after the +exchange. Compression and encryption contexts are reset. + +It is recommended that the keys are changed after each gigabyte of +transmitted data or after each hour of connection time, whichever comes +sooner. However, since the re-exchange is a public key operation, it +requires a fair amount of processing power and should not be performed +too often. + +More application data may be sent after the SSH_MSG_NEWKEYS packet has +been sent; key exchange does not affect the protocols that lie above the +SSH transport layer. + +8. Service Request + +After the key exchange, the client requests a service. The service is +identified by a name. The format of names and procedures for defining +new names are defined in [SSH-ARCH]. + +Currently, the following names have been reserved: + + ssh-userauth + ssh-connection + +Similar local naming policy is applied to the service names that is +applied to the algorithm names; a local service should use the +servicename@domain syntax. + + byte SSH_MSG_SERVICE_REQUEST + string service name + +If the server rejects the service request, it SHOULD send an appropriate +SSH_MSG_DISCONNECT message and MUST disconnect. + +When the service starts, it may have access to the session identifier +generated during the key exchange. + +If the server supports the service (and permits the client to use it), +it MUST respond with + byte SSH_MSG_SERVICE_ACCEPT + string service name + +Message numbers used by services should be in the area reserved for them +(see Section ``Summary of Message Numbers''). The transport level will +continue to process its own messages. + +Note that after a key exchange with implicit server authentication, the +client MUST wait for response to its service request message before +sending any further data. + +9. Additional Messages + +Either party may send any of the following messages at any time. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 16] + +INTERNET-DRAFT 7 November 1997 + +9.1. Disconnection Message + + byte SSH_MSG_DISCONNECT + uint32 reason code + string description [[RFC-2044]] + string language tag [[RFC-1766]] + +This message causes immediate termination of the connection. All +implementations MUST be able to process this message; they SHOULD be +able to send this message. + +The sender MUST NOT send or receive any data after this message, and the +recipient MUST NOT accept any data after receiving this message. The +description field gives a more specific explanation in a human-readable +form. The error code gives the reason in a more machine-readable format +(suitable for localization), and can have the following values: + + #define SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 + #define SSH_DISCONNECT_PROTOCOL_ERROR 2 + #define SSH_DISCONNECT_KEY_EXCHANGE_FAILED 3 + #define SSH_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 + #define SSH_DISCONNECT_MAC_ERROR 5 + #define SSH_DISCONNECT_COMPRESSION_ERROR 6 + #define SSH_DISCONNECT_SERVICE_NOT_AVAILABLE 7 + #define SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 + #define SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 + #define SSH_DISCONNECT_CONNECTION_LOST 10 + #define SSH_DISCONNECT_BY_APPLICATION 11 + +If the description string is displayed, control character filtering +discussed in [SSH-ARCH] should be used to avoid attacks by sending +terminal control characters. + +9.2. Ignored Data Message + + byte SSH_MSG_IGNORE + string data + +All implementations MUST understand (and ignore) this message at any +time (after receiving the protocol version). No implementation is +required to send them. This message can be used as an additional +protection measure against advanced traffic analysis techniques. + +9.3. Debug Message + + byte SSH_MSG_DEBUG + boolean always_display + string message [[RFC-2044]] + string language tag [[RFC-1766]] + +All implementations MUST understand this message, but they are allowed +to ignore it. This message is used to pass information to the other +side that may help debugging. If always_display is true, the message + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 17] + +INTERNET-DRAFT 7 November 1997 + +SHOULD be displayed. Otherwise, it SHOULD NOT be displayed unless +debugging information has been explicitly requested by the user. + +The message doesn't need to contain a newline. It is, however, allowed +to consist of multiple lines separated by CRLF (carriage return - line +feed) pairs. + +If the message string is displayed, terminal control character filtering +discussed in [SSH-ARCH] should be used to avoid attacks by sending +terminal control characters. + +9.4. Reserved Messages + +An implementation MUST respond to all unrecognized messages with an +SSH_MSG_UNIMPLEMENTED message in the order in which they were received. +Such messages MUST be otherwise ignored. Later protocol versions may +define other meanings for these message types. + + byte SSH_MSG_UNIMPLEMENTED + uint32 packet sequence number of rejected message + +10. Summary of Message Numbers + +The following message numbers have been defined in this protocol. + + #define SSH_MSG_DISCONNECT 1 + #define SSH_MSG_IGNORE 2 + #define SSH_MSG_UNIMPLEMENTED 3 + #define SSH_MSG_DEBUG 4 + #define SSH_MSG_SERVICE_REQUEST 5 + #define SSH_MSG_SERVICE_ACCEPT 6 + + #define SSH_MSG_KEXINIT 20 + #define SSH_MSG_NEWKEYS 21 + + /* Numbers 30-49 used for kex packets. + Different kex methods may reuse message numbers in + this range. */ + #define SSH_MSG_KEXDH_INIT 30 + #define SSH_MSG_KEXDH_REPLY 31 + +11. Security Considerations + +This protocol provides a secure encrypted channel over an unsecure +network. It performs server host authentication, key exchange, +encryption, and integrity protection. It also derives a unique session +id that may be used by higher-level protocols. + +It is expected that this protocol will sometimes be used without +insisting on reliable association between the server host key and the +server host name. Such use is inherently insecure, but may be necessary +in non-security critical environments, and still provides protection +against passive attacks. However, implementors of protocols running on + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 18] + +INTERNET-DRAFT 7 November 1997 + +top of this protocol should keep this possibility in mind. + +This protocol is designed to be used over a reliable transport. If +transmission errors or message manipulation occur, the connection is +closed. The connection SHOULD be re-established if this occurs. Denial +of service attacks of this type ("wire cutter") are almost impossible to +avoid. + +12. References + +[FIPS-186] Federal Information Processing Standards Publication (FIPS +PUB) 186, Digital Signature Standard, 18 May 1994. + +[Orm96] Orman, H., "The Oakley Key Determination Protocol", version 1, +TR97-92, Department of Computer Science Technical Report, University of +Arizona. + +[RFC-1034] Mockapetris, P., "Domain Names - Concepts and Facilities", +November 1987. + +[RFC-1766] Alvestrand, H., "Tags for the Identification of Languages", +March 1995. + +[RFC-1950] Deutch, P. and Gailly, J-L., "ZLIB Compressed Data Format +Specification version 3.3", May 1996. + +[RFC-1951] Deutch, P., "DEFLATE Compressed Data Format Specification +version 1.3", May 1996. + +[RFC-2044] Yergeau, F., "UTF-8, a Transformation Format of Unicode and +ISO 10646", October 1996. + +[RFC-2104] Krawczyk, H., Bellare, M., and Canetti, R., "HMAC: Keyed- +Hashing for Message Authentication", February 1997 + +[RFC-2119] Bradner, S., "Key words for use in RFCs to indicate +Requirement Levels", March 1997. + +[RFC-2144] Adams, C., "The CAST-128 Encryption Algorithm", May 1997. + +[Schneier] Schneier, B., "Applied Cryptography Second Edition: +protocols, algorithms, and source code in C", 2nd edition, John Wiley & +Sons, New York, NY, 1996. + +[SSH-ARCH] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH Protocol +Architecture", Internet Draft, draft-ietf-secsh-architecture-00.txt + +#91;SSH-USERAUTH] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH +Authentication Protocol", Internet Draft, draft-ietf-secsh- +userauth-02.txt + +[SSH-CONNECT] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH Connection +Protocol", Internet Draft, draft-ietf-secsh-connect-02.txt + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 19] + +INTERNET-DRAFT 7 November 1997 + +[PGP] (XXX n.a.) + +[PKIX-Part1] (XXX n.a.) + +13. Authors' Addresses + + Tatu Ylonen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: ylo@ssh.fi + + Tero Kivinen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: kivinen@ssh.fi + + Markku-Juhani O. Saarinen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: mjos@ssh.fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 20] diff --git a/info/userauth.txt b/info/userauth.txt new file mode 100644 index 0000000..bb2d72c --- /dev/null +++ b/info/userauth.txt @@ -0,0 +1,647 @@ +Network Working Group T. Ylonen +INTERNET-DRAFT T. Kivinen +draft-ietf-secsh-userauth-03.txt M. Saarinen +Expires in six months SSH + 7 November 1997 + + SSH Authentication Protocol + +Status of This memo + +This document is an Internet-Draft. Internet-Drafts are working +documents of the Internet Engineering Task Force (IETF), its areas, +and its working groups. Note that other groups may also distribute +working documents as Internet-Drafts. + +Internet-Drafts are draft documents valid for a maximum of six +months and may be updated, replaced, or obsoleted by other documents +at any time. It is inappropriate to use Internet-Drafts as reference +material or to cite them other than as ``work in progress.'' + +To learn the current status of any Internet-Draft, please check +the ``1id-abstracts.txt'' listing contained in the Internet-Drafts +Shadow Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe), +munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), +or ftp.isi.edu (US West Coast). + +Abstract + +SSH is a protocol for secure remote login and other secure network +services over an insecure network. + +This document describes the SSH authentication protocol framework and +public key, password, and host-based client authentication methods. +Additional authentication methods are deferred to separate documents. + +The SSH authentication protocol runs on top the SSH transport layer +protocol and provides a single authenticated tunnel for the SSH +connection protocol. + + + + + + + + + + + + + + + + + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 1] + +INTERNET-DRAFT 7 November 1997 + +Table of Contents + +1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . 2 +2. The Authentication Protocol Framework . . . . . . . . . . . . . 2 + 2.1. Authentication Requests . . . . . . . . . . . . . . . . . . 3 + 2.2. Responses to Authentication Requests . . . . . . . . . . . . 3 + 2.3. The none Authentication Request . . . . . . . . . . . . . . 4 + 2.4. Completion of User Authentication . . . . . . . . . . . . . 5 + 2.5. Banner Message . . . . . . . . . . . . . . . . . . . . . . . 5 +3. Authentication Protocol Message Numbers . . . . . . . . . . . . 5 +4. Public Key Authentication Method: publickey . . . . . . . . . . 6 +5. Password Authentication Method: password . . . . . . . . . . . . 7 +6. Host-Based Authentication: hostbased . . . . . . . . . . . . . . 9 +7. Security Considerations . . . . . . . . . . . . . . . . . . . . 10 +8. References . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 +9. Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 11 + + + +1. Introduction + +The SSH authentication protocol is a general-purpose user authentication +protocol. It is intended to be run over the SSH transport layer +protocol [SSH-TRANS]. This protocol assumes that the underlying +protocols provide integrity and confidentiality protection. + +This document should be read only after reading the SSH architecture +document [SSH-ARCH]. This document freely uses terminology and notation +from the architecture document without reference or further explanation. + +The service name for this protocol is "ssh-userauth". + +When this protocol starts, it receives the session identifier from the +lower-level protocol. The session identifier uniquely identifies this +session and is suitable for signing to prove ownership of a private key. +This protocol also needs to know whether the lower-level protocol +provides confidentiality protection. + +2. The Authentication Protocol Framework + +The server drives the authentication by telling the client which +authentications can usefully continue the dialog at any given time. The +client has the freedom to try the methods listed by the server in any +order. This gives the server complete control over the authentication +process if it so desired, but also gives enough flexibility for the +client to use the methods it supports or that are most convenient for +the user when multiple methods are offered by the server. + +Authentication methods are identified by names, as defined in [SSH- +ARCH]. The "none" method is reserved, and MUST NOT be listed as +supported. However, it MAY be sent by the client. The server MUST +always reject this request, unless the client is to be allowed in +without any authentication, in which case the server MUST accept this + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 2] + +INTERNET-DRAFT 7 November 1997 + +request. The main purpose of sending this request is to get the list of +supported methods from the server. + +The server SHOULD have a timeout for authentication, and disconnect if +the authentication has not been accepted within the timeout period. The +RECOMMENDED timeout period is 10 minutes. Additionally, the +implementation SHOULD limit the number of failed authentication attempts +a client may perform in a single session (the RECOMMENDED limit is 20 +attempts). If the threshold is exceeded, the server SHOULD disconnect. + +2.1. Authentication Requests + +All authentication requests MUST use the following message format. Only +the first few fields are defined; the remaining fields depend on the +authentication method. + + byte SSH_MSG_USERAUTH_REQUEST + string user name (in ISO-10646 UTF-8 encoding) + string service name (in US-ASCII) + string method name (US-ASCII) + rest of the packet is method-specific + +The user name and service are repeated in every new authentication +attempt, and MAY change. The server implementation MUST carefully check +them in every message, and MUST flush any accumulated authentication +state if they change. If it is unable to flush some authentication +state, it MUST disconnect if the user or service name changes. + +The service name specifies the service to start after authentication. +There may be several different authenticated services provided. If the +requested service is not available, the server MAY disconnect +immediately or any time later. Sending a proper disconnect message is +RECOMMENDED. In any case, if the service does not exist, authentication +MUST NOT be accepted. + +If the requested user does not exist, the server MAY disconnect, or MAY +send a bogus list of acceptable authentications but never accept any. +This makes it possible for the server to avoid disclosing information +about which accounts exist. In any case, if the user does not exist, +the authentication request MUST NOT be accepted. + +While there is usually little point for clients to send requests that +the server does not list as acceptable, sending such requests is not an +error, and the server SHOULD simply reject requests that it does not +recognize. + +An authentication request MAY result in a further exchange of messages. +All such messages depend on the authentication method used, and the +client MAY at any time continue with a new SSH_MSG_USERAUTH_REQUEST +message, in which case the server MUST abandon the previous +authentication attempt and continue with the new one. + + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 3] + +INTERNET-DRAFT 7 November 1997 + +2.2. Responses to Authentication Requests + +If the server rejects the authentication request, it MUST respond with + + byte SSH_MSG_USERAUTH_FAILURE + string authentications that can continue + boolean partial success + +"Authentications that can continue" is a comma-separated list of +authentication method names that may productively continue the +authentication dialog. + +It is RECOMMENDED that servers only include those methods in the list +that are actually useful. However, it is not illegal to include methods +that cannot be used to authenticate the user. + +Already successfully completed authentications SHOULD NOT be included in +the list, unless they really should be performed again for some reason. + +"Partial success" MUST be true if the authentication request to which +this is a response was successful. It MUST be false if the request was +not successfully processed. + +When the server accepts authentication, it MUST respond with + + byte SSH_MSG_USERAUTH_SUCCESS + +Note that this is not sent after each step in a multi-method authentica- +tion sequence, but only when authentication is complete. + +The client MAY send several authentication requests without waiting for +responses from previous requests. The server MUST acknowledge any +failed requests with a SSH_MSG_USERAUTH_FAILURE message. However, +SSH_MSG_USERAUTH_SUCCESS MUST sent only once, and once +SSH_MSG_USERAUTH_SUCCESS has been sent, any further authentication +requests received after that SHOULD be silently ignored. + +Any non-authentication messages sent by the client after the request +that resulted in SSH_MSG_USERAUTH_SUCCESS being sent MUST be passed to +the service being run on top of this protocol. Such messages can be +identified by their message numbers (see Section ``Message Numbers''). + +2.3. The none Authentication Request + +A client may request a list of authentication methods that may continue +by using the "none" authentication method. + +If no authentication at all is needed for the user, the server MUST +return SSH_MSG_USERAUTH_SUCCESS. Otherwise, the server MUST return +SSH_MSG_USERAUTH_FAILURE and MAY return with it a list of authentication +methods that can continue. + +This method MUST NOT be listed as supported by the server. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 4] + +INTERNET-DRAFT 7 November 1997 + +2.4. Completion of User Authentication + +Authentication is complete when the server has responded with +SSH_MSG_USERAUTH_SUCCESS; all authentication related messages received +after sending this message SHOULD be silently ignored. + +After sending SSH_MSG_USERAUTH_SUCCESS, the server starts the requested +service. + +2.5. Banner Message + +In some jurisdictions, sending a warning message before authentication +may be relevant to getting legal protection. Many UNIX machines, for +example, normally display text from /etc/issue, or use "tcp wrappers" or +similar software to display a banner before issuing a login prompt. + +The SSH server may send a SSH_MSG_USERAUTH_BANNER message at any time +before authentication is successful. This message contains text to be +displayed to the client user before authentication is attempted. The +format is as follows. + + byte SSH_MSG_USERAUTH_BANNER + string message (ISO-10646 UTF-8) + string language tag (as defined in RFC 1766) + +The client SHOULD by default display the message on the screen. +However, since the message is likely to be sent for every login attempt, +and since some client software will need to open a separate window for +this warning, the client software may allow the user to explicitly +disable the display of banners from the server. The message may consist +of multiple lines. + +If the message string is displayed, control character filtering +discussed in [SSH-ARCH] SHOULD be used to avoid attacks by sending +terminal control characters. + +3. Authentication Protocol Message Numbers + +All message numbers used by this authentication protocol are in the +range 50..79, which is part of the range reserved for protocols running +on top of the SSH transport layer protocol. + +Message numbers 80 and higher are reserved for protocols running after +this authentication protocol, so receiving one of them before +authentication is complete is an error, to which the server MUST respond +by disconnecting (preferably with a proper disconnect message sent first +to ease troubleshooting). + +After successful authentication, such messages are passed to the higher- +level service. + +These are the general authentication message codes: + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 5] + +INTERNET-DRAFT 7 November 1997 + + #define SSH_MSG_USERAUTH_REQUEST 50 + #define SSH_MSG_USERAUTH_FAILURE 51 + #define SSH_MSG_USERAUTH_SUCCESS 52 + #define SSH_MSG_USERAUTH_BANNER 53 + +In addition to the above, there is a range of message numbers (25..29) +reserved for method-specific messages. These messages are only sent by +the server (client only sends SSH_MSG_USERAUTH_REQUEST messages). +Different authentication methods reuse the same message numbers. + +4. Public Key Authentication Method: publickey + +The only REQUIRED authentication method is public key authentication. +All implementations MUST support this method; however, not all users +need to have public keys, and most local policies are not likely to +require public key authentication for all users in near future. + +With this method, the possession of a private key serves as +authentication. This method works by sending a signature created with a +private key of the user. The server MUST check that the key is a valid +authenticator for the user, and MUST check that the signature is valid. +If both hold, the authentication request MUST be accepted; otherwise it +MUST be rejected. (Note that the server MAY require additional +authentications after successful authentication.) + +Private keys are often stored encrypted at the client host, and the user +must supply a passphrase before the signature can be generated. Even if +they are not, the signing operation involves some expensive computation. +To avoid unnecessary processing and user interaction, the following +message is provided for querying whether authentication using the key +would be acceptable. + + byte SSH_MSG_USERAUTH_REQUEST + string user name + string service + string "publickey" + boolean FALSE + string public key algorithm name + string public key blob + +Public key algorithms are defined in the transport layer specification +[SSH-TRANS]. The public key blob may contain certificates. + +Any public key algorithm may be offered for use in authentication. In +particular, the list is not constrained by what was negotiated during +key exchange (as that was affected by which algorithms the server had a +host key). If the server does not support some algorithm, it MUST +simply reject the request. + +The server MUST respond to this message with either +SSH_MSG_USERAUTH_FAILURE or with + + byte SSH_MSG_USERAUTH_PK_OK + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 6] + +INTERNET-DRAFT 7 November 1997 + + string public key algorithm name from the request + string public key blob from the request + +To do actual authentication, the client MAY then send a signature +generated using the private key. Client MAY send the signature directly +without first verifying whether the key is acceptable. The signature is +sent using the following packet + + byte SSH_MSG_USERAUTH_REQUEST + string user name + string service + string "publickey" + boolean TRUE + string public key algorithm name + string public key to be used for authentication + string signature + +Signature is a signature by the corresponding private key over the +following data, in this order: + +o session identifier, and + +o packet payload without the signature. + +When the server receives this message, it MUST check whether the +supplied key is acceptable for authentication, and if so, it MUST check +whether the signature is correct. + +If both checks succeed, this method is successful. Note that the server +may require additional authentications. The server MUST respond with +SSH_MSG_USERAUTH_SUCCESS (if no more authentications are needed), or +SSH_MSG_USERAUTH_FAILURE (if the request failed, or more authentications +are needed). + +The following method-specific message numbers are used by the publickey +authentication method. + + /* Key-based */ + #define SSH_MSG_USERAUTH_PK_OK 60 + +5. Password Authentication Method: password + +Password authentication uses the following packets. Note that a server +MAY request the user to change password. All implementations SHOULD +support password authentication. + + byte SSH_MSG_USERAUTH_REQUEST + string user name + string service + string "password" + boolean FALSE + string plaintext password (ISO-10646 UTF-8) + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 7] + +INTERNET-DRAFT 7 November 1997 + +Note that the password is encoded in ISO-10646 UTF-8. It is up to the +server how it interprets the password and validates it against the +password database. However, if the client reads the password in some +other encoding (e.g., ISO 8859-1 (ISO Latin1)), it MUST convert the +password to ISO-10646 UTF-8 before transmitting, and the server MUST +convert the password to the encoding used on that system for passwords. + +Note that even though the cleartext password is transmitted in the +packet, the entire packet is encrypted by the transport layer. Both the +server and the client should check whether the underlying transport +layer provides confidentiality (i.e., encryption is being used). If no +confidentiality is provided ("none" cipher), password authentication +SHOULD be disabled. If there is no confidentiality or no MAC, password +change SHOULD be disabled. + +Normally, the server responds to this message with success or failure. +However, the server MAY also respond with +SSH_MSG_USERAUTH_PASSWD_CHANGEREQ. + + byte SSH_MSG_USERAUTH_PASSWD_CHANGEREQ + string prompt (ISO-10646 UTF-8) + string language tag (as defined in RFC 1766) + +In this case, the software client SHOULD request a new password from the +user, and send a new request using the following message. The client +may also send this message instead of the normal password authentication +request without the server asking for it. + + byte SSH_MSG_USERAUTH_REQUEST + string user name + string service + string "password" + boolean TRUE + string plaintext old password (ISO-10646 UTF-8) + string plaintext new password (ISO-10646 UTF-8) + +The server must reply to request message with SSH_MSG_USERAUTH_SUCCESS, +SSH_MSG_USERAUTH_FAILURE, or another SSH_MSG_USERAUTH_PASSWD_CHANGEREQ. +The meaning of these is as follows: + + SSH_MSG_USERAUTH_SUCCESS + Password has been changed, and authentication has been + successfully completed. + + SSH_MSG_USERAUTH_FAILURE with partial success + The password has been changed, but more authentications are + needed. + + SSH_MSG_USERAUTH_FAILURE without partial success + The password has not been changed. Either password changing was + not supported, or the old password was bad. Note that if the + server has already sent SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, we know + that it supports changing the password. + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 8] + +INTERNET-DRAFT 7 November 1997 + + SSH_MSG_USERAUTH_CHANGEREQ + The password was not changed because the new password was not + acceptable (e.g. too easy to guess). + +The following method-specific message numbers are used by the password +authentication method. + + #define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 60 + +6. Host-Based Authentication: hostbased + +Some sites wish to allow authentication based on the host where the user +is coming from and the user name on the remote host. While this form of +authentication is not suitable for high-security sites, it can be very +convenient in many environments. This form of authentication is +OPTIONAL. When used, special care SHOULD be taken to prevent a regular +user from obtaining the private host key. +The client requests this form of authentication by sending the following +message. It is similar to the UNIX "rhosts" and "hosts.equiv" styles of +authentication, except that the identity of the client host is checked +more rigorously. + +This method works by having the client send a signature created with the +private key of the client host, which the server checks with that host's +public key. Once the client host's identity is established, +authorization, but no further authentication, is performed based on the +user names on the server and client, and the client host name. + + byte SSH_MSG_USERAUTH_REQUEST + string user name + string service + string "hostbased" + string public key algorithm for host key + string public host key and certificates for client host + string client host name (FQDN; US-ASCII) + string client user name on the remote host (ISO-10646 UTF-8) + string signature + +Public key algorithm names for use in "public key algorithm for host +key" are defined in the transport layer specification. The "public host +key for client host" may include certificates. + +Signature is a signature with the private host key of the following +data, in this order: + +o session identifier, and + +o packet payload without the signature. + +The server MUST verify that the host key actually belongs to the client +host named in the message, that the given user on that host is allowed +to log in, and that the signature is a valid signature on the +appropriate value by the given host key. The server MAY ignore the + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 9] + +INTERNET-DRAFT 7 November 1997 + +client user name, if it wants to authenticate only the client host. + +It is RECOMMENDED that whenever possible, the server perform additional +checks to verify that the network address obtained from the (untrusted) +network matches the given client host name. This makes exploiting +compromised host keys more difficult. Note that this may require +special handling for connections coming through a firewall. + +7. Security Considerations + +The purpose of this protocol is to perform client user authentication. +It assumed that this runs over a secure transport layer protocol, which +has already authenticated the server machine, established an encrypted +communications channel, and computed a unique session identifier for +this session. The transport layer provides forward secrecy for password +authentication and other methods that rely on secret data. + +The server may go into a "sleep" period after repeated unsuccesful +authentications to make key search harder. + +If the transport layer does not provide encryption, authentication +methods that rely on secret data SHOULD be disabled. If it does not +provide MAC protection, requests to change authentication data (e.g. +password change) SHOULD be disabled to avoid an attacker from modifying +the ciphertext without being noticed, rendering the new authentication +data unusable (denial of service). + +Several authentication methods with different security characteristics +are allowed. It is up to the server's local policy to decide which +methods (or combinations of methods) it is willing to accept for each +user. Authentication is no stronger than the weakest combination +allowed. + +Special care should be taken when designing debug messages. These +messages may reveal surprising amounts of information about the host if +not properly designed. Debug messages can be disabled (during user +authentication phase) if high security is sought after. + +8. References + +[RFC-1766] Alvestrand, H., "Tags for the Identification of Languages", +March 1995. + +[RFC-2044] Yergeau, F., "UTF-8, a Transformation Format of Unicode and +ISO 10646", October 1996. + +[SSH-ARCH] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH Protocol +Architecture", Internet Draft, draft-secsh-architecture-00.txt + +[SSH-TRANS] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH Transport +Layer Protocol", Internet Draft, draft-secsh-transport-02.txt + +[SSH-CONNECT] Ylonen, T., Kivinen, T, and Saarinen, M., "SSH Connection + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 10] + +INTERNET-DRAFT 7 November 1997 + +Protocol", Internet Draft, draft-secsh-connect-02.txt + +9. Author's Address + + Tatu Ylonen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: ylo@ssh.fi + + Tero Kivinen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: kivinen@ssh.fi + + Markku-Juhani O. Saarinen + SSH Communications Security Ltd. + Tekniikantie 12 + FIN-02150 ESPOO + Finland + E-mail: mjos@ssh.fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +T. Ylonen, T. Kivinen, and M. Saarinen [page 11] diff --git a/not-for-export b/not-for-export new file mode 100644 index 0000000..bae482a --- /dev/null +++ b/not-for-export @@ -0,0 +1,10 @@ +Crypto-1.1a2/Demo/cipher +Crypto-1.1a2/stream +Crypto-1.1a2/block +Crypto-1.1a2/framewks/block.in +Crypto-1.1a2/framewks/stream.in +Crypto-1.1a2/curiosa +Crypto-1.1a2/Protocol/PGP +Crypto-1.1a2/Protocol/AllOrNothing.py +Crypto-1.1a2/PublicKey/RSA.py +Crypto-1.1a2/PublicKey/ElGamal.py diff --git a/stream/ARC4.c b/stream/ARC4.c new file mode 100644 index 0000000..590fabb --- /dev/null +++ b/stream/ARC4.c @@ -0,0 +1,72 @@ + +/* + * arc4.c : Implementation for the Alleged-RC4 stream cipher + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +typedef struct +{ + PCTObject_HEAD; + unsigned char state[256]; + unsigned char x,y; +} ARC4object; + +#define ARC4decrypt ARC4encrypt /* Encryption and decryption are symmetric */ + +static inline void ARC4encrypt(self, block, len) + ARC4object *self; + unsigned char *block; + int len; +{ + register int i, x=self->x, y=self->y; + + for (i=0; i<len; i++) + { + x = (x + 1) % 256; + y = (y + self->state[x]) % 256; + { + register int t; /* Exchange state[x] and state[y] */ + t = self->state[x]; + self->state[x] = self->state[y]; + self->state[y] = t; + } + { + register int xorIndex; /* XOR the data with the stream data */ + xorIndex=(self->state[x]+self->state[y]) % 256; + block[i] ^= self->state[xorIndex]; + } + } + self->x=x; + self->y=y; +} + + +static void ARC4init(self, key, keylen) + ARC4object *self; + unsigned char *key; + int keylen; +{ + register int i, index1, index2; + + for(i=0; i<256; i++) self->state[i]=i; + self->x=0; self->y=0; + index1=0; index2=0; + for(i=0; i<256; i++) + { + register int t; + index2 = ( key[index1] + self->state[i] + index2) % 256; + t = self->state[i]; + self->state[i] = self->state[index2]; + self->state[index2] = t; + index1 = (index1 + 1) % keylen; + } +} + + + diff --git a/stream/Sapphire.c b/stream/Sapphire.c new file mode 100644 index 0000000..00a9978 --- /dev/null +++ b/stream/Sapphire.c @@ -0,0 +1,125 @@ + +/* + * sapphire.c : Implementation for the Sapphire stream cipher + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +typedef struct +{ + PCTObject_HEAD; + unsigned char state[256]; + unsigned char index[5];/* Rotor, ratchet, avalanche, last_plain, last_cipher */ +} Sapphireobject; + +#define rotor self->index[0] +#define ratchet self->index[1] +#define avalanche self->index[2] +#define last_plain self->index[3] +#define last_cipher self->index[4] + +static inline void Sapphireencrypt(self, block, len) + Sapphireobject *self; + unsigned char *block; + unsigned int len; +{ + unsigned char temp; + unsigned int i; + + for(i=0; i<len; i++) + { + ratchet += self->state[rotor++]; + temp = self->state[last_cipher]; + self->state[last_cipher] = self->state[ratchet]; + self->state[ratchet] = self->state[last_plain]; + self->state[last_plain] = self->state[rotor]; + self->state[rotor] = temp; + avalanche += self->state[temp]; + + temp = block[i]; + block[i] ^= self->state[ 0xFF & (self->state[rotor] + self->state[ratchet]) ] + ^ self->state[self->state[(self->state[avalanche] + + self->state[last_plain] + + self->state[last_cipher] + ) & 0xFF + ] + ]; + last_plain = temp; + last_cipher = block[i]; + } +} + +static inline void Sapphiredecrypt(self, block, len) + Sapphireobject *self; + unsigned char *block; + unsigned int len; +{ + unsigned char temp; + unsigned int i; + + for(i=0; i<len; i++) + { + ratchet += self->state[rotor++]; + temp = self->state[last_cipher]; + self->state[last_cipher] = self->state[ratchet]; + self->state[ratchet] = self->state[last_plain]; + self->state[last_plain] = self->state[rotor]; + self->state[rotor] = temp; + avalanche += self->state[temp]; + temp = block[i]; + block[i] ^= self->state[ 0xFF & (self->state[rotor] +self->state[ratchet]) ] + ^ self->state[self->state[(self->state[avalanche] + + self->state[last_plain] + + self->state[last_cipher] + ) & 0xFF + ] + ]; + last_cipher = temp; + last_plain = block[i]; + } +} + +static void Sapphireinit(self, key, keylen) + Sapphireobject *self; + unsigned char *key; + int keylen; +{ + int i; + unsigned char toswap, keypos, rsum, swaptemp; + + for (i = 0; i < 256; i++) + self->state[i] = i; + + keypos = 0; + rsum = 0; + for (i = 255; i >= 0; i--) + { unsigned int retry_limiter=0, mask=1; + while (mask < i) + mask = (mask << 1) + 1; + do + { + rsum = self->state[rsum] + key[keypos++]; + if (keypos >= keylen) + { + keypos = 0; + rsum += keylen; + } + toswap = mask & rsum; + if (++retry_limiter > 11) + toswap %= i; + } while (toswap > i); + swaptemp = self->state[i]; + self->state[i] = self->state[toswap]; + self->state[toswap] = swaptemp; + } + rotor=self->state[1]; + ratchet=self->state[3]; + avalanche=self->state[5]; + last_plain=self->state[7]; + last_cipher=self->state[rsum]; +} diff --git a/stream/XOR.c b/stream/XOR.c new file mode 100644 index 0000000..5244d63 --- /dev/null +++ b/stream/XOR.c @@ -0,0 +1,51 @@ +/* + * xor.c : Source for the trivial cipher which XORs the message with the key. + * The key can be up to 32 bytes long. + * + * Part of the Python Cryptography Toolkit, version 1.1 + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + */ + +typedef struct +{ + PCTObject_HEAD; + unsigned char key[32]; + int keylen, last_pos; +} XORobject; + +static inline void +XORinit(self, key, len) + XORobject *self; + unsigned char *key; + int len; +{ + int i; + + if (32 <= len) len=32; + self->keylen = len; + self->last_pos = 0; + + for(i=0; i<len; i++) + { + self->key[i] = key[i]; + } +} + +static inline void XORencrypt(self, block, len) + XORobject *self; + unsigned char *block; + int len; +{ + int i, j = self->last_pos; + for(i=0; i<len; i++, j=(j+1) % self->keylen) + { + block[i] ^= self->key[j]; + } + self->last_pos = j; +} + +#define XORdecrypt XORencrypt @@ -0,0 +1,41 @@ +# +# Test script for the Python Cryptography package. +# + +import sys +args = sys.argv[1:] +quiet = "--quiet" in args +if quiet: args.remove('--quiet') + +from Crypto.Util import test + +if not quiet: + print 'Public-key Functions:' + print '=====================' + +if args: test.TestPKModules(args, verbose= not quiet) +else: test.TestPKModules(verbose= not quiet) + + +if not quiet: + print '\nHash Functions:' + print '===============' + +if args: test.TestHashModules(args, verbose= not quiet) +else: test.TestHashModules(verbose= not quiet) + +if not quiet: + print '\nStream Ciphers:' + print '===============' + +if args: test.TestStreamModules(args, verbose= not quiet) +else: test.TestStreamModules(verbose= not quiet) + +if not quiet: + print '\nBlock Ciphers:' + print '==============' + +if args: test.TestBlockModules(args, verbose= not quiet) +else: test.TestBlockModules(verbose= not quiet) + + |