Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | 41x 41x 41x 41x 41x 41x 41x 12x 1x 11x 11x 13x 13x 13x 13x 13x 13x 13x 13x 13x 2x 2x 11x | import base64url from './base64url';
import { hashSha256 } from 'micro-stacks/crypto-sha';
import { sign, signSync } from '@noble/secp256k1';
import { derToJoseES256 } from './ecdsa-sig-formatter';
import { Json, SignedToken } from './types';
import { createSigningInput } from './create-signing-input';
import { MissingParametersError, utf8ToBytes } from 'micro-stacks/common';
export class TokenSigner {
tokenType: string;
rawPrivateKey: string;
constructor(signingAlgorithm = 'ES256K', rawPrivateKey: string) {
if (!rawPrivateKey)
throw new MissingParametersError('TokenSigner: rawPrivateKey is required to sign a token');
this.tokenType = 'JWT';
this.rawPrivateKey = rawPrivateKey;
}
header(header = {}) {
const defaultHeader = {
typ: this.tokenType,
alg: 'ES256K',
};
return {
...defaultHeader,
...header,
};
}
async sign(payload: Json): Promise<string>;
async sign(payload: Json, expanded: true, customHeader?: Json): Promise<SignedToken>;
async sign(payload: Json, expanded: false, customHeader?: Json): Promise<string>;
async sign(
payload: Json,
expanded = false,
customHeader: Json = {}
): Promise<SignedToken | string> {
// generate the token header
const header = this.header(customHeader as unknown as {});
// prepare the message to be signed
const signingInput = createSigningInput(payload, header);
const signingInputHash = hashSha256(utf8ToBytes(signingInput));
return this.createWithSignedHash(payload, expanded, header, signingInput, signingInputHash);
}
signSync(payload: Json): string;
signSync(payload: Json, expanded: true, customHeader?: Json): SignedToken;
signSync(payload: Json, expanded: false, customHeader?: Json): string;
signSync(payload: Json, expanded = false, customHeader: Json = {}): SignedToken | string {
// generate the token header
const header = this.header(customHeader as unknown as {});
// prepare the message to be signed
const signingInput = createSigningInput(payload, header);
const signingInputHash = hashSha256(utf8ToBytes(signingInput));
return this.createWithSignedHashSync(payload, expanded, header, signingInput, signingInputHash);
}
async createWithSignedHash(
payload: Json,
expanded: boolean,
header: { typ: string; alg: string },
signingInput: string,
signingInputHash: Uint8Array
): Promise<SignedToken | string> {
const sig = await sign(signingInputHash, this.rawPrivateKey, {
// whether a signature s should be no more than 1/2 prime order.
// true makes signatures compatible with libsecp256k1
// false makes signatures compatible with openssl <-- stacks currently uses this
canonical: false,
// https://github.com/paulmillr/noble-secp256k1#signmsghash-privatekey
// additional entropy k' for deterministic signature, follows section 3.6 of RFC6979. When true, it would automatically be filled with 32 bytes of cryptographically secure entropy
// TODO: how can we make this default true?
// extraEntropy: false,
});
const formatted: string = derToJoseES256(sig);
if (expanded) {
const signedToken: SignedToken = {
header: [base64url.encode(JSON.stringify(header))],
payload: JSON.stringify(payload),
signature: [formatted],
};
return signedToken;
} else {
return [signingInput, formatted].join('.');
}
}
createWithSignedHashSync(
payload: Json,
expanded: boolean,
header: { typ: string; alg: string },
signingInput: string,
signingInputHash: Uint8Array
): SignedToken | string {
const sig = signSync(signingInputHash, this.rawPrivateKey, {
// whether a signature s should be no more than 1/2 prime order.
// true makes signatures compatible with libsecp256k1
// false makes signatures compatible with openssl <-- stacks currently uses this
canonical: false,
// https://github.com/paulmillr/noble-secp256k1#signmsghash-privatekey
// additional entropy k' for deterministic signature, follows section 3.6 of RFC6979. When true, it would automatically be filled with 32 bytes of cryptographically secure entropy
// TODO: how can we make this default true?
// extraEntropy: false,
});
const formatted: string = derToJoseES256(sig);
if (expanded) {
const signedToken: SignedToken = {
header: [base64url.encode(JSON.stringify(header))],
payload: JSON.stringify(payload),
signature: [formatted],
};
return signedToken;
} else {
return [signingInput, formatted].join('.');
}
}
}
|