Auth0 Internal Cryptography Toolkit
Clone or download

README.md

magic

Build Status

A lightweight wrapper around the crypto interface to OpenSSL and the libsodium library to provide a standard cryptography API for internal use, consistent with best current practices recommended by the product security team at Auth0. Named not for what it is intended to do, but for what it is intended to prevent.

All public functions support both callbacks and promises (and therefore async/await), allowing easy integration into any preexisting codebase. All constructions requiring secret keys will generate them as necessary if they are not supplied, and return them for future use.

Install

npm install auth0-magic

Usage

core api

The core api implements the recommended algorithms for each cryptographic operation. When in doubt, use them.

magic.auth.sign | magic.verify.sign

Implements ed25519 signatures using libsodium.js. Efficient and without some of the concerns inherent in ECDSA, ed25519 has been accepted and standardized by the IETF. By default, the api expects to be given a secret key as a seed, from which the actual keypair is derived (allowing easier, more concise storage). However, it may be used directly with a keypair, requiring only a boolean flag for the verify call.

// seed generation

// callback
magic.auth.sign(message, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:       'ed25519',
  //   sk:        <Buffer af b4 b8 a8 2f 59 cb  ... >,
  //   payload:   <Buffer 41 20 73 63 72 65 61  ... >,
  //   signature: <Buffer e5 b7 ce 0e 92 71 0c  ... > }
});

// promise
magic.auth.sign(message)
  .then((output) => {
    console.log(output);
    // { alg:       'ed25519',
    //   sk:        <Buffer af b4 b8 a8 2f 59 cb  ... >,
    //   payload:   <Buffer 41 20 73 63 72 65 61  ... >,
    //   signature: <Buffer e5 b7 ce 0e 92 71 0c  ... > }
  })
  .catch((err) => {
    return reject(err);
  });
});

// supplied seed
const seed = '0d05d0...';

// callback
magic.auth.sign(message, seed, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:       'ed25519',
  //   sk:        <Buffer 0d 05 d0 99 d3 2d 00  ... >,
  //   payload:   <Buffer 41 20 73 63 72 65 61  ... >,
  //   signature: <Buffer 54 4a d1 ab a9 c7 19  ... > }
});

// promise
magic.auth.sign(message, seed)
  .then((output) => {
    console.log(output);
    // { alg:       'ed25519',
    //   sk:        <Buffer 0d 05 d0 99 d3 2d 00  ... >,
    //   payload:   <Buffer 41 20 73 63 72 65 61  ... >,
    //   signature: <Buffer 54 4a d1 ab a9 c7 19  ... > }
  })
  .catch((err) => {
    return reject(err);
  });
});

// supplied key
const sk = 'bf288a...';

// callback
magic.auth.sign(message, sk, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:       'ed25519',
  //   sk:        <Buffer bf 28 8a 58 28 36 37  ... >,
  //   payload:   <Buffer 41 20 73 63 72 65 61  ... >,
  //   signature: <Buffer b9 ca 8e 69 12 34 35  ... > }
});

// promise
magic.auth.sign(message, sk)
  .then((output) => {
    console.log(output);
   // { alg:       'ed25519',
   //   sk:        <Buffer bf 28 8a 58 28 36 37  ... >,
   //   payload:   <Buffer 41 20 73 63 72 65 61  ... >,
   //   signature: <Buffer b9 ca 8e 69 12 34 35  ... > }
  })
  .catch((err) => {
    return reject(err);
  });
});

Verification has a very similar interface, requiring only the additional flag if the public key is presented directly, and returning without error if the signature is valid.

// supplied seed
const seed      = '0d05d0...';
const signature = '544ad1...';

// callback
magic.verify.sign(message, seed, signature, (err) => {
  if (err) { return cb(err); }
  console.log('verified');
  // verified
});

// promise
magic.verify.sign(message, seed, signature)
  .then(() => {
    console.log('verified');
    // verified
  })
  .catch((err) => {
    return reject(err);
  });
});

// supplied key
const pk        = 'bf288a...';
const signature = 'b9ca8e...';

