[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Object Oriented Crypto API

Sorry I took so long to respond.

Hal wrote:
> I enjoyed Ray's message about the crypto library interface.  I haven't
> had time to study it closely, but I have a couple of quick comments:
> I thought Wei's library looked pretty easy to use already.  Maybe Ray
> could show an example of what would be needed with Wei's library to do
> some "typical" crypto function, say encrypting a message with someone
> else's RSA key.  Then we could compare it with how the same function
> would look with Ray's proposed interface.

  Wei's library is easy to use from a certain standpoint. It depends
on a ASN.1 stream paradigm to stack cryptographic layers. However, I
think it is lacking in certain areas which makes it difficult
to use, and it is completely missing many functions such as
key distribution and management.

To RSA encrypt with Wei's library, you have to open the key file
(or read it into memory somehow), and instantiate a "BufferedTransformation"
which is a sort of internal stream library. Then you construct
a RSAPublicKey object around the BufferedTransformation, then you
generate a random blockcipher key, and tell the RSAPublicKey object
to encrypt it. Next, you encode your plaintext with the block cipher
separately using the blockcipher key. I'm going to ignore the
actual syntax of Wei's library for the moment (because I don't remember
it) and use pseudo code.

key_data_stream = FileSource("publickey.data") /* like ifstream() */
RSAPublicKey rsa_object(key_data_stream);
random_blockcipher_key = /* generate the key somehow, note, key generation
	                    is not standardized across all encryption
                            algorithms, so the application writer
                            must know how to generate the session key
                            manually */
rsa_object.Encrypt(random_blockcipher_key, encryptedkey);
DESEncryption desenc(random_blockcipher_key);
desenc.ProcessBlock(plaintext, ciphertext);
/* write "encryptedkey" and "ciphertext" somewhere */

Under my scheme, it would look something like this

/* we are given PlainText p, a KeyID which is of the format
   KeyID ::= identifier ['::' keyserver]
   identifier ::= RFC822_EMAIL_ADDR | HEX_STRING;

   example: "Ray Cromwell" <[email protected]>::keyserver.com
   we are doing RSA encryption with DES

DESEncryptionAlgorithm des;
RSAEncryptionAlgorithm rsa(des);

Encrypt(rsa, KeyId, p, c);

Here's the explaination of what's going on underneath (refer to
my OO Crypto API article if needed)

DES is a BlockCipherEncryptionAlgorithm (child of EncryptionAlgorithm)
RSA is a PublicKeyEncryptionAlgorithm (also a child of EncryptionAlgorithm)
that expects to be constructed with a BlockCipherEncryptionAlgorithm
because it uses a blockcipher as the underlying encryption technique
and only encrypts the session key. Any old BlockCipherEncryptionAlgorithm
will do, DES, IDEA, etc. RSA doesn't care.

Encrypt() is a global function which takes as its first argument
an EncryptionAlgorithm, second, a KeyID, and third/fourth a plaintext
and ciphertext tokenized stream (to be explained later). Encrypt()
doesn't care what the cryptosystem is, it's a single entry point
for the application developer.

Encrypt's pseudocode looks like this

Encrypt(EncryptionAlgorithm encalg, KeyID kid, Plaintext p, Ciphertext c)
    KeyDomain kdom=encalg.GetKeyDomain(kid);
    EncryptionKey ek = GetKey(kdom, kid);
    encalg.encrypt(ek, p, c);

Line 1 asks the EncryptionAlgorithm (whatever type it really is), to
return a KeyDomain for that cryptosystem. A KeyDomain is an abstract
universal object for fetching any key type from any place. It could
for instance, be fetching the key from a disk file, from an email
signature, or am internet key server.

Line 2 calls a global key management function GetKey which queries
a KeyDomain with the KeyID to return an EncryptionKey.

Line 3 calls the encrypt function on the EncryptionAlgorithm.

I have toyed with other interfaces. For instance, since we want to
support the definition of new KeyDomain types, we really should allow
an overloaded Encrypt where the EncryptionKey is passed as an argument,
so that the application developer can use third party KeyDomains.

Every EncryptionAlgorithm (hereafter abbreviated EA, where DA is
a DecryptionAlgorithm) knows how to generate a KeyPair which
contains an encryption and decryption key such that
DA(keypair.decryption_key, EA(keypair.encryption_key, plaintext)) == plaintext
Whether the cipher is symmetric or not is irrevelent. The RSAEncryptionAlgorithm
encrypt() function basically calls generate_key() on the block cipher
and uses that as the session key. Application developers are shielded
from the representation of keys and the generation of them.

The real dream is to have a generic crypto library which can encrypt
anything using any algorithm fetching keys from any medium and
reading and writing any valid crypto file format. Application developers
could write code to operate on PGP file formats, RSAREF, PEM, or
anything without having to know anything about those formats at all.
The only thing that is standardized is the KeyID format. Sort of
a Universal Resource Name (URN) for key identification. Perhaps
"key://keyserver.domain/keyid" would be better.

Reading and Writing any file format
  How would an application be able to operate on a RIPEM message, and a
PGP file without knowing about the format of either?

  The general scheme is to use a tokenized stream which records
what has been done to the plaintext, and then some stream encoding
objects which "map" the stream to the local format as long as the
stream is consistent with the algorithms the file format supports. Think
of the stream as a string in a regular language (in the sense of automata
theory). The stream "mapper" is a deterministic finite automaton
which processes the "string" (the stream tokens) and determines
1) whether the string is acceptable by the language (file format)
it's mapping to, and 2) generates side effects which write out the
format to a buffer or file.

Consider the following symbol set,

A tokenized stream might look like

RSA_ENCRYPTION PUBLICKEY_REF [pkey data] [encrypted session key]
DES_ENCRYPTION [ciphertext]

A PGPEncoder would reject this stream because it doesn't use IDEA.

Encoders would have the job of verifying consistency of the
stream with the underlying file format, and also whether or not
the stream was encoded properly in the first place. If the
stream is invalid, exceptions are thrown. If some tokens are
missing (such as a timestamp), the Encoder can supply them.

> The other point is that there needs to be the ability to encrypt only
> a bit of a message at a time.  Particularly with public key the first
> message may be special in that it generates a session key which is used
> for the remainder.  So an interface for piecewise encryption and
> decryption is necessary.

The way to do this is to provide secondary interfaces across all
Algorithms which allow the operations of Init, Update, and Finalize,
much like RSA's MD5 interface operates.


From owner-cypherpunks  Tue Aug 15 15:06:28 1995