JavaScript: proper user of SJCL encryption in Google Apps Script

I’m using the minified version of SJCL to encrypt the backup files requested by the users. It is in a Google Apps Script add-on for Google Sheets.

I’m mostly concerned with correct use, exception handling and best practices of adding the encryption part in the script. It is currently working as expected in backup and in restore process.

The Backup (and Encryption)

User enter passphrase

It starts by asking the user to enter and re-enter the passphrase in a prompt. 1) One concern here is that the text field of the prompt doesn’t mask the passphrase. To fix that I would show a dialog with HTML and form (password input). But then I would need to work client-server call and, while that is not an issue, the prompt is much simpler, less complicated and it won’t be necessary to put a HTML – that could be questionable by the user?

const passphrase1 = ui.prompt(
  'Backup',
  'Enter passphrase:',
  ui.ButtonSet.OK_CANCEL);
if (passphrase1.getSelectedButton() === ui.Button.CANCEL) return 0;

const passphrase2 = ui.prompt(
  'Backup',
  'Please re-enter this passphrase:',
  ui.ButtonSet.OK_CANCEL);
if (passphrase2.getSelectedButton() === ui.Button.CANCEL) return 0;

Test passphrase

Here, the script tests if the passphrases match, and it also tests passphrase length and if it has at least upper, lower, numbers and special characters. It is a very simple test – so pA$$w0rd00 pass the test – and I actually didn’t want to test anything like GnuPG does. In this case, is nothing (no test, like GnuPG) better than this simple test?

const passphrase = passphrase1.getResponseText();
if (passphrase !== passphrase2.getResponseText() || passphrase.length < 10 || testPassphrasePolicy(passphrase)) {
  ui.alert(
    'Backup',
    'Invalid passphrase.',
    ui.ButtonSet.OK);
  return 1;
}