// callback
magic.verify.sign(message, pk, signature, true, (err) => {
  if (err) { return cb(err); }
  console.log('verified');
  // verified
});

// promise
magic.verify.sign(message, pk, signature, true)
  .then(() => {
    console.log('verified');
    // verified
  })
  .catch((err) => {
    return reject(err);
  });
});

magic.auth.mac | magic.verify.mac

Implements HMAC-SHA384 using OpenSSL through crypto. The HMAC algorithm is the most common message authentication code construction, standardized by the IETF and NIST. The choice of SHA384 is due to its widespread availability and to provide a consistent hash function throughout magic, as SHA256 may be susceptible to length extension attacks in certain situations. Both HMAC-SHA256 and HMAC-SHA512 are available in the alternative api.

// key generation

// callback
magic.auth.mac(message, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:     'hmacsha384',
  //   sk:      <Buffer 97 9b 18 78 50 6f bf  ... >,
  //   payload: <Buffer 41 20 73 63 72 65 61  ... >,
  //   mac:     <Buffer 2d 15 ab 58 08 9a d7  ... > }
});

// promise
magic.auth.mac(message)
  .then((output) => {
    console.log(output);
    // { alg:     'hmacsha384',
    //   sk:      <Buffer 97 9b 18 78 50 6f bf  ... >,
    //   payload: <Buffer 41 20 73 63 72 65 61  ... >,
    //   mac:     <Buffer 2d 15 ab 58 08 9a d7  ... > }
  })
  .catch((err) => {
    return reject(err);
  });
});

// supplied key
const key = '49d013...';

// callback
magic.auth.mac(message, key, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:     'hmacsha384',
  //   sk:      <Buffer 49 d0 13 6e 72 15 f4  ... >,
  //   payload: <Buffer 41 20 73 63 72 65 61  ... >,
  //   mac:     <Buffer f1 9d c0 5a ae 8a f1  ... > }
});

// promise
magic.auth.mac(message, key)
  .then((output) => {
    console.log(output);
    // { alg:     'hmacsha384',
    //   sk:      <Buffer 49 d0 13 6e 72 15 f4  ... >,
    //   payload: <Buffer 41 20 73 63 72 65 61  ... >,
    //   mac:     <Buffer f1 9d c0 5a ae 8a f1  ... > }
  })
  .catch((err) => {
    return reject(err);
  });
});

Once again verification has a similar interface, and returns without error if the mac is valid.

// supplied key
const key = '49d013...';
const mac = 'f19dc0...';

// callback
magic.verify.mac(message, key, mac, (err) => {
  if (err) { return cb(err); }
  console.log('verified');
  // verified
});

// promise
magic.verify.mac(message, key, mac)
  .then(() => {
    console.log('verified');
    // verified
  })
  .catch((err) => {
    return reject(err);
  });
});

magic.encrypt.pki | magic.decrypt.pki

Implements x25519 static Diffie-Hellman key exchange, and employs the resultant shared secret for xsalsa20poly1305 authenticated encryption using libsodium.js. This allows for an efficient, simple symmetric authenticated encryption scheme to be used in an asymmetric setting. A very closely related symmetric authenticated encryption scheme (using ChaCha20-Poly1305) has been standardized by the IETF. As a static Diffie-Hellman exchange, the API is slightly different than most asymmetric encryption schemes - for encryption both the recipient public key and sender private key are required, whereas for decryption the recipient private key and sender public key are required. Usually, only the keys of the recipient are required for encryption, though x25519-xsalsa20poly1305 has the benefit of being an authenticated scheme as well.

// key generation

// callback
magic.encrypt.pki(message, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:        'x25519-xsalsa20poly1305',
  //   sk:         <Buffer d7 d5 dd 2c 2a eb f1 ... >,
  //   pk:         <Buffer d2 b2 e2 05 7a 2a ab ... >,
  //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
  //   nonce:      <Buffer b3 4f 59 af 96 e4 4c ... >,
  //   ciphertext: <Buffer 3c 3d 0e 8b c6 34 83 ... > }
});

