esp-idf-provisioning-web 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +84 -21
  3. package/dist/ESPDevice.d.ts +4 -19
  4. package/dist/ESPDevice.d.ts.map +1 -1
  5. package/dist/index.cjs.js +1 -1
  6. package/dist/index.d.ts +4 -3
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.es.js +4623 -3094
  9. package/dist/security/Security0.d.ts.map +1 -1
  10. package/dist/security/Security1.d.ts +17 -0
  11. package/dist/security/Security1.d.ts.map +1 -0
  12. package/dist/types.d.ts +15 -1
  13. package/dist/types.d.ts.map +1 -1
  14. package/dist/utils/crypto.d.ts +9 -0
  15. package/dist/utils/crypto.d.ts.map +1 -1
  16. package/package.json +26 -24
  17. package/src/ESPDevice.ts +79 -74
  18. package/src/ESPProvisionManager.ts +0 -0
  19. package/src/constants.ts +0 -0
  20. package/src/generated/.gitkeep +0 -0
  21. package/src/generated/proto.d.ts +0 -0
  22. package/src/generated/proto.js +1864 -659
  23. package/src/index.ts +9 -4
  24. package/src/proto/constants.proto +0 -0
  25. package/src/proto/sec0.proto +0 -0
  26. package/src/proto/sec1.proto +0 -0
  27. package/src/proto/sec2.proto +0 -0
  28. package/src/proto/session.proto +0 -0
  29. package/src/proto/wifi_config.proto +0 -0
  30. package/src/proto/wifi_constants.proto +0 -0
  31. package/src/proto/wifi_ctrl.proto +0 -0
  32. package/src/proto/wifi_scan.proto +0 -0
  33. package/src/protocol/protocol.ts +0 -0
  34. package/src/protocol/wifiConfig.ts +6 -6
  35. package/src/protocol/wifiScan.ts +4 -4
  36. package/src/security/Security.ts +0 -0
  37. package/src/security/Security0.ts +5 -3
  38. package/src/security/Security1.ts +110 -0
  39. package/src/transport/BLETransport.ts +0 -0
  40. package/src/transport/SoftAPTransport.ts +2 -2
  41. package/src/transport/Transport.ts +0 -0
  42. package/src/types.ts +17 -1
  43. package/src/utils/crypto.ts +43 -9
