public void Base56EncodeDecodeTest() { using (WebClient wc = new WebClient()) { String base56FullVectors = wc.DownloadString("https://raw.githubusercontent.com/sqrldev/sqrl-test-vectors/master/vectors/base56-full-format-vectors.txt"); String[] lines = base56FullVectors.Split("\n"); bool first = true; foreach (var line in lines) { if (first || string.IsNullOrEmpty(line)) { first = false; continue; } string[] data = line.Replace("\"", "").Split(','); string s = SQRL.GenerateTextualIdentityBase56(Sodium.Utilities.Base64ToBinary(data[0], string.Empty, Sodium.Utilities.Base64Variant.UrlSafeNoPadding)); byte[] alpha = SQRL.Base56DecodeIdentity(s); string inputData = Sodium.Utilities.BinaryToBase64(alpha, Sodium.Utilities.Base64Variant.UrlSafeNoPadding); Assert.Equal(inputData.CleanUpString(), data[0].CleanUpString()); } } }
public void GenerateIndexedSecretTest() { using (WebClient wc = new WebClient()) { String insVectors = wc.DownloadString("https://raw.githubusercontent.com/sqrldev/sqrl-test-vectors/master/vectors/ins-vectors.txt"); String[] lines = insVectors.Split("\n"); bool first = true; foreach (var line in lines) { if (first || string.IsNullOrEmpty(line)) { first = false; continue; } string[] data = line.CleanUpString().Replace("\"", "").Split(','); var ins = SQRL.CreateIndexedSecret( new Uri($"sqrl://{data[1]}"), string.Empty, Sodium.Utilities.Base64ToBinary(data[0], string.Empty, Sodium.Utilities.Base64Variant.UrlSafeNoPadding), Encoding.UTF8.GetBytes(data[2]), true); Assert.Equal(Sodium.Utilities.BinaryToBase64(ins, Sodium.Utilities.Base64Variant.UrlSafeNoPadding), data[3]); } } }
public void FromBase64URLWithoutPadding_2_equals_padding_needed() { var input = "YQ"; var result = SQRL.FromBase64URL(input); Assert.AreEqual("a", result); }
/// <summary> /// Creates a QuickPass from the given <paramref name="password"/> and /// <paramref name="imk"/> using the QuickPass settings stored in /// <paramref name="identity"/>, stores it in memory and establishes a /// timer that will clear the QuickPass after the timeout set forth in /// <paramref name="identity"/>'s QuickPass settings. /// </summary> /// <param name="password">The full identity master password.</param> /// <param name="imk">The identity's unencrypted Identity Master Key (IMK).</param> /// <param name="ilk">The identity's unencrypted Identity Lock Key (ILK).</param> /// <param name="identity">The identity that the QuickPass should be set for.</param> /// <param name="progress">An object implementing the IProgress interface for tracking the operation's progress (optional).</param> /// <param name="progressText">A string representing a text descrition for the progress indicator (optional).</param> public async void SetQuickPass(string password, byte[] imk, byte[] ilk, SQRLIdentity identity, IProgress <KeyValuePair <int, string> > progress = null, string progressText = null) { if (string.IsNullOrEmpty(password)) { Log.Warning("Can't use QuickPass on an empty password, aborting SetQuickPass()!"); return; } QuickPassItem qpi = new QuickPassItem() { EstablishedDate = DateTime.Now, QuickPassLength = identity.Block1.HintLength, IdentityUniqueId = identity.Block0.UniqueIdentifier.ToHex(), ScryptRandomSalt = SodiumCore.GetRandomBytes(16), Nonce = SodiumCore.GetRandomBytes(24), QuickPassTimeoutSecs = identity.Block1.PwdTimeoutMins * 60, ClearQuickPassOnIdle = identity.Block1.OptionFlags.ClearQuickPassOnIdle, ClearQuickPassOnSleep = identity.Block1.OptionFlags.ClearQuickPassOnSleep, ClearQuickPassOnSwitchingUser = identity.Block1.OptionFlags.ClearQuickPassOnSwitchingUser, Timer = new Timer() }; qpi.Timer.Enabled = false; qpi.Timer.AutoReset = false; // Dont restart timer after calling elapsed qpi.Timer.Interval = QP_GENERAL_TIMEOUT_SEC; qpi.Timer.Elapsed += QuickPassTimerElapsed; string quickPass = password.Substring(0, qpi.QuickPassLength); var enScryptResult = await SQRL.EnScryptTime( quickPass, qpi.ScryptRandomSalt, (int)Math.Pow(2, 9), QP_KEYDERIV_SEC, progress, progressText); qpi.ScryptIterationCount = enScryptResult.IterationCount; qpi.EncryptedImk = StreamEncryption.Encrypt(imk, qpi.Nonce, enScryptResult.Key); qpi.EncryptedIlk = StreamEncryption.Encrypt(ilk, qpi.Nonce, enScryptResult.Key); // If we already have a QuickPass entry for this identity, remove it first if (HasQuickPass(qpi.IdentityUniqueId)) { ClearQuickPass(qpi.IdentityUniqueId, QuickPassClearReason.Unspecified); } // Now, add the QuickPass item to our list and start the timer lock (_dataSyncObj) { _quickPassItems.Add(qpi.IdentityUniqueId, qpi); qpi.Timer.Start(); } Log.Information("QuickPass set for identity {IdentityUniqueId}", qpi.IdentityUniqueId); }
public IActionResult Index() { var vm = new IndexVM { SQRLLoginLink = $"sqrl://{Request.Host}/{SQRL.LoginLink(RequestIP)}" }; return(View(vm)); }
/// <summary> /// Verifies whether the user has copied down and entered the correct rescue code, /// and, upon successful verification, imports the identity into the database and /// navigates to the "Export Identity" screen. /// </summary> public async void VerifyRescueCode() { var progressBlock1 = new Progress <KeyValuePair <int, string> >(); var progressBlock2 = new Progress <KeyValuePair <int, string> >(); var progressDialog = new ProgressDialogViewModel(new List <Progress <KeyValuePair <int, string> > >() { progressBlock1, progressBlock2 }, this); progressDialog.ShowDialog(); var block1Task = SQRL.DecryptBlock1(this.Identity, this.Password, progressBlock1); var block2Task = SQRL.DecryptBlock2(this.Identity, SQRL.CleanUpRescueCode(this.RescueCode), progressBlock2); await Task.WhenAll(block1Task, block2Task); progressDialog.Close(); string msg = ""; if (!block1Task.Result.DecryptionSucceeded) { msg = _loc.GetLocalizationValue("InvalidPasswordMessage") + Environment.NewLine; } if (!block2Task.Result.DecryptionSucceeded) { msg = _loc.GetLocalizationValue("InvalidRescueCodeMessage") + Environment.NewLine; } if (!string.IsNullOrEmpty(msg)) { _ = await new MessageBoxViewModel(_loc.GetLocalizationValue("ErrorTitleGeneric"), $"{msg}", MessageBoxSize.Medium, MessageBoxButtons.OK, MessageBoxIcons.ERROR) .ShowDialog(this); } else { try { _identityManager.ImportIdentity(this.Identity, true); } catch (InvalidOperationException e) { await new MessageBoxViewModel(_loc.GetLocalizationValue("ErrorTitleGeneric"), e.Message, MessageBoxSize.Medium, MessageBoxButtons.OK, MessageBoxIcons.ERROR) .ShowDialog(this); } finally { ((MainWindowViewModel)_mainWindow.DataContext).Content = new ExportIdentityViewModel(); } } }
/// <summary> /// Creates a PDF document displaying the given identity as QR code and text and /// providing some guidance for the user. /// </summary> /// <param name="fileName">The full file name (including the path) for the document.</param> /// <param name="identity">The identity for which to create the document.</param> /// <param name="blockTypes">Spciefies a list of block types to include.</param> public static void CreateIdentityDocument(string fileName, SQRLIdentity identity, List <ushort> blockTypes) { if (string.IsNullOrEmpty(fileName) || identity == null || blockTypes == null || blockTypes.Count < 1) { throw new ArgumentException(string.Format("{0}, {1} and {2} must be specified and valid!", nameof(fileName), nameof(identity), nameof(blockTypes))); } _pageNr = 0; string title = "\"" + identity.IdentityName + "\" " + _loc.GetLocalizationValue("FileDialogFilterName"); string identityEncryptionMessage = blockTypes.Contains(1) ? _loc.GetLocalizationValue("IdentityDocEncMsgPassword") : _loc.GetLocalizationValue("IdentityDocEncMsgRC"); string textualIdentityMessage = _loc.GetLocalizationValue("IdentityDocumentTextualIdentityMessage"); string guidanceMessage = _loc.GetLocalizationValue("IdentityDocumentGuidanceMessage"); var identityBytes = identity.ToByteArray(includeHeader: true, blockTypes); string textualIdentity = SQRL.GenerateTextualIdentityBase56(identityBytes); var metadata = new SKDocumentPdfMetadata { Author = _assemblyName, Creation = DateTime.Now, Creator = _assemblyName, Keywords = "SQRL,Identity", Modified = DateTime.Now, Producer = "SkiaSharp", Subject = "SQRL Identity Document", Title = "SQRL Identity Document", EncodingQuality = 300, RasterDpi = 300 }; using (SKBitmap qrCode = CreateQRCode(identityBytes)) using (var stream = new SKFileWStream(fileName)) using (_document = SKDocument.CreatePdf(stream, metadata)) { StartNextPage(); float qrCodeWidth = (qrCode.Width <= QRCODE_MAX_SIZE) ? qrCode.Width : QRCODE_MAX_SIZE; float qrCodeHeight = (qrCode.Height <= QRCODE_MAX_SIZE) ? qrCode.Height : QRCODE_MAX_SIZE; DrawTextBlock(title, _fontBold, 23, SKColors.Black, 10f); DrawTextBlock(identityEncryptionMessage, _fontRegular, 12, SKColors.DarkGray, -5f, SKTextAlign.Left, 1.3f); DrawBitmap(qrCode, SKTextAlign.Center, qrCodeWidth, qrCodeHeight, 15f); DrawTextBlock(textualIdentityMessage, _fontRegular, 12, SKColors.DarkGray, 10f, SKTextAlign.Left, 1.3f); DrawTextBlock(textualIdentity, _fontMonoBold, 12, SKColors.Black, 15f, SKTextAlign.Center, 1.3f);; DrawTextBlock(guidanceMessage, _fontRegular, 12, SKColors.DarkGray, 0f, SKTextAlign.Left, 1.3f); EndPage(); _document.Close(); } }
public async void CopyToClipboard() { string identity = SQRL.GenerateTextualIdentityBase56(this.Identity.ToByteArray()); await Application.Current.Clipboard.SetTextAsync(identity); await new Views.MessageBox(_loc.GetLocalizationValue("IdentityExportedMessageBoxTitle"), _loc.GetLocalizationValue("IdentityCopiedToClipboardMessageBoxText"), MessageBoxSize.Medium, MessageBoxButtons.OK, MessageBoxIcons.OK) .ShowDialog <MessagBoxDialogResult>(_mainWindow); }
/// <summary> /// Copies the textual version of the current identity to the clipboard. /// </summary> public async void CopyToClipboard() { var textualIdentityBytes = this.Identity.ToByteArray(includeHeader: true, _blocksToExport); string identity = SQRL.GenerateTextualIdentityBase56(textualIdentityBytes); await Application.Current.Clipboard.SetTextAsync(identity); await new MessageBoxViewModel(_loc.GetLocalizationValue("IdentityExportedMessageBoxTitle"), _loc.GetLocalizationValue("IdentityCopiedToClipboardMessageBoxText"), MessageBoxSize.Medium, MessageBoxButtons.OK, MessageBoxIcons.OK) .ShowDialog(this); }
/// <summary> /// Creates a PDF document displaying the given identity as QR code and text and /// providing some guidance for the user. /// </summary> /// <param name="fileName">The full file name (including the path) for the document.</param> /// <param name="identity">The identity for which to create the document.</param> /// <param name="blockTypes">Spciefies a list of block types to include.</param> public static void CreateIdentityDocument(string fileName, SQRLIdentity identity, List <ushort> blockTypes) { if (string.IsNullOrEmpty(fileName) || identity == null) { throw new ArgumentException(string.Format("{0} and {1} must be specified and valid!", nameof(fileName), nameof(identity))); } float yPos = MARGIN_TOP; string title = "\"" + identity.IdentityName + "\" " + _loc.GetLocalizationValue("FileDialogFilterName"); string qrCodeMessage = _loc.GetLocalizationValue("IdentityDocumentQRCodeMessage"); string textualIdentityMessage = _loc.GetLocalizationValue("IdentityDocumentTextualIdentityMessage"); string guidanceMessage = _loc.GetLocalizationValue("IdentityDocumentGuidanceMessage"); var identityBytes = identity.ToByteArray(includeHeader: true, blockTypes); string textualIdentity = SQRL.GenerateTextualIdentityBase56(identityBytes); var metadata = new SKDocumentPdfMetadata { Author = _assemblyName, Creation = DateTime.Now, Creator = _assemblyName, Keywords = "SQRL,Identity", Modified = DateTime.Now, Producer = "SkiaSharp", Subject = "SQRL Identity Document", Title = "SQRL Identity Document", EncodingQuality = 300, RasterDpi = 300 }; using (SKBitmap qrCode = CreateQRCode(identityBytes)) using (var stream = new SKFileWStream(fileName)) using (var document = SKDocument.CreatePdf(stream, metadata)) using (var canvas = document.BeginPage(PAGE_WIDTH, PAGE_HEIGHT)) { float qrCodeWidth = (qrCode.Width <= QRCODE_MAX_SIZE) ? qrCode.Width : QRCODE_MAX_SIZE; float qrCodeHeight = (qrCode.Height <= QRCODE_MAX_SIZE) ? qrCode.Height : QRCODE_MAX_SIZE; float qrCodeXPos = PAGE_WIDTH / 2 - qrCodeWidth / 2; yPos += 10 + DrawTextBlock(canvas, title, yPos, _fontBold, 23, SKColors.Black); yPos += -15 + DrawTextBlock(canvas, qrCodeMessage, yPos, _fontRegular, 12, SKColors.DarkGray, SKTextAlign.Left, 1.3f); canvas.DrawBitmap(qrCode, new SKRect(qrCodeXPos, yPos, qrCodeXPos + qrCodeWidth, yPos + qrCodeHeight)); yPos += qrCodeHeight + 15; yPos += 10 + DrawTextBlock(canvas, textualIdentityMessage, yPos, _fontRegular, 12, SKColors.DarkGray, SKTextAlign.Left, 1.3f); yPos += 15 + DrawTextBlock(canvas, textualIdentity, yPos, _fontMonoBold, 12, SKColors.Black, SKTextAlign.Center, 1.3f); yPos += 00 + DrawTextBlock(canvas, guidanceMessage, yPos, _fontRegular, 12, SKColors.DarkGray, SKTextAlign.Left, 1.3f); DrawFooter(canvas); document.EndPage(); document.Close(); } }
/// <summary> /// Handles CPS response for all requests /// </summary> /// <param name="header">Sets the title and heading line on the SQRL Abort HTML Generated page</param> /// <param name="message">Sets the body of the SQRL Abort HTML Generated page</param> /// <param name="backUrlText">Sets the text on the Back Now link on the SQRL Abort HTML Generated page</param> /// <param name="succesUrl">Success URL passed in if the response is a successful Auth Ident</param> public static void HandlePendingCPS(string header = "", string message = "", string backUrlText = "", Uri succesUrl = null) { /* There are some points in the application where all else fails and we still wantt o try and gracefully exit CPS * in these instances we may not have access to the localization so we are hard coding these here as backup * they only get called this way in extreme cases. */ if (string.IsNullOrEmpty(header)) { header = "Authentication Aborted"; } if (string.IsNullOrEmpty(message)) { message = "SQRL's CPS authentication has been aborted. You will be automatically sent back to the previous page in a few seconds. If this does not work, please press your browser's BACK button or click the link below."; } if (string.IsNullOrEmpty(backUrlText)) { backUrlText = "Go Back Now"; } // Note we want to handle CPS if it already exists, but we don't want to start up the CPS Server for no reason. var sqrlIntance = SQRL.GetInstance(false); if (sqrlIntance.cps != null && sqrlIntance.cps.PendingResponse) { if (succesUrl == null) { sqrlIntance.cps.CPSAbortHeader = header; sqrlIntance.cps.CPSAbortMessage = message; sqrlIntance.cps.CPSAbortLinkText = backUrlText; if (sqrlIntance.cps.Can != null) { sqrlIntance.cps.cpsBC.Add(sqrlIntance.cps.Can); } else { sqrlIntance.cps.cpsBC.Add(sqrlIntance.cps.AbortURL); } } else { sqrlIntance.cps.cpsBC.Add(succesUrl); } while (sqrlIntance.cps.PendingResponse) { ; } } }
public async void IUKRescueCodeEncryptDecryptTest() { for (int i = 0; i < 10; i++) { SQRLIdentity identity = new SQRLIdentity(); byte[] iuk = SQRL.CreateIUK(); string rescueCode = SQRL.CreateRescueCode(); identity = await SQRL.GenerateIdentityBlock2(iuk, rescueCode, identity); var decryptResult = await SQRL.DecryptBlock2(identity, rescueCode); Assert.Equal(Sodium.Utilities.BinaryToHex(iuk), Sodium.Utilities.BinaryToHex(decryptResult.Iuk)); } }
/// <summary> /// Decrypts the Identity Master Key (IMK) for for the given <paramref name="identityUniqueId"/> /// using the provided <paramref name="quickPass"/> and returns it. If <c>null</c> is passed for /// <paramref name="identityUniqueId"/>, the decrypted IML for the current identity will be returned. /// </summary> /// <param name="quickPass">The QuickPass (first x characters from the identity's master password /// which was used to encrypt the IMK when setting the QuickPass entry for the identity.</param> /// <param name="identityUniqueId">The hex representation of the identity's unique id (block 0), /// or <c>null</c> if you want to decrypt the current identity's IMK.</param> /// <param name="progress">An object implementing the IProgress interface for tracking the operation's progress (optional).</param> /// <param name="progressText">A string representing a text descrition for the progress indicator (optional).</param> /// <returns>The decrypted block 1 keys (IMK, ILK) for the given <paramref name="identityUniqueId"/>, or /// <c>null</c> if no such QuickPass entry exists.</returns> public async Task <QuickPassDecryptedKeys> GetQuickPassDecryptedImk(string quickPass, string identityUniqueId = null, IProgress <KeyValuePair <int, string> > progress = null, string progressText = null) { QuickPassItem qpi = null; if (identityUniqueId == null) { if (_identityManager.CurrentIdentity == null) { return(null); } identityUniqueId = _identityManager.CurrentIdentity?.Block0?.UniqueIdentifier?.ToHex(); } if (identityUniqueId == null) { Log.Error("Could not resolve current identity in {MethodName}, throwing Exception!", nameof(GetQuickPassDecryptedImk)); throw new InvalidOperationException("Cannot return QuickPass-decrypted IMK without an identity!"); } lock (_dataSyncObj) { if (!_quickPassItems.ContainsKey(identityUniqueId)) { Log.Warning("No identity found for id {IdentityUniqueId} in {MethodName}", identityUniqueId, nameof(GetQuickPassDecryptedImk)); return(null); } qpi = _quickPassItems[identityUniqueId]; } byte[] key = await SQRL.EnScryptCT(quickPass, qpi.ScryptRandomSalt, (int)Math.Pow(2, 9), qpi.ScryptIterationCount, progress, progressText); byte[] decryptedImk = StreamEncryption.Decrypt(qpi.EncryptedImk, qpi.Nonce, key); byte[] decryptedIlk = StreamEncryption.Decrypt(qpi.EncryptedIlk, qpi.Nonce, key); Log.Information("QuickPass retrieved for identity {IdentityUniqueId}", identityUniqueId); return(new QuickPassDecryptedKeys(decryptedImk, decryptedIlk)); }
public async void LMKILKPasswordEncryptDecryptTest() { for (int i = 0; i < 50; i++) { SQRLIdentity identity = new SQRLIdentity(); byte[] iuk = SQRL.CreateIUK(); string password = Sodium.Utilities.BinaryToHex(Sodium.SodiumCore.GetRandomBytes(32), Sodium.Utilities.HexFormat.None, Sodium.Utilities.HexCase.Lower); identity = await SQRL.GenerateIdentityBlock1(iuk, password, identity); byte[] imk = SQRL.CreateIMK(iuk); byte[] ilk = SQRL.CreateILK(iuk); var decryptedData = await SQRL.DecryptBlock1(identity, password); Assert.Equal(Sodium.Utilities.BinaryToHex(imk), Sodium.Utilities.BinaryToHex(decryptedData.Imk)); Assert.Equal(Sodium.Utilities.BinaryToHex(ilk), Sodium.Utilities.BinaryToHex(decryptedData.Ilk)); } }
public void VerifyDetached() { //client=dmVyPTENCmNtZD1pZGVudA0KaWRrPXhZdjhvRzY2RE1HWEs4VENXQjU0QkFPcTk5LWFDcEZiMEw2NGE5b3JBcWsNCm9wdD1jcHN-c3VrDQo&server=dmVyPTENCm51dD1HdWZJYkQzVVhlMVpGYkpxdVNOQ2ZBDQp0aWY9NQ0KcXJ5PS9zcXJsP251dD1HdWZJYkQzVVhlMVpGYkpxdVNOQ2ZBDQpzdWs9dVUwUG1rRnpDVFMyUjkxOWpfQ3VKb0EtR0h2T0lvN3JqZ0Q3QUhVTVptNA0K&ids=xvbYlKjPU_bVkSacpXakcJhJX6ZaiPa8cX9wGtVbeEHROddvmQ5ec-xDuMmuPg4-p7Os-PDTO8ZJC12oW-48CQ var ids = "xvbYlKjPU_bVkSacpXakcJhJX6ZaiPa8cX9wGtVbeEHROddvmQ5ec-xDuMmuPg4-p7Os-PDTO8ZJC12oW-48CQ"; var client = "dmVyPTENCmNtZD1pZGVudA0KaWRrPXhZdjhvRzY2RE1HWEs4VENXQjU0QkFPcTk5LWFDcEZiMEw2NGE5b3JBcWsNCm9wdD1jcHN-c3VrDQo"; var server = "dmVyPTENCm51dD1HdWZJYkQzVVhlMVpGYkpxdVNOQ2ZBDQp0aWY9NQ0KcXJ5PS9zcXJsP251dD1HdWZJYkQzVVhlMVpGYkpxdVNOQ2ZBDQpzdWs9dVUwUG1rRnpDVFMyUjkxOWpfQ3VKb0EtR0h2T0lvN3JqZ0Q3QUhVTVptNA0K"; var idk = "xYv8oG66DMGXK8TCWB54BAOq99-aCpFb0L64a9orAqk"; var msg = client + server; var msgBytes = Encoding.UTF8.GetBytes(msg); var sigBytes = SQRL.FromBase64URL(ids); var keyBytes = SQRL.FromBase64URL(idk); var result = Sodium.PublicKeyAuth.VerifyDetached(sigBytes, msgBytes, keyBytes); Assert.IsTrue(result); }
public void EnHashTest() { using (WebClient wc = new WebClient()) { String enHashVectors = wc.DownloadString("https://raw.githubusercontent.com/sqrldev/sqrl-test-vectors/master/vectors/enhash-vectors.txt"); String[] lines = enHashVectors.Split("\n"); bool first = true; foreach (var line in lines) { if (first || string.IsNullOrEmpty(line)) { first = false; continue; } string[] data = line.Replace("\"", "").Split(','); byte[] ary = SQRL.EnHash(Sodium.Utilities.Base64ToBinary(data[0], "", Sodium.Utilities.Base64Variant.UrlSafeNoPadding)); string hex = data[1]; string result = Sodium.Utilities.BinaryToBase64(ary, Sodium.Utilities.Base64Variant.UrlSafeNoPadding); Assert.Equal(hex.CleanUpString(), result.CleanUpString()); } } }
public void GenerateSiteKeysTest() { using (WebClient wc = new WebClient()) { String identityVectors = wc.DownloadString("https://raw.githubusercontent.com/sqrldev/sqrl-test-vectors/master/vectors/identity-vectors.txt"); String[] lines = identityVectors.Split("\n"); bool first = true; foreach (var line in lines) { if (first || string.IsNullOrEmpty(line)) { first = false; continue; } string[] data = line.CleanUpString().Replace("\"", "").Split(','); var keys = SQRL.CreateSiteKey(new Uri($"sqrl://{data[3]}"), data[4], Sodium.Utilities.Base64ToBinary(data[2], string.Empty, Sodium.Utilities.Base64Variant.UrlSafeNoPadding), true); Assert.Equal(Sodium.Utilities.BinaryToBase64(keys.PublicKey, Sodium.Utilities.Base64Variant.UrlSafeNoPadding), data[5]); } } }
public async void EnScryptTest() { using (WebClient wc = new WebClient()) { String enScryptVectors = wc.DownloadString("https://raw.githubusercontent.com/sqrldev/sqrl-test-vectors/master/vectors/enscrypt-vectors.txt"); String[] lines = enScryptVectors.Split(Environment.NewLine); bool first = true; foreach (var line in lines) { if (first || string.IsNullOrEmpty(line)) { first = false; continue; } string[] data = line.Replace("\"", "").Split(','); byte[] ary = await SQRL.EnScryptCT(data[0], Encoding.UTF8.GetBytes(data[1]), (int)Math.Pow(2, 9), int.Parse(data[2])); string hex = data[4]; string result = Sodium.Utilities.BinaryToHex(ary); Assert.Equal(hex.CleanUpString(), result.CleanUpString()); } } }
/// <summary> /// Creates a new <c>App</c> instance. /// </summary> public App() : base() { this.Localization = new LocalizationExtension(); SQRL.StartCPSServer(); SQRL.CPS.CPSRequestReceived += CPSRequestReceived; }
static async Task Main(string[] args) { SQRL sqrl = SQRL.GetInstance(true); Console.WriteLine("Importing Identity"); var progress = new Progress <KeyValuePair <int, string> >(percent => { Console.WriteLine($"{percent.Value}: {percent.Key}%"); }); SQRLIdentity newId = SQRLIdentity.FromFile(Path.Combine(@"C:\Users\jose\Downloads\SQRL-Test-Identity-Resources\", @"Spec-Vectors-Identity.sqrl")); //SQRLIdentity newId = SQRLIdentity.FromFile(Path.Combine(Directory.GetCurrentDirectory(), @"980591756918003626376697.sqrl")); SQRLOpts optsFlags = (sqrl.cps != null && sqrl.cps.Running ? SQRLOpts.SUK | SQRLOpts.CPS : SQRLOpts.SUK); SQRLOptions opts = new SQRLOptions(optsFlags); bool run = true; var block1Keys = await SQRL.DecryptBlock1(newId, "Zingo-Bingo-Slingo-Dingo", progress); if (block1Keys.DecryptionSucceeded) { try { do { Console.WriteLine("Enter SQRL URL:"); string url = Console.ReadLine(); Uri requestURI = new Uri(url); string AltID = ""; var siteKvp = SQRL.CreateSiteKey(requestURI, AltID, block1Keys.Imk); Dictionary <byte[], PriorSiteKeysResult> priorSiteKeys = null; if (newId.Block3 != null && newId.Block3.Edition > 0) { byte[] decryptedBlock3 = SQRL.DecryptBlock3(block1Keys.Imk, newId, out bool allGood); List <byte[]> oldIUKs = new List <byte[]>(); if (allGood) { int skip = 0; int ct = 0; while (skip < decryptedBlock3.Length) { oldIUKs.Add(decryptedBlock3.Skip(skip).Take(32).ToArray()); skip += 32; ; if (++ct >= 3) { break; } } SQRL.ZeroFillByteArray(ref decryptedBlock3); priorSiteKeys = SQRL.CreatePriorSiteKeys(oldIUKs, requestURI, AltID); oldIUKs.Clear(); } } //SQRL.ZeroFillByteArray(ref decryptedData.Item2); //decryptedData.Item2.ZeroFill(); var serverRespose = SQRL.GenerateQueryCommand(requestURI, siteKvp, opts, null, 0, priorSiteKeys); if (!serverRespose.CommandFailed) { if (!serverRespose.CurrentIDMatch && !serverRespose.PreviousIDMatch) { StringBuilder additionalData = null; if (!string.IsNullOrEmpty(serverRespose.SIN)) { additionalData = new StringBuilder(); byte[] ids = SQRL.CreateIndexedSecret(requestURI, AltID, block1Keys.Imk, Encoding.UTF8.GetBytes(serverRespose.SIN)); additionalData.AppendLineWindows($"ins={Sodium.Utilities.BinaryToBase64(ids, Utilities.Base64Variant.UrlSafeNoPadding)}"); } Console.WriteLine("The site doesn't recognize this ID, would you like to proceed and create one? (Y/N)"); if (Console.ReadLine().StartsWith("Y", StringComparison.OrdinalIgnoreCase)) { serverRespose = SQRL.GenerateNewIdentCommand(serverRespose.NewNutURL, siteKvp, serverRespose.FullServerRequest, block1Keys.Ilk, opts, additionalData); } } else if (serverRespose.PreviousIDMatch) { byte[] ursKey = null; ursKey = SQRL.GetURSKey(serverRespose.PriorMatchedKey.Key, Sodium.Utilities.Base64ToBinary( serverRespose.SUK, string.Empty, Sodium.Utilities.Base64Variant.UrlSafeNoPadding)); StringBuilder additionalData = null; if (!string.IsNullOrEmpty(serverRespose.SIN)) { additionalData = new StringBuilder(); byte[] ids = SQRL.CreateIndexedSecret(requestURI, AltID, block1Keys.Imk, Encoding.UTF8.GetBytes(serverRespose.SIN)); additionalData.AppendLineWindows($"ins={Sodium.Utilities.BinaryToBase64(ids, Utilities.Base64Variant.UrlSafeNoPadding)}"); byte[] pids = SQRL.CreateIndexedSecret(serverRespose.PriorMatchedKey.Value.SiteSeed, Encoding.UTF8.GetBytes(serverRespose.SIN)); additionalData.AppendLineWindows($"pins={Sodium.Utilities.BinaryToBase64(pids, Utilities.Base64Variant.UrlSafeNoPadding)}"); } serverRespose = SQRL.GenerateIdentCommandWithReplace( serverRespose.NewNutURL, siteKvp, serverRespose.FullServerRequest, block1Keys.Ilk, ursKey, serverRespose.PriorMatchedKey.Value.KeyPair, opts, additionalData); } else if (serverRespose.CurrentIDMatch) { int askResponse = 0; if (serverRespose.HasAsk) { Console.WriteLine(serverRespose.AskMessage); Console.WriteLine($"Enter 1 for {serverRespose.GetAskButtons[0]} or 2 for {serverRespose.GetAskButtons[1]}"); int resp; do { string response = Console.ReadLine(); int.TryParse(response, out resp); if (resp == 0) { Console.WriteLine("Invalid Entry, please enter 1 or 2 as shown above"); } } while (resp == 0); askResponse = resp; } StringBuilder addClientData = null; if (askResponse > 0) { addClientData = new StringBuilder(); addClientData.AppendLineWindows($"btn={askResponse}"); } if (serverRespose.SQRLDisabled) { Console.WriteLine("SQRL Is Disabled, to Continue you must enable it. Do you want to? (Y/N)"); if (Console.ReadLine().StartsWith("Y", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("Enter your Rescue Code (No Sapces or Dashes)"); string rescueCode = Console.ReadLine().Trim(); progress = new Progress <KeyValuePair <int, string> >(percent => { Console.WriteLine($"Decrypting with Rescue Code: {percent.Key}%"); }); var iukData = await SQRL.DecryptBlock2(newId, rescueCode, progress); if (iukData.DecryptionSucceeded) { byte[] ursKey = null; ursKey = SQRL.GetURSKey(iukData.Iuk, Sodium.Utilities.Base64ToBinary(serverRespose.SUK, string.Empty, Sodium.Utilities.Base64Variant.UrlSafeNoPadding)); iukData.Iuk.ZeroFill(); serverRespose = SQRL.GenerateEnableCommand(serverRespose.NewNutURL, siteKvp, serverRespose.FullServerRequest, ursKey, addClientData, opts); } else { throw new Exception("Failed to Decrypt Block 2, Invalid Rescue Code"); } } } Console.WriteLine("Which Command Would you like to Issue?:"); Console.WriteLine("*********************************************"); Console.WriteLine("0- Ident (Default/Enter) "); Console.WriteLine("1- Disable "); Console.WriteLine("2- Remove "); Console.WriteLine("3- Cancel "); Console.WriteLine("4- Re-Key "); Console.WriteLine("10- Quit "); Console.WriteLine("*********************************************"); var value = Console.ReadLine(); int.TryParse(value, out int selection); switch (selection) { case 0: { serverRespose = SQRL.GenerateSQRLCommand(SQRLCommands.ident, serverRespose.NewNutURL, siteKvp, serverRespose.FullServerRequest, addClientData, opts); if (sqrl.cps != null && sqrl.cps.PendingResponse) { sqrl.cps.cpsBC.Add(new Uri(serverRespose.SuccessUrl)); } } break; case 1: { Console.WriteLine("This will disable all use of this SQRL Identity on the server, are you sure you want to proceed?: (Y/N)"); if (Console.ReadLine().StartsWith("Y", StringComparison.OrdinalIgnoreCase)) { serverRespose = SQRL.GenerateSQRLCommand(SQRLCommands.disable, serverRespose.NewNutURL, siteKvp, serverRespose.FullServerRequest, addClientData, opts); if (sqrl.cps != null && sqrl.cps.PendingResponse) { sqrl.cps.cpsBC.Add(sqrl.cps.Can); } } } break; case 2: { Console.WriteLine("Enter your Rescue Code (No Sapces or Dashes)"); string rescueCode = Console.ReadLine().Trim(); progress = new Progress <KeyValuePair <int, string> >(percent => { Console.WriteLine($"Decrypting with Rescue Code: {percent.Key}%"); }); var iukData = await SQRL.DecryptBlock2(newId, rescueCode); if (iukData.DecryptionSucceeded) { byte[] ursKey = SQRL.GetURSKey(iukData.Iuk, Sodium.Utilities.Base64ToBinary(serverRespose.SUK, string.Empty, Sodium.Utilities.Base64Variant.UrlSafeNoPadding)); iukData.Iuk.ZeroFill(); serverRespose = SQRL.GenerateSQRLCommand(SQRLCommands.remove, serverRespose.NewNutURL, siteKvp, serverRespose.FullServerRequest, addClientData, opts, null, ursKey); if (sqrl.cps != null && sqrl.cps.PendingResponse) { sqrl.cps.cpsBC.Add(sqrl.cps.Can); } } else { throw new Exception("Failed to Decrypt Block 2, Invalid Rescue Code"); } } break; case 3: { if (sqrl.cps != null && sqrl.cps.PendingResponse) { sqrl.cps.cpsBC.Add(sqrl.cps.Can); } } break; default: { Console.WriteLine("bye"); run = false; } break; } } } } while (run); } catch (Exception ex) { Console.WriteLine($"Error: {ex.ToString()}"); } } else { Console.WriteLine("Failed to Decrypt Block 1, bad password!"); } }