示例#1
0
        public YubiCryptEngine(Assembly callingAssembly)
        {
            List <Assembly> assemblies = new List <Assembly>();

            if (callingAssembly != null)
            {
                assemblies.Add(callingAssembly);
            }
            assemblies.Add(typeof(YubiCryptEngine).GetTypeInfo().Assembly);

            var configuration = new ContainerConfiguration().WithAssemblies(assemblies);

            using (var container = configuration.CreateContainer())
            {
                SymmetricCipherProviders       = container.GetExports <ISymmetricCipherProvider>();
                KeyDerivationFunctionProviders = container.GetExports <ITwoFactorKeyDerivationFunctionProvider>();
                MACProviders = container.GetExports <IMACProvider>();
            }
#if !DEBUG
            if (!KeyDerivationFunctionProviders.Any())
            {
                throw new CompositionFailedException("No Key Derivation Function providers were found.");
            }

            if (!SymmetricCipherProviders.Any())
            {
                throw new CompositionFailedException("No Symmetric Cipher providers were found.");
            }

            if (!MACProviders.Any())
            {
                throw new CompositionFailedException("No Message Authentication Code providers were found.");
            }
#endif
            KeyDerivationFunctionProviders = KeyDerivationFunctionProviders.OrderBy(p => p.Name);
            SymmetricCipherProviders       = SymmetricCipherProviders.OrderBy(p => p.Name);
            MACProviders = MACProviders.OrderBy(p => p.Name);
        }
示例#2
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);
        }
示例#3
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);
            }
        }