Design: ssir coder

There are secure message usage in ateditors WebRTC sync. So design the simply encryption wrap and unwrap program for deliver and receive in secure.

The encryption algorithm use PBKDF2 key material to derive AES-GCM key. Then encrypt the secure message and encode use hex or base64 for deliver and receive.

The ArrayBuffer format of coder:
AAAA BBBB CCCC DDDD EEEE

AAAA: 32 bytes random for padding.
BBBB CCCC: double 32 bytes random AES iv.
DDDD: 32 bytes random PBKDF2 salt.
EEEE: AES encryption data of secure message.

The wrap implement process is:

Step Action

1    use crypto.getRandomValues generate fixed size PBKDF2 salt
2    set hash algorithm to fixed SHA-512
3    use crypto.subtle.digest salt with fixed hash algorithm generate PBKDF2 keyData
4    use crypto.subtle.importKey with PBKDF2 keyData generate PBKDF2 key material
5    set iterations to fixed enough big value
6    set AesDerivedKeyParams length to fixed 256
7    use crypto.subtle.deriveKey with PBKDF2 key material and salt/iterations/hash/length generate AES-GCM key
8    use crypto.getRandomValues generate fixed size AesGcmParams iv
9    use crypto.subtle.encrypt with key/iv encrypt secure message to cipher
a    use crypto.getRandomValues generate enough size Uint8Array to hold data
b    fulfilled the Uint8Array with iv/salt/cipher after skip padding data size

To unwrap the message is sample reversed process is:

Step Action

1    extract salt
2    set hash algorithm to fixed SHA-512
3    use crypto.subtle.digest salt with fixed hash algorithm generate PBKDF2 keyData
4    use crypto.subtle.importKey with PBKDF2 keyData generate PBKDF2 key material
5    set iterations to fixed enough big value
6    set AesDerivedKeyParams length to fixed 256
7    use crypto.subtle.deriveKey with PBKDF2 key material and salt/iterations/hash/length generate AES-GCM key
8    extract AesGcmParams iv
9    extract cipher
a    use crypto.subtle.decrypt with key/iv decrypt cipher to secure message

Implement sample code:

// The ArrayBuffer format of wrap coder:
// AAAA BBBB CCCC DDDD EEEE
// AAAA: 32 bytes random for padding.
// BBBB CCCC: double 32 bytes random AES iv.
// DDDD: 32 bytes random PBKDF2 salt.
// EEEE: AES encryption data of secure message.
export const wrapcoder = async (secureBuffer: ArrayBuffer) => {
  let wrap = new Uint8Array(keySize);
  if (secureBuffer) {
    const keySize = 32;
    const salt = crypto.getRandomValues(new Uint8Array(keySize));
    const hash = 'SHA-512';
    const keyData = await crypto.subtle.digest(hash, salt);
    const keyMaterial = await crypto.subtle.importKey(
      "raw",
      keyData,
      {
        name: "PBKDF2"
      },
      false,
      [ "deriveKey" ]
    );
    const iterations = 225519;
    const keyLength = keySize * 8;
    const wrappingKey = await crypto.subtle.deriveKey(
      {
        "name": "PBKDF2",
        salt,
        iterations,
        hash
      },
      keyMaterial,
      {
        "name": "AES-GCM",
        "length": keyLength,
      },
      true,
      [ "encrypt" ]
    );
    const iv = crypto.getRandomValues(new Uint8Array(keySize * 2));
    const cipher = await crypto.subtle.encrypt(
      {
        name: "AES-GCM",
        iv
      },
      wrappingKey,
      secureBuffer
    );
		// AAAA BBBB CCCC DDDD EEEE(dynamic length)
    wrap = crypto.getRandomValues(new Uint8Array(keySize * 4 + cipher.byteLength));
		// BBBB CCCC: double 32 bytes random AES iv.
    wrap.set(iv, keySize);
		// DDDD: 32 bytes random PBKDF2 salt.
    wrap.set(salt, keySize * 3);
		// EEEE: AES encryption data of secure message.
    wrap.set(new Uint8Array(cipher), keySize * 4);
  }
  return wrap;
}

export const unwrapcoder = async (wrappedBuffer: ArrayBuffer) => {
  const buffer = new Uint8Array(wrappedBuffer);
  const keySize = 32;
  const salt = buffer.subarray(keySize * 3, keySize * 4);
  const hash = 'SHA-512';
  const keyData = await crypto.subtle.digest(hash, salt);
  const keyMaterial = await crypto.subtle.importKey(
    "raw",
    keyData,
    {
      name: "PBKDF2"
    },
    false,
    [ "deriveBits", "deriveKey" ]
  );
  const iterations = 225519;
  const keyLength = keySize * 8;
  const unwrappingKey = await crypto.subtle.deriveKey(
    {
      "name": "PBKDF2",
      salt,
      iterations,
      hash
    },
    keyMaterial,
    {
      "name": "AES-GCM",
      "length": keyLength,
    },
    true,
    [ "decrypt" ]
  );
  const iv = buffer.subarray(keySize, keySize * 3);
  const cipher = buffer.subarray(keySize * 4);
  const secureBuffer = await crypto.subtle.decrypt(
    {
      name: "AES-GCM",
      iv
    },
    unwrappingKey,
    cipher
  );
	return secureBuffer;
}

And the sample coder message can used for ateditors sync function like:

eyJpY2UiOnsidXJsIjoic3R1bjphbmZsZXh0LmNvbTo1MTIxNSJ9LCJsYWJlbCI6InRlc3QiLCJzaWduYWxpbmciOnsiZ3JvdXAiOiJmaXJlZm94IiwidXJsIjoid3NzOi8vYW5mbGV4dC5jb206NTEyMTcifSwic3NpciI6IjdCT09PWXNUN0VvTE4tY05VSUlZdTZUcFNOQ3ZWVFZtZ2dCSE5DeVY0Tjd6dE1VOXJ4TlJYeWl2LUJtZTlBMzg3WHljYUNSU1d0RWRKNXRBUlY3UFkxZkhEYlV3UW9sNVBLZXg0WHo0anZ4QnRxRGNmSlFDVEh2RnhDWmprdGc0bk9qWGhOdkxjN252cjIwdWtFeFBhWkFhVFhsYjFpVGlLV0RCekZZQzZJTXVHVTVQOXo0MVdaSF83SVZLRFZjWkJfalhDRjVzMmxIQnNDNEZRVUxLbjcyTDVGTmJjZlRGUmxhalFDc0o1eVBtM2pBSnVLTHAtVERLM0UyQmh3by1jUUN5UWd5ME1UdDIxN0YwWW16T3RmWEd4SlgxcC1GTW1CcmctcEFXbFp1ZnBnQld4c0hVUmZSeXAtNTZuMmN0VXl6Z2lrQkZRQ0dhQnUxc0ZEZnowaHVyd0EifQ

Yon can fill the coder in ateditors sync option code field and press Apply coder button to fill option fields of sync.

Then press Connect signaling button. Normally after connected signaling server you can call the group member to communicate by WebRTC.

37

Top articles