// promise
magic.encrypt.pki(message)
  .then((output) => {
    console.log(output);
    // { alg:        'x25519-xsalsa20poly1305',
    //   sk:         <Buffer d7 d5 dd 2c 2a eb f1 ... >,
    //   pk:         <Buffer d2 b2 e2 05 7a 2a ab ... >,
    //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
    //   nonce:      <Buffer b3 4f 59 af 96 e4 4c ... >,
    //   ciphertext: <Buffer 3c 3d 0e 8b c6 34 83 ... > }
  }).catch((err) => {
    return reject(err);
  });
});

// supplied key
const sk = 'd7d5dd...';
const pk = 'd2b2e2...';

// callback
magic.encrypt.pki(message, sk, pk, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:        'x25519-xsalsa20poly1305',
  //   sk:         <Buffer d7 d5 dd 2c 2a eb f1 ... >,
  //   pk:         <Buffer d2 b2 e2 05 7a 2a ab ... >,
  //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
  //   nonce:      <Buffer b3 4f 59 af 96 e4 4c ... >,
  //   ciphertext: <Buffer 3c 3d 0e 8b c6 34 83 ... > }
});

// promise
magic.encrypt.pki(message, sk, pk)
  .then((output) => {
    console.log(output);
    // { alg:        'x25519-xsalsa20poly1305',
    //   sk:         <Buffer d7 d5 dd 2c 2a eb f1 ... >,
    //   pk:         <Buffer d2 b2 e2 05 7a 2a ab ... >,
    //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
    //   nonce:      <Buffer b3 4f 59 af 96 e4 4c ... >,
    //   ciphertext: <Buffer 3c 3d 0e 8b c6 34 83 ... > }
  }).catch((err) => {
    return reject(err);
  });
});

Decryption then returns the plaintext directly, without the metadata.

const sk = 'e5e5c6...';
const pk = 'fea66a...';

// callback
magic.decrypt.pki(sk, pk, ciphertext, nonce, (err, plaintext) => {
  if (err) { return cb(err); }
  console.log(plaintext);
  // <Buffer 41 20 73 63 72 65 61 ... >
});

// promise
magic.decrypt.pki(sk, pk, ciphertext, nonce)
  .then((plaintext) => {
    console.log(plaintext);
    // <Buffer 41 20 73 63 72 65 61 ... >
  }).catch((err) => {
    return reject(err);
  });
});

magic.encrypt.aead | magic.decrypt.aead

Implements xsalsa20poly1305 authenticated encryption using libsodium.js. A very closely related symmetric authenticated encryption scheme (using ChaCha20-Poly1305) has been standardized by the IETF. The scheme is fast, simple, and as an AEAD construction provides each of confidentiality, authentication, and integrity on the message.

// key generation

// callback
magic.encrypt.aead(message, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:        'xsalsa20poly1305',
  //   sk:         <Buffer d7 d5 dd 2c 2a eb f1 ... >,
  //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
  //   nonce:      <Buffer b3 4f 59 af 96 e4 4c ... >,
  //   ciphertext: <Buffer 3c 3d 0e 8b c6 34 83 ... > }
});

// promise
magic.encrypt.aead(message)
  .then((output) => {
    console.log(output);
    // { alg:        'xsalsa20poly1305',
    //   sk:         <Buffer d7 d5 dd 2c 2a eb f1 ... >,
    //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
    //   nonce:      <Buffer b3 4f 59 af 96 e4 4c ... >,
    //   ciphertext: <Buffer 3c 3d 0e 8b c6 34 83 ... > }
  }).catch((err) => {
    return reject(err);
  });
});

// supplied key
const sk = 'd7d5dd...';

// callback
magic.encrypt.aead(message, sk, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:        'xsalsa20poly1305',
  //   sk:         <Buffer d7 d5 dd 2c 2a eb f1 ... >,
  //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
  //   nonce:      <Buffer b3 4f 59 af 96 e4 4c ... >,
  //   ciphertext: <Buffer 3c 3d 0e 8b c6 34 83 ... > }
});

