AES-GCM Secret/Symmetric Key Validation with Encrypted Buffer Comparison of SubtleCrypto and SJCL

Our application has a group chat feature which involves end-to-end encryption of group chat messages.

The group is hosted at server.

All the encryption and decryption is handled on the clientside.

It also utilizes the same mechanism of encryption for the real-time messages.

The group chat works by foremost someone creating this group at serverside, when a client is instating its creation they generate a new key [clientside], and validate it on their end [successful encryption and decryption of a same static text from SubtleCrypto], and then successfully hosts it by sending the encrypted buffer to server where groups are managed, storing it, and then others can join if they know the secret key. We use the same AES-GCM Symmetric Key / Secret Key that is generated for this purpose. The server-side doesn’t have the key stored anywhere.

Now, to validate whether the key a client trying to join this group is valid or not before joining is, with the key that was shared to them [by other means, such as email etc], at client-side, encrypt the SAME static text, and send its buffer. Then the buffer value stored at the server-side on creation time is compared to this new client joining with the buffer of the newly encrypted static text they performed on client-side, and if the buffer is equal on server-side, they are authorized into this group.

Now with reference to my previous question(s), I’m attempting to replace Web API SubtleCrypto to SJCL, and the newly generated SJCL encrypted buffer is always smaller than the SubtleCrypto. While encrypting and decrypting between each other is already established, the problem at hand is that their buffers don’t match, even though they’re both using the same key, IV, and AES-GCM mode. And they both have to simultaneously work for backwards compatibility of different client versions.

Here is an example:

const buf2hex = (buffer) =>
{
    return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}

const string2hex = (input) =>
{
    let hex;
    let result = "";
    let i = 0;
    for (i = 0; i < input.length; i++)
    {
        hex = input.charCodeAt(i).toString(16);
        result += ("000" + hex).slice(-4);
    }
    return result
}

const hex2bytes = (string) =>
{
    const normal = string.length % 2 ? "0" + string : string; // Make even length
    const bytes = new Uint8Array(normal.length / 2);
    for (let index = 0; index < bytes.length; ++index)
    {
        const c1 = normal.charCodeAt(index * 2);
        const c2 = normal.charCodeAt(index * 2 + 1);
        const n1 = c1 - (c1 < 58 ? 48 : 87);
        const n2 = c2 - (c2 < 58 ? 48 : 87);
        bytes[index] = n1 * 16 + n2;
    }
    return bytes;
}

//JWK K Value
const generateKey = async () =>
{
    const key = await window.crypto.subtle.generateKey(
    {
        name: "AES-GCM",
        length: 128
    }, true, ["encrypt", "decrypt"]);
    const key_exported = await window.crypto.subtle.exportKey("jwk", key);
    return key_exported.k;
}

//CryptoKey generated from SubtleCrypto:
const generateSubtleCryptoKey = async (kvalue) =>
{
    return window.crypto.subtle.importKey(
        "jwk",
        {
            k: kvalue,
            alg: "A128GCM",
            ext: true,
            key_ops: ["encrypt", "decrypt"],
            kty: "oct",
        },
        {
            name: "AES-GCM",
            length: 128
        },
        false,
        ["encrypt", "decrypt"]
    );
}

//Cipher generated from SJCL:
const generateCipherSJCL = (kkey) =>
{
    const ekkeyB64 = kkey.replace(/-/g, '+').replace(/_/g, '/');    // Base64url -> Base64 (ignore optional padding)
    const ebkey = sjcl.codec.base64.toBits(ekkeyB64);               // conert to bitArray
    const ecipher = new sjcl.cipher.aes(ebkey);
    return ecipher;
}

const encryptText = "STATIC TEXT";

const compareBuffers = async () =>
{
    const kkey = await generateKey();
    const cryptokey = await generateSubtleCryptoKey(kkey)
    const ecipher = generateCipherSJCL(kkey);

    const subtleCrypto = await window.crypto.subtle.encrypt(
    {
        name: "AES-GCM",
        iv: new Uint8Array(12)
    }, cryptokey, new TextEncoder().encode(JSON.stringify(encryptText)));

    const encryptionIv = sjcl.codec.hex.toBits(buf2hex(new Uint8Array(12).buffer));
    const encryptedMessageFormat = sjcl.codec.hex.toBits(string2hex(JSON.stringify(encryptText)));
    const sjclEncrypted = sjcl.mode.gcm.encrypt(ecipher, encryptedMessageFormat, encryptionIv);

    const originalEncryptedSJCL = Buffer.from(sjclEncrypted);
    console.log({subtleCrypto});
    console.log({originalEncryptedSJCL});
    const e1 = Buffer.from(new Uint8Array(sjclEncrypted));
    const e2 = Buffer.from(new Uint8Array(subtleCrypto));
    console.log({e1, e2});                                  //{e1: Uint8Array(11), e2: Uint8Array(29)}
    console.log(Buffer.compare(e1, e2));                    //should be 0, equal buffer.
}

