public Message(Stream input, RSAPrivateKey key, Converter <Guid, Salt> sessionSecret)
            {
                (_protected = new MemoryStream(0)).Dispose();
                _hash    = new HashStream(new SHA256Managed(), input);
                _payload = input;

                ReadHeader(_hash, out _version, out _state, out _transferId, out _salt);

                Salt secret;

                if (!UsesSessionKey)
                {
                    // Begin private key decryption
                    _payload = key.Decrypt(input);
                    _hash.ChangeStream(_payload);
                    // Decrypt the aes key used in this package
                    byte[] keybits = IOStream.Read(_hash, 32);
                    secret = Salt.FromBytes(keybits);
                }
                else
                {
                    secret = sessionSecret(_transferId);
                    Check.IsEqual(32, Check.NotNull(secret).Length);
                }

                AESCryptoKey sessionKey = new AESCryptoKey(
                    // Prefix the key with the message's salt and compute a SHA256 hash to be used as the key
                    Hash.SHA256(_salt.GetData(secret.ToArray()).ToStream()).ToArray(),
                    // Compute an IV for this aes key and salt combination
                    IV(secret, _salt)
                    );

                _payload = sessionKey.Decrypt(_payload);
                _hash.ChangeStream(_payload);
            }
            //private bool _verified; /* this is a debugging aid used to ensure all messages are signed or verified */

            public Message(TransferState state, Guid transferId, RSAPublicKey key, Converter <Guid, Salt> sessionSecret)
            {
                _version    = VersionHeader;
                _state      = state;
                _transferId = transferId;
                _salt       = new Salt(Salt.Size.b256);
                _protected  = new MemoryStream();
                _payload    = new NonClosingStream(_protected);
                _hash       = new HashStream(new SHA256Managed());
                WriteHeader(_hash);
                Salt secret;

                if (!UsesSessionKey)
                {
                    // Outer encryption is straight PKI based on the remote public key
                    _payload = key.Encrypt(_payload);
                    _hash.ChangeStream(_payload);
                    // Preceed the message with a new, AES key
                    secret = new Salt(Salt.Size.b256);
                    _hash.Write(secret.ToArray(), 0, 32);
                }
                else
                {
                    secret = sessionSecret(_transferId);
                    Check.IsEqual(32, Check.NotNull(secret).Length);
                }

                AESCryptoKey sessionKey = new AESCryptoKey(
                    // Prefix the key with the message's salt and compute a SHA256 hash to be used as the key
                    Hash.SHA256(_salt.GetData(secret.ToArray()).ToStream()).ToArray(),
                    // Compute an IV for this aes key and salt combination
                    IV(secret, _salt)
                    );

                _payload = sessionKey.Encrypt(_payload);
                _hash.ChangeStream(_payload);
            }
            //private bool _verified; /* this is a debugging aid used to ensure all messages are signed or verified */

            public Message(TransferState state, Guid transferId, RSAPublicKey key, Converter<Guid, Salt> sessionSecret)
            {
                _version = VersionHeader;
                _state = state;
                _transferId = transferId;
                _salt = new Salt(Salt.Size.b256);
                _protected = new MemoryStream();
                _payload = new NonClosingStream(_protected);
                _hash = new HashStream(new SHA256Managed());
                WriteHeader(_hash);
                Salt secret;

                if (!UsesSessionKey)
                {
                    // Outer encryption is straight PKI based on the remote public key
                    _payload = key.Encrypt(_payload);
                    _hash.ChangeStream(_payload);
                    // Preceed the message with a new, AES key
                    secret = new Salt(Salt.Size.b256);
                    _hash.Write(secret.ToArray(), 0, 32);
                }
                else
                {
                    secret = sessionSecret(_transferId);
                    Check.IsEqual(32, Check.NotNull(secret).Length);
                }

                AESCryptoKey sessionKey = new AESCryptoKey(
                    // Prefix the key with the message's salt and compute a SHA256 hash to be used as the key
                    Hash.SHA256(_salt.GetData(secret.ToArray()).ToStream()).ToArray(),
                    // Compute an IV for this aes key and salt combination
                    IV(secret, _salt)
                );

                _payload = sessionKey.Encrypt(_payload);
                _hash.ChangeStream(_payload);
            }
            public Message(Stream input, RSAPrivateKey key, Converter<Guid, Salt> sessionSecret)
            {
                (_protected = new MemoryStream(0)).Dispose();
                _hash = new HashStream(new SHA256Managed(), input);
                _payload = input;

                ReadHeader(_hash, out _version, out _state, out _transferId, out _salt);
                
                Salt secret;
                if (!UsesSessionKey)
                {
                    // Begin private key decryption
                    _payload = key.Decrypt(input);
                    _hash.ChangeStream(_payload);
                    // Decrypt the aes key used in this package
                    byte[] keybits = IOStream.Read(_hash, 32);
                    secret = Salt.FromBytes(keybits);
                }
                else
                {
                    secret = sessionSecret(_transferId);
                    Check.IsEqual(32, Check.NotNull(secret).Length);
                }

                AESCryptoKey sessionKey = new AESCryptoKey(
                    // Prefix the key with the message's salt and compute a SHA256 hash to be used as the key
                    Hash.SHA256(_salt.GetData(secret.ToArray()).ToStream()).ToArray(),
                    // Compute an IV for this aes key and salt combination
                    IV(secret, _salt)
                );
                
                _payload = sessionKey.Decrypt(_payload);
                _hash.ChangeStream(_payload);
            }