Example #1
0
        // Why accept an outputStream instead of returning a Stream? Flexibility.
        // Client apps can write the encrypted output directly to an Network Stream, a File, a Memory Stream, etc
        // It's up to the client implementation where to direct the output
        public void EncryptFile(Stream inputFileStream, Stream outputFileStream, string passphrase, CipherSuite cipherSuite)
        {
            //Check if we support this cipher suite

            ITwoFactorKeyDerivationFunctionProvider twoFactorKeyDerivationProvider = KeyDerivationFunctionProviders.Where(p => p.IDByte == cipherSuite.KeyDerivationFunctionIDByte).SingleOrDefault();
            ISymmetricCipherProvider symmetricCipherProvider = SymmetricCipherProviders.Where(p => p.IDByte == cipherSuite.SymmetricCipherIDByte).SingleOrDefault();
            IMACProvider             macProvider             = MACProviders.Where(p => p.IDByte == cipherSuite.MACIDByte).SingleOrDefault();

            if (twoFactorKeyDerivationProvider == null)
            {
                throw new Exception("Unsupported Key Derivation Function with ID byte " + cipherSuite.KeyDerivationFunctionIDByte);
            }

            if (symmetricCipherProvider == null)
            {
                throw new Exception("Unsupported Symmetric Cipher with ID byte " + cipherSuite.SymmetricCipherIDByte);
            }

            if (macProvider == null)
            {
                throw new Exception("Unsupported Message Authentication Code with ID byte " + cipherSuite.MACIDByte);
            }


            Random randomGen = new Random();

            var salt = new byte[32];

            randomGen.NextBytes(salt); //random salt (RNG doesn't have to be cryptographically strong)
            var passphraseBytes = Encoding.UTF8.GetBytes(passphrase);


            byte[] encryptionKey = new byte[symmetricCipherProvider.KeySize / 8];
            byte[] hmacKey       = new byte[symmetricCipherProvider.KeySize / 8];
            byte[] iv            = new byte[symmetricCipherProvider.BlockSize / 8];

            twoFactorKeyDerivationProvider.DeriveHMACAndEncryptionKey(passphraseBytes, salt, ref hmacKey, ref encryptionKey);

            randomGen.NextBytes(iv);

            var tokenSerialNumberBytes = twoFactorKeyDerivationProvider.GetExternalTokenSerial();
            var inputValidation        = Combine(passphraseBytes, tokenSerialNumberBytes);
            var inputValidationHash    = CalculateInputValidation(inputValidation);

            //Write Header
            var headerStream = new BinaryWriter(outputFileStream);

            //Header: YCF1 (4bytes) + Cipher Suite (4bytes) + Salt Seed (32bytes) + IV (16bytes) + Iterations (4bytes) + Input Validation (32bytes)
            headerStream.Write(YC_FILE_SPEC_BYTES);
            headerStream.Write(YC_FILE_SPEC_VERSION);
            headerStream.Write(IdBytesFromCipherSuite(cipherSuite));
            headerStream.Write(salt);
            headerStream.Write(iv);
            headerStream.Write((Int32)twoFactorKeyDerivationProvider.NumberOfOterations);
            headerStream.Write(inputValidationHash);

            symmetricCipherProvider.Encrypt(inputFileStream, outputFileStream, encryptionKey, iv);

            var streamHMAC = macProvider.CalculateMAC(outputFileStream, hmacKey);

            headerStream.Write(streamHMAC);
        }
Example #2
0
        public void DecryptFile(Stream inputFileStream, Stream outputFileStream, string passphrase)
        {
            if (inputFileStream.Position != 0)
            {
                if (inputFileStream.CanSeek)
                {
                    inputFileStream.Seek(0, SeekOrigin.Begin);
                }
                else
                {
                    throw new ArgumentException("Unable to seek inputFileStream to the beggining.");
                }
            }

            using (var headerStream = new BinaryReader(inputFileStream))
            {
                var fileSpecMagicBytes = headerStream.ReadBytes(3);
                if (!fileSpecMagicBytes.SequenceEqual(YC_FILE_SPEC_BYTES))
                {
                    throw new Exception("Wrong file header");
                }

                var fileSpecVersion = headerStream.ReadByte();
                if (fileSpecVersion != YC_FILE_SPEC_VERSION)
                {
                    throw new Exception("Invalid YubiCrypt file version.");
                }

                var cipherSuiteBytes = headerStream.ReadBytes(4);

                var cipherSuite = CipherSuiteFromIdBytes(cipherSuiteBytes);

                var cipherSuiteKDF = KeyDerivationFunctionProviders.Where(p => p.IDByte == cipherSuite.KeyDerivationFunctionIDByte).SingleOrDefault();
                if (cipherSuiteKDF == null)
                {
                    throw new Exception("Unsupported Key Derivation Function with ID byte " + cipherSuite.KeyDerivationFunctionIDByte);
                }

                var cipherSuiteSymmetricCipher = SymmetricCipherProviders.Where(p => p.IDByte == cipherSuite.SymmetricCipherIDByte).SingleOrDefault();
                if (cipherSuiteSymmetricCipher == null)
                {
                    throw new Exception("Unsupported Key Derivation Function with ID byte " + cipherSuite.SymmetricCipherIDByte);
                }

                var cipherSuiteMAC = MACProviders.Where(p => p.IDByte == cipherSuite.MACIDByte).SingleOrDefault();
                if (cipherSuiteMAC == null)
                {
                    throw new Exception("Unsupported Message Authentication Code with ID byte " + cipherSuite.MACIDByte);
                }

                var passphraseBytes = Encoding.UTF8.GetBytes(passphrase);
                var salt            = headerStream.ReadBytes(32);
                var iv              = headerStream.ReadBytes(16);
                var iterations      = headerStream.ReadInt32();
                var inputValidation = headerStream.ReadBytes(32);


                var tokenSerialNumberBytes = cipherSuiteKDF.GetExternalTokenSerial();
                var inputValidationInput   = Combine(passphraseBytes, tokenSerialNumberBytes);
                var inputValidationHash    = CalculateInputValidation(inputValidationInput);

                if (!inputValidationHash.SequenceEqual(inputValidation))
                {
                    throw new Exception("Invalid credentials. Check if you are entering the correct passphrase and using the correct Yubikey token.");
                }



                byte[] encryptionKey = new byte[cipherSuiteSymmetricCipher.KeySize / 8];
                byte[] hmacKey       = new byte[cipherSuiteSymmetricCipher.KeySize / 8];
                cipherSuiteKDF.NumberOfOterations = iterations;
                cipherSuiteKDF.DeriveHMACAndEncryptionKey(passphraseBytes, salt, ref hmacKey, ref encryptionKey);

                //Check file HMAC
                if (!cipherSuiteMAC.ValidateMAC(inputFileStream, hmacKey))
                {
                    throw new Exception("Invalid HMAC.");
                }

                //Header is 92 bytes long
                inputFileStream.Seek(92, SeekOrigin.Begin);
                cipherSuiteSymmetricCipher.Decrypt(inputFileStream, outputFileStream, encryptionKey, iv);
            }
        }