// promise
magic.encrypt.aead(message, sk)
  .then((output) => {
    console.log(output);
    // { alg:        'xsalsa20poly1305',
    //   sk:         <Buffer d7 d5 dd 2c 2a eb f1 ... >,
    //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
    //   nonce:      <Buffer b3 4f 59 af 96 e4 4c ... >,
    //   ciphertext: <Buffer 3c 3d 0e 8b c6 34 83 ... > }
  }).catch((err) => {
    return reject(err);
  });
});

Decryption then returns the plaintext directly, without the metadata.

const sk = 'e5e5c6...';

// callback
magic.decrypt.aead(sk, ciphertext, nonce, (err, plaintext) => {
  if (err) { return cb(err); }
  console.log(plaintext);
  // <Buffer 41 20 73 63 72 65 61 ... >
});

// promise
magic.decrypt.aead(sk, ciphertext, nonce)
  .then((plaintext) => {
    console.log(plaintext);
    // <Buffer 41 20 73 63 72 65 61 ... >
  }).catch((err) => {
    return reject(err);
  });
});

magic.util.hash

Implements SHA2-384 (henceforth just SHA384) using OpenSSL through crypto. Unlike SHA256 and SHA512 - which are available through the alternative api - SHA384 is resistant to length extension attacks, a capability which may be relevant in some circumstances. The SHA2 family is standardized by NIST, and the most commonly used fast, cryptographically secure hash function.

// callback
magic.util.hash(message, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:     'sha384',
  //   payload: <Buffer 41 20 73 63 72 65 61  ... >,
  //   hash:    <Buffer 15 0b f9 4d e3 2b 5a  ... > }
});

// promise
magic.util.hash(message)
  .then((output) => {
    console.log(output);
    // { alg:     'sha384',
    //   payload: <Buffer 41 20 73 63 72 65 61  ... >,
    //   hash:    <Buffer 15 0b f9 4d e3 2b 5a  ... > }
  })
  .catch((err) => {
    return reject(err);
  });
});

magic.password.hash | magic.verify.password

Implements argon2id password hashing using libsodium.js. The winner of the Password Hashing Competition and now the OWASP recommendation, argon2id is robust against both memory tradeoff and side-channel attacks. The output of the argon2id function is encoded with a prefix and other metadata, and so output.hash is encoded as a string, not a raw binary buffer as is normal for the rest of the magic api. Nor is the raw password itself returned.

const pw = 'ascream...';

// callback
magic.password.hash(password, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:  'argon2id',
  //   hash: '$argon2id$v=19$m=65536,t=2,p=1$yLZ6CoF5exPHbHjvbZ3esQ$yAM5pHM9KnTYDg/9Nr9rgDdQqRpAe8JVky4mJ7escHM' }
});

// promise
magic.password.hash(password)
  .then((output) => {
    console.log(output);
    // { alg:  'argon2id',
    //   hash: '$argon2id$v=19$m=65536,t=2,p=1$yLZ6CoF5exPHbHjvbZ3esQ$yAM5pHM9KnTYDg/9Nr9rgDdQqRpAe8JVky4mJ7escHM' }
  })
  .catch((err) => {
    return reject(err);
  });
});

Due to the metadata in the hash output, it must be provided in the same encoded format for verification.

const pw   = 'ascream...';
const hash = '$argon2id$v=19$m=65536,t=2,p=1$yLZ6CoF5exPHbHjvbZ3esQ$yAM5pHM9KnTYDg/9Nr9rgDdQqRpAe8JVky4mJ7escHM';

// callback
magic.verify.password(password, hash, (err) => {
  if (err) { return cb(err); }
  console.log('verified');
  // verified
});

// promise
magic.verify.password(password, hash)
  .then(() => {
    console.log('verified');
    // verified
  })
  .catch((err) => {
    return reject(err);
  });
});

magic.util.rand

Employs OpenSSL through crypto to return the requested number of random bytes, generated in a cryptographically secure manner.

// callback
magic.util.rand(length, (err, bytes) => {
  if (err) { return done(err); }
  console.log(bytes);
  // <Buffer d3 12 78 83 3a f3 32 ... >
});

