private Salt BeginUpload(Guid transferId, string location, long length, out int maxMessageLength)
            {
                byte[] serverKeyBits;
                byte[] nonce  = GetNonce(transferId, location, out serverKeyBits);
                Hash   hnonce = Hash.SHA256(nonce);

                //STEP 2: Create and send session key
                Salt clientKeyBits = new Salt(Salt.Size.b256);
                Salt sessionSecret = SessionSecret(clientKeyBits, serverKeyBits);

                using (Message req = new Message(TransferState.UploadRequest, transferId, _publicKey, NoSession))
                {
                    req.Write(hnonce.ToArray());
                    req.Write(length);
                    req.Write(location);
                    req.Write(clientKeyBits.ToArray());

                    Stream response = SendPayload(req, location, req.ToStream(_privateKey));
                    using (Message rsp = new Message(response, _privateKey, s => sessionSecret))
                    {
                        Check.Assert <InvalidOperationException>(rsp.State == TransferState.UploadResponse);
                        maxMessageLength = Check.InRange(rsp.ReadInt32(), 0, int.MaxValue);
                        rsp.VerifySignature(_publicKey);
                    }
                }
                return(sessionSecret);
            }
Example #2
0
 /// <summary> Returns the key generated with the current password and the provided salt </summary>
 public AESCryptoKey CreateKey(Salt salt)
 {
     DerivedBytes.Salt = salt.ToArray();
     DerivedBytes.Reset();
     byte[] key = DerivedBytes.GetBytes(32);
     return(new AESCryptoKey(key, IV));
 }
 private void WriteHeader(Stream stream)
 {
     PrimitiveSerializer.Int32.WriteTo(_version, stream);
     PrimitiveSerializer.Int32.WriteTo((int)_state, stream);
     stream.Write(_transferId.ToByteArray(), 0, 16);
     stream.Write(_salt.ToArray(), 0, 32);
 }
        private HashDerivedBytes(HMAC algo, Stream password, Salt salt, int iterations)
        {
            _block    = 1;
            _hashAlgo = algo;
            Check.Assert <ArgumentException>(_hashAlgo.CanReuseTransform);
            // delta from RFC2898 - key with a hash of password, it would happen anyway if password.Length > 32
            HashAlgorithm ha = (HashAlgorithm)CryptoConfig.CreateFromName(_hashAlgo.HashName);

            _hashAlgo.Key = ha.ComputeHash(password);
            _salt         = salt.ToArray();
            _iterations   = (uint)Check.InRange(iterations, 1, int.MaxValue);
        }
Example #5
0
        /// <summary> Encrypts the stream with the current password and salt </summary>
        public override Stream Encrypt(Stream stream)
        {
            try
            {
                Salt salt = this.Salt;
                stream.Write(salt.ToArray(), 0, salt.Length);

                AESCryptoKey key = CreateKey();
                return(new DisposingStream(key.Encrypt(stream))
                       .WithDisposeOf(key));
            }
            catch (InvalidOperationException) { throw; }
            catch { throw CryptographicException(); }
        }
            private byte[] IV(Salt secret, Salt salt)
            {
                // Long story, this has been a little difficult to finally settle upon an algorithm.
                // We know we don't want to the same value each time, and we prefer not to use only
                // the public salt.  My biggest concern is not generating a IV value that interacts
                // with AES in a way that might divulge information unintentionally.  Since we are
                // using the same values (salt+secret) to derive the IV this is a very real risk.
                // To mitigate this risk the primary interaction of secret in the derivation of this
                // value is compressed into a CRC of both the salt and secret.  This value is then
                // masked with computations of salt to produce the 16 IV bytes needed.
                byte[] sbytes = salt.ToArray();
                byte[] result = new byte[16];
                // compute a mask from CRC32(salt + secret)
                int mask = new Crc32(_salt.GetData(secret.ToArray()).ToArray()).Value;

                // xor part of the mask with the sum of two salt bytes
                for (int i = 0; i < 16; i++)
                {
                    result[i] = (byte)((mask >> i) ^ (sbytes[i] + sbytes[i + 16]));
                }

                return(result);
            }
            //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);
            }
Example #8
0
 /// <summary>
 /// Constructs the Rfc2898DeriveBytes implementation.
 /// </summary>
 public PBKDF2(byte[] password, Salt salt, int iterations)
     : base(password, salt.ToArray(), iterations)
 {
 }
            private bool Download(string location, StreamCache output)
            {
                int  maxMessageLength;
                long fileLength, bytesReceived = 0;
                Guid transferId = Guid.NewGuid();

                byte[] serverKeyBits;
                byte[] nonce  = GetNonce(transferId, location, out serverKeyBits);
                Hash   hnonce = Hash.SHA256(nonce);

                //STEP 2: Create and send session key
                Salt clientKeyBits = new Salt(Salt.Size.b256);
                Salt sessionKey    = SessionSecret(clientKeyBits, serverKeyBits);

                using (Message req = new Message(TransferState.DownloadRequest, transferId, _publicKey, NoSession))
                {
                    req.Write(hnonce.ToArray());
                    req.Write(location);
                    req.Write(clientKeyBits.ToArray());

                    Stream response = SendPayload(req, location, req.ToStream(_privateKey));
                    using (Message rsp = new Message(response, _privateKey, s => sessionKey))
                    {
                        Check.Assert <InvalidOperationException>(rsp.State == TransferState.DownloadResponse);
                        maxMessageLength = Check.InRange(rsp.ReadInt32(), 0, int.MaxValue);
                        fileLength       = Check.InRange(rsp.ReadInt64(), 0, 0x00FFFFFFFFFFFFFFL);
                        byte[] bytes = rsp.ReadBytes(100 * 1000 * 1024);
                        rsp.VerifySignature(_publicKey);

                        using (Stream io = output.Open(FileAccess.Write))
                        {
                            io.SetLength(fileLength);
                            if (bytes.Length > 0)
                            {
                                io.Seek(0, SeekOrigin.Begin);
                                io.Write(bytes, 0, bytes.Length);
                                bytesReceived += bytes.Length;
                            }
                        }
                    }
                }
                //STEP 3...n: Continue downloading other chunks of the file
                if (bytesReceived < fileLength)
                {
                    bool[] failed = new bool[1];
                    using (WorkQueue queue = new WorkQueue(LimitThreads))
                    {
                        queue.OnError += (o, e) => { failed[0] = true; };
                        while (bytesReceived < fileLength && !failed[0] && !_abort.WaitOne(0, false))
                        {
                            int         len  = (int)Math.Min(fileLength - bytesReceived, maxMessageLength);
                            BytesToRead task = new BytesToRead(
                                this, LimitThreads, transferId, sessionKey, location, output, bytesReceived, len);
                            queue.Enqueue(task.Send);
                            OnProgressChanged(location, bytesReceived, fileLength);
                            bytesReceived += len;
                        }

                        queue.Complete(true, failed[0] ? 5000 : 7200000);
                    }

                    if (_abort.WaitOne(0, false))
                    {
                        return(false);
                    }
                    Check.Assert <InvalidDataException>(failed[0] == false);

                    // STEP 4: Complete the transfer
                    using (Message req = new Message(TransferState.DownloadCompleteRequest, transferId, _publicKey, s => sessionKey))
                    {
                        SendPayload(req, location, req.ToStream(_privateKey)).Dispose();
                    }
                }
                OnProgressChanged(location, fileLength, fileLength);
                return(true);
            }