Skip to content

Commit 9d5fa7f

Browse files
committed
External signatures
1 parent e2685b7 commit 9d5fa7f

File tree

5 files changed

+75
-25
lines changed

5 files changed

+75
-25
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,31 @@ const resp = new TimeStampResp(
101101
signingOptions: {
102102
// Signing options
103103
signingHashAlgorithm: "SHA256"|"SHA384"|"SHA512",
104+
105+
// Enable external signatures
106+
externalSignature: boolean
104107
}
105108
}
106109
);
107110
```
108111

112+
### External signatures
113+
114+
The `externalSignature` option can be used to enable external signatures. When this option is enabled, the `sign` method will not sign the response.
115+
116+
```javascript
117+
const tsr = new TimeStampResp({
118+
...,
119+
signingOptions: {
120+
externalSignature: true
121+
}
122+
});
123+
124+
const signature = Buffer.from("0123456789abcdef", "hex");
125+
126+
tsr.setSignature(signature);
127+
```
128+
109129
The `TimeStampResp` constructor returns an instance of `TimeStampResp`.
110130
The `buffer` property of the `TimeStampResp` object contains the DER-encoded response.
111131

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@xevolab/timestamping-token",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "Time Stamping Authority (TSA) implementation.",
55
"keywords": [
66
"Timestamping Authority",

src/query.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Author : Francesco
33
* Created at: 2023-12-09 17:52
44
* Edited by : Francesco
5-
* Edited at : 2023-12-10 10:13
5+
* Edited at : 2023-12-13 20:20
66
*
77
* Copyright (c) 2023 Xevolab S.R.L.
88
*/
@@ -11,7 +11,7 @@ import * as asn1js from "asn1js";
1111
import { KeyObject, X509Certificate } from "crypto";
1212

1313
import { hashAlgIdentifier, hashAlgOID, parseOID } from "./parseOID";
14-
import { TimeStampResp } from "./response";
14+
import { TimeStampResp, SignOptions } from "./response";
1515

1616
export type RequestObject = {
1717
version: number,
@@ -151,18 +151,20 @@ export class TimeStampReq {
151151
}
152152

153153
sign(params: {
154-
key: KeyObject,
154+
key?: KeyObject,
155155
certs?: X509Certificate[],
156-
}): TimeStampResp {
156+
}, signingOptions?: SignOptions): TimeStampResp {
157157
if (!this.request) throw new Error("Request not initialized.");
158158

159159
return new TimeStampResp(this.request.messageImprint.hashedMessage, {
160160
hashAlgorithm: this.request.messageImprint.hashAlgorithm,
161161
nonce: this.request.nonce,
162162
certReq: this.request.certReq,
163163

164-
key: params.key,
164+
key: params.key || undefined,
165165
certs: params.certs,
166+
167+
signingOptions
166168
});
167169
}
168170

src/response.ts

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Author : Francesco
33
* Created at: 2023-12-09 17:52
44
* Edited by : Francesco
5-
* Edited at : 2023-12-10 10:06
5+
* Edited at : 2023-12-13 20:41
66
*
77
* Copyright (c) 2023 Xevolab S.R.L.
88
*/
@@ -23,14 +23,18 @@ export class TimeStampResp {
2323
public certReq?: boolean;
2424

2525
// Credentials
26-
private key: KeyObject;
26+
private key?: KeyObject;
2727
public certs?: X509Certificate[];
2828

2929
// Response
3030
public signingTime!: Date;
3131
public serialNumber!: Buffer;
3232
public buffer!: Buffer;
3333

34+
// Signature
35+
public payloadDigest?: Buffer;
36+
public signedDigest?: Buffer;
37+
3438
/**
3539
* Creates an instance of TimeStampResp.
3640
* This class is used to generate a response
@@ -62,16 +66,16 @@ export class TimeStampResp {
6266
nonce?: number | Buffer | Uint8Array,
6367
certReq?: boolean,
6468

65-
key: KeyObject,
69+
key?: KeyObject,
6670
certs?: X509Certificate[],
6771

6872
signingOptions?: SignOptions,
6973
}) {
7074
this.hashedMessage = hashedMessage;
7175
this.hashAlgorithm = hashAlgorithm;
7276

73-
this.version = version || 2;
74-
this.reqPolicy = reqPolicy;
77+
this.version = version || 1;
78+
this.reqPolicy = reqPolicy || "1.2.3.4";
7579
this.nonce = nonce;
7680
this.certReq = certReq;
7781

@@ -110,7 +114,8 @@ export class TimeStampResp {
110114

111115
// --> Loading the key(s)
112116

113-
if (!key || !(key instanceof KeyObject)) throw new Error("Invalid key; needs to be a KeyObject.");
117+
if (key && !(key instanceof KeyObject)) throw new Error("Invalid key; needs to be a KeyObject.");
118+
if (!key && !opts?.externalSignature) throw new Error("Missing key. Please, provide a key or enable external signature function.");
114119

115120
// --> Loading the certificates
116121

@@ -145,7 +150,7 @@ export class TimeStampResp {
145150
// version
146151
// TODO: support v1 and get version from request
147152
// NOTE: is it really needed?
148-
new asn1js.Integer({ value: 2 }),
153+
new asn1js.Integer({ value: this.version }),
149154

150155
/**
151156
* TSAPolicyId ::= OBJECT IDENTIFIER
@@ -244,15 +249,24 @@ export class TimeStampResp {
244249
]
245250
});
246251

247-
const messageDigest = createHash("SHA-512").update(Buffer.from(payload.toBER(false))).digest("hex");
252+
this.payloadDigest = createHash(signingHashAlgorithm).update(Buffer.from(payload.toBER(false))).digest();
248253

249254
// --> Generating the signature
250255

251256
// The signature is generated using the private key of the TSA, by signing the DER encoded
252257
// payload and, if present, the signed attributes.
253-
const signature = sign("SHA512", Buffer.from((new asn1js.Set({
254-
value: getSignedAttributes(messageDigest, this.signingTime, certs),
255-
}).toBER(false))), keyForAlg(key));
258+
let signature = Buffer.from((new asn1js.Set({
259+
value: getSignedAttributes(this.payloadDigest, this.signingTime, certs),
260+
}).toBER(false)));
261+
this.signedDigest = createHash(signingHashAlgorithm).update(signature).digest()
262+
263+
// If the signature is generated using an external signer, the signature is filled with only
264+
// the hash of the payload.
265+
if (opts?.externalSignature || !key) {
266+
signature = Buffer.from("00", "hex");
267+
} else {
268+
signature = sign(signingHashAlgorithm, signature, keyForAlg(key));
269+
}
256270

257271
/**
258272
* TimeStampResp ::= SEQUENCE {
@@ -506,7 +520,7 @@ export class TimeStampResp {
506520
*/
507521
new asn1js.Constructed({
508522
idBlock: { tagClass: 3, tagNumber: 0 },
509-
value: getSignedAttributes(messageDigest, this.signingTime, certs),
523+
value: getSignedAttributes(this.payloadDigest, this.signingTime, certs),
510524
}),
511525

512526
/**
@@ -525,7 +539,7 @@ export class TimeStampResp {
525539
* SignatureValue ::= OCTET STRING
526540
*/
527541
new asn1js.OctetString({
528-
valueHex: signature.buffer
542+
valueHex: signature
529543
}),
530544

531545
]
@@ -541,10 +555,24 @@ export class TimeStampResp {
541555
return Buffer.from(TimeStampResp.toBER(false));
542556
}
543557

558+
public setSignature(signature: Buffer) {
559+
const tmp = asn1js.fromBER(this.buffer);
560+
// @ts-ignore
561+
let ref = tmp.result.valueBlock.value[1].valueBlock.value[1].valueBlock.value[0].valueBlock.value;
562+
ref = ref.at(-1).valueBlock.value[0].valueBlock.value.at(-1);
563+
console.log(Buffer.from(ref.valueBlock.valueHexView));
564+
565+
ref.valueBlock.valueHexView = signature
566+
567+
this.buffer = Buffer.from(tmp.result.toBER(false));
568+
}
569+
544570
}
545571

546-
type SignOptions = {
547-
signingHashAlgorithm: "SHA512",
572+
export type SignOptions = {
573+
signingHashAlgorithm?: "SHA256" | "SHA384" | "SHA512",
574+
575+
externalSignature?: boolean
548576
}
549577

550578
// ---------------------------------------------------------------------------------------------
@@ -561,7 +589,7 @@ function keyForAlg(key: KeyObject): KeyObject | SignKeyObjectInput {
561589
throw new TypeError(`key is is not supported`);
562590
}
563591

564-
function getSignedAttributes(messageHash: string, signingTime: Date, certs: X509Certificate[] = []) {
592+
function getSignedAttributes(messageHash: Buffer, signingTime: Date, certs: X509Certificate[] = []) {
565593
return [
566594
// contentType
567595
new asn1js.Sequence({
@@ -601,7 +629,7 @@ function getSignedAttributes(messageHash: string, signingTime: Date, certs: X509
601629
}),
602630
new asn1js.Set({
603631
value: [
604-
new asn1js.OctetString({ valueHex: Buffer.from(messageHash, "hex") })
632+
new asn1js.OctetString({ valueHex: messageHash })
605633
]
606634
})
607635
]

0 commit comments

Comments
 (0)