// promise
magic.util.rand(length)
  .then((bytes) => {
    console.log(bytes);
    // <Buffer d3 12 78 83 3a f3 32 ... >
  })
  .catch((err) => {
    return reject(err);
});

magic.util.uid

Employs OpenSSL through crypto to return a base64url encoded uid. The input is not the length of the returned uid, but rather a security parameter taken as the unencoded byte length of the identifer. The returned string will be roughly a third longer than it. The default security parameter (if one is not provided) is 32 bytes, returning a uid of 43 chars.

// default security parameter

// callback
magic.util.uid((err, uid) => {
  if (err) { return done(err); }
  console.log(uid);
  // 74iUE8utrO4vuR9MvdeEAZ2eVAMFch02P81uN-tlvIk
});

// promise
magic.util.uid()
  .then((uid) => {
    console.log(uid);
    // 74iUE8utrO4vuR9MvdeEAZ2eVAMFch02P81uN-tlvIk
  })
  .catch((err) => {
    return reject(err);
});

// provided security parameter length

// callback
magic.util.uid(24, (err, uid) => {
  if (err) { return done(err); }
  console.log(uid);
  // Md7Al-OnKydNF-ZsE5WBdgGVCcVIEcGu
});

// promise
magic.util.uid(24)
  .then((uid) => {
    console.log(uid);
    // Md7Al-OnKydNF-ZsE5WBdgGVCcVIEcGu
  })
  .catch((err) => {
    return reject(err);
});

magic.EncryptStream || magic.DecryptStream

Implements xchacha20poly1305 authenticated encryption using libsodium.js. The ChaCha20-Poly1305 symmetric authenticated encryption scheme been standardized by the IETF. The scheme is fast, simple, and as an AEAD construction provides confidentiality, authentication, and integrity on the message.

  // key generation

  const readStream = fs.createReadStream('./plaintext.txt');
  const writeStream = fs.createWriteStream('./ciphertext.txt');
  const encryptStream = new magic.EncryptStream()
  readStream
    .pipe(encryptStream)
    .pipe(writeStream)
    .on('finish', function() {
      console.log('encrypted file written')
    })

The generated key is found in encryptStream.key.

  let key = 'a0c4..' // supplied key; 32-bytes hex encoded string or 32-bytes Buffer

  const readStream = fs.createReadStream('./plaintext.txt');
  const writeStream = fs.createWriteStream('./ciphertext.txt');
  const encryptStream = new magic.EncryptStream(key)
  readStream
    .pipe(encryptStream)
    .pipe(writeStream)
    .on('finish', function() {
      console.log('encrypted file written')
    })

For decryption, the encryptStream.key should be passed to magic.DecryptStream.

  const decryptStream = new magic.DecryptStream(encryptStream.key)
  readStream
    .pipe(decryptStream)
    .pipe(writeStream)
    .on('finish', function() {
      console.log('decrypted file written')
    })

alt api

The alt api implements alternative algorithms for each cryptographic operation. They should only be used over the core api when required by an external specification or interoperability concerns.

magic.alt.auth.RSASSA_PSS_SHA{256,384,512} | magic.alt.verify.RSASSA_PSS_SHA{256,384,512}

Implements RSA PKCS#1 v2.1 over SHA2, better known as RSAPSS-SHA. Available with each of the SHA256, SHA384, or SHA512 variants of SHA2. The protocol is standardized by the IETF. The PSS acronym stands for probablistic signature scheme, a construction which is theoretically more robust than the older RSA PKCS#1 v1.5 protocol also available in the alternative api. When possible, this is the preferred RSA variant, although the ed25519 signature scheme in the core api is preferred above any use of RSA at all. For key generation, the key (private only) is returned in PEM encoding.

// key generation

// callback
magic.alt.auth.RSASSA_PSS_SHA256(message, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:       'rsapss-sha256',
  //   sk:        '-----BEGIN RSA PRIVATE KEY-----\nMIIEp ... NZ3Yw==\n-----END RSA PRIVATE KEY-----',
  //   payload:   <Buffer 41 20 73 63 72 65 61 ... >,
  //   signature: <Buffer 86 a8 d2 d7 67 01 8a ... > }
});