compareBuffers();

I suppose I should preface this by stating that I have very limited cryptography knowledge, but why would the buffers differ when they’re both encrypted and decrypted across both libraries when the mechanism is same?

background color not displayed after building the react app

I have a react app using material ui, created drawer in it and there is a problem with its background color(color is not displayed), and burger menu toogle(when drawer is opened, burger menu button is lost), after I do npm run build and then start it with serve -s build
here are my deps:

"@emotion/react": "^11.4.1",
        "@fortawesome/fontawesome-free": "^5.15.4",
        "@fortawesome/fontawesome-svg-core": "^1.2.36",
        "@material-ui/core": "^4.12.3",
        "@material-ui/icons": "^4.11.2",
        "react": "^17.0.2",
        "react-dom": "^17.0.2",
        "react-icons": "^4.2.0",
        "react-redux": "^7.2.5",
        "react-router": "^6.0.0-beta.8",
        "react-router-dom": "^6.0.0-beta.8",
        "react-scripts": "4.0.3",
        "web-vitals": "^1.1.2"

drawer code:

 const isMobile=useMediaQuery(theme.breakpoints.down(885))
const classes = useStyles();
const drawerContentClass = isMobile===true?
classes.content:clsx(classes.content, { [classes.contentShift]: open})


<div className={classes.root}>
            <CssBaseline />
            <AppBar
              position="fixed"
              className={clsx(classes.appBar, {
                [classes.appBarShift]: open,
              })}
            >
              <Toolbar style={{background:'#00314D'}}>
                  <IconButton
                    color="inherit"
                    aria-label="open drawer"
                    onClick={()=>clickFn(!open)}
                    edge="start"
                  >
                    <MenuIcon />
                  </IconButton>
              </Toolbar>
            </AppBar>
            <Drawer
              className={classes.drawer}
              variant="persistent"
              transitionDuration={{
                enter: theme.transitions.duration.leavingScreen,
                exit: theme.transitions.duration.leavingScreen
              }}
              classes={{
                paper: classes.drawerPaper
              }}
              PaperProps={{ elevation: 9 }}
              anchor="left"
              open={open}
              onClose={closeDrawer}
            >
                     //drawer content
              </Drawer>
            <main className={drawerContentClass} style={{backgroundColor:'rgb(0, 49, 77)'}}> //tried to hardcode here, but this not helped
                <div className={classes.drawerHeader} />
                
            </main>
        </div>

Show preloader gif only when input fields are filled?

I have a site built via flask that takes the information provided by the user and performs some action . I also have a loader that shows up when this action is done in the background, the trouble is , if the user were to click on the button “process” without filling the details , the loader is still shown .

How do i make the loader appear only when all the input fields are entered by the user and then clicks on “process” button

   <form action="/result" method="post">
    <tbody>
 <tr>
        <td><input type='text' name="ip" id='ipadd' required="required" placeholder="8.8.8.8" pattern="((^|.)((25[0-5])|(2[0-4]d)|(1dd)|([1-9]?d))){4}$" value style="margin-bottom: 5px;" autocomplete="off"></td>

       </tr>
    </tbody>

  </table>

  <button  class="btn btn-outline btn-success"  style="padding-right: 15pt;"onclick="loading();">Process</button>
   </form>


  <script type="text/javascript">// <![CDATA[
        function loading(){
            $("#loading").show();
            $("#content").hide();
        }
// ]]></script>

Why react map function is not working properly?

when I use the map in react, do I get an unexpected token error?

import React, { useState, useEffect } from "react";
import axios from "axios";

export default function ApiHook() {
  const [employees, setEmployees] = useState([]);

  useEffect(() => {
    axios
      .get("http://dummy.restapiexample.com/api/v1/employees")
      .then((response) => {
        console.log(response);
        setEmployees(response.data);
      })
      .catch((e) => console.log(e));
  }, []);
  return (
      {employees.map((employee) => {
        return <p>{employee.employee_name}</p>;
      })}
  );
}

