UNPKG

5.69 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.generateCurve25519KeyPair = generateCurve25519KeyPair;
4exports.generateCurve25519SharedSecKey = generateCurve25519SharedSecKey;
5exports.HKDF = HKDF;
6exports.writeUInt64LE = writeUInt64LE;
7exports.chacha20_poly1305_decryptAndVerify = chacha20_poly1305_decryptAndVerify;
8exports.chacha20_poly1305_encryptAndSeal = chacha20_poly1305_encryptAndSeal;
9exports.layerEncrypt = layerEncrypt;
10exports.layerDecrypt = layerDecrypt;
11const tslib_1 = require("tslib");
12const assert_1 = tslib_1.__importDefault(require("assert"));
13const crypto_1 = tslib_1.__importDefault(require("crypto"));
14const futoin_hkdf_1 = tslib_1.__importDefault(require("futoin-hkdf"));
15const tweetnacl_1 = tslib_1.__importDefault(require("tweetnacl"));
16if (!crypto_1.default.getCiphers().includes("chacha20-poly1305")) {
17 assert_1.default.fail("The cipher 'chacha20-poly1305' is not supported with your current running nodejs version v" + process.version + ". " +
18 "At least a nodejs version of v10.17.0 (excluding v11.0 and v11.1) is required!");
19}
20/**
21 * @group Cryptography
22 */
23function generateCurve25519KeyPair() {
24 return tweetnacl_1.default.box.keyPair();
25}
26/**
27 * @group Cryptography
28 */
29function generateCurve25519SharedSecKey(priKey, pubKey) {
30 return tweetnacl_1.default.scalarMult(priKey, pubKey);
31}
32/**
33 * @group Cryptography
34 */
35function HKDF(hashAlg, salt, ikm, info, size) {
36 return (0, futoin_hkdf_1.default)(ikm, size, { hash: hashAlg, salt: salt, info: info });
37}
38const MAX_UINT32 = 0x00000000FFFFFFFF;
39const MAX_INT53 = 0x001FFFFFFFFFFFFF;
40function uintHighLow(number) {
41 (0, assert_1.default)(number > -1 && number <= MAX_INT53, "number out of range");
42 (0, assert_1.default)(Math.floor(number) === number, "number must be an integer");
43 let high = 0;
44 const signbit = number & 0xFFFFFFFF;
45 const low = signbit < 0 ? (number & 0x7FFFFFFF) + 0x80000000 : signbit;
46 if (number > MAX_UINT32) {
47 high = (number - low) / (MAX_UINT32 + 1);
48 }
49 return [high, low];
50}
51/**
52 * @group Utils
53 */
54function writeUInt64LE(number, buffer, offset = 0) {
55 const hl = uintHighLow(number);
56 buffer.writeUInt32LE(hl[1], offset);
57 buffer.writeUInt32LE(hl[0], offset + 4);
58}
59//Security Layer Enc/Dec
60/**
61 * @group Cryptography
62 */
63function chacha20_poly1305_decryptAndVerify(key, nonce, aad, ciphertext, authTag) {
64 if (nonce.length < 12) { // openssl 3.x.x requires 98 bits nonce length
65 nonce = Buffer.concat([
66 Buffer.alloc(12 - nonce.length, 0),
67 nonce,
68 ]);
69 }
70 const decipher = crypto_1.default.createDecipheriv("chacha20-poly1305", key, nonce, { authTagLength: 16 });
71 if (aad) {
72 decipher.setAAD(aad, { plaintextLength: ciphertext.length });
73 }
74 decipher.setAuthTag(authTag);
75 const plaintext = decipher.update(ciphertext);
76 decipher.final(); // final call verifies integrity using the auth tag. Throws error if something was manipulated!
77 return plaintext;
78}
79/**
80 * @group Cryptography
81 */
82function chacha20_poly1305_encryptAndSeal(key, nonce, aad, plaintext) {
83 if (nonce.length < 12) { // openssl 3.x.x requires 98 bits nonce length
84 nonce = Buffer.concat([
85 Buffer.alloc(12 - nonce.length, 0),
86 nonce,
87 ]);
88 }
89 const cipher = crypto_1.default.createCipheriv("chacha20-poly1305", key, nonce, { authTagLength: 16 });
90 if (aad) {
91 cipher.setAAD(aad, { plaintextLength: plaintext.length });
92 }
93 const ciphertext = cipher.update(plaintext);
94 cipher.final(); // final call creates the auth tag
95 const authTag = cipher.getAuthTag();
96 return {
97 ciphertext: ciphertext,
98 authTag: authTag,
99 };
100}
101/**
102 * @group Cryptography
103 */
104function layerEncrypt(data, encryption) {
105 let result = Buffer.alloc(0);
106 const total = data.length;
107 for (let offset = 0; offset < total;) {
108 const length = Math.min(total - offset, 0x400);
109 const leLength = Buffer.alloc(2);
110 leLength.writeUInt16LE(length, 0);
111 const nonce = Buffer.alloc(8);
112 writeUInt64LE(encryption.accessoryToControllerCount++, nonce, 0);
113 const encrypted = chacha20_poly1305_encryptAndSeal(encryption.accessoryToControllerKey, nonce, leLength, data.slice(offset, offset + length));
114 offset += length;
115 result = Buffer.concat([result, leLength, encrypted.ciphertext, encrypted.authTag]);
116 }
117 return result;
118}
119/**
120 * @group Cryptography
121 */
122function layerDecrypt(packet, encryption) {
123 if (encryption.incompleteFrame) {
124 packet = Buffer.concat([encryption.incompleteFrame, packet]);
125 encryption.incompleteFrame = undefined;
126 }
127 let result = Buffer.alloc(0);
128 const total = packet.length;
129 for (let offset = 0; offset < total;) {
130 const realDataLength = packet.slice(offset, offset + 2).readUInt16LE(0);
131 const availableDataLength = total - offset - 2 - 16;
132 if (realDataLength > availableDataLength) { // Fragmented packet
133 encryption.incompleteFrame = packet.slice(offset);
134 break;
135 }
136 const nonce = Buffer.alloc(8);
137 writeUInt64LE(encryption.controllerToAccessoryCount++, nonce, 0);
138 const plaintext = chacha20_poly1305_decryptAndVerify(encryption.controllerToAccessoryKey, nonce, packet.slice(offset, offset + 2), packet.slice(offset + 2, offset + 2 + realDataLength), packet.slice(offset + 2 + realDataLength, offset + 2 + realDataLength + 16));
139 result = Buffer.concat([result, plaintext]);
140 offset += (18 + realDataLength);
141 }
142 return result;
143}
144//# sourceMappingURL=hapCrypto.js.map
\No newline at end of file