Esempio n. 1
0
        /// <summary>
        /// Unpacks a cipher formerly packed with <see cref="PackHeaderAndCypher(CryptoHeader, byte[])"/>.
        /// </summary>
        /// <param name="packedCipher">Packed cipher containing a header and its cipher.</param>
        /// <param name="header">Receives the extracted header.</param>
        /// <param name="cipher">Receives the extracted cipher.</param>
        public static void UnpackHeaderAndCipher(byte[] packedCipher, out CryptoHeader header, out byte[] cipher)
        {
            if (packedCipher == null)
            {
                throw new ArgumentNullException("packedCipher");
            }

            int lastSeparatorPos = IndexOfLastSeparator(packedCipher, ExpectedSeparatorCount);

            if (lastSeparatorPos < 0)
            {
                throw new CryptoExceptionInvalidCipherFormat();
            }

            // Read cipher part
            int cipherLength = packedCipher.Length - lastSeparatorPos - 1;

            cipher = new byte[cipherLength];
            Array.Copy(packedCipher, lastSeparatorPos + 1, cipher, 0, cipherLength);

            // Read header part
            string headerString = Encoding.UTF8.GetString(packedCipher, 0, lastSeparatorPos + 1);

            header = UnpackHeader(headerString);
        }
Esempio n. 2
0
        /// <summary>
        /// Pack an encryption header and its cipher together to a byte array.
        /// </summary>
        /// <param name="header">Header containing the encryption parameters.</param>
        /// <param name="cipher">Cipher generated with those encryption parameters.</param>
        /// <returns>Packed cipher.</returns>
        public static byte[] PackHeaderAndCypher(CryptoHeader header, byte[] cipher)
        {
            if (header == null)
            {
                throw new ArgumentNullException("header");
            }
            if (cipher == null)
            {
                throw new ArgumentNullException("cipher");
            }

            StringBuilder sb = new StringBuilder();

            sb.Append(header.AppName);
            sb.Append(Separator);
            sb.Append(header.AlgorithmName);
            sb.Append(Separator);
            sb.Append(CryptoUtils.BytesToBase64String(header.Nonce));
            sb.Append(Separator);
            sb.Append(header.KdfName);
            sb.Append(Separator);
            sb.Append(CryptoUtils.BytesToBase64String(header.Salt));
            sb.Append(Separator);
            sb.Append(header.Cost);
            sb.Append(Separator);
            string stringHeader = sb.ToString();

            byte[] binaryHeader = CryptoUtils.StringToBytes(stringHeader);

            byte[] result = new byte[cipher.Length + binaryHeader.Length];
            Array.Copy(binaryHeader, 0, result, 0, binaryHeader.Length);
            Array.Copy(cipher, 0, result, binaryHeader.Length, cipher.Length);
            return(result);
        }