What document contentTypes does Chrome render as monospaced HTML?

Some Chrome URLs are just text files that are rendered as monospaced text files.
As an example, this is the Public Suffix List (https://publicsuffix.org/list/public_suffix_list.dat), and looks like this:

Public Suffix List

You can also get to this type of page by going to any raw GitHub user content, like https://raw.githubusercontent.com/github/docs/main/README.md.

This is the HTML wrapper I see for these type of monospaced text documents:

<html data-lt-installed="true">
  <head></head>
  <body>
    <pre style="word-wrap: break-word; white-space: pre-wrap;">
    "Lots of text!"
    </pre>
  </body>
</html>

Some contentType values predictably get rendered in this HTML. image/png does not get rendered this way, but text/css and text/js do. Additionally, application/json also gets rendered in this way, like https://ip-ranges.amazonaws.com/ip-ranges.json.

Question

For which document.contentType values does Chrome render this monospaced text content with this HTML wrapper?

Why does express validator doesn’t run on desired routes?

i am trying to validate request body and path params in post and delete routes respectively

this is my body validation rules :-

const { body, validationResult } = require('express-validator');

const requestBodyValidationRules = [
  body('originalUrl')
    .not()
    .isEmpty()
    .withMessage({
      error: 'Invalid Request Body',
      detail: {
        originalUrl: 'Request Syntax Error! originalUrl parameter is required',
      },
    }),
  body('originalUrl')
    .isURL({
      protocols: ['http', 'https', 'ftp'],
      require_protocol: true,
    })
    .withMessage({
      error: 'Invalid Request Body',
      detail: {
        originalUrl: 'Invalid URL format, please try again!',
      },
    }),
  body('convertedUrlId')
    .optional({ checkFalsy: true })
    .matches(/^[~A-Za-z0-9/./_/-]*$/)
    .withMessage({
      error: 'Invalid characters',
      detail: {
        convertedUrlId:
          'Invalid characters! Only [A-Z],[a-z],[0-9], _ , - , . , ~ are allowed',
      },
    }),
  body('convertedUrlId')
    .optional({ checkFalsy: true })
    .isLength({ min: 5, max: 6 })
    .withMessage({
      error: 'Invalid length',
      detail: {
        convertedUrlId:
          'Invalid Length! Character length >=5 and <7 characters are allowed',
      },
    }),
  (req, res, next) => {
    const errors = validationResult(req);
    console.log('errors', errors);
    if (!errors.isEmpty()) {
      const errorMsg = errors.array()[0].msg;
      return res.status(400).json(errorMsg);
    }
    next();
  },
];
module.exports = requestBodyValidationRules;

this is my param validation rules

const { param, validationResult } = require('express-validator');

const requestParamValidationRules = [
  param('convertedUrlId')
    .isLength({ min: 5, max: 6 })
    .withMessage({
      error: 'Invalid length',
      detail: {
        convertedUrlId:
          'Invalid Length! Character length >=5 and <7 characters are allowed',
      },
    }),
  param('convertedUrlId')
    .matches(/^[~A-Za-z0-9/./_/-]*$/)
    .withMessage({
      error: 'Invalid characters',
      detail: {
        convertedUrlId:
          'Invalid characters! Only [A-Z],[a-z],[0-9], _ , - , . , ~ are allowed',
      },
    }),
  (req, res, next) => {
    const errors = validationResult(req);
    console.log('errors', errors);
    if (!errors.isEmpty()) {
      const errorMsg = errors.array()[0].msg;
      return res.status(400).json(errorMsg);
    }
    next();
  },
];

module.exports = requestParamValidationRules;

this my app.js

app.get(
  '/api-docs',
  swaggerUI.serve,
  swaggerUI.setup(swaggerJsDocs, swaggerOptions)
);
app.get('/:shortUrl', redirectUrlRouter);
app.post(
  '/users/anon-user/urls',
  checkRequestBodyParams,
  fetchTimeStamp,
  convertAnonUrlRouter
);
app.post(
  '/users/auth-user/urls',
  checkRequestBodyParams,
  fetchTimeStamp,
  convertAuthUrlRouter
);
app.get('/users/auth-user/urls', fetchAuthUserHistoryRouter);
app.patch('/users/auth-user/urls', fetchTimeStamp, editConvertedUrlRouter);
app.delete(
  '/users/auth-user/urls/:convertedUrlId',
  checkPathParams,
  deleteConvertedUrlRouter
);

app.use((req, res) => {
  res.status(404).json({ error: 'route not found' });
});

when ever i am trying to post, delete validations are running, why is that so?

Desired behaviour :- for post, request body validations should be running

How to get 1 to n key value in an array of objects and push them to a new array?(Javascript) [duplicate]

I have an array of objects,

[
 {trial1: 'a1', trial2: 'a2', trial3: 'a3'},
 {trial1: 'b1', trial2: 'b2', trial3: 'b3'},
 {trial1: 'c1', trial2: 'c2', trial3: 'c3'},
 ]

How do I get an array of arrays like this

[
 ['a1','a2','a3'],
 ['b1','b2','b3'],
 ['c1','c2','c3']
]

And also an array like this

[
 ['a1','b1','c1'],
 ['a2','b2','c2'],
 ['a3','b3','c3']
]

File Upload in WebdriverIO where we need to access the file explorer in Windows

This is a field where either I have to access the file explorer in Windows and then select the file to upload or, I have to drag and drop the file to upload the file. I am using the following code in WebdriverIO, but the file upload in not happening.

 const fileUpload = $("//div[@id='customer_information']//div[@aria-hidden='true']");
 const path = require('path');
 const filePath = path.join(__dirname, '../../Test Data/Captute0.png');
 //const filePath = path.join(__dirname, 'F:\WebDriverIO\Capture.png');
 fileUpload.setValue(filePath);

How to import JS in React.js

I downloaded a template and put all the CSS in a folder and imported it into the App.js file, and now I want to import the JavaScript files, but it gives an error, I even used / * global jQuery * / and put it in the index.js file. I also imported js, but it still gives an error

Is it possible to import JS file at all? If so, thank you for your reply

Error image:
See the error image

What should I use for the first parameter of the .apply() method when calling it within an XMLHttpRequest?

I’m trying to write a function which will act on an array of data passed into it using AJAX. It’s not doing anything, and the last thing left that I can find that I might be doing wrong is the first parameter of my .apply() call (i.e. the owner of my underlyingFunction() method).

Here’s my code:

function XMLfunction(targetElement,param1,param2) {
    var workingArray = new Array(targetElement);
    var request;
    if (window.XMLHttpRequest) {
        request = new XMLHttpRequest();
    } else {
        request = new ActiveXObject("Microsoft.XMLHTTP");
    }
    request.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            var parser = new DOMParser();
            var xml = parser.parseFromString(this.responseText,"text/xml");
            for (var i = 0; i < xml.childNodes[0].childNodes.length; i++) {
                workingArray.push(xml.childNodes[0].childNodes[i].innerHTML);
            }
            alert (workingArray); // workingArray looks exactly like it's supposed to
            underlyingFunction().apply(this,workingArray);  // underlyingFunction() works perfectly when called directly in a script
            alert ("applied"); // XMLfunction() stops before it gets to this line
        }
    }
    request.open("GET","myURL.php?param1="+param1+"&param2="+param2,true);
    request.send();
}

The first parameter of .apply() here is this, but I have tried using document and, out of desperation, window as well. What should it be? Or what else am I doing wrong?

Dynamic select option is not working anymore after change to new firebase project

This is a follow-up question from a question that I asked and answered before:

https://stackoverflow.com/a/69762698/16136444

In summary, if I pick a choice on the 1st v-select, it will only display limited choices on the 2nd v-select, which are related to the first choice that I made on the 1st v-select. Then, after choices are made on the 2nd v-select, it will auto-generate and display the code assigned to that 2nd choice on the 3rd v-select.

Before it was successfully done on my testing firebase project and it works and it still is. But after I created a new firebase project and do the same thing, it did not work out.

  1. Old firestore collection on my testing project
  • Scope of Works > All docs contain scope value > Specialization > docs containing specialization & code

old firestore on my testing project 1
old firestore on my testing project 2

  1. New firestore collection created for production project
    new firestore on my production project 1
    new firestore on my production project 2

So, the only thing that is changed is I removed spacing & use PascalCase for the name of the collection and documents. Other than it is all just the same. So now I used .collection("ScopeOfWork") (new) instead of .collection("Scope of Works")(old) to read from firestore

What could possibly be wrong with it? Any advice?

Thank you :))