private void CompleteUpload(Guid transferId, Salt sessionKey, string location, Hash fullHash) { // STEP 4: Finalize the transfer using (Message req = new Message(TransferState.UploadCompleteRequest, transferId, _publicKey, s => sessionKey)) { req.Write(location); req.Write(fullHash.ToArray()); Stream response = SendPayload(req, location, req.ToStream(_privateKey)); using (Message rsp = new Message(response, _privateKey, s => sessionKey)) { Check.Assert <InvalidOperationException>(rsp.State == TransferState.UploadCompleteResponse); rsp.VerifySignature(_publicKey); } } }
private void TransferBytes(Guid transferId, Salt sessionKey, string location, long offset, byte[] bytes) { // STEP 3...n: Send a block of bytes using (Message req = new Message(TransferState.SendBytesRequest, transferId, _publicKey, s => sessionKey)) { req.Write(offset); req.Write(bytes); Stream response = SendPayload(req, location, req.ToStream(_privateKey)); using (Message rsp = new Message(response, _privateKey, s => sessionKey)) { Check.Assert <InvalidOperationException>(rsp.State == TransferState.SendBytesResponse); Check.Assert <InvalidOperationException>(rsp.ReadInt64() == offset); rsp.VerifySignature(_publicKey); } } }
private static void ReadHeader(Stream input, out int ver, out TransferState state, out Guid txid, out Salt salt) { ver = PrimitiveSerializer.Int32.ReadFrom(input); Check.Assert <InvalidDataException>(ver == VersionHeader); int istate = PrimitiveSerializer.Int32.ReadFrom(input); Check.Assert <InvalidDataException>(Enum.IsDefined(typeof(TransferState), istate)); state = (TransferState)istate; byte[] bytes = new byte[16]; Check.Assert <InvalidDataException>(bytes.Length == input.Read(bytes, 0, bytes.Length)); txid = new Guid(bytes); bytes = new byte[32]; Check.Assert <InvalidDataException>(bytes.Length == input.Read(bytes, 0, bytes.Length)); salt = Salt.FromBytes(bytes); }
/// <summary> /// Called to send a specific length of bytes to a server identified by serverKey. The transfer /// is a blocking call and returns on success or raises an exception. If Abort() is called durring /// the transfer, or if a ProgressChanged event handler raises the OperationCanceledException, the /// transfer is silently terminated and the method will return false. /// </summary> /// <param name="location">A string of up to 1024 bytes in length</param> /// <param name="length">The length in bytes to send from the stream</param> /// <param name="rawInput">The stream to read the data from</param> /// <returns>True if the file was successfully received by the server</returns> public bool Upload(string location, long length, Stream rawInput) { Guid transferId = Guid.NewGuid(); int maxMessageLength; // STEP 1: Send a NonceRequest, Create Salt sessionKey = BeginUpload(transferId, location, length, out maxMessageLength); // STEP 2: Send the data Hash fullHash; bool[] failed = new bool[1]; using (HashStream input = new HashStream(new SHA256Managed(), rawInput)) using (WorkQueue queue = new WorkQueue(LimitThreads)) { queue.OnError += (o, e) => { failed[0] = true; }; long pos = 0; while (pos < length && !failed[0] && !_abort.WaitOne(0, false)) { int len = (int)Math.Min(length - pos, maxMessageLength); byte[] buffer = new byte[len]; IOStream.Read(input, buffer, len); BytesToSend task = new BytesToSend(this, LimitThreads, transferId, sessionKey, location, pos, buffer); queue.Enqueue(task.Send); OnProgressChanged(location, pos, length); pos += len; } queue.Complete(true, failed[0] ? 5000 : 300000); fullHash = input.FinalizeHash();//hash of all data transferred } if (_abort.WaitOne(0, false)) { return(false); } Check.Assert <InvalidDataException>(failed[0] == false); // STEP 4: Complete the transfer CompleteUpload(transferId, sessionKey, location, fullHash); OnProgressChanged(location, length, length); return(true); }
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); }
/// <summary> Combines the salt with the data provided </summary> public SaltedData(Salt salt, Stream data) : this(salt, IOStream.ReadAllBytes(data)) { }
/// <summary> Returns a stream that combines the salt and data </summary> public static Stream CombineStream(Salt salt, Stream data) { return(new IO.CombinedStream(salt.ToStream(), data)); }
/// <summary> /// Constructs the Rfc2898DeriveBytes implementation. /// </summary> public PBKDF2(byte[] password, Salt salt, int iterations) : base(password, salt.ToArray(), iterations) { }
/// <summary> Returns a salted hash for the password </summary> public PasswordHash CreateHash(Salt salt) { return(new PasswordHash(this, salt)); }
/// <summary> Creates the hash from the given data and salt </summary> public PasswordHash(string data, Salt salt) : this(true, Password.Encoding.GetBytes(data), salt) { }
/// <summary> /// Constructs the byte generation routine with the specified key, salt, and iteration count /// </summary> public HashDerivedBytes(byte[] password, Salt salt, int iterations) : this(false, password, salt, iterations) { }
private LocalHostKey(DataProtectionScope scope, Salt salt) : this(scope) { _salt = salt; }
/// <summary> Creates the hash from the given data and salt </summary> public PasswordHash(SecureString data, Salt salt) : this(true, SecureStringUtils.ToByteArray(data, Password.Encoding), salt) { }
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); }
private Salt SessionKey(Guid transferId) { return(Salt.FromString(ReadState(transferId, "session-key"))); }
/// <summary> Creates the password from the given bytes and salt </summary> public PasswordKey(IPasswordDerivedBytes derivedBytes, Salt salt) { _derivedBytes = derivedBytes; _salt = salt; _iv = null; }
/// <summary> /// Constructs the byte generation routine with the specified key, salt, and iteration count /// </summary> public HashDerivedBytes(Stream password, Salt salt, int iterations) : this(Create(), password, salt, iterations) { }
/// <summary> /// Constructs the byte generation routine with the specified key, salt, and iteration count /// </summary> public HashDerivedBytes(THash algo, Stream password, Salt salt, int iterations) : this((HMAC)algo, password, salt, iterations) { }
/// <summary> Combines the salt with the data provided </summary> public SaltedData(Salt salt, byte[] data) { _salt = salt; _data = (byte[])data.Clone(); }
/// <summary> Creates a salted hash from the given bytes and salt </summary> public PasswordHash(Stream bytes, Salt salt) { using (bytes) using (HashDerivedBytes <HMACSHA256> hashBytes = new HashDerivedBytes <HMACSHA256>(bytes, salt, StandardIterations)) _hash = new SaltedData(salt, hashBytes.GetBytes(32)); }
private LocalHostKey(DataProtectionScope scope) { _scope = scope; _salt = null; }
/// <summary> Creates a salted hash from the given password and salt </summary> public PasswordHash(Password bytes, Salt salt) : this(bytes.ReadBytes(), salt) { }
/// <summary> /// Sets or changes the salt for this encryption key /// </summary> public LocalHostKey WithSalt(Salt salt) { return(new LocalHostKey(_scope, salt)); }
/// <summary> Creates the password from the given bytes and salt </summary> public PasswordKey(bool clear, byte[] bytes, Salt salt) : this(new HashDerivedBytes <HMACSHA256>(clear, Check.NotEmpty(bytes), salt, DefaultIterations), salt) { }