Example #1
0
        public static Result <Argon2Kdf, InvalidDataException> Deserialize(IDictionary <string, object> properties)
        {
            var ret = new Argon2Kdf();

            if (!properties.TryGet <string>("kdf", out var kdf))
            {
                return(new InvalidDataException("The kdf dictionary does not have the required 'kdf' field."));
            }

            if (kdf != KdfName)
            {
                return(new InvalidDataException($"The kdf needs to be '{KdfName}' (was '{kdf}')."));
            }

            if (!properties.TryGet <int>("version", out var version))
            {
                return(new InvalidDataException("The kdf dictionary does not have the required 'version' field."));
            }

            if (version != Version)
            {
                return(new InvalidDataException(
                           $"Only version 0x{Version:X4} is supported at the moment (was 0x{version:X4})"));
            }

            if (!properties.TryGet <int>("opslimit", out var opslimit))
            {
                return(new InvalidDataException("The kdf dictionary does not have the required 'opslimit' field."));
            }

            ret.OpsLimit = opslimit;

            if (!properties.TryGet <int>("memlimit", out var memlimit))
            {
                return(new InvalidDataException("The kdf dictionary does not have the required 'memlimit' field."));
            }

            ret.MemLimit = memlimit;

            if (!properties.TryGet <string>("salt", out var salt))
            {
                return(new InvalidDataException("The kdf dictionary does not have the required 'salt' field."));
            }

            var fb64 = salt.FromBase64();

            if (!fb64)
            {
                return(new InvalidDataException("The salt is not a valid base64 string."));
            }

            ret.Salt = fb64.Value;

            return(ret);
        }
Example #2
0
        public static Result <InvalidDataException> Decrypt(Stream input, Stream output, string password)
        {
            var header = input.GetBytes(4);

            if (!header.SequenceEqual(MagicHeader))
            {
                return(new InvalidDataException("The magic header was not present in the input data."));
            }

            var cipherInfoRes = _readDict(input);

            if (!cipherInfoRes)
            {
                return(new InvalidDataException("Failed to read cipher info from stream.", cipherInfoRes.Error));
            }

            var(cipherInfo, cipherBytes) = cipherInfoRes.Value;

            if (!cipherInfo.TryGet <string>("iv", out var ivRaw))
            {
                return(new InvalidDataException("Failed to read initialization vector from initial dictionary. Most likely the data is corrupt."));
            }

            var ivRes = ivRaw.FromBase64();

            if (!ivRes)
            {
                return(new InvalidDataException("The initialization vector was not a valid base64 string.", ivRes.Error));
            }

            var iv = ivRes.Value;

            if (!cipherInfo.TryGet <string>("cipher", out var cipherVal))
            {
                return(new InvalidDataException("The cipher string was not present in the cipher info dictionary."));
            }

            ICipher cipher;

            switch (cipherVal)
            {
            case Aes256GcmCipher.CipherNameStatic:
                cipher = new Aes256GcmCipher();
                break;

            default:
                return(new InvalidDataException($"The cipher '{cipherVal}' is not supported."));
            }

            if (!cipherInfo.TryGet <string>("kdf", out var kdfVal))
            {
                return(new InvalidDataException("The kdf string was not present in the cipher info dictionary."));
            }

            IKdf kdf;

            switch (kdfVal)
            {
            case Argon2Kdf.KdfName:
            {
                var kdfRes = Argon2Kdf.Deserialize(cipherInfo);
                if (!kdfRes)
                {
                    return(new InvalidDataException("Failed to initialize argon2 kdf.", kdfRes.Error));
                }

                kdf = kdfRes.Value;
            }
            break;

            default:
                return(new InvalidDataException($"The kdf '{kdfVal}' is not supported."));
            }

            var checksumRes = _readDict(input);

            if (!checksumRes)
            {
                return(new InvalidDataException("Failed to read checksum from stream.", checksumRes.Error));
            }

            var(checksum, checksumBytes) = checksumRes.Value;

            if (!checksum.TryGet <string>("algo", out var checksumAlgo))
            {
                return(new InvalidDataException("No algorithm field from checksum dictionary."));
            }

            byte[] dictChecksum;
            switch (checksumAlgo)
            {
            case "sha256":
                dictChecksum = Sha256(cipherBytes);
                break;

            default:
                return(new InvalidDataException($"The given checksum algorithm '{checksumAlgo}' is not supported."));
            }

            if (!checksum.TryGet <string>("checksum", out var checksumString))
            {
                return(new InvalidDataException("No checksum field from checksum dictionary."));
            }

            var expChecksum = checksumString.FromBase64();

            if (!expChecksum)
            {
                return(new InvalidDataException("The checksum field was not a valid base64 string.", expChecksum.Error));
            }

            if (!dictChecksum.SequenceEqual(expChecksum.Value))
            {
                return(new InvalidDataException("Header checksum mismatch."));
            }

            byte[] key = Array.Empty <byte>();
            try
            {
                key = kdf.Derive(password.ToUtf8Bytes(), 32);

                cipher.Decrypt(input, output, key, iv);

                return(Result <InvalidDataException> .Success);
            }
            catch (Exception e)
            {
                return(new InvalidDataException("Failed to process the data.", e));
            }
            finally
            {
                Array.Clear(key, 0, key.Length);
                Array.Clear(iv, 0, iv.Length);
            }
        }