@semaphore-protocol/identity 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +97 -0
- package/dist/index.mjs +146 -0
- package/dist/index.node.js +154 -0
- package/dist/types/identity.d.ts +47 -0
- package/dist/types/identity.test.d.ts +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/types/index.d.ts +5 -0
- package/dist/types/utils.d.ts +12 -0
- package/package.json +42 -0
- package/src/identity.test.ts +92 -0
- package/src/identity.ts +135 -0
- package/src/index.ts +3 -0
- package/src/types/index.ts +5 -0
- package/src/utils.ts +23 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Ethereum Foundation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">
|
|
3
|
+
Semaphore identity
|
|
4
|
+
</h1>
|
|
5
|
+
<p align="center">A library for managing Semaphore identities.</p>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://github.com/semaphore-protocol/semaphore.js">
|
|
10
|
+
<img src="https://img.shields.io/badge/project-semaphore-blue.svg?style=flat-square">
|
|
11
|
+
</a>
|
|
12
|
+
<a href="https://github.com/semaphore-protocol/semaphore.js/blob/main/packages/identity/LICENSE">
|
|
13
|
+
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.js.svg?style=flat-square">
|
|
14
|
+
</a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/@semaphore-protocol/identity">
|
|
16
|
+
<img alt="NPM version" src="https://img.shields.io/npm/v/@semaphore-protocol/identity?style=flat-square" />
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://npmjs.org/package/@semaphore-protocol/identity">
|
|
19
|
+
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/identity.svg?style=flat-square" />
|
|
20
|
+
</a>
|
|
21
|
+
<a href="https://bundlephobia.com/package/@semaphore-protocol/identity">
|
|
22
|
+
<img alt="npm bundle size (scoped)" src="https://img.shields.io/bundlephobia/minzip/@semaphore-protocol/identity" />
|
|
23
|
+
</a>
|
|
24
|
+
<a href="https://eslint.org/">
|
|
25
|
+
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint" />
|
|
26
|
+
</a>
|
|
27
|
+
<a href="https://prettier.io/">
|
|
28
|
+
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier" />
|
|
29
|
+
</a>
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
<div align="center">
|
|
33
|
+
<h4>
|
|
34
|
+
<a href="https://t.me/joinchat/B-PQx1U3GtAh--Z4Fwo56A">
|
|
35
|
+
🗣️ Chat & Support
|
|
36
|
+
</a>
|
|
37
|
+
<span> | </span>
|
|
38
|
+
<a href="https://semaphore-protocol.github.io/semaphore.js/identity">
|
|
39
|
+
📘 Docs
|
|
40
|
+
</a>
|
|
41
|
+
</h4>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 🛠 Install
|
|
47
|
+
|
|
48
|
+
### npm or yarn
|
|
49
|
+
|
|
50
|
+
Install the `@semaphore-protocol/identity` package with npm:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm i @semaphore-protocol/identity
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
or yarn:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
yarn add @semaphore-protocol/identity
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 📜 Usage
|
|
63
|
+
|
|
64
|
+
### Creating an identity with a random strategy:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { Identity } from "@semaphore-protocol/identity"
|
|
68
|
+
// const { Identity } = require("@semaphore-protocol/identity") // with commonJS
|
|
69
|
+
|
|
70
|
+
const identity = new Identity()
|
|
71
|
+
|
|
72
|
+
const trapdoor = identity.getTrapdoor()
|
|
73
|
+
const nullifier = identity.getNullifier()
|
|
74
|
+
const secret = identity.getSecret()
|
|
75
|
+
const multipartSecret = identity.getMultipartSecret()
|
|
76
|
+
|
|
77
|
+
const identityCommitment = identity.genIdentityCommitment()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Creating an identity with a message strategy:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { Identity, Strategy } from "@semaphore-protocol/identity"
|
|
84
|
+
|
|
85
|
+
const identity = new Identity(Strategy.MESSAGE, "message")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Creating an identity with a serialized strategy:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { Identity, Strategy } from "@semaphore-protocol/identity"
|
|
92
|
+
|
|
93
|
+
const identity = new Identity()
|
|
94
|
+
const serializedIdentity = identity.serializeIdentity()
|
|
95
|
+
|
|
96
|
+
const identity2 = new Identity(Strategy.SERIALIZED, serializedIdentity)
|
|
97
|
+
```
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @semaphore-protocol/identity
|
|
3
|
+
* @version 0.1.0
|
|
4
|
+
* @file A library for managing Semaphore identities.
|
|
5
|
+
* @copyright Ethereum Foundation 2022
|
|
6
|
+
* @license MIT
|
|
7
|
+
* @see [Github]{@link https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/proof}
|
|
8
|
+
*/
|
|
9
|
+
import { BigNumber } from '@ethersproject/bignumber';
|
|
10
|
+
import { poseidon } from 'circomlibjs';
|
|
11
|
+
import { randomBytes } from '@ethersproject/random';
|
|
12
|
+
import _sha256 from 'crypto-js/sha256';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Returns an hexadecimal sha256 hash of the message passed as parameter.
|
|
16
|
+
* @param message The string to hash.
|
|
17
|
+
* @returns The hexadecimal hash of the message.
|
|
18
|
+
*/
|
|
19
|
+
function sha256(message) {
|
|
20
|
+
var hash = _sha256(message);
|
|
21
|
+
return hash.toString();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generates a random big number.
|
|
25
|
+
* @param numberOfBytes The number of bytes of the number.
|
|
26
|
+
* @returns The generated random number.
|
|
27
|
+
*/
|
|
28
|
+
function genRandomNumber(numberOfBytes) {
|
|
29
|
+
if (numberOfBytes === void 0) { numberOfBytes = 31; }
|
|
30
|
+
return BigNumber.from(randomBytes(numberOfBytes)).toBigInt();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// The strategy used to generate the Identity.
|
|
34
|
+
var Strategy;
|
|
35
|
+
(function (Strategy) {
|
|
36
|
+
Strategy[Strategy["RANDOM"] = 0] = "RANDOM";
|
|
37
|
+
Strategy[Strategy["MESSAGE"] = 1] = "MESSAGE";
|
|
38
|
+
Strategy[Strategy["SERIALIZED"] = 2] = "SERIALIZED"; // Identity parameters are passed from outside.
|
|
39
|
+
})(Strategy || (Strategy = {}));
|
|
40
|
+
var Identity = /** @class */ (function () {
|
|
41
|
+
/**
|
|
42
|
+
* Initializes the class attributes based on the strategy passed as parameter.
|
|
43
|
+
* @param strategy The strategy for identity generation.
|
|
44
|
+
* @param metadata Additional data needed to create identity for given strategy.
|
|
45
|
+
*/
|
|
46
|
+
function Identity(strategy, metadata) {
|
|
47
|
+
if (strategy === void 0) { strategy = Strategy.RANDOM; }
|
|
48
|
+
this._secret = [];
|
|
49
|
+
switch (strategy) {
|
|
50
|
+
case Strategy.RANDOM: {
|
|
51
|
+
this._identityTrapdoor = genRandomNumber();
|
|
52
|
+
this._identityNullifier = genRandomNumber();
|
|
53
|
+
this._secret = [this._identityNullifier, this._identityTrapdoor];
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case Strategy.MESSAGE: {
|
|
57
|
+
if (!metadata) {
|
|
58
|
+
throw new Error("The message is not defined");
|
|
59
|
+
}
|
|
60
|
+
if (typeof metadata !== "string") {
|
|
61
|
+
throw new Error("The message is not a string");
|
|
62
|
+
}
|
|
63
|
+
var messageHash = sha256(metadata);
|
|
64
|
+
this._identityTrapdoor = BigNumber.from("0x".concat(sha256("".concat(messageHash, "identity_trapdoor")))).toBigInt();
|
|
65
|
+
this._identityNullifier = BigNumber.from("0x".concat(sha256("".concat(messageHash, "identity_nullifier")))).toBigInt();
|
|
66
|
+
this._secret = [this._identityNullifier, this._identityTrapdoor];
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case Strategy.SERIALIZED: {
|
|
70
|
+
if (!metadata) {
|
|
71
|
+
throw new Error("The serialized identity is not defined");
|
|
72
|
+
}
|
|
73
|
+
if (typeof metadata === "string") {
|
|
74
|
+
try {
|
|
75
|
+
metadata = JSON.parse(metadata);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
throw new Error("The serialized identity cannot be parsed");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!("identityNullifier" in metadata) ||
|
|
82
|
+
!("identityTrapdoor" in metadata) ||
|
|
83
|
+
!("secret" in metadata)) {
|
|
84
|
+
throw new Error("The serialized identity does not contain the right parameter");
|
|
85
|
+
}
|
|
86
|
+
var identityNullifier = metadata.identityNullifier, identityTrapdoor = metadata.identityTrapdoor, secret = metadata.secret;
|
|
87
|
+
this._identityNullifier = BigNumber.from("0x".concat(identityNullifier)).toBigInt();
|
|
88
|
+
this._identityTrapdoor = BigNumber.from("0x".concat(identityTrapdoor)).toBigInt();
|
|
89
|
+
this._secret = secret.map(function (item) { return BigNumber.from("0x".concat(item)).toBigInt(); });
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
default:
|
|
93
|
+
throw new Error("The provided strategy is not supported");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns the identity trapdoor.
|
|
98
|
+
* @returns The identity trapdoor.
|
|
99
|
+
*/
|
|
100
|
+
Identity.prototype.getTrapdoor = function () {
|
|
101
|
+
return this._identityTrapdoor;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Returns the identity nullifier.
|
|
105
|
+
* @returns The identity nullifier.
|
|
106
|
+
*/
|
|
107
|
+
Identity.prototype.getNullifier = function () {
|
|
108
|
+
return this._identityNullifier;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Returns the secret.
|
|
112
|
+
* @returns The secret.
|
|
113
|
+
*/
|
|
114
|
+
Identity.prototype.getSecret = function () {
|
|
115
|
+
return this._secret;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Returns the Poseidon hash of the secret.
|
|
119
|
+
* @returns The hash of the secret.
|
|
120
|
+
*/
|
|
121
|
+
Identity.prototype.getSecretHash = function () {
|
|
122
|
+
return poseidon(this._secret);
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Generates the identity commitment from the secret.
|
|
126
|
+
* @returns identity commitment
|
|
127
|
+
*/
|
|
128
|
+
Identity.prototype.genIdentityCommitment = function () {
|
|
129
|
+
return poseidon([this.getSecretHash()]);
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Serializes the class attributes and returns a stringified object.
|
|
133
|
+
* @returns The stringified serialized identity.
|
|
134
|
+
*/
|
|
135
|
+
Identity.prototype.serializeIdentity = function () {
|
|
136
|
+
var data = {
|
|
137
|
+
identityNullifier: this._identityNullifier.toString(16),
|
|
138
|
+
identityTrapdoor: this._identityTrapdoor.toString(16),
|
|
139
|
+
secret: this._secret.map(function (item) { return item.toString(16); })
|
|
140
|
+
};
|
|
141
|
+
return JSON.stringify(data);
|
|
142
|
+
};
|
|
143
|
+
return Identity;
|
|
144
|
+
}());
|
|
145
|
+
|
|
146
|
+
export { Identity, Strategy };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @semaphore-protocol/identity
|
|
3
|
+
* @version 0.1.0
|
|
4
|
+
* @file A library for managing Semaphore identities.
|
|
5
|
+
* @copyright Ethereum Foundation 2022
|
|
6
|
+
* @license MIT
|
|
7
|
+
* @see [Github]{@link https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/proof}
|
|
8
|
+
*/
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
12
|
+
|
|
13
|
+
var bignumber = require('@ethersproject/bignumber');
|
|
14
|
+
var circomlibjs = require('circomlibjs');
|
|
15
|
+
var random = require('@ethersproject/random');
|
|
16
|
+
var _sha256 = require('crypto-js/sha256');
|
|
17
|
+
|
|
18
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
19
|
+
|
|
20
|
+
var _sha256__default = /*#__PURE__*/_interopDefaultLegacy(_sha256);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Returns an hexadecimal sha256 hash of the message passed as parameter.
|
|
24
|
+
* @param message The string to hash.
|
|
25
|
+
* @returns The hexadecimal hash of the message.
|
|
26
|
+
*/
|
|
27
|
+
function sha256(message) {
|
|
28
|
+
var hash = _sha256__default["default"](message);
|
|
29
|
+
return hash.toString();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Generates a random big number.
|
|
33
|
+
* @param numberOfBytes The number of bytes of the number.
|
|
34
|
+
* @returns The generated random number.
|
|
35
|
+
*/
|
|
36
|
+
function genRandomNumber(numberOfBytes) {
|
|
37
|
+
if (numberOfBytes === void 0) { numberOfBytes = 31; }
|
|
38
|
+
return bignumber.BigNumber.from(random.randomBytes(numberOfBytes)).toBigInt();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// The strategy used to generate the Identity.
|
|
42
|
+
exports.Strategy = void 0;
|
|
43
|
+
(function (Strategy) {
|
|
44
|
+
Strategy[Strategy["RANDOM"] = 0] = "RANDOM";
|
|
45
|
+
Strategy[Strategy["MESSAGE"] = 1] = "MESSAGE";
|
|
46
|
+
Strategy[Strategy["SERIALIZED"] = 2] = "SERIALIZED"; // Identity parameters are passed from outside.
|
|
47
|
+
})(exports.Strategy || (exports.Strategy = {}));
|
|
48
|
+
var Identity = /** @class */ (function () {
|
|
49
|
+
/**
|
|
50
|
+
* Initializes the class attributes based on the strategy passed as parameter.
|
|
51
|
+
* @param strategy The strategy for identity generation.
|
|
52
|
+
* @param metadata Additional data needed to create identity for given strategy.
|
|
53
|
+
*/
|
|
54
|
+
function Identity(strategy, metadata) {
|
|
55
|
+
if (strategy === void 0) { strategy = exports.Strategy.RANDOM; }
|
|
56
|
+
this._secret = [];
|
|
57
|
+
switch (strategy) {
|
|
58
|
+
case exports.Strategy.RANDOM: {
|
|
59
|
+
this._identityTrapdoor = genRandomNumber();
|
|
60
|
+
this._identityNullifier = genRandomNumber();
|
|
61
|
+
this._secret = [this._identityNullifier, this._identityTrapdoor];
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case exports.Strategy.MESSAGE: {
|
|
65
|
+
if (!metadata) {
|
|
66
|
+
throw new Error("The message is not defined");
|
|
67
|
+
}
|
|
68
|
+
if (typeof metadata !== "string") {
|
|
69
|
+
throw new Error("The message is not a string");
|
|
70
|
+
}
|
|
71
|
+
var messageHash = sha256(metadata);
|
|
72
|
+
this._identityTrapdoor = bignumber.BigNumber.from("0x".concat(sha256("".concat(messageHash, "identity_trapdoor")))).toBigInt();
|
|
73
|
+
this._identityNullifier = bignumber.BigNumber.from("0x".concat(sha256("".concat(messageHash, "identity_nullifier")))).toBigInt();
|
|
74
|
+
this._secret = [this._identityNullifier, this._identityTrapdoor];
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case exports.Strategy.SERIALIZED: {
|
|
78
|
+
if (!metadata) {
|
|
79
|
+
throw new Error("The serialized identity is not defined");
|
|
80
|
+
}
|
|
81
|
+
if (typeof metadata === "string") {
|
|
82
|
+
try {
|
|
83
|
+
metadata = JSON.parse(metadata);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
throw new Error("The serialized identity cannot be parsed");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (!("identityNullifier" in metadata) ||
|
|
90
|
+
!("identityTrapdoor" in metadata) ||
|
|
91
|
+
!("secret" in metadata)) {
|
|
92
|
+
throw new Error("The serialized identity does not contain the right parameter");
|
|
93
|
+
}
|
|
94
|
+
var identityNullifier = metadata.identityNullifier, identityTrapdoor = metadata.identityTrapdoor, secret = metadata.secret;
|
|
95
|
+
this._identityNullifier = bignumber.BigNumber.from("0x".concat(identityNullifier)).toBigInt();
|
|
96
|
+
this._identityTrapdoor = bignumber.BigNumber.from("0x".concat(identityTrapdoor)).toBigInt();
|
|
97
|
+
this._secret = secret.map(function (item) { return bignumber.BigNumber.from("0x".concat(item)).toBigInt(); });
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
default:
|
|
101
|
+
throw new Error("The provided strategy is not supported");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Returns the identity trapdoor.
|
|
106
|
+
* @returns The identity trapdoor.
|
|
107
|
+
*/
|
|
108
|
+
Identity.prototype.getTrapdoor = function () {
|
|
109
|
+
return this._identityTrapdoor;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Returns the identity nullifier.
|
|
113
|
+
* @returns The identity nullifier.
|
|
114
|
+
*/
|
|
115
|
+
Identity.prototype.getNullifier = function () {
|
|
116
|
+
return this._identityNullifier;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Returns the secret.
|
|
120
|
+
* @returns The secret.
|
|
121
|
+
*/
|
|
122
|
+
Identity.prototype.getSecret = function () {
|
|
123
|
+
return this._secret;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Returns the Poseidon hash of the secret.
|
|
127
|
+
* @returns The hash of the secret.
|
|
128
|
+
*/
|
|
129
|
+
Identity.prototype.getSecretHash = function () {
|
|
130
|
+
return circomlibjs.poseidon(this._secret);
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Generates the identity commitment from the secret.
|
|
134
|
+
* @returns identity commitment
|
|
135
|
+
*/
|
|
136
|
+
Identity.prototype.genIdentityCommitment = function () {
|
|
137
|
+
return circomlibjs.poseidon([this.getSecretHash()]);
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Serializes the class attributes and returns a stringified object.
|
|
141
|
+
* @returns The stringified serialized identity.
|
|
142
|
+
*/
|
|
143
|
+
Identity.prototype.serializeIdentity = function () {
|
|
144
|
+
var data = {
|
|
145
|
+
identityNullifier: this._identityNullifier.toString(16),
|
|
146
|
+
identityTrapdoor: this._identityTrapdoor.toString(16),
|
|
147
|
+
secret: this._secret.map(function (item) { return item.toString(16); })
|
|
148
|
+
};
|
|
149
|
+
return JSON.stringify(data);
|
|
150
|
+
};
|
|
151
|
+
return Identity;
|
|
152
|
+
}());
|
|
153
|
+
|
|
154
|
+
exports.Identity = Identity;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { SerializedIdentity } from "./types";
|
|
2
|
+
export declare enum Strategy {
|
|
3
|
+
RANDOM = 0,
|
|
4
|
+
MESSAGE = 1,
|
|
5
|
+
SERIALIZED = 2
|
|
6
|
+
}
|
|
7
|
+
export default class Identity {
|
|
8
|
+
private _identityTrapdoor;
|
|
9
|
+
private _identityNullifier;
|
|
10
|
+
private _secret;
|
|
11
|
+
/**
|
|
12
|
+
* Initializes the class attributes based on the strategy passed as parameter.
|
|
13
|
+
* @param strategy The strategy for identity generation.
|
|
14
|
+
* @param metadata Additional data needed to create identity for given strategy.
|
|
15
|
+
*/
|
|
16
|
+
constructor(strategy?: Strategy, metadata?: string | SerializedIdentity);
|
|
17
|
+
/**
|
|
18
|
+
* Returns the identity trapdoor.
|
|
19
|
+
* @returns The identity trapdoor.
|
|
20
|
+
*/
|
|
21
|
+
getTrapdoor(): bigint;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the identity nullifier.
|
|
24
|
+
* @returns The identity nullifier.
|
|
25
|
+
*/
|
|
26
|
+
getNullifier(): bigint;
|
|
27
|
+
/**
|
|
28
|
+
* Returns the secret.
|
|
29
|
+
* @returns The secret.
|
|
30
|
+
*/
|
|
31
|
+
getSecret(): bigint[];
|
|
32
|
+
/**
|
|
33
|
+
* Returns the Poseidon hash of the secret.
|
|
34
|
+
* @returns The hash of the secret.
|
|
35
|
+
*/
|
|
36
|
+
getSecretHash(): bigint;
|
|
37
|
+
/**
|
|
38
|
+
* Generates the identity commitment from the secret.
|
|
39
|
+
* @returns identity commitment
|
|
40
|
+
*/
|
|
41
|
+
genIdentityCommitment(): bigint;
|
|
42
|
+
/**
|
|
43
|
+
* Serializes the class attributes and returns a stringified object.
|
|
44
|
+
* @returns The stringified serialized identity.
|
|
45
|
+
*/
|
|
46
|
+
serializeIdentity(): string;
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns an hexadecimal sha256 hash of the message passed as parameter.
|
|
3
|
+
* @param message The string to hash.
|
|
4
|
+
* @returns The hexadecimal hash of the message.
|
|
5
|
+
*/
|
|
6
|
+
export declare function sha256(message: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Generates a random big number.
|
|
9
|
+
* @param numberOfBytes The number of bytes of the number.
|
|
10
|
+
* @returns The generated random number.
|
|
11
|
+
*/
|
|
12
|
+
export declare function genRandomNumber(numberOfBytes?: number): bigint;
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@semaphore-protocol/identity",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A library for managing Semaphore identities.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "dist/index.node.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
"import": "./dist/index.mjs",
|
|
9
|
+
"require": "./dist/index.node.js"
|
|
10
|
+
},
|
|
11
|
+
"types": "dist/types/index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/",
|
|
14
|
+
"src/",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"repository": "https://github.com/privacy-scaling-explorations/zk-kit",
|
|
19
|
+
"homepage": "https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/proof",
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
|
|
22
|
+
"build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
|
|
23
|
+
"prepublishOnly": "yarn build",
|
|
24
|
+
"docs": "typedoc src/index.ts --out ../../docs/identity"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/crypto-js": "^4.1.1",
|
|
31
|
+
"rollup-plugin-cleanup": "^3.2.1",
|
|
32
|
+
"rollup-plugin-typescript2": "^0.31.2",
|
|
33
|
+
"typedoc": "^0.22.11"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@ethersproject/bignumber": "^5.5.0",
|
|
37
|
+
"@ethersproject/random": "^5.5.1",
|
|
38
|
+
"circomlibjs": "0.0.8",
|
|
39
|
+
"crypto-js": "^4.1.1"
|
|
40
|
+
},
|
|
41
|
+
"gitHead": "6c1b469a4b5ffaac562f478fc2bbc78731734e81"
|
|
42
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import Identity, { Strategy } from "./identity"
|
|
2
|
+
|
|
3
|
+
describe("Identity", () => {
|
|
4
|
+
describe("Create identity", () => {
|
|
5
|
+
it("Should not create a identity if the strategy is wrong", () => {
|
|
6
|
+
const fun = () => new Identity("wrong" as any)
|
|
7
|
+
|
|
8
|
+
expect(fun).toThrow("strategy is not supported")
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it("Should create a identity", () => {
|
|
12
|
+
const identity = new Identity()
|
|
13
|
+
|
|
14
|
+
expect(typeof identity).toBe("object")
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it("Should not create a identity with a message strategy if there is no message", () => {
|
|
18
|
+
const fun = () => new Identity(Strategy.MESSAGE)
|
|
19
|
+
|
|
20
|
+
expect(fun).toThrow("The message is not defined")
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it("Should not create a identity with a message strategy if the message is not a string", () => {
|
|
24
|
+
const fun = () => new Identity(Strategy.MESSAGE, 1 as any)
|
|
25
|
+
|
|
26
|
+
expect(fun).toThrow("The message is not a string")
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it("Should create a identity with a message strategy", () => {
|
|
30
|
+
const identity = new Identity(Strategy.MESSAGE, "message")
|
|
31
|
+
|
|
32
|
+
const trapdoor = identity.getTrapdoor()
|
|
33
|
+
const nullifier = identity.getNullifier()
|
|
34
|
+
const identitySecret = identity.getSecret()
|
|
35
|
+
|
|
36
|
+
expect(typeof identity).toBe("object")
|
|
37
|
+
expect(trapdoor).toBe(
|
|
38
|
+
BigInt("58952291509798197436757858062402199043831251943841934828591473955215726495831")
|
|
39
|
+
)
|
|
40
|
+
expect(nullifier).toBe(
|
|
41
|
+
BigInt("44673097405870585416457571638073245190425597599743560105244308998175651589997")
|
|
42
|
+
)
|
|
43
|
+
expect(identitySecret).toHaveLength(2)
|
|
44
|
+
expect(typeof identitySecret).toBe("object")
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("Should generate an identity commitment secret", () => {
|
|
48
|
+
const identity = new Identity(Strategy.MESSAGE, "message")
|
|
49
|
+
const identityCommitment = identity.genIdentityCommitment()
|
|
50
|
+
|
|
51
|
+
expect(identityCommitment).toBe(
|
|
52
|
+
BigInt("1720349790382552497189398984241859233944354304766757200361065203741879866188")
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("Should serialize an identity", () => {
|
|
57
|
+
const identity = new Identity()
|
|
58
|
+
const serialized = identity.serializeIdentity()
|
|
59
|
+
|
|
60
|
+
expect(typeof serialized).toBe("string")
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it("Should not create a identity with a serialized strategy if there is no serialize identity", () => {
|
|
64
|
+
const fun = () => new Identity(Strategy.SERIALIZED)
|
|
65
|
+
|
|
66
|
+
expect(fun).toThrow("serialized identity is not defined")
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it("Should not create a identity with a serialized strategy if the serialize identity cannot be parsed", () => {
|
|
70
|
+
const fun = () => new Identity(Strategy.SERIALIZED, "{a}")
|
|
71
|
+
|
|
72
|
+
expect(fun).toThrow("The serialized identity cannot be parsed")
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("Should not create a identity with a serialized strategy if the serialize identity does not contain the right parameters", () => {
|
|
76
|
+
const fun = () =>
|
|
77
|
+
new Identity(Strategy.SERIALIZED, {
|
|
78
|
+
identityTrapdoor: "234"
|
|
79
|
+
} as any)
|
|
80
|
+
|
|
81
|
+
expect(fun).toThrow("The serialized identity does not contain the right parameter")
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it("Should create a identity with a serialized strategy", () => {
|
|
85
|
+
const identity1 = new Identity()
|
|
86
|
+
const serialized = identity1.serializeIdentity()
|
|
87
|
+
const identity2 = new Identity(Strategy.SERIALIZED, serialized)
|
|
88
|
+
|
|
89
|
+
expect(identity2).toStrictEqual(identity1)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
})
|
package/src/identity.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { BigNumber } from "@ethersproject/bignumber"
|
|
2
|
+
import { poseidon } from "circomlibjs"
|
|
3
|
+
import { SerializedIdentity } from "./types"
|
|
4
|
+
import { genRandomNumber, sha256 } from "./utils"
|
|
5
|
+
|
|
6
|
+
// The strategy used to generate the Identity.
|
|
7
|
+
export enum Strategy {
|
|
8
|
+
RANDOM, // Identity is generated randomly.
|
|
9
|
+
MESSAGE, // Identity is generated from a message.
|
|
10
|
+
SERIALIZED // Identity parameters are passed from outside.
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default class Identity {
|
|
14
|
+
private _identityTrapdoor: bigint
|
|
15
|
+
private _identityNullifier: bigint
|
|
16
|
+
|
|
17
|
+
private _secret: bigint[] = []
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Initializes the class attributes based on the strategy passed as parameter.
|
|
21
|
+
* @param strategy The strategy for identity generation.
|
|
22
|
+
* @param metadata Additional data needed to create identity for given strategy.
|
|
23
|
+
*/
|
|
24
|
+
constructor(strategy: Strategy = Strategy.RANDOM, metadata?: string | SerializedIdentity) {
|
|
25
|
+
switch (strategy) {
|
|
26
|
+
case Strategy.RANDOM: {
|
|
27
|
+
this._identityTrapdoor = genRandomNumber()
|
|
28
|
+
this._identityNullifier = genRandomNumber()
|
|
29
|
+
this._secret = [this._identityNullifier, this._identityTrapdoor]
|
|
30
|
+
break
|
|
31
|
+
}
|
|
32
|
+
case Strategy.MESSAGE: {
|
|
33
|
+
if (!metadata) {
|
|
34
|
+
throw new Error("The message is not defined")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof metadata !== "string") {
|
|
38
|
+
throw new Error("The message is not a string")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const messageHash = sha256(metadata)
|
|
42
|
+
|
|
43
|
+
this._identityTrapdoor = BigNumber.from(`0x${sha256(`${messageHash}identity_trapdoor`)}`).toBigInt()
|
|
44
|
+
this._identityNullifier = BigNumber.from(`0x${sha256(`${messageHash}identity_nullifier`)}`).toBigInt()
|
|
45
|
+
this._secret = [this._identityNullifier, this._identityTrapdoor]
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
case Strategy.SERIALIZED: {
|
|
49
|
+
if (!metadata) {
|
|
50
|
+
throw new Error("The serialized identity is not defined")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (typeof metadata === "string") {
|
|
54
|
+
try {
|
|
55
|
+
metadata = JSON.parse(metadata) as SerializedIdentity
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw new Error("The serialized identity cannot be parsed")
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (
|
|
62
|
+
!("identityNullifier" in metadata) ||
|
|
63
|
+
!("identityTrapdoor" in metadata) ||
|
|
64
|
+
!("secret" in metadata)
|
|
65
|
+
) {
|
|
66
|
+
throw new Error("The serialized identity does not contain the right parameter")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { identityNullifier, identityTrapdoor, secret } = metadata
|
|
70
|
+
|
|
71
|
+
this._identityNullifier = BigNumber.from(`0x${identityNullifier}`).toBigInt()
|
|
72
|
+
this._identityTrapdoor = BigNumber.from(`0x${identityTrapdoor}`).toBigInt()
|
|
73
|
+
this._secret = secret.map((item) => BigNumber.from(`0x${item}`).toBigInt())
|
|
74
|
+
|
|
75
|
+
break
|
|
76
|
+
}
|
|
77
|
+
default:
|
|
78
|
+
throw new Error("The provided strategy is not supported")
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Returns the identity trapdoor.
|
|
84
|
+
* @returns The identity trapdoor.
|
|
85
|
+
*/
|
|
86
|
+
public getTrapdoor(): bigint {
|
|
87
|
+
return this._identityTrapdoor
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Returns the identity nullifier.
|
|
92
|
+
* @returns The identity nullifier.
|
|
93
|
+
*/
|
|
94
|
+
public getNullifier(): bigint {
|
|
95
|
+
return this._identityNullifier
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Returns the secret.
|
|
100
|
+
* @returns The secret.
|
|
101
|
+
*/
|
|
102
|
+
public getSecret(): bigint[] {
|
|
103
|
+
return this._secret
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Returns the Poseidon hash of the secret.
|
|
108
|
+
* @returns The hash of the secret.
|
|
109
|
+
*/
|
|
110
|
+
public getSecretHash(): bigint {
|
|
111
|
+
return poseidon(this._secret)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Generates the identity commitment from the secret.
|
|
116
|
+
* @returns identity commitment
|
|
117
|
+
*/
|
|
118
|
+
public genIdentityCommitment(): bigint {
|
|
119
|
+
return poseidon([this.getSecretHash()])
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Serializes the class attributes and returns a stringified object.
|
|
124
|
+
* @returns The stringified serialized identity.
|
|
125
|
+
*/
|
|
126
|
+
public serializeIdentity(): string {
|
|
127
|
+
const data: SerializedIdentity = {
|
|
128
|
+
identityNullifier: this._identityNullifier.toString(16),
|
|
129
|
+
identityTrapdoor: this._identityTrapdoor.toString(16),
|
|
130
|
+
secret: this._secret.map((item) => item.toString(16))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return JSON.stringify(data)
|
|
134
|
+
}
|
|
135
|
+
}
|
package/src/index.ts
ADDED
package/src/utils.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BigNumber } from "@ethersproject/bignumber"
|
|
2
|
+
import { randomBytes } from "@ethersproject/random"
|
|
3
|
+
import _sha256 from "crypto-js/sha256"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns an hexadecimal sha256 hash of the message passed as parameter.
|
|
7
|
+
* @param message The string to hash.
|
|
8
|
+
* @returns The hexadecimal hash of the message.
|
|
9
|
+
*/
|
|
10
|
+
export function sha256(message: string): string {
|
|
11
|
+
const hash = _sha256(message)
|
|
12
|
+
|
|
13
|
+
return hash.toString()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generates a random big number.
|
|
18
|
+
* @param numberOfBytes The number of bytes of the number.
|
|
19
|
+
* @returns The generated random number.
|
|
20
|
+
*/
|
|
21
|
+
export function genRandomNumber(numberOfBytes = 31): bigint {
|
|
22
|
+
return BigNumber.from(randomBytes(numberOfBytes)).toBigInt()
|
|
23
|
+
}
|