Skip to content

Commit ef7d0f7

Browse files
committed
add CertificateVerify
1 parent 6ba7ec0 commit ef7d0f7

File tree

5 files changed

+278
-50
lines changed

5 files changed

+278
-50
lines changed

deno.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tls/enum",
3-
"version": "0.3.5",
3+
"version": "0.3.6",
44
"exports": "./src/mod.ts",
55
"publish": {
66
"exclude": ["dist/"]
@@ -14,6 +14,7 @@
1414
},
1515
"imports": {
1616
"@noble/curves": "npm:@noble/curves@^1.7.0",
17+
"@noble/hashes": "npm:@noble/hashes@^1.6.1",
1718
"@peculiar/x509": "npm:@peculiar/x509@^1.12.3",
1819
"@std/assert": "jsr:@std/assert@^1.0.2",
1920
"@tls/struct": "jsr:@tls/struct@^0.3.1"

src/dep.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export { x448 } from '@noble/curves/ed448'
77
export * as utils from "@noble/curves/abstract/utils"
88
export { HexaDecimal } from "@tls/struct"
99
export * as x509 from "@peculiar/x509"
10+
export * from "@noble/hashes/sha2"
1011

src/signaturescheme.js

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// deno-lint-ignore-file no-slow-types
22
// @ts-self-types="../type/signaturescheme.d.ts"
33

4-
import { Uint16 } from "./dep.ts";
4+
import { Constrained, Struct, Uint16 } from "./dep.ts";
55
import { Enum } from "./enum.js";
6+
import { sha256 } from "@noble/hashes/sha256"
67

78
/**
89
* Enumeration of signature schemes as defined in RFC 8446.
@@ -36,8 +37,8 @@ export class SignatureScheme extends Enum {
3637
/* Legacy algorithms */
3738
static RSA_PKCS1_SHA1 = new SignatureScheme('RSA_PKCS1_SHA1', 0x0201);
3839
static ECDSA_SHA1 = new SignatureScheme('ECDSA_SHA1', 0x0203);
39-
40-
/* Reserved Code Points */
40+
41+
/* Reserved Code Points */
4142
static dsa_sha1_RESERVED = new SignatureScheme('dsa_sha1_RESERVED', 0x0202);
4243
static dsa_sha256_RESERVED = new SignatureScheme('dsa_sha256_RESERVED', 0x0402);
4344
static dsa_sha384_RESERVED = new SignatureScheme('dsa_sha384_RESERVED', 0x0502);
@@ -73,6 +74,84 @@ export class SignatureScheme extends Enum {
7374
* @returns {Uint16} The Uint16 representation of the SignatureScheme.
7475
*/
7576
get Uint16() { return Uint16.fromValue(+this); }
77+
78+
async certificateVerify(clientHelloMsg, serverHelloMsg, certificateMsg, RSAprivateKey) {
79+
const signature = await signatureFrom(clientHelloMsg, serverHelloMsg, certificateMsg, RSAprivateKey)
80+
return new CertificateVerify(this, signature)
81+
}
82+
}
83+
84+
export class CertificateVerify extends Uint8Array {
85+
static from(array) {
86+
const copy = Uint8Array.from(array)
87+
const algorithm = SignatureScheme.from(copy);
88+
const signature = Signature.from(copy.subarray(2))
89+
return new CertificateVerify(algorithm, signature.opaque)
90+
}
91+
constructor(signatureScheme, signature) {
92+
const signatureConstrained = new Signature(signature);
93+
const struct = new Struct(
94+
signatureScheme.Uint16,
95+
signatureConstrained
96+
)
97+
super(struct);
98+
this.algorithm = signatureScheme;
99+
this.signature = signature
100+
}
101+
}
102+
103+
export class Signature extends Constrained {
104+
static from(array){
105+
const copy = Uint8Array.from(array);
106+
const lengthOf = Uint16.from(copy).value;
107+
return new Signature(copy.subarray(2, 2 + lengthOf))
108+
}
109+
constructor(opaque){
110+
super(0,2**16-1, opaque)
111+
this.opaque = opaque
112+
}
113+
}
114+
115+
async function signatureFrom(clientHelloMsg, serverHelloMsg, certificateMsg, RSAprivateKey) {
116+
const leading = Uint8Array.of(
117+
//NOTE 64 space characters
118+
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
119+
//NOTE 'TLS 1.3, server CertificateVerify'
120+
84, 76, 83, 32, 49, 46, 51, 44, 32, 115, 101, 114, 118, 101, 114, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 86, 101, 114, 105, 102, 121,
121+
//NOTE single null char
122+
0
123+
)
124+
const transcriptHash = sha256.create()
125+
.update(clientHelloMsg)
126+
.update(serverHelloMsg)
127+
.update(certificateMsg)
128+
.digest();
129+
130+
const data = Struct.createFrom(
131+
leading,
132+
transcriptHash
133+
)
134+
135+
const signBuffer = await crypto.subtle.sign(
136+
{
137+
name: "RSA-PSS",// RSAprivateKey.algorithm.name,
138+
saltLength: 256 / 8
139+
},
140+
RSAprivateKey,
141+
data
142+
)
143+
144+
/* const verify = await crypto.subtle.verify(
145+
{
146+
name: "RSA-PSS",//'RSASSA-PKCS1-v1_5',
147+
saltLength: 256 / 8
148+
},
149+
RSAPublicKey, //rsapublickey in Certificate
150+
sign,
151+
data
152+
) */
153+
154+
return new Uint8Array(signBuffer)
76155
}
77156

78157
// npx -p typescript tsc ./src/signaturescheme.js --declaration --allowJs --emitDeclarationOnly --lib ESNext --outDir ./dist

test/signaturescheme_test.js

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { SignatureScheme } from "../src/signaturescheme.js";
1+
import { SignatureScheme, CertificateVerify } from "../src/signaturescheme.js";
22
import { assertEquals } from "jsr:@std/assert";
3+
import { HexaDecimal, sha256 } from "../src/dep.ts";
34

45
console.log(SignatureScheme.ED448);
56

@@ -16,3 +17,62 @@ Deno.test("SignatureAlgorithmSchema", () => {
1617
assertEquals(test, back)
1718
})
1819

20+
const clientHelloMsg = HexaDecimal.fromString(
21+
`01 00 00 c0 03 03 cb 34 ec b1 e7 81 63
22+
ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83
23+
02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b
24+
00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00
25+
12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23
26+
00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2
27+
3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a
28+
af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03
29+
02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06
30+
02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01`).byte
31+
32+
const serverHelloMsg = HexaDecimal.fromString(
33+
`02 00 00 56 03 03 a6 af 06 a4 12 18 60 dc 5e
34+
6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e d3 e2
35+
69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 76 11
36+
20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69
37+
b1 b0 4e 75 1f 0f 00 2b 00 02 03 04`).byte
38+
39+
const certificateMsg = HexaDecimal.fromString(
40+
`0b 00 01 b9 00 00 01 b5 00 01 b0 30 82
41+
01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48
42+
86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03
43+
72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17
44+
0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06
45+
03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7
46+
0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f
47+
82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26
48+
d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c
49+
1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52
50+
4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74
51+
80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93
52+
ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03
53+
01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06
54+
03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01
55+
01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a
56+
72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea
57+
e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01
58+
51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be
59+
c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b
60+
1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8
61+
96 12 29 ac 91 87 b4 2b 4d e1 00 00`).byte;
62+
63+
const rsaKey = await crypto.subtle.generateKey(
64+
{
65+
name: "RSA-PSS",
66+
hash: "SHA-256", // SHA-1, SHA-256, SHA-384, or SHA-512
67+
publicExponent: new Uint8Array([1, 0, 1]), // 0x03 or 0x010001
68+
modulusLength: 2048, // 1024, 2048, or 4096
69+
},
70+
true,
71+
["sign", "verify"],
72+
)
73+
74+
Deno.test("CertificateVerify", async () => {
75+
const test = await SignatureScheme.RSA_PSS_PSS_SHA256.certificateVerify(clientHelloMsg, serverHelloMsg, certificateMsg, rsaKey.privateKey)
76+
const back = CertificateVerify.from(test)
77+
assertEquals(test.toString(), back.toString())
78+
})

type/signaturescheme.d.ts

Lines changed: 132 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,138 @@
1-
import { Enum } from "./enum.d.ts"
2-
import { Uint16, Constrained } from "../src/dep.ts";
1+
import { Constrained, Uint16 } from "../src/dep.ts";
2+
import { Enum } from "../src/enum.js";
3+
34
/**
45
* Enumeration of signature schemes as defined in RFC 8446.
56
* @see https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.3
67
*/
78
export class SignatureScheme extends Enum {
8-
/** RSASSA-PKCS1-v1_5 algorithms */
9-
static RSA_PKCS1_SHA256: SignatureScheme;
10-
static RSA_PKCS1_SHA384: SignatureScheme;
11-
static RSA_PKCS1_SHA512: SignatureScheme;
12-
13-
/** ECDSA algorithms */
14-
static ECDSA_SECP256R1_SHA256: SignatureScheme;
15-
static ECDSA_SECP384R1_SHA384: SignatureScheme;
16-
static ECDSA_SECP521R1_SHA512: SignatureScheme;
17-
18-
/** RSASSA-PSS algorithms with public key OID rsaEncryption */
19-
static RSA_PSS_RSAE_SHA256: SignatureScheme;
20-
static RSA_PSS_RSAE_SHA384: SignatureScheme;
21-
static RSA_PSS_RSAE_SHA512: SignatureScheme;
22-
23-
/** EdDSA algorithms */
24-
static ED25519: SignatureScheme;
25-
static ED448: SignatureScheme;
26-
27-
/** RSASSA-PSS algorithms with public key OID RSASSA-PSS */
28-
static RSA_PSS_PSS_SHA256: SignatureScheme;
29-
static RSA_PSS_PSS_SHA384: SignatureScheme;
30-
static RSA_PSS_PSS_SHA512: SignatureScheme;
31-
32-
/**
33-
* Parses an octet array and returns a valid SignatureScheme.
34-
*
35-
* @static
36-
* @param {Uint8Array} octet - The octet array to parse.
37-
* @returns {SignatureScheme} The corresponding SignatureScheme instance.
38-
* @throws {Error} If the octet does not correspond to a known SignatureScheme.
39-
*/
40-
static from(octet: Uint8Array): SignatureScheme;
41-
42-
/** Returns the bit length of the SignatureScheme. */
43-
get bit(): number;
44-
45-
/**
46-
* Converts the SignatureScheme to a Uint16 representation.
47-
*
48-
* @returns {Uint16} The Uint16 representation of the SignatureScheme.
49-
*/
50-
get Uint16(): Uint16;
9+
/** RSASSA-PKCS1-v1_5 algorithms */
10+
static RSA_PKCS1_SHA256: SignatureScheme;
11+
static RSA_PKCS1_SHA384: SignatureScheme;
12+
static RSA_PKCS1_SHA512: SignatureScheme;
13+
14+
/** ECDSA algorithms */
15+
static ECDSA_SECP256R1_SHA256: SignatureScheme;
16+
static ECDSA_SECP384R1_SHA384: SignatureScheme;
17+
static ECDSA_SECP521R1_SHA512: SignatureScheme;
18+
19+
/** RSASSA-PSS algorithms with public key OID rsaEncryption */
20+
static RSA_PSS_RSAE_SHA256: SignatureScheme;
21+
static RSA_PSS_RSAE_SHA384: SignatureScheme;
22+
static RSA_PSS_RSAE_SHA512: SignatureScheme;
23+
24+
/** EdDSA algorithms */
25+
static ED25519: SignatureScheme;
26+
static ED448: SignatureScheme;
27+
28+
/** RSASSA-PSS algorithms with public key OID RSASSA-PSS */
29+
static RSA_PSS_PSS_SHA256: SignatureScheme;
30+
static RSA_PSS_PSS_SHA384: SignatureScheme;
31+
static RSA_PSS_PSS_SHA512: SignatureScheme;
32+
33+
/** Legacy algorithms */
34+
static RSA_PKCS1_SHA1: SignatureScheme;
35+
static ECDSA_SHA1: SignatureScheme;
36+
37+
/** Reserved Code Points */
38+
static dsa_sha1_RESERVED: SignatureScheme;
39+
static dsa_sha256_RESERVED: SignatureScheme;
40+
static dsa_sha384_RESERVED: SignatureScheme;
41+
static dsa_sha512_RESERVED: SignatureScheme;
42+
43+
/**
44+
* Parses an octet array and returns a valid SignatureScheme.
45+
* @param {Uint8Array} octet - The octet array to parse.
46+
* @returns {SignatureScheme} The corresponding SignatureScheme instance.
47+
* @throws {Error} If the octet does not correspond to a known SignatureScheme.
48+
*/
49+
static from(octet: Uint8Array): SignatureScheme;
50+
51+
/**
52+
* Returns the bit length of the SignatureScheme.
53+
* @returns {number} The bit length, which is always 16.
54+
*/
55+
get bit(): number;
56+
57+
/**
58+
* Converts the SignatureScheme to a Uint16 representation.
59+
* @returns {Uint16} The Uint16 representation of the SignatureScheme.
60+
*/
61+
get Uint16(): Uint16;
62+
63+
/**
64+
* Generates a CertificateVerify object.
65+
* @param {Uint8Array} clientHelloMsg - Client Hello message.
66+
* @param {Uint8Array} serverHelloMsg - Server Hello message.
67+
* @param {Uint8Array} certificateMsg - Certificate message.
68+
* @param {CryptoKey} RSAprivateKey - RSA private key.
69+
* @returns {Promise<CertificateVerify>} CertificateVerify object.
70+
*/
71+
certificateVerify(
72+
clientHelloMsg: Uint8Array,
73+
serverHelloMsg: Uint8Array,
74+
certificateMsg: Uint8Array,
75+
RSAprivateKey: CryptoKey
76+
): Promise<CertificateVerify>;
5177
}
78+
79+
/**
80+
* Represents a CertificateVerify structure.
81+
*/
82+
export class CertificateVerify extends Uint8Array {
83+
/** The signature algorithm used. */
84+
algorithm: SignatureScheme;
85+
86+
/** The signature. */
87+
signature: Uint8Array;
88+
89+
/**
90+
* Parses a byte array into a CertificateVerify object.
91+
* @param {Uint8Array} array - The byte array to parse.
92+
* @returns {CertificateVerify} The parsed CertificateVerify object.
93+
*/
94+
static from(array: Uint8Array): CertificateVerify;
95+
96+
/**
97+
* Constructs a new CertificateVerify object.
98+
* @param {SignatureScheme} signatureScheme - The signature scheme.
99+
* @param {Uint8Array} signature - The signature.
100+
*/
101+
constructor(signatureScheme: SignatureScheme, signature: Uint8Array);
102+
}
103+
104+
/**
105+
* Represents a constrained signature.
106+
*/
107+
export class Signature extends Constrained {
108+
/** The raw opaque signature data. */
109+
opaque: Uint8Array;
110+
111+
/**
112+
* Parses a byte array into a Signature object.
113+
* @param {Uint8Array} array - The byte array to parse.
114+
* @returns {Signature} The parsed Signature object.
115+
*/
116+
static from(array: Uint8Array): Signature;
117+
118+
/**
119+
* Constructs a new Signature object.
120+
* @param {Uint8Array} opaque - The raw opaque signature data.
121+
*/
122+
constructor(opaque: Uint8Array);
123+
}
124+
125+
/**
126+
* Generates a signature from input data and a private RSA key.
127+
* @param {Uint8Array} clientHelloMsg - Client Hello message.
128+
* @param {Uint8Array} serverHelloMsg - Server Hello message.
129+
* @param {Uint8Array} certificateMsg - Certificate message.
130+
* @param {CryptoKey} RSAprivateKey - RSA private key.
131+
* @returns {Promise<Uint8Array>} The generated signature.
132+
*/
133+
export function signatureFrom(
134+
clientHelloMsg: Uint8Array,
135+
serverHelloMsg: Uint8Array,
136+
certificateMsg: Uint8Array,
137+
RSAprivateKey: CryptoKey
138+
): Promise<Uint8Array>;

0 commit comments

Comments
 (0)