@@ -1 +1 @@
1
- {"version":3,"file":"Security0.d.ts","sourceRoot":"","sources":["../../src/security/Security0.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC;;GAEG;AACH,qBAAa,SAAU,SAAQ,QAAQ;IACrC,OAAO,CAAC,WAAW,CAAkB;IAE/B,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI9C,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI9C,sBAAsB,IAAI,OAAO,CAAC,UAAU,CAAC;IAU7C,2BAA2B,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAStE,aAAa,IAAI,OAAO;CAGzB"}
1
+ {"version":3,"file":"Security0.d.ts","sourceRoot":"","sources":["../../src/security/Security0.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC;;GAEG;AACH,qBAAa,SAAU,SAAQ,QAAQ;IACrC,OAAO,CAAC,WAAW,CAAkB;IAE/B,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI9C,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI9C,sBAAsB,IAAI,OAAO,CAAC,UAAU,CAAC;IAU7C,2BAA2B,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtE,aAAa,IAAI,OAAO;CAGzB"}
@@ -0,0 +1,17 @@
1
+ import { Security } from './Security';
2
+ /** ESP-IDF Security 1: X25519, optional PoP, and one continuous AES-CTR stream. */
3
+ export declare class Security1 extends Security {
4
+ private proofOfPossession;
5
+ private phase;
6
+ private keyPair?;
7
+ private publicKey?;
8
+ private devicePublicKey?;
9
+ private cipher?;
10
+ constructor(proofOfPossession: string, patchVersion?: number);
11
+ getSessionSetupRequest(): Promise<Uint8Array>;
12
+ processSessionSetupResponse(response: Uint8Array): Promise<void>;
13
+ encrypt(data: Uint8Array): Promise<Uint8Array>;
14
+ decrypt(data: Uint8Array): Promise<Uint8Array>;
15
+ isEstablished(): boolean;
16
+ }
17
+ //# sourceMappingURL=Security1.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Security1.d.ts","sourceRoot":"","sources":["../../src/security/Security1.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,mFAAmF;AACnF,qBAAa,SAAU,SAAQ,QAAQ;IAOzB,OAAO,CAAC,iBAAiB;IANrC,OAAO,CAAC,KAAK,CAAmF;IAChG,OAAO,CAAC,OAAO,CAAC,CAAgB;IAChC,OAAO,CAAC,SAAS,CAAC,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAC,CAAa;IACrC,OAAO,CAAC,MAAM,CAAC,CAAe;gBAEV,iBAAiB,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM;IAU9D,sBAAsB,IAAI,OAAO,CAAC,UAAU,CAAC;IAwB7C,2BAA2B,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAmDhE,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAK9C,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAIpD,aAAa,IAAI,OAAO;CAGzB"}
package/dist/types.d.ts CHANGED
@@ -1,3 +1,17 @@
1
+ /** Public proto-ver metadata, including application-defined capability lists. */
2
+ export interface ESPProvisioningMetadata {
3
+ ver?: string;
4
+ cap?: string[];
5
+ }
6
+ export interface ESPProvisioningInfo {
7
+ prov: ESPProvisioningMetadata & {
8
+ sec_ver?: number;
9
+ sec_patch_ver?: number;
10
+ };
11
+ [name: string]: ESPProvisioningMetadata;
12
+ }
13
+ /** Resolve the security configuration after reading public discovery data. */
14
+ export type ESPSecurityResolver = (info: ESPProvisioningInfo) => ESPSecurityConfig | undefined | Promise<ESPSecurityConfig | undefined>;
1
15
  export type ESPSecurityConfig = ESPSecurity1Config | ESPSecurity2Config;
2
16
  export interface ESPSecurity1Config {
3
17
  type: 'Security1';
@@ -63,7 +77,7 @@ export interface ESPScanStatusResponse {
63
77
  * Device interface for ESP provisioning
64
78
  */
65
79
  export interface ESPDeviceInterface {
66
- connect(security?: ESPSecurityConfig): Promise<void>;
80
+ connect(security?: ESPSecurityConfig | ESPSecurityResolver): Promise<void>;
67
81
  scanWifiList(): Promise<ESPWifiAp[]>;
68
82
  provision(ssid: string, passphrase: string): Promise<void>;
69
83
  fetchWifiStatus(): Promise<ESPWifiStatus>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,oBAAY,eAAe;IACzB,IAAI,IAAI;IACR,GAAG,IAAI;IACP,cAAc,IAAI;IAClB,OAAO,IAAI;IACX,MAAM,IAAI;IACV,UAAU,IAAI;IACd,OAAO,IAAI;IACX,WAAW,IAAI;CAChB;AAED;;GAEG;AACH,oBAAY,eAAe;IACzB,SAAS,IAAI;IACb,UAAU,IAAI;IACd,YAAY,IAAI;IAChB,MAAM,IAAI;CACX;AAED;;GAEG;AACH,oBAAY,oBAAoB;IAC9B,SAAS,IAAI;IACb,eAAe,IAAI;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErD,YAAY,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAErC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAE1C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAE9D,UAAU,IAAI,IAAI,CAAC;IAEnB,SAAS,IAAI,eAAe,GAAG,GAAG,CAAC;CACpC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,WAAW,uBAAuB;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,uBAAuB,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7E,CAAC,IAAI,EAAE,MAAM,GAAG,uBAAuB,CAAC;CACzC;AAED,8EAA8E;AAC9E,MAAM,MAAM,mBAAmB,GAAG,CAChC,IAAI,EAAE,mBAAmB,KACtB,iBAAiB,GAAG,SAAS,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC;AAE5E,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,oBAAY,eAAe;IACzB,IAAI,IAAI;IACR,GAAG,IAAI;IACP,cAAc,IAAI;IAClB,OAAO,IAAI;IACX,MAAM,IAAI;IACV,UAAU,IAAI;IACd,OAAO,IAAI;IACX,WAAW,IAAI;CAChB;AAED;;GAEG;AACH,oBAAY,eAAe;IACzB,SAAS,IAAI;IACb,UAAU,IAAI;IACd,YAAY,IAAI;IAChB,MAAM,IAAI;CACX;AAED;;GAEG;AACH,oBAAY,oBAAoB;IAC9B,SAAS,IAAI;IACb,eAAe,IAAI;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3E,YAAY,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAErC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAE1C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAE9D,UAAU,IAAI,IAAI,CAAC;IAEnB,SAAS,IAAI,eAAe,GAAG,GAAG,CAAC;CACpC"}
@@ -41,4 +41,13 @@ export declare function aesDecrypt(key: Uint8Array, counter: Uint8Array, data: U
41
41
  * Concatenate multiple Uint8Arrays
42
42
  */
43
43
  export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
44
+ /** Stateful AES-CTR, preserving partial blocks across requests and responses. */
45
+ export declare class AESCTRStream {
46
+ private key;
47
+ private counter;
48
+ private offset;
49
+ private constructor();
50
+ static create(key: Uint8Array, counter: Uint8Array): Promise<AESCTRStream>;
51
+ update(data: Uint8Array): Promise<Uint8Array>;
52
+ }
44
53
  //# sourceMappingURL=crypto.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/utils/crypto.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAI9D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAMlD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIpD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAErD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAGlE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAUvF;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,UAAU,CAAC,CAcrB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,UAAU,CAAC,CAcrB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAS/D"}
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/utils/crypto.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAI9D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAMlD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIpD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAErD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAGlE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAUvF;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,UAAU,CAAC,CAcrB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,UAAU,CAAC,CAcrB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAS/D;AAED,iFAAiF;AACjF,qBAAa,YAAY;IAGH,OAAO,CAAC,GAAG;IAAa,OAAO,CAAC,OAAO;IAF3D,OAAO,CAAC,MAAM,CAAK;IAEnB,OAAO;WAEM,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC;IAQ1E,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;CAkBpD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esp-idf-provisioning-web",
3
- "version": "0.0.3",
3
+ "version": "0.0.6",
4
4
  "description": "ESP-IDF WiFi Provisioning SDK for web browsers",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",
@@ -12,6 +12,17 @@
12
12
  "require": "./dist/index.cjs.js"
13
13
  }
14
14
  },
15
+ "scripts": {
16
+ "prebuild": "pbjs -t static-module -w es6 -o src/generated/proto.js src/proto/*.proto && pbts -o src/generated/proto.d.ts src/generated/proto.js",
17
+ "build": "vite build && tsc --emitDeclarationOnly",
18
+ "dev": "vite build --watch",
19
+ "example": "npx http-server -p 8080 -o /examples/ble-provisioning.html",
20
+ "serve": "npx http-server -p 8080",
21
+ "test": "jest",
22
+ "lint": "eslint src --ext .ts",
23
+ "format": "prettier --write \"src/**/*.ts\"",
24
+ "prepack": "npm run build"
25
+ },
15
26
  "keywords": [
16
27
  "esp32",
17
28
  "esp-idf",
@@ -26,20 +37,21 @@
26
37
  "author": "",
27
38
  "license": "MIT",
28
39
  "dependencies": {
29
- "protobufjs": "^7.0.0"
40
+ "@types/web-bluetooth": "0.0.22",
41
+ "protobufjs": "^7.6.6"
30
42
  },
31
43
  "devDependencies": {
32
- "@types/jest": "^29.5.0",
33
- "@types/web-bluetooth": "0.0.21",
34
- "@typescript-eslint/eslint-plugin": "8.46.2",
35
- "@typescript-eslint/parser": "8.46.2",
36
- "eslint": "9.38.0",
37
- "jest": "30.2.0",
38
- "prettier": "^3.0.0",
39
- "protobufjs-cli": "^1.0.0",
40
- "ts-jest": "^29.0.0",
41
- "typescript": "^5.0.0",
42
- "vite": "7.1.11"
44
+ "@eslint/js": "10.0.1",
45
+ "@types/jest": "^30.0.0",
46
+ "@typescript-eslint/eslint-plugin": "8.72.0",
47
+ "@typescript-eslint/parser": "8.72.0",
48
+ "eslint": "10.13.0",
49
+ "jest": "30.5.2",
50
+ "prettier": "^3.9.10",
51
+ "protobufjs-cli": "^1.3.3",
52
+ "ts-jest": "^29.4.15",
53
+ "typescript": "^5.9.3",
54
+ "vite": "7.3.7"
43
55
  },
44
56
  "files": [
45
57
  "dist",
@@ -53,15 +65,5 @@
53
65
  },
54
66
  "bugs": {
55
67
  "url": "https://github.com/ho-229/esp-idf-provisioning-web/issues"
56
- },
57
- "scripts": {
58
- "prebuild": "pbjs -t static-module -w es6 -o src/generated/proto.js src/proto/*.proto && pbts -o src/generated/proto.d.ts src/generated/proto.js",
59
- "build": "vite build && tsc --emitDeclarationOnly",
60
- "dev": "vite build --watch",
61
- "example": "npx http-server -p 8080 -o /examples/ble-provisioning.html",
62
- "serve": "npx http-server -p 8080",
63
- "test": "jest",
64
- "lint": "eslint src --ext .ts",
65
- "format": "prettier --write \"src/**/*.ts\""
66
68
  }
67
- }
69
+ }
package/src/ESPDevice.ts CHANGED
@@ -3,12 +3,16 @@ import {
3
3
  ESPWifiAp,
4
4
  ESPWifiStatus,
5
5
  ESPSecurityConfig,
6
+ ESPSecurityResolver,
7
+ ESPProvisioningInfo,
6
8
  } from './types';
7
9
  import { Transport } from './transport/Transport';
8
10
  import { BLETransport } from './transport/BLETransport';
9
11
  import { SoftAPTransport } from './transport/SoftAPTransport';
10
12
  import { Security } from './security/Security';
11
13
  import { Security0 } from './security/Security0';
14
+ import { Security1 } from './security/Security1';
15
+ import { bytesToString, stringToBytes } from './utils/crypto';
12
16
  import { FALLBACK_NU_LOOKUP, FALLBACK_SERVICE_UUID, PROTO_ENDPOINTS } from './constants';
13
17
  import {
14
18
  getScanResultRequest,
@@ -34,6 +38,7 @@ export class ESPDevice implements ESPDeviceInterface {
34
38
  private device: BluetoothDevice | URL;
35
39
  private transportImpl?: Transport;
36
40
  private securityImpl?: Security;
41
+ private requestQueue: Promise<void> = Promise.resolve();
37
42
 
38
43
  /**
39
44
  * Create a new ESPDevice instance
@@ -43,22 +48,47 @@ export class ESPDevice implements ESPDeviceInterface {
43
48
  this.device = device;
44
49
  }
45
50
 
46
- /**
47
- * Connect to the device
48
- * @param proofOfPossession - Proof of possession for secure modes
49
- * @param softAPPassword - Password for SoftAP mode (not used in browser)
50
- * @param username - Username for authentication
51
- */
52
- async connect(security?: ESPSecurityConfig): Promise<void> {
53
- // Initialize transport
54
- await this.initializeTransport();
55
-
56
- // Initialize security
57
- this.initializeSecurity(security);
58
-
59
- // Establish secure session if needed
60
- if (security) {
61
- await this.establishSession();
51
+ /** Connect, read public metadata, resolve credentials, then complete the handshake. */
52
+ async connect(security?: ESPSecurityConfig | ESPSecurityResolver): Promise<void> {
53
+ if (this.transportImpl) throw new Error('Device already connected or connecting');
54
+ const transport = this.device instanceof URL
55
+ ? new SoftAPTransport(this.device.toString())
56
+ : new BLETransport(this.device, FALLBACK_SERVICE_UUID, new Map(Object.entries(FALLBACK_NU_LOOKUP)));
57
+ this.transportImpl = transport;
58
+ try {
59
+ await transport.connect();
60
+ const response = await transport.sendData(PROTO_ENDPOINTS.PROTO_VER, stringToBytes('ESP'));
61
+ const info: ESPProvisioningInfo = JSON.parse(bytesToString(response));
62
+ if (!info || !info.prov || typeof info.prov !== 'object' || Array.isArray(info.prov)) {
63
+ throw new Error('Invalid proto-ver metadata');
64
+ }
65
+ if (this.transportImpl !== transport || !transport.isConnected()) {
66
+ throw new Error('Device disconnected during discovery');
67
+ }
68
+ const config = typeof security === 'function' ? await security(info) : security;
69
+ if (this.transportImpl !== transport || !transport.isConnected()) {
70
+ throw new Error('Device disconnected while resolving security');
71
+ }
72
+ const version = config?.type === 'Security1' ? 1 : config?.type === 'Security2' ? 2 : 0;
73
+ if (info.prov.sec_ver !== undefined && info.prov.sec_ver !== version) {
74
+ throw new Error(`Device requires Security${info.prov.sec_ver}; provide a matching security configuration`);
75
+ }
76
+ let session: Security;
77
+ if (!config) {
78
+ session = new Security0();
79
+ } else if (config.type === 'Security1') {
80
+ session = new Security1(config.proofOfPossession, info.prov.sec_patch_ver);
81
+ } else {
82
+ throw new Error('Unsupported security configuration');
83
+ }
84
+ await this.establishSession(transport, session);
85
+ if (this.transportImpl !== transport || !transport.isConnected()) {
86
+ throw new Error('Device disconnected during handshake');
87
+ }
88
+ this.securityImpl = session;
89
+ } catch (error) {
90
+ if (this.transportImpl === transport) this.disconnect();
91
+ throw error;
62
92
  }
63
93
  }
64
94
 
@@ -148,19 +178,30 @@ export class ESPDevice implements ESPDeviceInterface {
148
178
  * @returns Response data from device
149
179
  */
150
180
  async sendData(path: string, data: Uint8Array): Promise<Uint8Array> {
151
- if (!this.transportImpl || !this.securityImpl) {
152
- throw new Error('Device not connected');
153
- }
154
-
155
- // Encrypt data if security is established
156
- const encryptedData = await this.securityImpl.encrypt(data);
157
- console.debug('Send encrypted data length:', encryptedData.length);
158
-
159
- // Send encrypted data
160
- const encryptedResponse = await this.transportImpl.sendData(path, encryptedData);
161
-
162
- // Decrypt response
163
- return await this.securityImpl.decrypt(encryptedResponse);
181
+ const transport = this.transportImpl;
182
+ const security = this.securityImpl;
183
+ if (!transport || !security?.isEstablished()) throw new Error('Device not connected');
184
+ if (path === PROTO_ENDPOINTS.PROV_SESSION) throw new Error('Session endpoint is managed by connect()');
185
+
186
+ // Security1 shares one CTR stream in both directions. Serialize the entire
187
+ // request/response pair, including custom endpoints.
188
+ const request = this.requestQueue.then(async () => {
189
+ if (this.transportImpl !== transport || this.securityImpl !== security || !transport.isConnected()) {
190
+ throw new Error('Device disconnected');
191
+ }
192
+ try {
193
+ if (path === PROTO_ENDPOINTS.PROTO_VER) return await transport.sendData(path, data);
194
+ const encrypted = await security.encrypt(data);
195
+ const response = await transport.sendData(path, encrypted);
196
+ return await security.decrypt(response);
197
+ } catch (error) {
198
+ // A lost response leaves the shared cipher position unknown.
199
+ if (this.transportImpl === transport) this.disconnect();
200
+ throw error;
201
+ }
202
+ });
203
+ this.requestQueue = request.then(() => {}, () => {});
204
+ return request;
164
205
  }
165
206
 
166
207
  /**
@@ -172,6 +213,7 @@ export class ESPDevice implements ESPDeviceInterface {
172
213
  this.transportImpl = undefined;
173
214
  }
174
215
  this.securityImpl = undefined;
216
+ this.requestQueue = Promise.resolve();
175
217
  }
176
218
 
177
219
  /**
@@ -181,51 +223,14 @@ export class ESPDevice implements ESPDeviceInterface {
181
223
  return this.device;
182
224
  }
183
225
 
184
- /**
185
- * Initialize security layer based on security type
186
- */
187
- private initializeSecurity(security?: ESPSecurityConfig): void {
188
- if (!security) {
189
- this.securityImpl = new Security0();
190
- } else {
191
- throw new Error('Unsupported security configuration');
192
- }
193
- }
194
-
195
- /**
196
- * Establish secure session with device
197
- */
198
- private async establishSession(): Promise<void> {
199
- if (!this.transportImpl || !this.securityImpl) {
200
- throw new Error('Transport or security not initialized');
201
- }
202
-
203
- // Get session setup request
204
- const setupRequest = await this.securityImpl.getSessionSetupRequest();
205
-
206
- // Send session setup request
207
- const setupResponse = await this.transportImpl.sendData(
208
- PROTO_ENDPOINTS.PROV_SESSION,
209
- setupRequest
210
- );
211
-
212
- // Process session setup response
213
- await this.securityImpl.processSessionSetupResponse(setupResponse);
214
- }
215
-
216
- /**
217
- * Initialize transport layer based on transport type
218
- */
219
- private async initializeTransport(): Promise<void> {
220
- if (this.device instanceof URL) {
221
- this.transportImpl = new SoftAPTransport(this.device.toString());
222
- } else { // BluetoothDevice
223
- this.transportImpl = new BLETransport(
224
- this.device,
225
- FALLBACK_SERVICE_UUID,
226
- new Map(Object.entries(FALLBACK_NU_LOOKUP))
227
- );
226
+ private async establishSession(transport: Transport, security: Security): Promise<void> {
227
+ while (!security.isEstablished()) {
228
+ if (this.transportImpl !== transport || !transport.isConnected()) {
229
+ throw new Error('Device disconnected during handshake');
230
+ }
231
+ const request = await security.getSessionSetupRequest();
232
+ const response = await transport.sendData(PROTO_ENDPOINTS.PROV_SESSION, request);
233
+ await security.processSessionSetupResponse(response);
228
234
  }
229
- await this.transportImpl.connect();
230
235
  }
231
236
  }
File without changes
package/src/constants.ts CHANGED
File without changes
File without changes
File without changes