// promise
magic.alt.auth.RSASSA_PSS_SHA256(message)
  .then((output) => {
    console.log(output);
    // { alg:       'rsapss-sha256',
    //   sk:        '-----BEGIN RSA PRIVATE KEY-----\nMIIEp ... NZ3Yw==\n-----END RSA PRIVATE KEY-----',
    //   payload:   <Buffer 41 20 73 63 72 65 61 ... >,
    //   signature: <Buffer 86 a8 d2 d7 67 01 8a ... > }
  })
  .catch((err) => {
    return reject(err);
  });
});

// supplied key
const sk = '-----BEGIN RSA PRIVATE KEY-----\nMIIEp ... NZ3Yw==\n-----END RSA PRIVATE KEY-----';

// callback
magic.alt.auth.RSASSA_PSS_SHA256(message, sk, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:       'rsapss-sha256',
  //   sk:        '-----BEGIN RSA PRIVATE KEY-----\nMIIEp ... NZ3Yw==\n-----END RSA PRIVATE KEY-----',
  //   payload:   <Buffer 41 20 73 63 72 65 61 ... >,
  //   signature: <Buffer 86 a8 d2 d7 67 01 8a ... > }
});

// promise
magic.alt.auth.RSASSA_PSS_SHA256(message, sk)
  .then((output) => {
    console.log(output);
    // { alg:       'rsapss-sha256',
    //   sk:        '-----BEGIN RSA PRIVATE KEY-----\nMIIEp ... NZ3Yw==\n-----END RSA PRIVATE KEY-----',
    //   payload:   <Buffer 41 20 73 63 72 65 61 ... >,
    //   signature: <Buffer 86 a8 d2 d7 67 01 8a ... > }
  })
  .catch((err) => {
    return reject(err);
  });
});

Verification can be done by supplying either a private key (from which the public key will be extracted) or the public key itself.

// supplied private key
const sk = '-----BEGIN RSA PRIVATE KEY-----\nMIIEp ... NZ3Yw==\n-----END RSA PRIVATE KEY-----';

// callback
magic.alt.verify.RSASSA_PSS_SHA256(message, sk, signature, (err) => {
  if (err) { return cb(err); }
  console.log('verified');
  // verified
});

// promise
magic.alt.verify.RSASSA_PSS_SHA256(message, sk)
  .then(() => {
    console.log('verified');
    // verified
  })
  .catch((err) => {
    return reject(err);
  });
});

// supplied public key
const pk = '-----BEGIN RSA PUBLIC KEY-----\nMIIBI ... DAQAB\n-----END RSA PUBLIC KEY-----';

// callback
magic.alt.verify.RSASSA_PSS_SHA256(message, pk, signature, (err) => {
  if (err) { return cb(err); }
  console.log('verified');
  // verified
});

// promise
magic.alt.verify.RSASSA_PSS_SHA256(message, pk)
  .then(() => {
    console.log('verified');
    // verified
  })
  .catch((err) => {
    return reject(err);
  });
});

magic.alt.auth.RSASSA_PKCS1V1_5_SHA{256,384,512} | magic.alt.verify.RSASSA_PKCS1V1_5_SHA{256,384,512}

Implements RSA PKCS#1 v1.5 over SHA2, standardized by the IETF. Available with each of the SHA256, SHA384, or SHA512 variants of SHA2. An alternative to magic.alt.verify.RSASSA_PSS_SHA{256,384,512}.

magic.alt.auth.HMAC_SHA{256,512} | magic.alt.verify.HMAC_SHA{256,512}

Implements HMAC-SHA2 using OpenSSL through crypto. Available with each of the SHA256, SHA384 (as magic.auth.mac), or SHA512 variants of SHA2. An alterative to magic.auth.mac.

magic.alt.encrypt.AES_{128,192,256}_CBC_HMAC_SHA{256,384,512} | magic.alt.decrypt.AES_{128,192,256}_CBC_HMAC_SHA{256,384,512}

Implements AES{128,192,256}CBC-SHA2 using OpenSSL through crypto. Available with a large number of variants; any key size of AES - AES128, AES192, or AES256, and any digest size of SHA2 - SHA256, SHA384, or SHA512. The protocol is standardized by NIST and provides authenticated eencryption using the industry standard symmetric encryption and authentication schemes, in an encrypt-than-authenticate construction.

// key generation

// callback
magic.alt.encrypt.AES_128_CBC_HMAC_SHA256(message, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:        'aes128cbc-hmacsha256',
  //   sek:        <Buffer 61 d6 4b 6a 70 84 a0 ... >,
  //   sak:        <Buffer 03 ce db e3 d2 d4 17 ... >,
  //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
  //   iv:         <Buffer d4 84 14 29 ae a4 11 ... >,
  //   ciphertext: <Buffer 75 f8 cf 94 07 81 46 ... >,
  //   mac:        <Buffer a4 40 4b 6c 1b 7f d8 ... > }
});

// promise
magic.alt.encrypt.AES_128_CBC_HMAC_SHA256(message)
  .then((output) => {
    console.log(output);
    // { alg:        'aes128cbc-hmacsha256',
    //   sek:        <Buffer 61 d6 4b 6a 70 84 a0 ... >,
    //   sak:        <Buffer 03 ce db e3 d2 d4 17 ... >,
    //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
    //   iv:         <Buffer d4 84 14 29 ae a4 11 ... >,
    //   ciphertext: <Buffer 75 f8 cf 94 07 81 46 ... >,
    //   mac:        <Buffer a4 40 4b 6c 1b 7f d8 ... > }
  }).catch((err) => {
    return reject(err);
  });
});

// supplied key
const sek = '61d64b...';
const sak = '03cedb...';

// callback
magic.alt.encrypt.AES_128_CBC_HMAC_SHA256(message, sek, sak, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:        'aes128cbc-hmacsha256',
  //   sek:        <Buffer 61 d6 4b 6a 70 84 a0 ... >,
  //   sak:        <Buffer 03 ce db e3 d2 d4 17 ... >,
  //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
  //   iv:         <Buffer d4 84 14 29 ae a4 11 ... >,
  //   ciphertext: <Buffer 75 f8 cf 94 07 81 46 ... >,
  //   mac:        <Buffer a4 40 4b 6c 1b 7f d8 ... > }
});

// promise
magic.alt.encrypt.AES_128_CBC_HMAC_SHA256(message, sek, sak)
  .then((output) => {
    console.log(output);
    // { alg:        'aes128cbc-hmacsha256',
    //   sek:        <Buffer 61 d6 4b 6a 70 84 a0 ... >,
    //   sak:        <Buffer 03 ce db e3 d2 d4 17 ... >,
    //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
    //   iv:         <Buffer d4 84 14 29 ae a4 11 ... >,
    //   ciphertext: <Buffer 75 f8 cf 94 07 81 46 ... >,
    //   mac:        <Buffer a4 40 4b 6c 1b 7f d8 ... > }
  }).catch((err) => {
    return reject(err);
  });
});

Decryption then returns the plaintext directly, without the metadata.

const sek = '61d64b...';
const sak = '03cedb...';

// callback
magic.alt.decrypt.AES_128_CBC_HMAC_SHA256(sek, sak, iv, ciphertext, mac, (err, plaintext) => {
  if (err) { return cb(err); }
  console.log(plaintext);
  // <Buffer 41 20 73 63 72 65 61 ... >
});

// promise
magic.alt.decrypt.AES_128_CBC_HMAC_SHA256(sek, sak, iv, ciphertext, mac)
  .then((plaintext) => {
    console.log(plaintext);
    // <Buffer 41 20 73 63 72 65 61 ... >
  }).catch((err) => {
    return reject(err);
  });
});

magic.alt.encrypt.AES_{128,192,256}_GCM | magic.alt.decrypt.AES_{128,192,256}_GCM

Implements AES{128,192,256}GCM using OpenSSL through crypto. AES-GCM is standardized by NIST and provides authenticated encryption using the industry standard symmetric encryption scheme with an authenticated block cipher mode, a clean and simple construction.

// key generation

// callback
magic.alt.encrypt.AES_128_GCM(message, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:        'aes128gcm',
  //   sk:         <Buffer 6c 5e 93 6c a4 b8 43 ... >,
  //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
  //   iv:         <Buffer 8e 81 31 91 d2 a3 2c ... >,
  //   ciphertext: <Buffer 0a 37 d4 86 69 1e c9 ... >,
  //   tag:        <Buffer 72 69 d6 25 18 92 9d ... > }
});

// promise
magic.alt.encrypt.AES_128_GCM(message)
  .then((output) => {
    console.log(output);
    // { alg:        'aes128gcm',
    //   sk:         <Buffer 6c 5e 93 6c a4 b8 43 ... >,
    //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
    //   iv:         <Buffer 8e 81 31 91 d2 a3 2c ... >,
    //   ciphertext: <Buffer 0a 37 d4 86 69 1e c9 ... >,
    //   tag:        <Buffer 72 69 d6 25 18 92 9d ... > }
  }).catch((err) => {
    return reject(err);
  });
});

// supplied key
const sk = '6c5e93...';

// callback
magic.alt.encrypt.AES_128_GCM(message, sk, (err, output) => {
  if (err) { return cb(err); }
  console.log(output);
  // { alg:        'aes128gcm',
  //   sk:         <Buffer 6c 5e 93 6c a4 b8 43 ... >,
  //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
  //   iv:         <Buffer 8e 81 31 91 d2 a3 2c ... >,
  //   ciphertext: <Buffer 0a 37 d4 86 69 1e c9 ... >,
  //   tag:        <Buffer 72 69 d6 25 18 92 9d ... > }
});

// promise
magic.alt.encrypt.AES_128_GCM(message, sk)
  .then((output) => {
    console.log(output);
    // { alg:        'aes128gcm',
    //   sk:         <Buffer 6c 5e 93 6c a4 b8 43 ... >,
    //   payload:    <Buffer 41 20 73 63 72 65 61 ... >,
    //   iv:         <Buffer 8e 81 31 91 d2 a3 2c ... >,
    //   ciphertext: <Buffer 0a 37 d4 86 69 1e c9 ... >,
    //   tag:        <Buffer 72 69 d6 25 18 92 9d ... > }
  }).catch((err) => {
    return reject(err);
  });
});

Decryption then returns the plaintext directly, without the metadata.

const sk = '6c5e93...';

// callback
magic.alt.decrypt.AES_128_GCM(sk, iv, ciphertext, tag, (err, plaintext) => {
  if (err) { return cb(err); }
  console.log(plaintext);
  // <Buffer 41 20 73 63 72 65 61 ... >
});

// promise
magic.alt.decrypt.AES_128_GCM(sk, iv, ciphertext, tag)
  .then((plaintext) => {
    console.log(plaintext);
    // <Buffer 41 20 73 63 72 65 61 ... >
  }).catch((err) => {
    return reject(err);
  });
});

magic.alt.password.bcrypt | magic.alt.verify.bcrypt

Implements bcrypt using node.bcrypt.js, wrapping the OpenBSD implementation of the algorithm. An alterative to magic.util.pwhash. The security parameter (rounds) is set to 10.

magic.alt.util.sha{256,512}

Implements SHA2 using OpenSSL through crypto. Each of the SHA256, SHA384 (as magic.util.hash), and SHA512 digest length variatns are available. An alterative to magic.util.hash.

Notes

– As a recommendation, magic should always be used with node.js buffers for all (non-boolean) inputs, with the exception of passwords. Due to the variety of tasks to which it may be put, the library attempts to be as unopinionated about encoding as it is opinionated about algorithms. There is minimal decoding functionality, which will attempt to break down any plaintext input as utf-8 and any cryptographic input (keys, ciphertexts, macs, signatures, etc.) as hex. If as a consumer of this library you decide to depend on this builtin decoder it is recommended that you extensively test it to make sure your inputs are being parsed appropriately. When in doubt, it is always safer to parse them yourself and pass in binary data.