DEV Community

foxgem
foxgem

Posted on

Quick notes on cryptography for js devs

Principles of Encryption API Selection

  • The standard library first.
  • Prefer well-known and audited libraries.
  • Before choosing a third-party library, do some investigation first.

Basics

  • Encode / Decode: Buffer Class.
  • Random numbers
    • The basis of cryptography.
    • It is extremely difficult to obtain true random numbers, commonly, pseudo-random numbers are used.
    • Don't use Math.random!
    • Use the random functions in the crypto module, e.g. crypto.randomBytes.
  • Hash
    • Irreversible.
    • The output is a fixed-length string.
    • Same input, same output.
    • Different inputs, different outputs.
    • Typical applications
      • Data integrity verification
      • Passphrase hashing
      • Derived Keys
  • Key
    • Derived Key: A key generated from a master key.
    • Key Encapsulation: encrypt and export the key.
    • Key Decapsulation: decrypts and imports the key
  • Authenticated Encryption: encryption and tamper-proof
  • Symmetric Encryption
    • Use the same key for encryption and decryption
    • Fast
    • Suitable for large data blocks
    • Key exchange is difficult
  • Asymmetric encryption
    • Public key + Private key
      • Encryption: public key encryption, private key decryption
      • Signing: private key encryption, public key decryption
    • Slow
    • Not suitable for large data blocks
    • Typical applications
      • Key exchange
      • Digital certificates
      • Digital signatures
  • Hybrid encryption
    • Combination of the above two
      • Asymmetric encryption for key exchange
      • Symmetric encryption for data manipulation
  • Encryption modes
    • Block encryption, grouping before encryption.
      • Never use ECB mode.
    • Stream encryption, dealing with continuous or unknown length byte streams, advantageous when the plaintext is small.

Hash

  • Don't use:
    • MD5
    • SHA-1
    • PBKDF2
    • bcrypt
  • Message digest: SHA-256
    • crypto.createHash('sha256')
    • Use stream mode for large data
    • Do not use for sensitive data such as passphrases
  • Message Authentication Code (HMAC)
    • hash and key (key to be known by both parties)
    • crypto.createHmac('sha256', 'secret')
  • Password hashing and KDF (Key Derivation Function)
    • Features.
      • salt built-in
      • Deliberated slow algorithms to offset the computational power boost caused by newer hardware.
    • argon2 (recommended)
    • scrypt
      • crypto.scrypt

Symmetric Encryption

  • Don't use:
    • DES, including 3DES.
    • Blowfish
      • TWofish is its successor, but not popular.
    • AES-ECB
      • No ECB mode, please.
  • AES
    • Common modes.
      • AES-CBC, when authentication is not required
        • crypto.createCipheriv('aes-256-cbc', ...)
        • crypto.createDecipheriv('aes-256-cbc', ...)
      • AES-GCM, when authentication is required
        • crypto.createCipheriv('aes-256-gcm', ...)
        • crypto.createDecipheriv('aes-256-gcm', ...)
    • For big data use stream mode
  • ChaCha20-Poly1305
    • ChaCha20 encryption + Poly1305 hashing
    • Equivalent to AES-GCM
    • Suitable for environments without AES hardware acceleration support
    • crypto.createCipheriv('chacha20-poly1305', ...)
    • crypto.createDecipheriv('chacha20-poly1305', ...)

Asymmetric Encryption

  • ASN.1 Data Structure
  • Key encoding format: PEM format

    • Public key: SPKI

      // import
      crypto.createPrivateKey(fs.readFileSync("public.pem"));
      
    • Private key.

      • PKCS #1 (old, RSA only)
      • PKCS #8 (new, for multiple algorithms)
      // import
      crypto.createPrivateKey(fs.readFileSync("private.pem"));
      
    • Export: crypto.KeyObject.export(option)

  • RSA

    • key
      • crypto.generateKeyPair('rsa', ...)
    • encryption and decryption

      • padding: add random data to the plaintext before encryption to improve the security of RSA.
        • Always used if no special reasons.
        • crypto.constants.RSA_PKCS1_OAEP_PADDING (recommended)
        • crypto.constants.RSA_PKCS1_PADDING (not recommended)
      • encryption

        crypto.publicEncrypt(
            {
                key: publicKey,
                padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
                oaepHash: "sha256",
            },
            plaintext
        );
        
      • decryption

        crypto.privateDecrypt(
            {
                key: privateKey,
                padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
                oaepHash: "sha256",
            },
            message
        );
        
    • Digital signature

      • padding:
        • crypto.constants.RSA_PKCS1_PADDING (default)
        • crypto.constants.RSA_PKCS1_PSS_PADDING (new version, recommended)
      • signing
      crypto.sign("sha256", message, {
          key: privateKey,
          padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
      });
      
      • verification
      crypto.verify(
          "sha256",
          message,
          {
              key: publicKey,
              padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
          },
          signature
      );
      
  • Elliptic Curve

    • Equivalent security to RSA
    • Shorter keys, faster and cheaper
    • Unlike RSA, its key pair does not support asymmetric encryption, i.e.: the public key decrypts the message encrypted by the private key, or vice versa.
    • Commonly used curves
      • P-256: secp256r1 / prime256v1, NIST standard
      • Curve25519
        • X25519 for key exchange
        • Ed25519 for digital signatures
      • secp256k1, used by Bitcoin
    • Typical Applications
      • Key exchange (ECDH)
      • Digital signatures
    • key
      • crypto.generateKeyPair('ec', ...)
    • ECDH
      • Unlike RSA (where party A encrypts the key with party B's public key and then sends it to party B to decrypt).
      • DH protocol
        • Alice and Bob each generate a key pair
        • They exchange the public keys
        • Combine their private keys to generate a shared key, i.e.
          • shareSecret(A public key, B private key) = shareSecret(B public key, A private key)
      • crypto.diffieHellman({A public key, B private key})
      • A reference implementation of shared keys.
        • crypto.diffieHellman gets shareSecret
        • Generate a random salt
        • sha256(shareSecret, salt) is the shared key, note: remember to pass the salt to the other side with the ciphertext.
    • Digital signatures
      • ECDSA (using prime256v1)
        • crypto.sign('sha256', message, privateKey)
        • crypto.verify('sha256', message, publicKey, signature)
      • EdDSA (using Ed25519), which does not require an externally configured hash function as it already has a built-in digital signature algorithm.
        • crypto.sign(null, message, privateKey)
        • crypto.verify(null, message, publicKey, signature)

Web Crypto

  • Used in the browser environment
  • Encoding
    • ArrayBuffer
    • atob and btoa
    • TextEncoder
  • Random numbers.
    • Window.crypto.getRandomValues
    • Window.crypto.randomUUID
  • keys
    • window.crypto.subtle.generateKey(algorithm, extractable, usages)
    • window.crypto.subtle.importKey(format, data, algorithm, extractable, usages)
    • window.crypto.subtle.exportKey(format, key)
    • window.crypto.subtle.wrapKey
    • window.crypto.subtle.unwrapKey
    • window.crypto.subtle.deriveKey
  • Hash
  • Encryption and decryption (both symmetric and asymmetric)
    • crypto.subtle.encrypt(algorithm, key, data)
    • crypto.subtle.decrypt(algorithm, key, data)
  • Digital signatures
    • window.crypto.subtle.sign
    • window.crypto.subtle.verify

References

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.