diff --git a/Snappass.NET/Controllers/PasswordController.cs b/Snappass.NET/Controllers/PasswordController.cs index d508bf7..238afa5 100644 --- a/Snappass.NET/Controllers/PasswordController.cs +++ b/Snappass.NET/Controllers/PasswordController.cs @@ -16,16 +16,23 @@ public PasswordController(IMemoryStore memoryStore, ILogger } [HttpGet] + [HttpPost] public IActionResult Preview(string key) { - if (!_memoryStore.Has(key)) + (string storageKey, string encryptionKey) = Encryption.ParseToken(key); + if (!_memoryStore.Has(storageKey)) { - _logger.LogWarning($@"password with key {key} requested, but not found"); + _logger.LogWarning($@"password with key {storageKey} requested, but not found"); return NotFound(); } - string encryptedPassword = _memoryStore.Retrieve(key); - string decrypted = Encryption.Decrypt(encryptedPassword, key); - return View("Preview", new PreviewModel { Key = decrypted }); + if (HttpContext.Request.Method == "POST") + { + string encryptedPassword = _memoryStore.Retrieve(storageKey); + string decrypted = Encryption.Decrypt(encryptedPassword, encryptionKey); + return View("Password", new PreviewModel { Key = decrypted }); + } + + return View("Preview"); } } } diff --git a/Snappass.NET/Controllers/ShareController.cs b/Snappass.NET/Controllers/ShareController.cs index acb0593..b1e2540 100644 --- a/Snappass.NET/Controllers/ShareController.cs +++ b/Snappass.NET/Controllers/ShareController.cs @@ -35,12 +35,15 @@ public IActionResult Share(string password, string ttl) "week" => TimeToLive.Week, "day" => TimeToLive.Day, "hour" => TimeToLive.Hour, - _ => throw new ArgumentException("Expected week, day or hour"), + "twoweeks" => TimeToLive.TwoWeeks, + _ => throw new ArgumentException("Expected twoweeks, week, day or hour"), }; TimeToLive timeToLive = Parse(ttl); - (string encryptedPassword, string key) = Encryption.Encrypt(password); - _memoryStore.Store(encryptedPassword, key, timeToLive); - var model = new GeneratedPassword { Key = key, BaseUri = GetBaseUrl() }; + string storageKey = Guid.NewGuid().ToString("N").ToUpper(); + (string encryptedPassword, string encryptionKey) = Encryption.Encrypt(password); + _memoryStore.Store(encryptedPassword, storageKey, timeToLive); + string token = Encryption.CreateToken(storageKey, encryptionKey); + var model = new GeneratedPassword { Token = token, BaseUri = GetBaseUrl() }; return View("Shared", model); } } diff --git a/Snappass.NET/Encryption.cs b/Snappass.NET/Encryption.cs index 712ea2c..89bd152 100644 --- a/Snappass.NET/Encryption.cs +++ b/Snappass.NET/Encryption.cs @@ -1,25 +1,47 @@ using System; +using System.Net; using Fernet; namespace Snappass { public class Encryption { - public static (string encryptedPassword, string key) Encrypt(string password) + private static readonly char tokenSeparator = '~'; + + public static (string encryptedPassword, string encryptionKey) Encrypt(string password) { - byte[] keyBytes = SimpleFernet.GenerateKey().UrlSafe64Decode(); + byte[] EncryptionKeyBytes = SimpleFernet.GenerateKey().UrlSafe64Decode(); var passwordBytes = System.Text.Encoding.Unicode.GetBytes(password); - string encrypted = SimpleFernet.Encrypt(keyBytes, passwordBytes); - string key = keyBytes.UrlSafe64Encode(); - return (encrypted, key); + string encryptedPassword = SimpleFernet.Encrypt(EncryptionKeyBytes, passwordBytes); + string encryptionKey = EncryptionKeyBytes.UrlSafe64Encode(); + return (encryptedPassword, encryptionKey); } - public static string Decrypt(string encrypted, string key) + public static string Decrypt(string encryptedPassword, string encryptionKey) { - var keyBytes = key.UrlSafe64Decode(); - var decryptedBytes = SimpleFernet.Decrypt(keyBytes, encrypted, out DateTime _); + var encryptionKeyBytes = encryptionKey.UrlSafe64Decode(); + var decryptedBytes = SimpleFernet.Decrypt(encryptionKeyBytes, encryptedPassword, out DateTime _); var decrypted = decryptedBytes.UrlSafe64Encode().FromBase64String().Replace("\0", ""); return decrypted; } + + public static (string storageKey, string decryptionKey) ParseToken(string token) + { + var tokenFragments = token.Split(tokenSeparator, 2); + var storageKey = tokenFragments[0]; + var decryptionKey = string.Empty; + + if (tokenFragments.Length > 1) + decryptionKey = WebUtility.UrlDecode(tokenFragments[1]); + + return (storageKey, decryptionKey); + } + public static string CreateToken(string storageKey, string encryptionKey) + { + var token = string.Join(tokenSeparator, storageKey, WebUtility.UrlEncode(encryptionKey)); + + return token; + + } } } diff --git a/Snappass.NET/MemoryStore.cs b/Snappass.NET/MemoryStore.cs index a8146db..3f0ae8e 100644 --- a/Snappass.NET/MemoryStore.cs +++ b/Snappass.NET/MemoryStore.cs @@ -71,18 +71,22 @@ public string Retrieve(string key) TimeToLive.Day => item.StoredDateTime.AddDays(1), TimeToLive.Week => item.StoredDateTime.AddDays(7), TimeToLive.Hour => item.StoredDateTime.AddHours(1), + TimeToLive.TwoWeeks => item.StoredDateTime.AddDays(14), + _ => item.StoredDateTime.AddHours(1), }; DateTime atTheLatest = GetAtTheLatest(item.TimeToLive); if (_dateTimeProvider.Now > atTheLatest) { static string ToString(TimeToLive ttl) => ttl switch { - TimeToLive.Week => "week", - TimeToLive.Day => "day", - TimeToLive.Hour => "hour", + TimeToLive.Week => "1 week", + TimeToLive.Day => "1 day", + TimeToLive.Hour => "1 hour", + TimeToLive.TwoWeeks => "2 weeks", + _ => "1 hour", }; var ttlString = ToString(item.TimeToLive); - _logger.Log(LogLevel.Warning, $@"Tried to retrieve password for key [{key}] after date is expired. Key set at [{item.StoredDateTime}] for 1 [{ttlString}]"); + _logger.Log(LogLevel.Warning, $@"Tried to retrieve password for key [{key}] after date is expired. Key set at [{item.StoredDateTime}] for [{ttlString}]"); _items.Remove(key); // ensure "read-once" is implemented return null; } diff --git a/Snappass.NET/Models/GeneratedPassword.cs b/Snappass.NET/Models/GeneratedPassword.cs index c5c5161..cf0916c 100644 --- a/Snappass.NET/Models/GeneratedPassword.cs +++ b/Snappass.NET/Models/GeneratedPassword.cs @@ -2,8 +2,8 @@ { public class GeneratedPassword { - public string Key { get; set; } + public string Token { get; set; } public string BaseUri { get; set; } - public string Uri => $@"{BaseUri}/Password/{Key}"; + public string Uri => $@"{BaseUri}/Password/{Token}"; } } diff --git a/Snappass.NET/SqliteStore.cs b/Snappass.NET/SqliteStore.cs index 80096b9..c303722 100644 --- a/Snappass.NET/SqliteStore.cs +++ b/Snappass.NET/SqliteStore.cs @@ -46,6 +46,7 @@ public override TimeToLive Parse(object value) 0 => TimeToLive.Hour, 1 => TimeToLive.Day, 2 => TimeToLive.Week, + 3 => TimeToLive.TwoWeeks, _ => TimeToLive.Hour, }; } @@ -103,6 +104,7 @@ FROM SECRET TimeToLive.Day => dateTime.AddDays(1), TimeToLive.Week => dateTime.AddDays(7), TimeToLive.Hour => dateTime.AddHours(1), + TimeToLive.TwoWeeks => dateTime.AddDays(14), _ => dateTime.AddHours(1) }; DateTime atTheLatest = GetAtTheLatest(secret.TimeToLive, secret.StoredDateTime); @@ -110,13 +112,14 @@ FROM SECRET { static string ToString(TimeToLive ttl) => ttl switch { - TimeToLive.Week => "week", - TimeToLive.Day => "day", - TimeToLive.Hour => "hour", + TimeToLive.Week => "1 week", + TimeToLive.Day => "1 day", + TimeToLive.Hour => "1 hour", + TimeToLive.TwoWeeks => "2 weeks", _ => "hour" }; var ttlString = ToString(secret.TimeToLive); - _logger.Log(LogLevel.Warning, $@"Tried to retrieve password for key [{key}] after date is expired. Key set at [{secret.StoredDateTime}] for 1 [{ttlString}]"); + _logger.Log(LogLevel.Warning, $@"Tried to retrieve password for key [{key}] after date is expired. Key set at [{secret.StoredDateTime}] for [{ttlString}]"); Remove(key); return null; } diff --git a/Snappass.NET/TimeToLive.cs b/Snappass.NET/TimeToLive.cs index e5fe43b..82a223e 100644 --- a/Snappass.NET/TimeToLive.cs +++ b/Snappass.NET/TimeToLive.cs @@ -2,6 +2,6 @@ { public enum TimeToLive { - Week, Day, Hour + Week, Day, Hour, TwoWeeks } } diff --git a/Snappass.NET/Views/Password/Password.cshtml b/Snappass.NET/Views/Password/Password.cshtml new file mode 100644 index 0000000..30aa282 --- /dev/null +++ b/Snappass.NET/Views/Password/Password.cshtml @@ -0,0 +1,25 @@ +@{ + ViewData["Title"] = "ViewPassword"; + Layout = "~/Views/_Layout.cshtml"; +} +
+
+ +

Save the following secret to a secure location.

+
+
+ +
+ +
+ +
+
+

The secret has now been permanently deleted from the system, and the URL will no longer work. Refresh this page to verify.

+
+
+ diff --git a/Snappass.NET/Views/Password/Preview.cshtml b/Snappass.NET/Views/Password/Preview.cshtml index f52ac7d..1b28aea 100644 --- a/Snappass.NET/Views/Password/Preview.cshtml +++ b/Snappass.NET/Views/Password/Preview.cshtml @@ -1,5 +1,5 @@ @{ - ViewData["Title"] = "ViewPassword"; + ViewData["Title"] = "PreviewPassword"; Layout = "~/Views/_Layout.cshtml"; }
@@ -14,18 +14,6 @@
-
-

Save the following secret to a secure location.

-
- - -
-

The secret has now been permanently deleted from the system, and the URL will no longer work. Refresh this page to verify.

-
- + diff --git a/Snappass.NET/Views/Share/Share.cshtml b/Snappass.NET/Views/Share/Share.cshtml index 660073c..f274b78 100644 --- a/Snappass.NET/Views/Share/Share.cshtml +++ b/Snappass.NET/Views/Share/Share.cshtml @@ -16,9 +16,10 @@
diff --git a/Snappass.NET/wwwroot/js/preview.js b/Snappass.NET/wwwroot/js/preview.js index eaa7999..fafca51 100644 --- a/Snappass.NET/wwwroot/js/preview.js +++ b/Snappass.NET/wwwroot/js/preview.js @@ -1,6 +1,9 @@ (function () { $('#revealSecret').click(function (e) { - $('#revealed').show(); - $('#revealSecret').prop("disabled", true); + var form = $('
') + .attr('id', 'revealSecretForm') + .attr('method', 'post'); + form.appendTo($('body')); + form.submit(); }); })(); \ No newline at end of file