/* * This file is part of Socialvoid.NET Project (https://github.com/Intellivoid/Socialvoid.NET). * Copyright (c) 2021 Socialvoid.NET Authors. * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this source code of library. * If not, see . */ /* * Credits to Devin Martin and the original OtpSharp library: * https://github.com/kspearrin/Otp.NET */ using System; using System.Security.Cryptography; namespace Socialvoid.Security.Otp { /// /// Represents a key in memory. /// since: v0.0.0 /// /// /// This will attempt to use the Windows data protection api to /// encrypt the key in memory. /// However, this type favors working over memory protection. /// This is an attempt to minimize exposure in memory, nothing more. /// This protection is flawed in many ways and is limited to Windows. /// /// In order to use the key to compute an hmac it must be temporarily /// decrypted, used, then re-encrypted. /// This does expose the key in memory for a time. /// If a memory dump occurs in this time the plaintext key will be part /// of it. Furthermore, there are potentially artifacts from the hmac /// computation, GC compaction, or any number of other leaks even after /// the key is re-encrypted. /// /// This type favors working over memory protection. If the particular /// platform isn't supported then, unless forced by modifying the /// IsPlatformSupported method, it will just store the key in a standard /// byte array. /// public class InMemoryKey : IKeyProvider { //------------------------------------------------- #region field's Region /// /// The key data in memory. /// since: v0.0.0 /// internal readonly byte[] _KeyData; /// /// The key length representing the length of the . /// since: v0.0.0 /// internal readonly int _keyLength; /// /// Used for locking. /// since: v0.0.0 /// private readonly object _stateSync = new(); #endregion //------------------------------------------------- #region Constructor's Region /// /// Creates an instance of a key. /// /// Plaintext key data public InMemoryKey(byte[] key) { if (key == null || key.Length == 0) { throw new ArgumentException("Key cannot be empty or null", nameof(key)); } _keyLength = key.Length; int paddedKeyLength = (int)Math.Ceiling((decimal)key.Length / (decimal)16) * 16; _KeyData = new byte[paddedKeyLength]; Array.Copy(key, _KeyData, key.Length); } #endregion //------------------------------------------------- #region Get Method's Region /// /// Gets a copy of the plaintext key. /// since: v0.0.0 /// /// /// This is internal rather than protected so that the tests can /// use this method. /// /// /// Plaintext Key /// internal byte[] GetCopyOfKey() { var plainKey = new byte[_keyLength]; lock(_stateSync) { Array.Copy(_KeyData, plainKey, _keyLength); } return plainKey; } /// /// Uses the key to get an HMAC using the specified algorithm and data. /// since: v0.0.0 /// /// /// The HMAC algorithm to use /// /// /// The data used to compute the HMAC /// /// /// HMAC of the key and data /// public byte[] ComputeHmac(OtpHashMode mode, byte[] data) { byte[] hashedValue = null; using(HMAC hmac = CreateHmacHash(mode)) { byte[] key = this.GetCopyOfKey(); try { hmac.Key = key; hashedValue = hmac.ComputeHash(data); } finally { KeyUtilities.Destroy(key); } } return hashedValue; } #endregion //------------------------------------------------- #region static Method's Region /// /// Create an HMAC object for the specified algorithm. /// since: v0.0.0 /// private static HMAC CreateHmacHash(OtpHashMode otpHashMode) { return otpHashMode switch { OtpHashMode.Sha256 => new HMACSHA256(), OtpHashMode.Sha512 => new HMACSHA512(), _ => new HMACSHA1() //OtpHashMode.Sha1 }; } #endregion //------------------------------------------------- } }