function testPassphrasePolicy (passphrase) {
  if (!/(a-z)+/.test(passphrase)) return 1;
  if (!/(A-Z)+/.test(passphrase)) return 1;
  if (!/(0-9)+/.test(passphrase)) return 1;
  if (!/(~!@#$%^*-_=+({)}/;:,.?)+/.test(passphrase)) return 1;

  return 0;
}

Encrypt data

The encryption part is pretty straight forward. The backupis a object like backup = { foe: 'abc', bar: 123 }. The steps are as follow:

  1. Stringify backup
  2. Encode to base64
  3. Compute SHA256
  4. Concatenate the base64 with : and the SHA256
  5. Encrypt with AES-128 in GCM mode and using the SHA256 as authentication data. If there is an error, simply return and show a generic message error.
  6. Test decryption. If there is an error, simply return and show a generic message error.
  7. Return a blob of the encrypted backup

I suspect steps 3 and 4 are overdoing, but before the addition of encryption that is how the script was doing an simple integrity test (with SHA1).

function encryptBackup_ (backup, passphrase) {
  const string = JSON.stringify(backup);
  const webSafeCode = Utilities.base64EncodeWebSafe(string, Utilities.Charset.UTF_8);

  const sha = computeDigest('SHA_256', webSafeCode, 'UTF_8');
  const data = webSafeCode + ':' + sha;

  let encrypted = '';
  try {
    encrypted = sjcl.encrypt(passphrase, data, { mode: "gcm", adata: sha });
  } catch (err) {
    ConsoleLog.error(err);
    return 0;
  }

  try {
    const decrypted = sjcl.decrypt(passphrase, encrypted);
    const parts = decrypted.split(':');
    const test_sha = computeDigest('SHA_256', parts(0), 'UTF_8');

    if (test_sha !== parts(1)) throw new Error('digestBackup_(): Bad decryption.');
  } catch (err) {
    ConsoleLog.error(err);
    return 0;
  }

  const date = Utilities.formatDate(DATE_NOW, 'GMT', 'yyyy-MM-dd-HH-mm-ss');
  const name = 'data' + date + '.backup';
  const blob = Utilities.newBlob(encrypted, 'application/octet-stream', name);

  return blob;
}

The Restore (and Decryption)

Once the user select a Drive file, the script requests the passphrase and tries to decrypt the file. This part is testing if it is possible to decrypt the file selected by the user.

The passphrase is being cached in a cache instance scoped to the current user and script so the script can retrieve it later to actually decrypt the file and restore the data. The cache expires in 120 seconds (session), and once it is retrieved later, it is immediately removed.

const ui = SpreadsheetApp.getUi();
const passphrase = ui.prompt(
  'Restore',
  'Enter passphrase:',
  ui.ButtonSet.OK_CANCEL);
if (passphrase.getSelectedButton() === ui.Button.CANCEL) return 0;

let decrypted = null;

try {
  decrypted = sjcl.decrypt(passphrase.getResponseText(), data);
} catch (err) {
  ConsoleLog.error(err);
  return 4;
}

const address = computeDigest(
  'SHA_1',
  file.getId() + SpreadsheetApp2.getActiveSpreadsheet().getId(),
  'UTF_8');
CacheService2.put('user', address, 'string', passphrase.getResponseText(), 120);

const parts = decrypted.split(':');
const test_sha = computeDigest('SHA_256', parts(0), 'UTF_8');

if (test_sha !== parts(1)) return 4;

const string = base64DecodeWebSafe(parts(0), 'UTF_8');
return JSON.parse(string);

indicator – Visual cues for enhancing user behavior

I have been working on a digital project where the product is an online presentation software where the user(Teacher) has presentation slides to select and conducts presentations to the students.

  • The user buys slides from the marketplace which get added to the library of the software.
  • The slides can only be moved and cannot be copied. So, the user creates a folder and moves slides into it to organize them. They can also self upload pdf files etc. in that folder.

My question is, when the user deletes the folder(on the main page), how can we let the user know that only the self-uploaded content was deleted and the bought content was moved back to the main page of the library? an animation? Text Toast?

Maybe it is self-evident on the main page, since moved content will visibly take space and show itself. But what if the user is deleting a folder inside a folder?

Please refer to the images.

enter image description here

enter image description here

Adding and Managing Custom User Fields

I am creating a courses website (similar to Udemy) on WordPress – using Tutor LMS.

Tutor LMS, like any other plugin, has its own templates including a user profile/dashboard where the default-wordpress user fields and also some other fields (added by Tutor plugin) are displayed.

Of course, the fields provided by wordpress aren’t sufficient for my case and I had to use a plugin like “Profile Builder Pro” in order to create a few extra fields (eg. Country – Sex – Date of Birth)..

Again, PB creates its own templates, including a profile editor to manage the extra fields I created with it.

The problem is, Tutor LMS seems to be completely oblivious to the extra fields I have created in PB and it also causes some integration issues sometimes (Profile Picture clashes and what not)..

So now I have 2 profile pages

  • Profile Page 1 : Can edit default WP fields + Tutor LMS fields
  • Profile Page 2 : Can edit default WP fields + Profile Builder fields

I don’t want the user to bounce between different pages – that are indeed differently styled and set up – in order for him/her to be able to setup his profile – I want to have a centralized profile page where all fields from all different plugins are integrated.

I know that the solution is probably not easy and will involve editing the plugins’ source code but I have no idea what to look for.

Ideally, I want all fields to be visible in Tutor LMS’s user profile page since it is better styled.

It is worth noting that both plugins have addons to integrate with BuddyPress – and so I installed BuddyPress hoping that it would act as a middleman between these 2 plugins – but I am not exactly sure what to do after installing BuddyPress, I didn’t expect it work like a charm – and it didn’t.

If your solution depends on trashing Profile Builder Pro for something else that provides the added fields functionality – please note that I also use Profile Builder to build my registration/login pages and ALSO for social login (facebook – twitter).. I don’t mind doing all that all over again – but the new solution must replace everything PB was able to do.

Thanks

filevault – M1 Mac (in DEP) first created user has no Secure Token

I have an M1 Mac that is in DEP (using Jamf). If I wipe the machine (from within Recovery Mode) and reinstall Big Sur 11.1, the first created user does not have a Secure Token, and so I can not enable FileVault.

Looking on the web I have found people who had similar issues, but the general solution seems to be “wipe the machine and reinstall macOS, and you’re all good”, which is exactly what I am doing.

See this one as an example: https://discussions.apple.com/thread/8487253

The best description of Secure Tokens (I am including the link because otherwise everyone else will point me there) is here: https://derflounder.wordpress.com/2018/01/20/secure-token-and-filevault-on-apple-file-system/

It looks like a bug in the OS – the initial created user has no Secure Token, and so I can not give any other users a Secure Token. Therefore I guess I am not allowed to use FileVault?

psionics – Can a user benefit from personal powers stored in an empowered object?

I realized while writing this question that I have another issue with empowered objects. Specifically, it is the object using the stored powers, rather than the wearer/wielder/user. Can such an object “target” the user with personal or “self-only” powers? I intend to have a more powerful psionic NPC loan a PC a Psychometabolic item, but most of the powers it’ll have access to are self-targeting and I realized I wasn’t sure if the PC could even use them.

php – Need help with generating a unique reference number based on the user id

I’m trying to create a unique reference ID based on the actual user ID so I don’t have to reveal the actual user ID on my site. This number will be published later using a shortcode. It is necessary to hide the real user ID and real name due to the privacy policy of my country. I was thinking of a user meta that is automatically generated after registration based on the actual user ID with a specific pattern:

  • 8-digits
  • contains the actual user ID
  • automatically completes the remaining digits with randomly generated numbers for fixing number length
{user_id}{generated_nr}, max 8-digits

I started very simply as follows, but I can’t set the length/width of the number considering the user id, so the missing digits are filled with auto-generated numbers. I also do not know how to generate the number only for newly registered users, nor do I know how to check if the number has already been assigned to another user:

function add_custom_ref_id( $user_id ) {

 $custom_ref_id = $user_id . wp_rand(10000000,99999999);
 return $custom_ref_id;

}

add_user_meta( $user_id, 'custom_ref_id', $custom_ref_id );

Concerns: Will the numbers be unique? Is this method secure? How can I avoid mistakes? Maybe combining them with the first letters of their first and last name? Like;

Name: John Doe, User ID: 24 > 24XXXXXXJD

And if yes, how?

I am always open to new suggestions and thank you in advance for proposing solutions.

Best regards.

PS: I already read the older threads but apart from the fact that they do not really meet my expectations, there could be better, more secure and more efficient methods out there.

indicator – Better visual cues for a enhancing user behaviour

So I have been working on a digital project where the product is an online presentation software where
the user(Teacher) has presentation slides to select and conducts presentations to the students.

The user buys slides from the marketplace which will be added to the library of the software.
The slides can only be moved and cannot be copied. So the user makes a folder and moves slides in it
to organize them. He can also put selfuploaded stuff like pdf in that folder.

My question is, when the user deletes the folder(on the mainpage) how can we let the user know that only the self uploaded content was deleted and the bought content was moved back
to the mainpage of the library? an animation? Text Toast?

Maybe it is selfevident on the main page, cuz moved content will visably take space and show itself. But what if the user
is deleting a folder inside a folder?

Please refer to the images.

Thanks in advance
enter image description here

enter image description here

Run PowerShell script as a different user and elevated

I know this looks like a duplicate question, but I’ve tried the solutions and they haven’t worked for me.

We need to run a script with our domain accounts but also make it elevated. This isn’t an issue on most devices, since the shortcut runs as admin, which prompts us for credentials. However if the user is a local admin, we are not prompted for credentials (just a yes/no UAC prompt).

I’m confused why this is not working:

# Get identity of script user
$identity = (Security.Principal.WindowsPrincipal)(Security.Principal.WindowsIdentity)::GetCurrent()

# Elevate the script if not already
if ($identity.IsInRole((Security.Principal.WindowsBuiltInRole)::Administrator)) {
    Write-Host -F Green 'ELEVATED'
} else {
    Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"& '$PSCommandPath'`""
    Exit
}

# Ensure the script has domain privileges
if ($identity.IsInRole('(domain)(admin group)')) {
    Write-Host -F Green 'DOMAIN ADMIN'
} else {
    Start-Process PowerShell -Verb RunAsUser "-NoProfile -ExecutionPolicy Bypass -Command `"& '$PSCommandPath'`""
    Pause # required, otherwise the Exit below closes the UAC prompt
    Exit
}
Pause

When the self-elevated script runs as user and domain credentials are entered, it loses elevation… i.e. when Start-Process -Verb RunAsUser powershell is run from an elevated PowerShell, it is not itself elevated.

I also tried the following:

Start-Process powershell -verb RunAs -argumentlist "Start-Process powershell.exe -Verb RunAsUser `"& pathtoscript.ps1`""

Which fails because the domain admin does not have access to the script directory… unless they’re elevated.

How to design Community Page USer Experience for immigration application

One of the ways I improve my website design and good user usability skills is by looking at other people’s critiques on websites. I used to like Concept Feedback but it has closed down.

How do I find similar sites?

(Please don’t tell me to Google it. I did. I am trying to post this question in an acceptable SE Q&A format)