Esempio n. 3
0
        /// <inheritdoc/>
        public byte[] Encrypt(
            byte[] message,
            SecureString password,
            KeyDerivationCostType costType,
            string encryptorName,
            string kdfName     = Pbkdf2.CryptoKdfName,
            string compression = null)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }
            ValidatePassword(password);
            if (_randomSource == null)
            {
                throw new ArgumentNullException(nameof(_randomSource));
            }
            if (string.IsNullOrWhiteSpace(encryptorName))
            {
                encryptorName = BouncyCastleXChaCha20.CryptoAlgorithmName;
            }
            if (string.IsNullOrWhiteSpace(kdfName))
            {
                encryptorName = Pbkdf2.CryptoKdfName;
            }
            ISymmetricEncryptionAlgorithm encryptor = new SymmetricEncryptionAlgorithmFactory().CreateAlgorithm(encryptorName);
            IKeyDerivationFunction        kdf       = new KeyDerivationFactory().CreateKdf(kdfName);

            // Prepare header
            CryptoHeader header = new CryptoHeader();

            header.PackageName   = PackageName;
            header.AlgorithmName = encryptor.Name;
            header.Nonce         = _randomSource.GetRandomBytes(encryptor.ExpectedNonceSize);
            header.KdfName       = kdf.Name;
            header.Salt          = _randomSource.GetRandomBytes(kdf.ExpectedSaltSizeBytes);
            int cost = kdf.RecommendedCost(costType);

            header.Cost        = cost.ToString();
            header.Compression = compression;

            try
            {
                if (string.Equals(CompressionGzip, header.Compression, StringComparison.InvariantCultureIgnoreCase))
                {
                    message = CompressUtils.Compress(message);
                }

                byte[] key    = kdf.DeriveKeyFromPassword(password, encryptor.ExpectedKeySize, header.Salt, cost);
                byte[] cipher = encryptor.Encrypt(message, key, header.Nonce);
                return(CryptoHeaderPacker.PackHeaderAndCypher(header, cipher));
            }
            catch (Exception ex)
            {
                throw new CryptoException("An unexpected error occured, while encrypting the message.", ex);
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Unpacks a cipher formerly packed with <see cref="PackHeaderAndCypher(CryptoHeader, byte[])"/>.
        /// </summary>
        /// <param name="packedCipher">Packed cipher containing a header and its cipher.</param>
        /// <param name="expectedAppName">The app name which must match.</param>
        /// <param name="header">Receives the extracted header.</param>
        /// <param name="cipher">Receives the extracted cipher.</param>
        /// <exception cref="CryptoExceptionInvalidCipherFormat">Thrown if it doesn't contain a valid header.</exception>
        /// <exception cref="CryptoUnsupportedRevisionException">Thrown if it was packed with a future incompatible version.</exception>
        public static void UnpackHeaderAndCipher(byte[] packedCipher, string expectedAppName, out CryptoHeader header, out byte[] cipher)
        {
            header = null;
            cipher = null;
            if (!HasMatchingHeader(packedCipher, expectedAppName, out int revision))
            {
                throw new CryptoExceptionInvalidCipherFormat();
            }

            if (revision > CryptoHeader.NewestSupportedRevision)
            {
                throw new CryptoUnsupportedRevisionException();
            }

            int expectedSeparatorCount = (revision > 1) ? 7 : 6;
            int lastSeparatorPos       = IndexOfLastSeparator(packedCipher, expectedSeparatorCount);

            if (lastSeparatorPos < 0)
            {
                throw new CryptoExceptionInvalidCipherFormat();
            }

            // Read cipher part
            int cipherLength = packedCipher.Length - lastSeparatorPos - 1;

            cipher = new byte[cipherLength];
            Array.Copy(packedCipher, lastSeparatorPos + 1, cipher, 0, cipherLength);

            // Read header part
            try
            {
                string   headerString = Encoding.UTF8.GetString(packedCipher, 0, lastSeparatorPos + 1);
                string[] parts        = headerString.Split(new char[] { Separator });
                header = new CryptoHeader
                {
                    PackageName   = expectedAppName,
                    Revision      = revision,
                    AlgorithmName = parts[1],
                    Nonce         = CryptoUtils.Base64StringToBytes(parts[2]),
                    KdfName       = string.IsNullOrEmpty(parts[3]) ? null : parts[3],
                    Salt          = string.IsNullOrEmpty(parts[4]) ? null : CryptoUtils.Base64StringToBytes(parts[4]),
                    Cost          = string.IsNullOrEmpty(parts[5]) ? null : parts[5],
                };
                if (revision > 1)
                {
                    header.Compression = parts[6];
                }
            }
            catch (Exception)
            {
                throw new CryptoExceptionInvalidCipherFormat();
            }
        }
        /// <summary>
        /// Encrypts a message with a user password, and adds a header containing all information
        /// necessary for the decryption (algorithm, nonce, salt, ...).
        /// </summary>
        /// <param name="message">Plain text message to encrypt.</param>
        /// <param name="password">Password to use for encryption, minimum length is 7 characters.</param>
        /// <param name="costType">The cost type to use for encryption.</param>
        /// <param name="randomSource">A cryptographically safe random source.</param>
        /// <param name="encryptorName">The name of an encryption algorithm which shall be used to
        /// do the encryption.</param>
        /// <param name="kdfName">The name of a key derivation function, which can convert the
        /// password to a key.</param>
        /// <returns>A binary array containing the cipher.</returns>
        public byte[] Encrypt(
            byte[] message,
            string password,
            KeyDerivationCostType costType,
            ICryptoRandomSource randomSource,
            string encryptorName,
            string kdfName = Pbkdf2.CryptoKdfName)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
            ValidatePassword(password);
            if (randomSource == null)
            {
                throw new ArgumentNullException("randomSource");
            }
            if (string.IsNullOrWhiteSpace(encryptorName))
            {
                encryptorName = BouncyCastleAesGcm.CryptoAlgorithmName;
            }
            if (string.IsNullOrWhiteSpace(kdfName))
            {
                encryptorName = Pbkdf2.CryptoKdfName;
            }
            ISymmetricEncryptionAlgorithm encryptor = new SymmetricEncryptionAlgorithmFactory().CreateAlgorithm(encryptorName);
            IKeyDerivationFunction        kdf       = new KeyDerivationFactory().CreateKdf(kdfName);

            // Prepare header
            CryptoHeader header = new CryptoHeader();

            header.AppName       = _appName;
            header.AlgorithmName = encryptor.Name;
            header.Nonce         = randomSource.GetRandomBytes(encryptor.ExpectedNonceSize);
            header.KdfName       = kdf.Name;
            header.Salt          = randomSource.GetRandomBytes(kdf.ExpectedSaltSizeBytes);
            int cost = kdf.RecommendedCost(costType);

            header.Cost = cost.ToString();

            try
            {
                byte[] key    = kdf.DeriveKeyFromPassword(password, encryptor.ExpectedKeySize, header.Salt, cost);
                byte[] cipher = encryptor.Encrypt(message, key, header.Nonce);
                return(CryptoHeaderPacker.PackHeaderAndCypher(header, cipher));
            }
            catch (Exception ex)
            {
                throw new CryptoException("An unexpected error occured, while encrypting the message.", ex);
            }
        }
