/
signing.ts
158 lines (138 loc) · 4.65 KB
/
signing.ts
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import { assert, sha256, timingSafeEqual } from "@ndn/util";
import { SigType } from "../an";
import { KeyLocator } from "../key-locator";
import type { Name } from "../name/mod";
import { SigInfo } from "../sig-info";
/**
* Low level signing function.
* @param input - Buffer of signed portion.
* @returns Promise resolves to signature value or rejects with error.
*/
export type LLSign = (input: Uint8Array) => Promise<Uint8Array>;
export namespace LLSign {
export const OP = Symbol("@ndn/packet#LLSign.OP");
/** Target packet compatible with low level signing function. */
export interface Signable {
[OP]: (signer: LLSign) => Promise<void>;
}
}
/**
* Low level verification function.
* @param input - Buffer of signed portion.
* @param sig - Buffer of signature value.
* @returns Promise resolves upon good signature or rejects upon bad signature.
*/
export type LLVerify = (input: Uint8Array, sig: Uint8Array) => Promise<void>;
export namespace LLVerify {
export const OP = Symbol("@ndn/packet#LLVerify.OP");
/** Target packet compatible with low level verification function. */
export interface Verifiable {
[OP]: (verifier: LLVerify) => Promise<void>;
}
}
interface PacketWithSignature {
readonly name: Name;
sigInfo?: SigInfo;
sigValue: Uint8Array;
}
/** High level signer, such as a named private key. */
export interface Signer {
/** Sign a packet. */
sign: (pkt: Signer.Signable) => Promise<void>;
}
export namespace Signer {
/** Target packet compatible with high level signer. */
export interface Signable extends PacketWithSignature, LLSign.Signable {}
/**
* Put SigInfo on packet if it does not exist.
* @param pkt - Target packet.
* @param sigType - Optionally set sigType.
* @param keyLocator - Optionally set keyLocator; `false` to delete KeyLocator.
* @returns Existing or modified SigInfo.
*/
export function putSigInfo(pkt: PacketWithSignature, sigType?: number, keyLocator?: KeyLocator.CtorArg | false): SigInfo {
pkt.sigInfo ??= new SigInfo();
if (sigType !== undefined) {
pkt.sigInfo.type = sigType;
}
if (keyLocator === false) {
pkt.sigInfo.keyLocator = undefined;
} else if (keyLocator !== undefined) {
pkt.sigInfo.keyLocator = new KeyLocator(keyLocator);
}
return pkt.sigInfo;
}
/**
* Create a Signer that signs a packet only if it does not already have a non-Null signature.
* @param signer - Inner signer.
*/
export function onlyIfUnsigned(signer: Signer): Signer {
return {
async sign(pkt) {
if (!pkt.sigInfo || pkt.sigInfo.type === SigType.Null) {
await signer.sign(pkt);
}
},
};
}
}
/** High level verifier, such as a named public key. */
export interface Verifier {
/**
* Verify a packet.
* @returns Promise resolves upon good signature/policy or rejects upon bad signature/policy.
*/
verify: (pkt: Verifier.Verifiable) => Promise<void>;
}
export namespace Verifier {
/** Target packet compatible with high level verifier. */
export interface Verifiable extends Readonly<PacketWithSignature>, LLVerify.Verifiable {}
/**
* Ensure packet has the correct SigType.
*
* @throws Error
* Thrown if `pkt` lacks SigInfo or its SigType differs from `expectedSigType`.
*/
export function checkSigType(pkt: Readonly<PacketWithSignature>, expectedSigType: number) {
assert(pkt.sigInfo?.type === expectedSigType, `packet does not have SigType ${expectedSigType}`);
}
/** Throw bad signature error if not OK. */
export function throwOnBadSig(ok: boolean): asserts ok {
assert(ok, "bad signature value");
}
}
/** Signer and Verifier that do nothing. */
export const noopSigning: Signer & Verifier = {
sign() {
return Promise.resolve();
},
verify() {
return Promise.resolve();
},
};
/** Signer and Verifier for SigType.Sha256 digest. */
export const digestSigning: Signer & Verifier = {
sign(pkt: Signer.Signable): Promise<void> {
Signer.putSigInfo(pkt, SigType.Sha256, false);
return pkt[LLSign.OP]((input) => sha256(input));
},
async verify(pkt: Verifier.Verifiable): Promise<void> {
Verifier.checkSigType(pkt, SigType.Sha256);
return pkt[LLVerify.OP](async (input, sig) => {
const h = await sha256(input);
const ok = timingSafeEqual(sig, h);
Verifier.throwOnBadSig(ok);
});
},
};
/**
* Signer for SigType.Null, a packet that is not signed.
* @see https://redmine.named-data.net/projects/ndn-tlv/wiki/NullSignature
*/
export const nullSigner: Signer = {
sign(pkt: Signer.Signable): Promise<void> {
Signer.putSigInfo(pkt, SigType.Null, false);
pkt.sigValue = new Uint8Array();
return Promise.resolve();
},
};