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); }
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); } }