Esempio n. 6
0
        private static CryptoHeader UnpackHeader(string headerString)
        {
            string[] parts = headerString.Split(new char[] { Separator }, StringSplitOptions.RemoveEmptyEntries);

            CryptoHeader result = new CryptoHeader();

            result.AppName       = parts[0];
            result.AlgorithmName = parts[1];
            result.Nonce         = CryptoUtils.Base64StringToBytes(parts[2]);
            result.KdfName       = parts[3];
            result.Salt          = CryptoUtils.Base64StringToBytes(parts[4]);
            result.Cost          = parts[5];
            return(result);
        }
Esempio n. 7
0
        /// <inheritdoc/>
        public byte[] Encrypt(
            byte[] message,
            byte[] key,
            string encryptorName,
            string compression = null)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (_randomSource == null)
            {
                throw new ArgumentNullException(nameof(_randomSource));
            }
            if (string.IsNullOrWhiteSpace(encryptorName))
            {
                encryptorName = BouncyCastleXChaCha20.CryptoAlgorithmName;
            }
            ISymmetricEncryptionAlgorithm encryptor = new SymmetricEncryptionAlgorithmFactory().CreateAlgorithm(encryptorName);

            // Prepare header
            CryptoHeader header = new CryptoHeader();

            header.PackageName   = PackageName;
            header.AlgorithmName = encryptor.Name;
            header.Nonce         = _randomSource.GetRandomBytes(encryptor.ExpectedNonceSize);
            header.Compression   = compression;

            try
            {
                if (string.Equals(CompressionGzip, header.Compression, StringComparison.InvariantCultureIgnoreCase))
                {
                    message = CompressUtils.Compress(message);
                }

                byte[] truncatedKey = CryptoUtils.TruncateKey(key, encryptor.ExpectedKeySize);
                byte[] cipher       = encryptor.Encrypt(message, truncatedKey, header.Nonce);
                return(CryptoHeaderPacker.PackHeaderAndCypher(header, cipher));
            }
            catch (Exception ex)
            {
                throw new CryptoException("An unexpected error occured, while encrypting the message.", ex);
            }
        }