From 48efa8b7e51a7ecd834149df3801b76c27e1dd03 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 11 Nov 2022 13:34:27 +0100 Subject: [PATCH] fix: CDK does not work in FIPS-restricted environments In an environment where node is compiled with FIPS restrictions in mind, `crypto.createHash('md5')` does not work, for fear of MD5 being used for crypto purposes. We do not use it for crypto purposes, just to come up with unique identifiers for certain constructs. Nevertheless, CDK cannot work if `md5` is not available from the Node standard library. Fall back to a pure JavaScript implementation if the built-in MD5 hash does not work, so that we produce equivalent templates. --- .../@aws-cdk/aws-apigateway/lib/deployment.ts | 6 +- packages/@aws-cdk/aws-ec2/lib/instance.ts | 8 +- packages/@aws-cdk/aws-ec2/lib/volume.ts | 7 +- packages/@aws-cdk/aws-glue/lib/code.ts | 6 +- .../aws-lambda-event-sources/lib/kafka.ts | 6 +- .../@aws-cdk/aws-lambda/lib/function-hash.ts | 10 +- .../@aws-cdk/aws-rds/lib/database-secret.ts | 7 +- .../lib/website-redirect.ts | 4 +- .../lib/vpc-endpoint-service-domain-name.ts | 6 +- .../core/lib/helpers-internal/index.ts | 5 +- packages/@aws-cdk/core/lib/private/md5.ts | 191 ++++++++++++++++++ .../core/lib/private/unique-resource-name.ts | 5 +- .../@aws-cdk/core/lib/private/uniqueid.ts | 4 +- .../@aws-cdk/core/test/private/md5.test.ts | 9 + .../test/private/unique-resource-name.test.ts | 4 +- .../lib/assertions/private/hash.ts | 6 +- .../cdk-build-tools/config/eslintrc.js | 10 + 17 files changed, 244 insertions(+), 50 deletions(-) create mode 100644 packages/@aws-cdk/core/lib/private/md5.ts create mode 100644 packages/@aws-cdk/core/test/private/md5.test.ts diff --git a/packages/@aws-cdk/aws-apigateway/lib/deployment.ts b/packages/@aws-cdk/aws-apigateway/lib/deployment.ts index f62a58f4f59be..b53cd45f27e23 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/deployment.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/deployment.ts @@ -1,5 +1,5 @@ -import * as crypto from 'crypto'; import { Lazy, RemovalPolicy, Resource, CfnResource } from '@aws-cdk/core'; +import { md5hash } from '@aws-cdk/core/lib/helpers-internal'; import { Construct } from 'constructs'; import { CfnDeployment } from './apigateway.generated'; import { Method } from './method'; @@ -173,9 +173,7 @@ class LatestDeploymentResource extends CfnDeployment { // if hash components were added to the deployment, we use them to calculate // a logical ID for the deployment resource. if (hash.length > 0) { - const md5 = crypto.createHash('md5'); - hash.map(x => this.stack.resolve(x)).forEach(c => md5.update(JSON.stringify(c))); - lid += md5.digest('hex'); + lid += md5hash(hash.map(x => this.stack.resolve(x)).map(c => JSON.stringify(c)).join('')); } return lid; diff --git a/packages/@aws-cdk/aws-ec2/lib/instance.ts b/packages/@aws-cdk/aws-ec2/lib/instance.ts index 8537ae0c32c51..4997f7dcbda3e 100644 --- a/packages/@aws-cdk/aws-ec2/lib/instance.ts +++ b/packages/@aws-cdk/aws-ec2/lib/instance.ts @@ -1,7 +1,7 @@ -import * as crypto from 'crypto'; import * as iam from '@aws-cdk/aws-iam'; import { Annotations, Aspects, Duration, Fn, IResource, Lazy, Resource, Stack, Tags } from '@aws-cdk/core'; +import { md5hash } from '@aws-cdk/core/lib/helpers-internal'; import { Construct } from 'constructs'; import { InstanceRequireImdsv2Aspect } from './aspects'; import { CloudFormationInit } from './cfn-init'; @@ -423,14 +423,14 @@ export class Instance extends Resource implements IInstance { if (recursing) { return originalLogicalId; } if (!(props.userDataCausesReplacement ?? props.initOptions)) { return originalLogicalId; } - const md5 = crypto.createHash('md5'); + const fragments = new Array(); recursing = true; try { - md5.update(JSON.stringify(context.resolve(this.userData.render()))); + fragments.push(JSON.stringify(context.resolve(this.userData.render()))); } finally { recursing = false; } - const digest = md5.digest('hex').slice(0, 16); + const digest = md5hash(fragments.join('')).slice(0, 16); return `${originalLogicalId}${digest}`; }, })); diff --git a/packages/@aws-cdk/aws-ec2/lib/volume.ts b/packages/@aws-cdk/aws-ec2/lib/volume.ts index fb45ac14b67af..3e01d3823207c 100644 --- a/packages/@aws-cdk/aws-ec2/lib/volume.ts +++ b/packages/@aws-cdk/aws-ec2/lib/volume.ts @@ -1,8 +1,7 @@ -import * as crypto from 'crypto'; - import { AccountRootPrincipal, Grant, IGrantable } from '@aws-cdk/aws-iam'; import { IKey, ViaServicePrincipal } from '@aws-cdk/aws-kms'; import { IResource, Resource, Size, SizeRoundingBehavior, Stack, Token, Tags, Names, RemovalPolicy } from '@aws-cdk/core'; +import { md5hash } from '@aws-cdk/core/lib/helpers-internal'; import { Construct } from 'constructs'; import { CfnVolume } from './ec2.generated'; import { IInstance } from './instance'; @@ -565,9 +564,7 @@ abstract class VolumeBase extends Resource implements IVolume { } private calculateResourceTagValue(constructs: Construct[]): string { - const md5 = crypto.createHash('md5'); - constructs.forEach(construct => md5.update(Names.uniqueId(construct))); - return md5.digest('hex'); + return md5hash(constructs.map(c => Names.uniqueId(c)).join('')); } } diff --git a/packages/@aws-cdk/aws-glue/lib/code.ts b/packages/@aws-cdk/aws-glue/lib/code.ts index 9f2f03d9884be..5a914064fc26f 100644 --- a/packages/@aws-cdk/aws-glue/lib/code.ts +++ b/packages/@aws-cdk/aws-glue/lib/code.ts @@ -1,9 +1,9 @@ -import * as crypto from 'crypto'; import * as fs from 'fs'; import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; import * as s3assets from '@aws-cdk/aws-s3-assets'; import * as cdk from '@aws-cdk/core'; +import { md5hash } from '@aws-cdk/core/lib/helpers-internal'; import * as constructs from 'constructs'; /** @@ -95,9 +95,7 @@ export class AssetCode extends Code { * Hash a string */ private hashcode(s: string): string { - const hash = crypto.createHash('md5'); - hash.update(s); - return hash.digest('hex'); + return md5hash(s); }; } diff --git a/packages/@aws-cdk/aws-lambda-event-sources/lib/kafka.ts b/packages/@aws-cdk/aws-lambda-event-sources/lib/kafka.ts index 39acc9fd414a4..975cf799c7220 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/lib/kafka.ts +++ b/packages/@aws-cdk/aws-lambda-event-sources/lib/kafka.ts @@ -1,4 +1,4 @@ -import * as crypto from 'crypto'; +import { md5hash } from '@aws-cdk/core/lib/helpers-internal'; import { ISecurityGroup, IVpc, SubnetSelection } from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; @@ -219,9 +219,7 @@ export class SelfManagedKafkaEventSource extends StreamEventSource { } private mappingId(target: lambda.IFunction) { - let hash = crypto.createHash('md5'); - hash.update(JSON.stringify(Stack.of(target).resolve(this.innerProps.bootstrapServers))); - const idHash = hash.digest('hex'); + const idHash = md5hash(JSON.stringify(Stack.of(target).resolve(this.innerProps.bootstrapServers))); return `KafkaEventSource:${idHash}:${this.innerProps.topic}`; } diff --git a/packages/@aws-cdk/aws-lambda/lib/function-hash.ts b/packages/@aws-cdk/aws-lambda/lib/function-hash.ts index 952f8f43eed8a..0d90d1974f514 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-hash.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-hash.ts @@ -1,5 +1,5 @@ -import * as crypto from 'crypto'; import { CfnResource, FeatureFlags, Stack } from '@aws-cdk/core'; +import { md5hash } from '@aws-cdk/core/lib/helpers-internal'; import { LAMBDA_RECOGNIZE_LAYER_VERSION, LAMBDA_RECOGNIZE_VERSION_PROPS } from '@aws-cdk/cx-api'; import { Function as LambdaFunction } from './function'; import { ILayerVersion } from './layers'; @@ -34,9 +34,7 @@ export function calculateFunctionHash(fn: LambdaFunction) { stringifiedConfig = stringifiedConfig + calculateLayersHash(fn._layers); } - const hash = crypto.createHash('md5'); - hash.update(stringifiedConfig); - return hash.digest('hex'); + return md5hash(stringifiedConfig); } export function trimFromStart(s: string, maxLength: number) { @@ -146,7 +144,5 @@ function calculateLayersHash(layers: ILayerVersion[]): string { layerConfig[layer.node.id] = properties; } - const hash = crypto.createHash('md5'); - hash.update(JSON.stringify(layerConfig)); - return hash.digest('hex'); + return md5hash(JSON.stringify(layerConfig)); } diff --git a/packages/@aws-cdk/aws-rds/lib/database-secret.ts b/packages/@aws-cdk/aws-rds/lib/database-secret.ts index 25b691c92f86b..8eb2dc91329ba 100644 --- a/packages/@aws-cdk/aws-rds/lib/database-secret.ts +++ b/packages/@aws-cdk/aws-rds/lib/database-secret.ts @@ -1,7 +1,7 @@ -import * as crypto from 'crypto'; import * as kms from '@aws-cdk/aws-kms'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import { Aws, Names } from '@aws-cdk/core'; +import { md5hash } from '@aws-cdk/core/lib/helpers-internal'; import { Construct } from 'constructs'; import { DEFAULT_PASSWORD_EXCLUDE_CHARS } from './private/util'; @@ -88,14 +88,13 @@ export class DatabaseSecret extends secretsmanager.Secret { }); if (props.replaceOnPasswordCriteriaChanges) { - const hash = crypto.createHash('md5'); - hash.update(JSON.stringify({ + const hash = md5hash(JSON.stringify({ // Use here the options that influence the password generation. // If at some point we add other password customization options // they sould be added here below (e.g. `passwordLength`). excludeCharacters, })); - const logicalId = `${Names.uniqueId(this)}${hash.digest('hex')}`; + const logicalId = `${Names.uniqueId(this)}${hash}`; const secret = this.node.defaultChild as secretsmanager.CfnSecret; secret.overrideLogicalId(logicalId.slice(-255)); // Take last 255 chars diff --git a/packages/@aws-cdk/aws-route53-patterns/lib/website-redirect.ts b/packages/@aws-cdk/aws-route53-patterns/lib/website-redirect.ts index c5a1b97ef4e74..20f0eacd8ec2e 100644 --- a/packages/@aws-cdk/aws-route53-patterns/lib/website-redirect.ts +++ b/packages/@aws-cdk/aws-route53-patterns/lib/website-redirect.ts @@ -1,4 +1,4 @@ -import * as crypto from 'crypto'; +import { md5hash } from '@aws-cdk/core/lib/helpers-internal'; import { DnsValidatedCertificate, ICertificate } from '@aws-cdk/aws-certificatemanager'; import { CloudFrontWebDistribution, OriginProtocolPolicy, PriceClass, ViewerCertificate, ViewerProtocolPolicy } from '@aws-cdk/aws-cloudfront'; import { ARecord, AaaaRecord, IHostedZone, RecordTarget } from '@aws-cdk/aws-route53'; @@ -97,7 +97,7 @@ export class HttpsRedirect extends Construct { }); domainNames.forEach((domainName) => { - const hash = crypto.createHash('md5').update(domainName).digest('hex').slice(0, 6); + const hash = md5hash(domainName).slice(0, 6); const aliasProps = { recordName: domainName, zone: props.zone, diff --git a/packages/@aws-cdk/aws-route53/lib/vpc-endpoint-service-domain-name.ts b/packages/@aws-cdk/aws-route53/lib/vpc-endpoint-service-domain-name.ts index f9998739fa5c7..210e47f071df5 100644 --- a/packages/@aws-cdk/aws-route53/lib/vpc-endpoint-service-domain-name.ts +++ b/packages/@aws-cdk/aws-route53/lib/vpc-endpoint-service-domain-name.ts @@ -1,4 +1,4 @@ -import * as crypto from 'crypto'; +import { md5hash } from '@aws-cdk/core/lib/helpers-internal'; import { IVpcEndpointService } from '@aws-cdk/aws-ec2'; import { Fn, Names, Stack } from '@aws-cdk/core'; import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from '@aws-cdk/custom-resources'; @@ -225,7 +225,5 @@ interface PrivateDnsConfiguration { * Hash a string */ function hashcode(s: string): string { - const hash = crypto.createHash('md5'); - hash.update(s); - return hash.digest('hex'); + const hash = md5hash(s); }; \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/helpers-internal/index.ts b/packages/@aws-cdk/core/lib/helpers-internal/index.ts index 217b9a9610dfd..292ff60fd2270 100644 --- a/packages/@aws-cdk/core/lib/helpers-internal/index.ts +++ b/packages/@aws-cdk/core/lib/helpers-internal/index.ts @@ -1 +1,4 @@ -export * from './cfn-parse'; \ No newline at end of file +export * from './cfn-parse'; + +// Other libraries are going to need this as well +export { md5hash } from '../private/md5'; \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/private/md5.ts b/packages/@aws-cdk/core/lib/private/md5.ts new file mode 100644 index 0000000000000..1f5a8c1efa7fd --- /dev/null +++ b/packages/@aws-cdk/core/lib/private/md5.ts @@ -0,0 +1,191 @@ +/** + * md5 hashing + * + * Uses the built-in 'crypto' library by default for a C implementation + * of md5, but in case the crypto library has been compiled to disable + * FIPS-noncompliant hash suites, fall back to a pure JS implementation of + * md5. + */ +import * as crypto from 'crypto'; + +let _impl: undefined | ((x: string) => string); + +/** + * Return a hash of the given input string, in hex format + */ +export function md5hash(x: string) { + if (!_impl) { + try { + crypto.createHash('md5'); + _impl = cryptoMd5; + } catch (e) { + _impl = jsMd5; + } + } + return _impl(x); +} + +/* eslint-disable no-bitwise */ +/* eslint-disable no-restricted-syntax */ + +export function cryptoMd5(x: string) { + const hash = crypto.createHash('md5'); + hash.update(x); + return hash.digest('hex'); +} + +export function jsMd5(s: string) { + return hex(md51(s)); +} + +function md5cycle(x: number[], k: number[]) { + let a = x[0], b = x[1], c = x[2], d = x[3]; + + a = ff(a, b, c, d, k[0], 7, -680876936); + d = ff(d, a, b, c, k[1], 12, -389564586); + c = ff(c, d, a, b, k[2], 17, 606105819); + b = ff(b, c, d, a, k[3], 22, -1044525330); + a = ff(a, b, c, d, k[4], 7, -176418897); + d = ff(d, a, b, c, k[5], 12, 1200080426); + c = ff(c, d, a, b, k[6], 17, -1473231341); + b = ff(b, c, d, a, k[7], 22, -45705983); + a = ff(a, b, c, d, k[8], 7, 1770035416); + d = ff(d, a, b, c, k[9], 12, -1958414417); + c = ff(c, d, a, b, k[10], 17, -42063); + b = ff(b, c, d, a, k[11], 22, -1990404162); + a = ff(a, b, c, d, k[12], 7, 1804603682); + d = ff(d, a, b, c, k[13], 12, -40341101); + c = ff(c, d, a, b, k[14], 17, -1502002290); + b = ff(b, c, d, a, k[15], 22, 1236535329); + + a = gg(a, b, c, d, k[1], 5, -165796510); + d = gg(d, a, b, c, k[6], 9, -1069501632); + c = gg(c, d, a, b, k[11], 14, 643717713); + b = gg(b, c, d, a, k[0], 20, -373897302); + a = gg(a, b, c, d, k[5], 5, -701558691); + d = gg(d, a, b, c, k[10], 9, 38016083); + c = gg(c, d, a, b, k[15], 14, -660478335); + b = gg(b, c, d, a, k[4], 20, -405537848); + a = gg(a, b, c, d, k[9], 5, 568446438); + d = gg(d, a, b, c, k[14], 9, -1019803690); + c = gg(c, d, a, b, k[3], 14, -187363961); + b = gg(b, c, d, a, k[8], 20, 1163531501); + a = gg(a, b, c, d, k[13], 5, -1444681467); + d = gg(d, a, b, c, k[2], 9, -51403784); + c = gg(c, d, a, b, k[7], 14, 1735328473); + b = gg(b, c, d, a, k[12], 20, -1926607734); + + a = hh(a, b, c, d, k[5], 4, -378558); + d = hh(d, a, b, c, k[8], 11, -2022574463); + c = hh(c, d, a, b, k[11], 16, 1839030562); + b = hh(b, c, d, a, k[14], 23, -35309556); + a = hh(a, b, c, d, k[1], 4, -1530992060); + d = hh(d, a, b, c, k[4], 11, 1272893353); + c = hh(c, d, a, b, k[7], 16, -155497632); + b = hh(b, c, d, a, k[10], 23, -1094730640); + a = hh(a, b, c, d, k[13], 4, 681279174); + d = hh(d, a, b, c, k[0], 11, -358537222); + c = hh(c, d, a, b, k[3], 16, -722521979); + b = hh(b, c, d, a, k[6], 23, 76029189); + a = hh(a, b, c, d, k[9], 4, -640364487); + d = hh(d, a, b, c, k[12], 11, -421815835); + c = hh(c, d, a, b, k[15], 16, 530742520); + b = hh(b, c, d, a, k[2], 23, -995338651); + + a = ii(a, b, c, d, k[0], 6, -198630844); + d = ii(d, a, b, c, k[7], 10, 1126891415); + c = ii(c, d, a, b, k[14], 15, -1416354905); + b = ii(b, c, d, a, k[5], 21, -57434055); + a = ii(a, b, c, d, k[12], 6, 1700485571); + d = ii(d, a, b, c, k[3], 10, -1894986606); + c = ii(c, d, a, b, k[10], 15, -1051523); + b = ii(b, c, d, a, k[1], 21, -2054922799); + a = ii(a, b, c, d, k[8], 6, 1873313359); + d = ii(d, a, b, c, k[15], 10, -30611744); + c = ii(c, d, a, b, k[6], 15, -1560198380); + b = ii(b, c, d, a, k[13], 21, 1309151649); + a = ii(a, b, c, d, k[4], 6, -145523070); + d = ii(d, a, b, c, k[11], 10, -1120210379); + c = ii(c, d, a, b, k[2], 15, 718787259); + b = ii(b, c, d, a, k[9], 21, -343485551); + + x[0] = add32(a, x[0]); + x[1] = add32(b, x[1]); + x[2] = add32(c, x[2]); + x[3] = add32(d, x[3]); +} + +function cmn(q: number, a: number, b: number, x: number, s: number, t: number) { + a = add32(add32(a, q), add32(x, t)); + return add32((a << s) | (a >>> (32 - s)), b); +} + +function ff(a: number, b: number, c: number, d: number, x: number, s: number, t: number) { + return cmn((b & c) | (~b & d), a, b, x, s, t); +} + +function gg(a: number, b: number, c: number, d: number, x: number, s: number, t: number) { + return cmn((b & d) | (c & ~d), a, b, x, s, t); +} + +function hh(a: number, b: number, c: number, d: number, x: number, s: number, t: number) { + return cmn(b ^ c ^ d, a, b, x, s, t); +} + +function ii(a: number, b: number, c: number, d: number, x: number, s: number, t: number) { + return cmn(c ^ (b | ~d), a, b, x, s, t); +} + +function md51(s: string) { + let n = s.length, + state = [1732584193, -271733879, -1732584194, 271733878], + i; + for (i = 64; i <= s.length; i += 64) { + md5cycle(state, md5blk(s.substring(i - 64, i))); + } + s = s.substring(i - 64); + let tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + for (i = 0; i < s.length; i++) { + tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3); + } + tail[i >> 2] |= 0x80 << (i % 4 << 3); + if (i > 55) { + md5cycle(state, tail); + for (i = 0; i < 16; i++) tail[i] = 0; + } + tail[14] = n * 8; + md5cycle(state, tail); + return state; +} + +function md5blk(s: string) { + let md5blks = [], i; + for (i = 0; i < 64; i += 4) { + md5blks[i >> 2] = + s.charCodeAt(i) + + (s.charCodeAt(i + 1) << 8) + + (s.charCodeAt(i + 2) << 16) + + (s.charCodeAt(i + 3) << 24); + } + return md5blks; +} + +let hex_chr = '0123456789abcdef'.split(''); + +function rhex(n: number) { + let s = '', j = 0; + for (; j < 4; j++) { + s += hex_chr[(n >> (j * 8 + 4)) & 0x0f] + hex_chr[(n >> (j * 8)) & 0x0f]; + } + return s; +} + +function hex(x: number[]) { + const ret = new Array(x.length); + for (let i = 0; i < x.length; i++) ret[i] = rhex(x[i]); + return ret.join(''); +} + +function add32(a: number, b: number) { + return (a + b) & 0xffffffff; +} \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/private/unique-resource-name.ts b/packages/@aws-cdk/core/lib/private/unique-resource-name.ts index cf816dc9a5758..938d25eedb79c 100644 --- a/packages/@aws-cdk/core/lib/private/unique-resource-name.ts +++ b/packages/@aws-cdk/core/lib/private/unique-resource-name.ts @@ -1,5 +1,4 @@ -import { createHash } from 'crypto'; -// import { unresolved } from './encoding'; +import { md5hash } from './md5'; /** * Options for creating a unique resource name. @@ -86,7 +85,7 @@ export function makeUniqueResourceName(components: string[], options: MakeUnique * The hash is limited in size. */ function pathHash(path: string[]): string { - const md5 = createHash('md5').update(path.join(PATH_SEP)).digest('hex'); + const md5 = md5hash(path.join(PATH_SEP)); return md5.slice(0, HASH_LEN).toUpperCase(); } diff --git a/packages/@aws-cdk/core/lib/private/uniqueid.ts b/packages/@aws-cdk/core/lib/private/uniqueid.ts index c9d1d333891db..e4ee21a264b47 100644 --- a/packages/@aws-cdk/core/lib/private/uniqueid.ts +++ b/packages/@aws-cdk/core/lib/private/uniqueid.ts @@ -1,5 +1,5 @@ -import * as crypto from 'crypto'; import { unresolved } from './encoding'; +import { md5hash } from './md5'; /** * Resources with this ID are hidden from humans @@ -76,7 +76,7 @@ export function makeUniqueId(components: string[]) { * The hash is limited in size. */ function pathHash(path: string[]): string { - const md5 = crypto.createHash('md5').update(path.join(PATH_SEP)).digest('hex'); + const md5 = md5hash(path.join(PATH_SEP)); return md5.slice(0, HASH_LEN).toUpperCase(); } diff --git a/packages/@aws-cdk/core/test/private/md5.test.ts b/packages/@aws-cdk/core/test/private/md5.test.ts new file mode 100644 index 0000000000000..a2f68d2fbf7c4 --- /dev/null +++ b/packages/@aws-cdk/core/test/private/md5.test.ts @@ -0,0 +1,9 @@ +import { jsMd5, cryptoMd5 } from '../../lib/private/md5'; + +test.each([ + '', + 'asdf', + 'hello', +])('test md5 equality for %p', s => { + expect(jsMd5(s)).toEqual(cryptoMd5(s)); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/core/test/private/unique-resource-name.test.ts b/packages/@aws-cdk/core/test/private/unique-resource-name.test.ts index c2dcdeff445bc..1de8fab4b92f8 100644 --- a/packages/@aws-cdk/core/test/private/unique-resource-name.test.ts +++ b/packages/@aws-cdk/core/test/private/unique-resource-name.test.ts @@ -1,8 +1,8 @@ -import { createHash } from 'crypto'; +import { md5hash } from '../../lib/helpers-internal'; import { makeUniqueResourceName } from '../../lib/private/unique-resource-name'; const pathHash = (path: string[]): string => { - return createHash('md5').update(path.join('/')).digest('hex').slice(0, 8).toUpperCase(); + return md5hash(path.join('/')).slice(0, 8).toUpperCase(); }; describe('makeUniqueResourceName tests', () => { diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/private/hash.ts b/packages/@aws-cdk/integ-tests/lib/assertions/private/hash.ts index 38649bbea4473..0dc4b4ad5f5c9 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/private/hash.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/private/hash.ts @@ -1,10 +1,8 @@ -import * as crypto from 'crypto'; +import { md5hash as coreMd5 } from '@aws-cdk/core/lib/helpers-internal'; export function md5hash(obj: any): string { if (!obj || (typeof(obj) === 'object' && Object.keys(obj).length === 0)) { throw new Error('Cannot compute md5 hash for falsy object'); } - const hash = crypto.createHash('md5'); - hash.update(JSON.stringify(obj)); - return hash.digest('hex'); + return coreMd5(JSON.stringify(obj)); } diff --git a/tools/@aws-cdk/cdk-build-tools/config/eslintrc.js b/tools/@aws-cdk/cdk-build-tools/config/eslintrc.js index e7abc89b0f610..61ab478724fed 100644 --- a/tools/@aws-cdk/cdk-build-tools/config/eslintrc.js +++ b/tools/@aws-cdk/cdk-build-tools/config/eslintrc.js @@ -153,6 +153,16 @@ module.exports = { // Are you sure | is not a typo for || ? 'no-bitwise': ['error'], + // No more md5, will break in FIPS environments + "no-restricted-syntax": [ + "error", + { + // Both qualified and unqualified calls + "selector": "CallExpression:matches([callee.name='createHash'], [callee.property.name='createHash']) Literal[value='md5']", + "message": "Use the md5hash() function from the core library if you want md5" + } + ], + // Oh ho ho naming. Everyone's favorite topic! // FIXME: there's no way to do this properly. The proposed tslint replacement // works very differently, also checking names in object literals, which we use all over the