New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@evervault/sdk

Package Overview
Dependencies
Maintainers
5
Versions
105
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@evervault/sdk - npm Package Compare versions

Comparing version 4.3.0 to 5.0.0

lib/core/cagePcrManager.js

12

lib/config.js
const { version } = require('../package.json');
const DEFAULT_API_URL = 'https://api.evervault.com';
const DEFAULT_FUNCTION_RUN_URL = 'https://run.evervault.com';
const DEFAULT_TUNNEL_HOSTNAME = 'https://relay.evervault.com:443';
const DEFAULT_CA_HOSTNAME = 'https://ca.evervault.com';
const DEFAULT_CAGES_CA_HOSTNAME = 'https://cages-ca.evervault.com';
const DEFAULT_CAGES_HOSTNAME = 'cages.evervault.com';
const DEFAULT_CAGES_HOSTNAME = 'cage.evervault.com';
const DEFAULT_POLL_INTERVAL = 5;
const DEFAULT_MAX_FILE_SIZE_IN_MB = 25;
const DEFAULT_ATTEST_POLL_INTERVAL = 7200;
const DEFAULT_ATTEST_POLL_INTERVAL = 300;
const DEFAULT_PCR_PROVIDER_POLL_INTERVAL = 300;

@@ -16,3 +16,2 @@ module.exports = () => ({

baseUrl: process.env.EV_API_URL || DEFAULT_API_URL,
functionRunUrl: process.env.EV_CAGE_RUN_URL || DEFAULT_FUNCTION_RUN_URL,
userAgent: `evervault-node/${version}`,

@@ -27,2 +26,5 @@ tunnelHostname: process.env.EV_TUNNEL_HOSTNAME || DEFAULT_TUNNEL_HOSTNAME,

process.env.EV_ATTEST_POLL_INTERVAL || DEFAULT_ATTEST_POLL_INTERVAL,
pcrProviderPollInterval:
process.env.EV_PCR_PROVIDER_POLL_INTERVAL ||
DEFAULT_PCR_PROVIDER_POLL_INTERVAL,
},

@@ -38,2 +40,3 @@ encryption: {

evVersion: 'DUB',
evVersionWithMetadata: 'BRU',
header: {

@@ -54,2 +57,3 @@ iss: 'evervault',

evVersion: 'NOC',
evVersionWithMetadata: 'LCY',
header: {

@@ -56,0 +60,0 @@ iss: 'evervault',

@@ -39,7 +39,6 @@ const RepeatedTimer = require('./repeatedTimer');

get = async (cageName) => {
get = (cageName) => {
const doc = this.attestationDocCache[cageName];
if (!doc) {
await this.loadCageDoc(cageName);
return this.attestationDocCache[cageName];
console.warn(`No attestation doc found for ${cageName}`);
}

@@ -65,5 +64,5 @@ return doc;

await Promise.all(
this.cages.map(async (cageName) =>
this.loadCageDoc(cageName, this.appUuid)
)
this.cages.map(async (cageName) => {
await this.loadCageDoc(cageName, this.appUuid);
})
);

@@ -70,0 +69,0 @@ };

const crypto = require('crypto');
const { P256 } = require('../curves');
const { Encoding } = require('../curves');
const Datatypes = require('../utils/datatypes');

@@ -8,2 +8,3 @@ const { errors } = require('../utils');

const PRIME256V1 = 'prime256v1';
const SECP256K1 = 'secp256k1';

@@ -30,3 +31,4 @@ const generateBytes = (byteLength) => {

derivedSecret,
data
data,
role
) => {

@@ -38,3 +40,4 @@ return await _traverseObject(

derivedSecret,
{ ...data }
{ ...data },
role
);

@@ -47,3 +50,4 @@ };

derivedSecret,
data
data,
role
) => {

@@ -57,3 +61,4 @@ if (Datatypes.isEncryptable(data)) {

Datatypes.ensureString(data),
Datatypes.getHeaderType(data)
Datatypes.getHeaderType(data),
role
);

@@ -68,3 +73,4 @@ } else if (Datatypes.isObjectStrict(data)) {

derivedSecret,
value
value,
role
);

@@ -81,3 +87,4 @@ }

derivedSecret,
value
value,
role
);

@@ -95,7 +102,7 @@ }

const getSharedSecret = (ecdh, publicKey, ephemeralPublicKey) => {
const getSharedSecret = (ecdh, publicKey, ephemeralPublicKey, curveName) => {
const secret = ecdh.computeSecret(Buffer.from(publicKey, 'base64'));
const uncompressedKey = crypto.ECDH.convertKey(
ephemeralPublicKey,
'prime256v1',
curveName,
'base64',

@@ -108,3 +115,3 @@ 'base64',

Buffer.from([0x00, 0x00, 0x00, 0x01]),
P256.encodePublicKey(uncompressedKey),
Encoding.encodePublicKey(curveName, uncompressedKey),
]);

@@ -123,6 +130,6 @@

str,
datatype
datatype,
role
) => {
const keyIv = await generateBytes(config.ivLength);
const cipher = crypto.createCipheriv(

@@ -136,9 +143,10 @@ config.cipherAlgorithm,

);
if (curve === PRIME256V1) {
if (curve === PRIME256V1 || (curve === SECP256K1 && role)) {
cipher.setAAD(Buffer.from(ecdhTeamKey, 'base64'));
}
const result = buildCipherBuffer(str, role);
const encryptedBuffer = Buffer.concat([
cipher.update(str, 'utf8'),
cipher.update(result),
cipher.final(),

@@ -152,14 +160,80 @@ cipher.getAuthTag(),

ecdhPublicKey,
encryptedBuffer.toString('base64')
encryptedBuffer.toString('base64'),
role
);
};
const _evVersionPrefix = base64RemovePadding(
Buffer.from(config.evVersion).toString('base64')
);
const buildEncodedMetadata = (role, encryptionTimestamp) => {
let buffer = [];
const _evEncryptedFileVersion = () => {
// Binary representation of a fixed map with 2 or 3 items, followed by the key-value pairs.
buffer.push(0x80 | (!role ? 2 : 3));
if (role) {
// `dr` (data role) => role_name
// Binary representation for a fixed string of length 2, followed by `dr`
buffer.push(0xa2);
buffer.push(...'dr'.split('').map((c) => c.charCodeAt(0)));
// Binary representation for a fixed string of role name length, followed by the role name itself.
buffer.push(0xa0 | role.length);
buffer.push(...role.split('').map((c) => c.charCodeAt(0)));
}
// "eo" (encryption origin) => 5 (Node SDK)
// Binary representation for a fixed string of length 2, followed by `eo`
buffer.push(0xa2);
buffer.push(...'eo'.split('').map((c) => c.charCodeAt(0)));
// Binary representation for the integer 5
buffer.push(5);
// "et" (encryption timestamp) => current time
// Binary representation for a fixed string of length 2, followed by `et`
buffer.push(0xa2);
buffer.push(...'et'.split('').map((c) => c.charCodeAt(0)));
// Binary representation for a 4-byte unsigned integer (uint 32), followed by the epoch time
buffer.push(0xce);
buffer.push((encryptionTimestamp >> 24) & 0xff);
buffer.push((encryptionTimestamp >> 16) & 0xff);
buffer.push((encryptionTimestamp >> 8) & 0xff);
buffer.push(encryptionTimestamp & 0xff);
return Buffer.from(buffer);
};
const buildCipherBuffer = (data, role) => {
let result;
if (role) {
const metadataBytes = buildEncodedMetadata(
role,
Math.floor(new Date().getTime() / 1000)
);
let offsetBuffer = Buffer.allocUnsafe(2);
offsetBuffer.writeUInt16LE(metadataBytes.length);
result = Buffer.concat([offsetBuffer, metadataBytes, Buffer.from(data)]);
} else {
result = Buffer.from(data);
}
return result;
};
const _evVersionPrefix = (role) =>
base64RemovePadding(
Buffer.from(
role ? config.evVersionWithMetadata : config.evVersion
).toString('base64')
);
const _evEncryptedFileVersion = (hasMetadata) => {
if (config.ecdhCurve == 'secp256k1') {
if (hasMetadata) {
return Buffer.from([0x04]);
}
return Buffer.from([0x02]);
} else if (config.ecdhCurve === 'prime256v1') {
if (hasMetadata) {
return Buffer.from([0x05]);
}
return Buffer.from([0x03]);

@@ -175,5 +249,6 @@ } else {

ecdhPublicKey,
encryptedData
encryptedData,
role
) => {
return `ev:${_evVersionPrefix}${
return `ev:${_evVersionPrefix(role)}${
datatype !== 'string' ? ':' + datatype : ''

@@ -185,2 +260,29 @@ }:${base64RemovePadding(keyIv)}:${base64RemovePadding(

const _encryptBytes = (
data,
setAuthData,
derivedSecret,
ecdhTeamKey,
keyIv
) => {
const cipher = crypto.createCipheriv(
config.cipherAlgorithm,
derivedSecret,
keyIv,
{
authTagLength: config.authTagLength,
}
);
if (setAuthData) {
cipher.setAAD(Buffer.from(ecdhTeamKey, 'base64'));
}
return Buffer.concat([
cipher.update(data),
cipher.final(),
cipher.getAuthTag(),
]);
};
const _encryptFile = async (

@@ -191,3 +293,4 @@ curve,

derivedSecret,
data
data,
role
) => {

@@ -202,33 +305,60 @@ const fileSizeInBytes = data.length;

const cipher = crypto.createCipheriv(
config.cipherAlgorithm,
const setAuthData = curve === PRIME256V1 || role ? true : false;
const encryptedBuffer = _encryptBytes(
data,
setAuthData,
derivedSecret,
keyIv,
{
authTagLength: config.authTagLength,
}
ecdhTeamKey,
keyIv
);
if (curve === PRIME256V1) {
cipher.setAAD(Buffer.from(ecdhTeamKey, 'base64'));
let encryptedMetadataBytes;
if (role) {
const metadataBytes = buildEncodedMetadata(
role,
Math.floor(new Date().getTime() / 1000)
);
encryptedMetadataBytes = _encryptBytes(
metadataBytes,
setAuthData,
derivedSecret,
ecdhTeamKey,
keyIv
);
}
const encryptedBuffer = Buffer.concat([
cipher.update(data),
cipher.final(),
cipher.getAuthTag(),
]);
return _formatFile(
keyIv,
ecdhPublicKey,
encryptedBuffer,
encryptedMetadataBytes
);
};
return _formatFile(keyIv, ecdhPublicKey, encryptedBuffer);
const _calculateOffsetToData = (encryptedMetadataBytes) => {
if (encryptedMetadataBytes) {
let offsetBuffer = Buffer.allocUnsafe(2);
offsetBuffer.writeUInt16LE(55 + 2 + encryptedMetadataBytes.length); // headers + metadata offset size + metadata offset
return offsetBuffer;
} else {
return Buffer.from([0x37, 0x00]); // 55 bytes to starting byte of data if no metadtata
}
};
const _formatFile = async (keyIv, ecdhPublicKey, encryptedData) => {
const _formatFile = async (
keyIv,
ecdhPublicKey,
encryptedData,
encryptedMetadataBytes = undefined
) => {
const evEncryptedFileIdentifier = Buffer.from([
0x25, 0x45, 0x56, 0x45, 0x4e, 0x43,
]);
const versionNumber = _evEncryptedFileVersion();
const offsetToData = Buffer.from([0x37, 0x00]);
const hasMetadata = encryptedMetadataBytes !== undefined;
const versionNumber = _evEncryptedFileVersion(hasMetadata);
const offsetToData = _calculateOffsetToData(encryptedMetadataBytes);
const flags = Buffer.from([0x00]);
const fileContents = Buffer.concat([
const fileHeaders = Buffer.concat([
evEncryptedFileIdentifier,

@@ -240,5 +370,18 @@ versionNumber,

flags,
Buffer.from(encryptedData),
]);
let fileContents;
if (encryptedMetadataBytes) {
let metadataOffsetBuffer = Buffer.allocUnsafe(2);
metadataOffsetBuffer.writeUInt16LE(encryptedMetadataBytes.length);
fileContents = Buffer.concat([
fileHeaders,
metadataOffsetBuffer,
encryptedMetadataBytes,
Buffer.from(encryptedData),
]);
} else {
fileContents = Buffer.concat([fileHeaders, Buffer.from(encryptedData)]);
}
const crc32Hash = CRC32(fileContents);

@@ -258,2 +401,3 @@

data,
role = undefined,
options = DEFAULT_ENCRYPT_OPTIONS

@@ -271,3 +415,4 @@ ) => {

derivedSecret,
data
data,
role
);

@@ -281,2 +426,3 @@ } else if (Datatypes.isObjectStrict(data)) {

data,
role,
options

@@ -290,3 +436,4 @@ );

derivedSecret,
[...data]
[...data],
role
);

@@ -300,3 +447,4 @@ } else if (Datatypes.isEncryptable(data)) {

Datatypes.ensureString(data),
Datatypes.getHeaderType(data)
Datatypes.getHeaderType(data),
role
);

@@ -308,3 +456,9 @@ } else {

return { encrypt, getSharedSecret, generateBytes };
return {
encrypt,
getSharedSecret,
generateBytes,
buildCipherBuffer,
buildEncodedMetadata,
};
};

@@ -25,18 +25,9 @@ const { errors, Datatypes } = require('../utils');

}
if (!additionalHeaders['x-async']) {
return phin({
url: path.startsWith('https://') ? path : `${config.baseUrl}/${path}`,
method,
headers,
data,
parse,
});
} else {
return phin({
url: path.startsWith('https://') ? path : `${config.baseUrl}/${path}`,
method,
headers,
data,
});
}
return phin({
url: path.startsWith('https://') ? path : `${config.baseUrl}/${path}`,
method,
headers,
data,
parse,
});
};

@@ -57,3 +48,3 @@

return await get('cages/key', {}, true).catch((_e) => {
throw new errors.CageKeyError(
throw new errors.EvervaultError(
"An error occurred while retrieving the cage's key"

@@ -67,3 +58,3 @@ );

}
throw errors.mapApiResponseToError(response);
throw errors.mapResponseCodeToError(response);
};

@@ -86,3 +77,3 @@

.catch((err) => {
throw new errors.CertError(
throw new errors.EvervaultError(
`Unable to download cert from ${config.certHostname} (${err.message})`

@@ -109,3 +100,3 @@ );

.catch((err) => {
throw new errors.CertError(
throw new errors.EvervaultError(
`Unable to download cert from ${config.cagesCertHostname} (${err.message})`

@@ -133,3 +124,3 @@ );

.catch((err) => {
throw new errors.CertError(
throw new errors.EvervaultError(
`Unable to download attestation doc from ${url} (${err.message})`

@@ -143,3 +134,3 @@ );

const response = await get('v2/relay-outbound').catch((e) => {
throw new errors.RelayOutboundConfigError(
throw new errors.EvervaultError(
`An error occoured while retrieving the Relay Outbound configuration: ${e}`

@@ -157,28 +148,27 @@ );

}
throw errors.mapApiResponseToError(response);
throw errors.mapResponseCodeToError(response);
};
const buildRunHeaders = ({ version, async }) => {
const headers = {};
if (version) {
headers['x-version-id'] = version;
}
if (async) {
headers['x-async'] = 'true';
}
return headers;
};
const runCage = (cageName, payload, options = {}) => {
const optionalHeaders = buildRunHeaders(options);
return post(
`${config.functionRunUrl}/${cageName}`,
const runFunction = async (functionName, payload) => {
const response = await post(
`${config.baseUrl}/functions/${functionName}/runs`,
{
...payload,
payload,
},
{
'Content-Type': 'application/json',
...optionalHeaders,
},
true
);
if (response.statusCode >= 200 && response.statusCode < 300) {
const responseBody = response.body;
if (responseBody.status === 'success') {
return response;
}
);
throw errors.mapFunctionFailureResponseToError(responseBody);
}
const responseBody = response.body;
throw errors.mapApiResponseToError(responseBody);
};

@@ -210,3 +200,2 @@

} catch (e) {
console.error(`Attempt ${retryCount + 1} failed: ${e.message}`);
retryCount++;

@@ -250,3 +239,6 @@ await new Promise((resolve) => setTimeout(resolve, retryDelayMs));

}
throw errors.mapApiResponseToError(response);
const resBody = Buffer.isBuffer(response.body)
? JSON.parse(response.body.toString())
: response.body;
throw errors.mapApiResponseToError(resBody);
};

@@ -295,3 +287,3 @@

}
throw errors.mapApiResponseToError(response);
throw errors.mapApiResponseToError(response.body);
};

@@ -301,3 +293,3 @@

getCageKey,
runCage,
runFunction,
getCert,

@@ -304,0 +296,0 @@ getCagesCert,

@@ -7,2 +7,3 @@ module.exports = {

AttestationDoc: require('./cageAttestationDoc'),
CagePcrManager: require('./cagePcrManager'),
};
module.exports = ({ http, crypto, run }) => {
return {
fetch: async (url, options) => {
const { body } = await http.runCage('proxy-cage', {
const { body } = await http.runFunction('proxy-cage', {
url,

@@ -6,0 +6,0 @@ options,

@@ -1,3 +0,1 @@

const { InitializationError } = require('../utils/errors');
module.exports = (defaultInterval, cb) => {

@@ -4,0 +2,0 @@ const createInterval = () => {

const crypto = require('crypto');
const ASN1 = require('uasn1');
const curveConstants = require('./constants');

@@ -10,8 +11,8 @@ /**

*/
const createCurve = (curveName, curveValues) => {
const asn1Encoder = buildEncoder(curveValues);
return (compressedPubKey) => {
const createCurve = () => {
return (curveName, compressedPubKey) => {
const asn1Encoder = buildEncoder(curveName);
const decompressed = crypto.ECDH.convertKey(
compressedPubKey,
'prime256v1',
curveName,
'base64',

@@ -25,3 +26,9 @@ 'hex',

const buildEncoder = ({ p, a, b, seed, generator, n, h }) => {
/**
* The seed parameter is optional according to the X9.62 standard
* for DER encoding public keys
* https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TR03111/BSI-TR-03111_V-2-0_pdf.pdf?__blob=publicationFile&v=1
*/
const buildEncoder = (curveName) => {
const curveParams = curveConstants[curveName];
return (decompressedKey) => {

@@ -44,19 +51,27 @@ const hexEncodedKey = ASN1(

// curve p value
ASN1.UInt(p)
ASN1.UInt(curveParams.p)
),
ASN1(
'30',
// curve a value
ASN1('04', a),
// curve b value
ASN1('04', b),
// curve seed value
ASN1.BitStr(seed)
),
curveParams.seed
? ASN1(
'30',
// curve a value
ASN1('04', curveParams.a),
// curve b value
ASN1('04', curveParams.b),
// curve seed value
ASN1.BitStr(curveParams.seed)
)
: ASN1(
'30',
// curve a value
ASN1('04', curveParams.a),
// curve b value
ASN1('04', curveParams.b)
),
// curve generate point in decompressed form
ASN1('04', generator),
ASN1('04', curveParams.generator),
// curve n value
ASN1.UInt(n),
ASN1.UInt(curveParams.n),
// curve h value
ASN1.UInt(h)
ASN1.UInt(curveParams.h)
)

@@ -73,3 +88,3 @@ ),

module.exports = {
createCurve,
encodePublicKey: createCurve(),
};
module.exports = {
P256: require('./p256'),
Encoding: require('./base'),
};

@@ -15,3 +15,9 @@ const crypto = require('crypto');

const Config = require('./config');
const { Crypto, Http, RelayOutboundConfig, AttestationDoc } = require('./core');
const {
Crypto,
Http,
RelayOutboundConfig,
AttestationDoc,
CagePcrManager,
} = require('./core');
const { TokenCreationError } = require('./utils/errors');

@@ -35,3 +41,3 @@ const console = require('console');

) {
throw new errors.InitializationError(
throw new errors.EvervaultError(
'The provided App ID is invalid. The App ID can be retrieved in the Evervault dashboard (App Settings).'

@@ -94,2 +100,3 @@ );

if (cageAttest.hasAttestationBindings()) {
//Store attestation documents from cages in cache
let attestationCache = new AttestationDoc(

@@ -101,7 +108,17 @@ this.config,

);
await attestationCache.init();
//Store client PCR providers to periodically pull new PCRs
const cagePcrManager = new CagePcrManager(
this.config,
cagesAttestationData
);
await cagePcrManager.init();
cageAttest.addAttestationListener(
this.config.http,
cagesAttestationData,
attestationCache
attestationCache,
cagePcrManager
);

@@ -121,21 +138,2 @@ } else {

async _shouldOverloadHttpModule(options, apiKey) {
// DEPRECATED: Remove this method in next major version
if (options.intercept || options.ignoreDomains) {
console.warn(
'\x1b[43m\x1b[30mWARN\x1b[0m The `intercept` and `ignoreDomains` config options in the Evervault Node.js SDK are deprecated and slated for removal.',
'\n\x1b[43m\x1b[30mWARN\x1b[0m More details: https://docs.evervault.com/reference/nodejs-sdk#evervaultenableoutboundrelay'
);
} else if (options.intercept !== false && options.enableOutboundRelay) {
// ^ preserves backwards compatibility with if relay is explictly turned off
console.warn(
'\x1b[43m\x1b[30mWARN\x1b[0m The `enableOutboundRelay` config option in the Evervault Node.js SDK is deprecated and slated for removal.',
'\n\x1b[43m\x1b[30mWARN\x1b[0m You can now use the `enableOutboundRelay()` method to enable outbound relay.',
'\n\x1b[43m\x1b[30mWARN\x1b[0m More details: https://docs.evervault.com/reference/nodejs-sdk#evervaultenableoutboundrelay'
);
await RelayOutboundConfig.init(
this.config,
this.http,
Boolean(options.debugRequests)
);
}
if (options.decryptionDomains && options.decryptionDomains.length > 0) {

@@ -153,18 +151,2 @@ const decryptionDomainsFilter = this._decryptionDomainsFilter(

);
} else if (
options.intercept === true ||
options.relay === true ||
(options.ignoreDomains && options.ignoreDomains.length > 0)
) {
const ignoreDomainsFilter = this._ignoreDomainFilter(
options.ignoreDomains
);
await this.httpsHelper.overloadHttpsModule(
apiKey,
this.config.http.tunnelHostname,
ignoreDomainsFilter,
Boolean(options.debugRequests),
this.http,
originalRequest
);
} else if (options.enableOutboundRelay) {

@@ -185,3 +167,2 @@ await this.httpsHelper.overloadHttpsModule(

_alwaysIgnoreDomains() {
const functionsHost = new URL(this.config.http.functionRunUrl).host;
const caHost = new URL(this.config.http.certHostname).host;

@@ -192,19 +173,5 @@ const apiHost = new URL(this.config.http.baseUrl).host;

return [functionsHost, cagesCaHost, caHost, apiHost, cagesHost];
return [cagesCaHost, caHost, apiHost, cagesHost];
}
_parsedDomainsToIgnore(ignoreDomains) {
ignoreDomains = ignoreDomains.concat(this._alwaysIgnoreDomains());
let ignoreExact = [];
let ignoreEndsWith = [];
ignoreDomains.forEach((domain) => {
let exact = domain.startsWith('www.') ? domain.slice(4) : domain;
ignoreExact.push(exact);
ignoreEndsWith.push('.' + exact);
ignoreEndsWith.push('@' + exact);
});
return [ignoreExact, ignoreEndsWith];
}
_decryptionDomainsFilter(decryptionDomains) {

@@ -231,10 +198,2 @@ return (domain) =>

_ignoreDomainFilter(ignoreDomains = []) {
const [ignoreExact, ignoreEndsWith] =
this._parsedDomainsToIgnore(ignoreDomains);
return (domain) =>
!this._isIgnoreRequest(domain, ignoreExact, ignoreEndsWith);
}
_relayOutboundConfigDomainFilter() {

@@ -250,6 +209,2 @@ return (domain) => {

_isIgnoreRequest(domain, ignoreExact, ignoreEndsWith) {
return this._exactOrEndsWith(domain, ignoreExact, ignoreEndsWith);
}
_exactOrEndsWith(domain, exactDomains, endsWithDomains) {

@@ -263,3 +218,3 @@ if (exactDomains.includes(domain)) return true;

_refreshKeys() {
_refreshKeys(role) {
this._ecdh.generateKeys();

@@ -270,3 +225,6 @@ this.defineHiddenProperty(

);
if (this.curve === EvervaultClient.CURVES.PRIME256V1) {
if (
this.curve === EvervaultClient.CURVES.PRIME256V1 ||
(this.curve === EvervaultClient.CURVES.SECP256K1 && role)
) {
this.defineHiddenProperty(

@@ -277,3 +235,4 @@ '_derivedAesKey',

this._ecdhTeamKey,
this._ecdhPublicKey
this._ecdhPublicKey,
this.curve
)

@@ -291,5 +250,12 @@ );

* @param {Object || String} data
* @param {String || undefined} role
* @returns {Promise<Object || String>}
*/
async encrypt(data) {
async encrypt(data, role = null) {
const dataRoleRegex = /^[a-z0-9-]{1,20}$/;
if (role !== null && !dataRoleRegex.test(role)) {
throw new Error(
'The provided Data Role slug is invalid. The slug can be retrieved in the Evervault dashboard (Data Roles section).'
);
}
if (!Datatypes.isDefined(this._derivedAesKey)) {

@@ -307,3 +273,3 @@ if (!Datatypes.isDefined(this._ecdhTeamKey)) {

}
this._refreshKeys();
this._refreshKeys(role);
}

@@ -315,3 +281,3 @@ if (!Datatypes.isDefined(this._refreshInterval)) {

(ref) => {
ref._refreshKeys();
ref._refreshKeys(role);
},

@@ -328,3 +294,4 @@ this.config.encryption[this.curve].keyCycleMinutes * 60 * 1000,

this._derivedAesKey,
data
data,
role
);

@@ -348,6 +315,5 @@ }

*/
async run(functionName, payload, options = {}) {
async run(functionName, payload) {
validationHelper.validateFunctionName(functionName);
validationHelper.validatePayload(payload);
validationHelper.validateFunctionName(functionName);
validationHelper.validateOptions(options);

@@ -357,3 +323,3 @@ if (this.retry) {

async () => {
return await this.http.runCage(functionName, payload, options);
return await this.http.runFunction(functionName, payload);
},

@@ -364,3 +330,3 @@ { retries: 3 }

} else {
const response = await this.http.runCage(functionName, payload, options);
const response = await this.http.runFunction(functionName, payload);
return response.body;

@@ -372,21 +338,2 @@ }

* @param {String} functionName
* @param {Object} data
* @param {Object} options
* @returns {Promise<*>}
*/
async encryptAndRun(functionName, data, options) {
console.warn(
'\x1b[43m\x1b[30mWARN\x1b[0m The `encrypt_and_run` method is deprecated and slated for removal. Please use the `encrypt` and `run` methods instead.'
);
validationHelper.validatePayload(data);
validationHelper.validateFunctionName(functionName);
validationHelper.validateOptions(options);
const payload = await this.encrypt(data);
return await this.run(functionName, payload, options);
}
/**
* @param {String} functionName
* @param {Object} payload

@@ -393,0 +340,0 @@ * @returns {Promise<*>}

@@ -87,6 +87,6 @@ const certHelper = require('./certHelper');

async function attestCageConnection(
function attestCageConnection(
hostname,
cert,
cagesAttestationInfo = {},
cagePcrManager,
attestationCache

@@ -103,5 +103,6 @@ ) {

// Pull cage name from cage hostname
const { cageName, appUuid } = parseCageNameAndAppFromHost(hostname);
const { cageName } = parseCageNameAndAppFromHost(hostname);
// check if PCRs for this cage have been given
const pcrs = cagesAttestationInfo[cageName];
const pcrs = cagePcrManager.get(cageName);
var pcrsList = [];

@@ -114,14 +115,4 @@ if (Array.isArray(pcrs)) {

let attestationDoc = await attestationCache.get(cageName);
if (!attestationDoc) {
await attestationCache.loadCageDoc(cageName);
attestationDoc = await attestationCache.get(cageName);
if (!attestationDoc) {
throw new CageAttestationError(
"Couldn't find attestation doc in cache",
hostname,
cert
);
}
}
let attestationDoc = attestationCache.get(cageName);
let attestationDocBytes = Buffer.from(attestationDoc, 'base64');

@@ -135,14 +126,3 @@

// Reload cache to check if deployment has happened between polling
if (!isConnectionValid) {
attestationCache.loadCageDoc(cageName);
isConnectionValid = await getCageAttestationDoc(
cageName,
cert,
pcrsList,
attestationCache
);
}
if (!isConnectionValid) {
console.warn(

@@ -201,7 +181,3 @@ `EVERVAULT WARN :: Connection to Cage ${cageName} failed attestation`

function addAttestationListener(
config,
cagesAttestationInfo,
attestationCache
) {
function addAttestationListener(config, attestationCache, cagePcrManager) {
tls.checkServerIdentity = function (hostname, cert) {

@@ -214,5 +190,6 @@ // only attempt attestation if the host is a cage

cert.raw,
cagesAttestationInfo,
cagePcrManager,
attestationCache
);
if (attestationResult != null) {

@@ -219,0 +196,0 @@ return attestationResult;

@@ -8,20 +8,14 @@ class EvervaultError extends Error {

class InitializationError extends EvervaultError {}
class FunctionTimeoutError extends EvervaultError {}
class AccountError extends EvervaultError {}
class FunctionNotReadyError extends EvervaultError {}
class ApiKeyError extends EvervaultError {}
class FunctionRuntimeError extends EvervaultError {
constructor(message, stack, id) {
super(message);
this.stack = stack;
this.id = id;
}
}
class CageKeyError extends EvervaultError {}
class RequestError extends EvervaultError {}
class CertError extends EvervaultError {}
class ForbiddenIPError extends EvervaultError {}
class DecryptError extends EvervaultError {}
class RelayOutboundConfigError extends EvervaultError {}
class CageAttestationError extends EvervaultError {

@@ -39,4 +33,22 @@ constructor(reason, host, cert) {

const mapApiResponseToError = ({ statusCode, body, headers }) => {
if (statusCode === 401) return new ApiKeyError('Invalid Api Key provided.');
const mapFunctionFailureResponseToError = ({ error, id }) => {
if (error) {
throw new FunctionRuntimeError(error.message, error.stack, id);
}
throw new EvervaultError('An unknown error occurred.');
};
const mapApiResponseToError = ({ code, detail }) => {
if (code === 'functions/request-timeout') {
throw new FunctionTimeoutError(detail);
}
if (code === 'functions/function-not-ready') {
throw new FunctionNotReadyError(detail);
}
throw new EvervaultError(detail);
};
const mapResponseCodeToError = ({ statusCode, body, headers }) => {
if (statusCode === 401)
return new EvervaultError('Invalid authorization provided.');
if (

@@ -46,3 +58,3 @@ statusCode === 403 &&

) {
return new ForbiddenIPError(
return new EvervaultError(
body.message || "IP is not present on the invoked Cage's whitelist."

@@ -52,3 +64,3 @@ );

if (statusCode === 403) {
return new ApiKeyError(
return new EvervaultError(
'The API key provided does not have the required permissions.'

@@ -58,15 +70,8 @@ );

if (statusCode === 422) {
return new DecryptError(body.message || 'Unable to decrypt data.');
return new EvervaultError(body.message || 'Unable to decrypt data.');
}
if (statusCode === 423)
return new AccountError(
body.message ||
'Your account is still being set up. Refer to the account status page on app.evervault.com'
);
if (statusCode === 424)
return new AccountError(
body.message ||
'An error occurred during account creation. Please contact evervault support.'
);
return new RequestError(`Request returned with status [${statusCode}]`);
if (body.message) {
return new EvervaultError(body.message);
}
return new EvervaultError(`Request returned with status [${statusCode}]`);
};

@@ -76,15 +81,11 @@

EvervaultError,
CageKeyError,
ApiKeyError,
AccountError,
InitializationError,
mapApiResponseToError,
RequestError,
CertError,
DecryptError,
ForbiddenIPError,
RelayOutboundConfigError,
mapResponseCodeToError,
mapFunctionFailureResponseToError,
CageAttestationError,
ExceededMaxFileSizeError,
TokenCreationError,
FunctionTimeoutError,
FunctionNotReadyError,
FunctionRuntimeError,
};

@@ -7,3 +7,3 @@ const crypto = require('crypto');

if (apiKey === '' || !Datatypes.isString(apiKey)) {
throw new errors.InitializationError(
throw new errors.EvervaultError(
'The API key must be a string and cannot be empty.'

@@ -21,3 +21,3 @@ );

if (appUuidHash !== appUuidHashFromApiKey) {
throw new errors.InitializationError(
throw new errors.EvervaultError(
`The API key is not valid for app ${appUuid}. Make sure to use an API key belonging to the app ${appUuid}.`

@@ -43,12 +43,2 @@ );

const validateOptions = (options = {}) => {
if (
Datatypes.isObjectStrict(options) &&
Datatypes.isDefined(options.version) &&
!Datatypes.isNumber(options.version)
) {
throw new errors.EvervaultError('Function version must be a number');
}
};
const validateRelayOutboundOptions = (options = {}) => {

@@ -72,4 +62,3 @@ if (

validateFunctionName,
validateOptions,
validateRelayOutboundOptions,
};
{
"name": "@evervault/sdk",
"version": "4.3.0",
"version": "5.0.0",
"description": "Node.js SDK for Evervault",

@@ -11,3 +11,3 @@ "main": "lib/index.js",

"test": "mocha 'tests/**/*.test.js'",
"test:e2e": "mocha 'e2e/**/*.test.js' --exit",
"test:e2e": "mocha 'e2e/**/*.test.js' --timeout 5000 --exit",
"test:filter": "mocha 'tests/**/*.test.js' --grep",

@@ -46,5 +46,7 @@ "test:coverage": "nyc --reporter=text npm run test"

"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"husky": "^7.0.2",
"lint-staged": "^11.1.2",
"mocha": "^10.0.0",
"msgpackr": "^1.9.9",
"nock": "^12.0.3",

@@ -51,0 +53,0 @@ "nyc": "^15.1.0",

@@ -5,4 +5,2 @@ [![Evervault](https://evervault.com/evervault.svg)](https://evervault.com/)

The [Evervault](https://evervault.com) Node.js SDK is a toolkit for encrypting data as it enters your server, and working with Functions. By default, initializing the SDK will result in all outbound HTTPS requests being intercepted and decrypted.
## Getting Started

@@ -16,185 +14,4 @@

See the Evervault [Node.js SDK documentation](https://docs.evervault.com/sdk/nodejs).
See the Evervault [Node.js SDK documentation](https://docs.evervault.com/sdks/nodejs) to learn how to install, set up, and use the SDK.
## Installation
Our Node.js SDK is distributed via [npm](https://www.npmjs.com/package/@evervault/sdk), and can be installed using your preferred package manager.
```sh
npm install --save @evervault/sdk
yarn add @evervault/sdk
```
## Setup
To make Evervault available for use in your app:
```js
const Evervault = require('@evervault/sdk');
// Initialize the client with your App ID and API Key
const evervaultClient = new Evervault('<APP_ID>', '<API_KEY>');
// Encrypt your sensitive data
const encrypted = await evervaultClient.encrypt({ ssn: '012-34-5678' });
// Process the encrypted data in a Function
const result = await evervaultClient.run('<FUNCTION_NAME>', encrypted);
// Send the decrypted data to a third-party API
await evervaultClient.enableOutboundRelay();
const response = await axios.post('https://example.com', encrypted);
// Use HTTPSProxyAgent to send data to a third-party
const httpsAgent = evervault.createRelayHttpsAgent();
const response = await axios.get('https://example.com', {
httpsAgent,
});
// Decrypt the data
const decrypted = await evervaultClient.decrypt(encrypted);
// Enable the Cages client
await evervaultClient.enableCages({ 'my-cage': { pcr8: '...' } });
const response = await axios.post(
'https://my-cage.my-app.cages.evervault.com',
encrypted
); // This connection will be attested by the Cages client
```
## Reference
The Evervault Node.js SDK exposes six functions.
### evervault.encrypt()
`evervault.encrypt()` encrypts data. To encrypt data at the server, simply pass a string, boolean, number, array, object or buffer into the `evervault.encrypt()` function. Store the encrypted data in your database as normal.
```javascript
async evervault.encrypt(data: string | boolean | number | Array | Object | Buffer);
```
| Parameter | Type | Description |
| --------- | ------------------------------------------------ | --------------------- |
| data | String, Boolean, Number, Array, Object or String | Data to be encrypted. |
### evervault.decrypt()
`evervault.decrypt()` decrypts data previously encrypted with the `encrypt()` function or through Evervault's Relay (Evervault's encryption proxy).
An API Key with the `decrypt` permission must be used to perform this operation.
```javascript
async evervault.decrypt(encrypted: string | Array | Object | Buffer);
```
| Parameter | Type | Description |
| --------- | ------------------------------- | --------------------- |
| encrypted | String, Array, Object or Buffer | Data to be decrypted. |
### evervault.createClientSideDecryptToken()
`evervault.createClientSideDecryptToken()` creates a token that can be used to authenticate a `decrypt()` request
from a frontend/client application.
An API Key with the `Create Token` permission must be used to perform this operation.
```javascript
async evervault.createClientSideDecryptToken(payload: string | Array | Object, expiry: Date);
```
| Parameter | Type | Description |
| --------- | ------------------------ | --------------------------------------------------------------------- |
| payload | String, Array, or Object | Data that the token can decrypt. |
| expiry | Date | The expiry of the token, must be < 10 mins from now. (Default 5 mins) |
### evervault.run()
`evervault.run()` invokes a Function with a given payload.
An API Key with the `run function` permission must be used to perform this operation.
```javascript
async evervault.run(functionName: String, payload: Object[, options: Object]);
```
| Parameter | Type | Description |
| ------------ | ------ | ----------------------------------------------------- |
| functionName | String | Name of the Function to be run |
| data | Object | Payload for the Function |
| options | Object | [Options for the Function run](#Function-Run-Options) |
#### Function Run Options
Options to control how your Function is run
| Option | Type | Default | Description |
| ------- | ------- | --------- | ---------------------------------------------------------------------------------------- |
| async | Boolean | false | Run your Function in async mode. Async Function runs will be queued for processing. |
| version | Number | undefined | Specify the version of your Function to run. By default, the latest version will be run. |
### evervault.createRunToken()
`evervault.createRunToken()` creates a single use, time bound token for invoking a Function.
An API Key with the `create a run token` permission must be used to perform this operation.
```javascript
async evervault.createRunToken(functionName: String, payload: Object);
```
| Parameter | Type | Description |
| ------------ | ------ | -------------------------------------------------------- |
| functionName | String | Name of the Function the run token should be created for |
| data | Object | Payload that the token can be used with |
### evervault.enableOutboundRelay()
`evervault.enableOutboundRelay()` configures your application to proxy HTTP requests using Outbound Relay based on the configuration created in the Evervault dashboard. See [Outbound Relay](https://docs.evervault.com/concepts/outbound-relay/overview) to learn more.
```javascript
async evervault.enableOutboundRelay([options: Object])
```
| Option | Type | Default | Description |
| ------------------- | --------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `decryptionDomains` | `Array` | `undefined` | Requests sent to any of the domains listed will be proxied through Outbound Relay. This will override the configuration created in the Evervault dashboard. |
| `debugRequests` | `Boolean` | `False` | Output request domains and whether they were sent through Outbound Relay. |
### evervault.createRelayHttpsAgent()
`evervault.createRelayHttpsAgent()` will return a `HttpsProxyAgent` configred to proxy traffic through Relay.
```javascript
evervault.createRelayHttpsAgent();
```
#### createRelayHttpsAgent axios example
```javascript
const httpsAgent = evervault.createRelayHttpsAgent();
const response = await axios.get('https://example.com', {
httpsAgent,
});
```
### evervault.enableCages()
`evervault.enableCages()` configures your client to automatically attest any requests to Cages. See the [Cage attestation docs](https://docs.evervault.com/products/cages#how-does-attestation-work-with-cages) to learn more.
```javascript
async evervault.enableCages([cageAttestationData: Object])
```
| Key | Type | Default | Description |
| ------------ | ---------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `<CageName>` | `Object` `Array` | `undefined` | Requests to a Cage specified in this object will include a check to verify that the PCRs provided in the object are included in the attestation document. The provided data can be either a single Object, or an Array of Objects to allow roll-over between different sets of PCRs. |
#### Cages Beta Example
```javascript
await evervault.enableCages({
'hello-cage': {
pcr8: '97c5395a83c0d6a04d53ff962663c714c178c24500bf97f78456ed3721d922cf3f940614da4bb90107c439bc4a1443ca',
},
});
```
## Contributing

@@ -201,0 +18,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc