@smithy/signature-v4a 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +6 -0
- package/dist-cjs/SignatureV4a.js +1 -0
- package/dist-cjs/constants.js +1 -0
- package/dist-cjs/credentialDerivation.js +1 -0
- package/dist-cjs/elliptic/Ec.js +1 -0
- package/dist-cjs/index.js +10727 -0
- package/dist-es/SignatureV4a.js +63 -0
- package/dist-es/constants.js +8 -0
- package/dist-es/credentialDerivation.js +81 -0
- package/dist-es/elliptic/Ec.js +9886 -0
- package/dist-es/index.js +4 -0
- package/dist-types/SignatureV4a.d.ts +40 -0
- package/dist-types/constants.d.ts +16 -0
- package/dist-types/credentialDerivation.d.ts +32 -0
- package/dist-types/elliptic/Ec.d.ts +2 -0
- package/dist-types/index.d.ts +1 -0
- package/dist-types/ts3.4/SignatureV4a.d.ts +40 -0
- package/dist-types/ts3.4/constants.d.ts +16 -0
- package/dist-types/ts3.4/credentialDerivation.d.ts +32 -0
- package/dist-types/ts3.4/elliptic/Ec.d.ts +2 -0
- package/dist-types/ts3.4/index.d.ts +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ALGORITHM_IDENTIFIER_V4A, AMZ_DATE_HEADER, AUTH_HEADER, SHA256_HEADER, TOKEN_HEADER, } from "@smithy/signature-v4";
|
|
2
|
+
import { getCanonicalHeaders } from "@smithy/signature-v4";
|
|
3
|
+
import { getPayloadHash } from "@smithy/signature-v4";
|
|
4
|
+
import { hasHeader } from "@smithy/signature-v4";
|
|
5
|
+
import { prepareRequest } from "@smithy/signature-v4";
|
|
6
|
+
import { SignatureV4Base } from "@smithy/signature-v4";
|
|
7
|
+
import { toHex } from "@smithy/util-hex-encoding";
|
|
8
|
+
import { toUint8Array } from "@smithy/util-utf8";
|
|
9
|
+
import { REGION_HEADER } from "./constants";
|
|
10
|
+
import { createSigV4aScope, getSigV4aSigningKey } from "./credentialDerivation";
|
|
11
|
+
import { Ec } from "./elliptic/Ec";
|
|
12
|
+
export class SignatureV4a extends SignatureV4Base {
|
|
13
|
+
constructor({ applyChecksum, credentials, region, service, sha256, uriEscapePath = true, }) {
|
|
14
|
+
super({
|
|
15
|
+
applyChecksum,
|
|
16
|
+
credentials,
|
|
17
|
+
region,
|
|
18
|
+
service,
|
|
19
|
+
sha256,
|
|
20
|
+
uriEscapePath,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async sign(toSign, options) {
|
|
24
|
+
return this.signRequest(toSign, options);
|
|
25
|
+
}
|
|
26
|
+
async signRequest(requestToSign, { signingDate = new Date(), signableHeaders, unsignableHeaders, signingRegion, signingService, } = {}) {
|
|
27
|
+
const credentials = await this.credentialProvider();
|
|
28
|
+
this.validateResolvedCredentials(credentials);
|
|
29
|
+
const region = signingRegion ?? (await this.regionProvider());
|
|
30
|
+
const request = prepareRequest(requestToSign);
|
|
31
|
+
const { longDate, shortDate } = this.formatDate(signingDate);
|
|
32
|
+
const scope = createSigV4aScope(shortDate, signingService ?? this.service);
|
|
33
|
+
const pKey = await getSigV4aSigningKey(this.sha256, credentials.accessKeyId, credentials.secretAccessKey);
|
|
34
|
+
request.headers[AMZ_DATE_HEADER] = longDate;
|
|
35
|
+
if (credentials.sessionToken) {
|
|
36
|
+
request.headers[TOKEN_HEADER] = credentials.sessionToken;
|
|
37
|
+
}
|
|
38
|
+
request.headers[REGION_HEADER] = region;
|
|
39
|
+
const payloadHash = await getPayloadHash(request, this.sha256);
|
|
40
|
+
if (!hasHeader(SHA256_HEADER, request.headers) && this.applyChecksum) {
|
|
41
|
+
request.headers[SHA256_HEADER] = payloadHash;
|
|
42
|
+
}
|
|
43
|
+
const canonicalHeaders = getCanonicalHeaders(request, unsignableHeaders, signableHeaders);
|
|
44
|
+
const canonicalRequest = this.createCanonicalRequest(request, canonicalHeaders, payloadHash);
|
|
45
|
+
const stringToSign = await this.createStringToSign(longDate, scope, canonicalRequest, ALGORITHM_IDENTIFIER_V4A);
|
|
46
|
+
const signature = await this.getSignature(pKey, stringToSign);
|
|
47
|
+
request.headers[AUTH_HEADER] =
|
|
48
|
+
`${ALGORITHM_IDENTIFIER_V4A} ` +
|
|
49
|
+
`Credential=${credentials.accessKeyId}/${scope}, ` +
|
|
50
|
+
`SignedHeaders=${this.getCanonicalHeaderList(canonicalHeaders)}, ` +
|
|
51
|
+
`Signature=${signature}`;
|
|
52
|
+
return request;
|
|
53
|
+
}
|
|
54
|
+
async getSignature(privateKey, stringToSign) {
|
|
55
|
+
const ecdsa = new Ec("p256");
|
|
56
|
+
const key = ecdsa.keyFromPrivate(privateKey);
|
|
57
|
+
const hash = new this.sha256();
|
|
58
|
+
hash.update(toUint8Array(stringToSign));
|
|
59
|
+
const hashResult = await hash.digest();
|
|
60
|
+
const signature = key.sign(hashResult);
|
|
61
|
+
return toHex(new Uint8Array(signature.toDER()));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { REGION_SET_PARAM } from "@smithy/signature-v4";
|
|
2
|
+
export const REGION_HEADER = REGION_SET_PARAM.toLowerCase();
|
|
3
|
+
export const ONE_AS_4_BYTES = [0x00, 0x00, 0x00, 0x01];
|
|
4
|
+
export const TWOFIFTYSIX_AS_4_BYTES = [0x00, 0x00, 0x01, 0x00];
|
|
5
|
+
export const N_MINUS_TWO = [
|
|
6
|
+
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa,
|
|
7
|
+
0xad, 0xa7, 0x17, 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x4f,
|
|
8
|
+
];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ALGORITHM_IDENTIFIER_V4A, KEY_TYPE_IDENTIFIER } from "@smithy/signature-v4";
|
|
2
|
+
import { toUint8Array } from "@smithy/util-utf8";
|
|
3
|
+
import { N_MINUS_TWO, ONE_AS_4_BYTES, TWOFIFTYSIX_AS_4_BYTES } from "./constants";
|
|
4
|
+
const signingKeyCache = {};
|
|
5
|
+
const cacheQueue = [];
|
|
6
|
+
export const createSigV4aScope = (shortDate, service) => `${shortDate}/${service}/${KEY_TYPE_IDENTIFIER}`;
|
|
7
|
+
export const clearCredentialCache = () => {
|
|
8
|
+
cacheQueue.length = 0;
|
|
9
|
+
Object.keys(signingKeyCache).forEach((cacheKey) => {
|
|
10
|
+
delete signingKeyCache[cacheKey];
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
export const getSigV4aSigningKey = async (sha256, accessKey, secretKey) => {
|
|
14
|
+
let outputBufferWriter = "";
|
|
15
|
+
const maxTrials = 254;
|
|
16
|
+
const aws4ALength = 5;
|
|
17
|
+
const inputKeyLength = aws4ALength + secretKey.length;
|
|
18
|
+
const inputKeyBuf = inputKeyLength <= 64 ? new Uint8Array(64) : new Uint8Array(inputKeyLength);
|
|
19
|
+
const aws4aArray = "AWS4A".split("");
|
|
20
|
+
for (let index = 0; index < aws4aArray.length; index++) {
|
|
21
|
+
inputKeyBuf[index] = aws4aArray[index].charCodeAt(0);
|
|
22
|
+
}
|
|
23
|
+
const secretKeyArray = secretKey.split("");
|
|
24
|
+
for (let index = 0; index < secretKeyArray.length; index++) {
|
|
25
|
+
inputKeyBuf[aws4aArray.length + index] = secretKeyArray[index].charCodeAt(0);
|
|
26
|
+
}
|
|
27
|
+
let trial = 1;
|
|
28
|
+
while (trial < maxTrials) {
|
|
29
|
+
outputBufferWriter = buildFixedInputBuffer(outputBufferWriter, accessKey, trial);
|
|
30
|
+
const secretKey = inputKeyBuf.subarray(0, inputKeyLength);
|
|
31
|
+
const hash = new sha256(secretKey);
|
|
32
|
+
const hashVal = toUint8Array(outputBufferWriter);
|
|
33
|
+
hash.update(hashVal);
|
|
34
|
+
const hashedOutput = await hash.digest();
|
|
35
|
+
if (isBiggerThanNMinus2(hashedOutput)) {
|
|
36
|
+
trial++;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
return addOneToArray(hashedOutput);
|
|
40
|
+
}
|
|
41
|
+
throw new Error("Cannot derive signing key: number of maximum trials exceeded.");
|
|
42
|
+
};
|
|
43
|
+
export const buildFixedInputBuffer = (bufferInput, accessKey, counter) => {
|
|
44
|
+
let outputBuffer = bufferInput;
|
|
45
|
+
outputBuffer += ONE_AS_4_BYTES.map((value) => String.fromCharCode(value)).join("");
|
|
46
|
+
outputBuffer += ALGORITHM_IDENTIFIER_V4A;
|
|
47
|
+
outputBuffer += String.fromCharCode(0x00);
|
|
48
|
+
outputBuffer += accessKey;
|
|
49
|
+
outputBuffer += String.fromCharCode(counter);
|
|
50
|
+
outputBuffer += TWOFIFTYSIX_AS_4_BYTES.map((value) => String.fromCharCode(value)).join("");
|
|
51
|
+
return outputBuffer;
|
|
52
|
+
};
|
|
53
|
+
export const isBiggerThanNMinus2 = (value) => {
|
|
54
|
+
for (let index = 0; index < value.length; index++) {
|
|
55
|
+
if (value[index] > N_MINUS_TWO[index]) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
else if (value[index] < N_MINUS_TWO[index]) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
};
|
|
64
|
+
export const addOneToArray = (value) => {
|
|
65
|
+
const output = new Uint8Array(32);
|
|
66
|
+
let carry = 1;
|
|
67
|
+
for (let index = value.length - 1; index >= 0; index--) {
|
|
68
|
+
const newValueAtIndex = (value[index] + carry) % 256;
|
|
69
|
+
if (newValueAtIndex < value[index]) {
|
|
70
|
+
carry = 1;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
carry = 0;
|
|
74
|
+
}
|
|
75
|
+
output[index] = newValueAtIndex;
|
|
76
|
+
}
|
|
77
|
+
if (carry !== 0) {
|
|
78
|
+
return new Uint8Array([carry, ...output]);
|
|
79
|
+
}
|
|
80
|
+
return output;
|
|
81
|
+
};
|