@semaphore-protocol/identity 0.1.0 → 0.2.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/README.md +15 -20
- package/dist/index.mjs +49 -88
- package/dist/index.node.js +48 -87
- package/dist/types/checkParameter.d.ts +1 -0
- package/dist/types/identity.d.ts +10 -27
- package/dist/types/index.d.ts +2 -2
- package/dist/types/utils.d.ts +6 -0
- package/package.json +4 -5
- package/src/checkParameter.ts +5 -0
- package/src/identity.test.ts +63 -53
- package/src/identity.ts +31 -95
- package/src/index.ts +3 -2
- package/src/utils.ts +15 -0
- package/dist/types/types/index.d.ts +0 -5
- package/src/types/index.ts +0 -5
package/README.md
CHANGED
|
@@ -18,9 +18,6 @@
|
|
|
18
18
|
<a href="https://npmjs.org/package/@semaphore-protocol/identity">
|
|
19
19
|
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/identity.svg?style=flat-square" />
|
|
20
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
21
|
<a href="https://eslint.org/">
|
|
25
22
|
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint" />
|
|
26
23
|
</a>
|
|
@@ -61,37 +58,35 @@ yarn add @semaphore-protocol/identity
|
|
|
61
58
|
|
|
62
59
|
## 📜 Usage
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
\# **new Identity**(identityOrMessage?: _string_): _Identity_
|
|
65
62
|
|
|
66
63
|
```typescript
|
|
67
64
|
import { Identity } from "@semaphore-protocol/identity"
|
|
68
|
-
// const { Identity } = require("@semaphore-protocol/identity") // with commonJS
|
|
69
65
|
|
|
70
|
-
|
|
66
|
+
// Trapdoor and nullifier are generated randomly.
|
|
67
|
+
const identity1 = new Identity()
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
const secret = identity.getSecret()
|
|
75
|
-
const multipartSecret = identity.getMultipartSecret()
|
|
69
|
+
// Trapdoor and nullifier are generated deterministically from a secret message.
|
|
70
|
+
const identity2 = new Identity("secret-message")
|
|
76
71
|
|
|
77
|
-
|
|
72
|
+
// Trapdoor and nullifier are generated from an existing identity.
|
|
73
|
+
const identity3 = new Identity(identity1.toString())
|
|
78
74
|
```
|
|
79
75
|
|
|
80
|
-
|
|
76
|
+
\# **getTrapdoor**(): _bigint_
|
|
81
77
|
|
|
82
78
|
```typescript
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const identity = new Identity(Strategy.MESSAGE, "message")
|
|
79
|
+
const trapdoor = identity.getTrapdoor()
|
|
86
80
|
```
|
|
87
81
|
|
|
88
|
-
|
|
82
|
+
\# **getNullifier**(): _bigint_
|
|
89
83
|
|
|
90
84
|
```typescript
|
|
91
|
-
|
|
85
|
+
const nullifier = identity.getNullifier()
|
|
86
|
+
```
|
|
92
87
|
|
|
93
|
-
|
|
94
|
-
const serializedIdentity = identity.serializeIdentity()
|
|
88
|
+
\# **generateCommitment**(): _bigint_
|
|
95
89
|
|
|
96
|
-
|
|
90
|
+
```typescript
|
|
91
|
+
const commitment = identity.generateCommitment()
|
|
97
92
|
```
|
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @semaphore-protocol/identity
|
|
3
|
-
* @version 0.
|
|
3
|
+
* @version 0.2.0
|
|
4
4
|
* @file A library for managing Semaphore identities.
|
|
5
5
|
* @copyright Ethereum Foundation 2022
|
|
6
6
|
* @license MIT
|
|
7
|
-
* @see [Github]{@link https://github.com/
|
|
7
|
+
* @see [Github]{@link https://github.com/semaphore-protocol/semaphore.js/tree/main/packages/proof}
|
|
8
8
|
*/
|
|
9
9
|
import { BigNumber } from '@ethersproject/bignumber';
|
|
10
10
|
import { poseidon } from 'circomlibjs';
|
|
11
11
|
import { randomBytes } from '@ethersproject/random';
|
|
12
12
|
import _sha256 from 'crypto-js/sha256';
|
|
13
13
|
|
|
14
|
+
function checkParameter(value, name, type) {
|
|
15
|
+
if (typeof value !== type) {
|
|
16
|
+
throw new TypeError("Parameter '".concat(name, "' is not a ").concat(type));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
/**
|
|
15
21
|
* Returns an hexadecimal sha256 hash of the message passed as parameter.
|
|
16
22
|
* @param message The string to hash.
|
|
@@ -28,119 +34,74 @@ function sha256(message) {
|
|
|
28
34
|
function genRandomNumber(numberOfBytes) {
|
|
29
35
|
if (numberOfBytes === void 0) { numberOfBytes = 31; }
|
|
30
36
|
return BigNumber.from(randomBytes(numberOfBytes)).toBigInt();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Checks if a string is a JSON.
|
|
40
|
+
* @param jsonString The JSON string.
|
|
41
|
+
* @returns True or false.
|
|
42
|
+
*/
|
|
43
|
+
function isJson(jsonString) {
|
|
44
|
+
try {
|
|
45
|
+
JSON.parse(jsonString);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
31
51
|
}
|
|
32
52
|
|
|
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
53
|
var Identity = /** @class */ (function () {
|
|
41
54
|
/**
|
|
42
55
|
* Initializes the class attributes based on the strategy passed as parameter.
|
|
43
|
-
* @param
|
|
44
|
-
* @param metadata Additional data needed to create identity for given strategy.
|
|
56
|
+
* @param identityOrMessage Additional data needed to create identity for given strategy.
|
|
45
57
|
*/
|
|
46
|
-
function Identity(
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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");
|
|
58
|
+
function Identity(identityOrMessage) {
|
|
59
|
+
if (identityOrMessage === undefined) {
|
|
60
|
+
this._trapdoor = genRandomNumber();
|
|
61
|
+
this._nullifier = genRandomNumber();
|
|
62
|
+
return;
|
|
94
63
|
}
|
|
64
|
+
checkParameter(identityOrMessage, "identityOrMessage", "string");
|
|
65
|
+
if (!isJson(identityOrMessage)) {
|
|
66
|
+
var messageHash = sha256(identityOrMessage);
|
|
67
|
+
this._trapdoor = BigNumber.from("0x".concat(sha256("".concat(messageHash, "identity_trapdoor")))).toBigInt();
|
|
68
|
+
this._nullifier = BigNumber.from("0x".concat(sha256("".concat(messageHash, "identity_nullifier")))).toBigInt();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
var _a = JSON.parse(identityOrMessage), trapdoor = _a[0], nullifier = _a[1];
|
|
72
|
+
this._trapdoor = BigNumber.from("0x".concat(trapdoor)).toBigInt();
|
|
73
|
+
this._nullifier = BigNumber.from("0x".concat(nullifier)).toBigInt();
|
|
95
74
|
}
|
|
96
75
|
/**
|
|
97
76
|
* Returns the identity trapdoor.
|
|
98
77
|
* @returns The identity trapdoor.
|
|
99
78
|
*/
|
|
100
79
|
Identity.prototype.getTrapdoor = function () {
|
|
101
|
-
return this.
|
|
80
|
+
return this._trapdoor;
|
|
102
81
|
};
|
|
103
82
|
/**
|
|
104
83
|
* Returns the identity nullifier.
|
|
105
84
|
* @returns The identity nullifier.
|
|
106
85
|
*/
|
|
107
86
|
Identity.prototype.getNullifier = function () {
|
|
108
|
-
return this.
|
|
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);
|
|
87
|
+
return this._nullifier;
|
|
123
88
|
};
|
|
124
89
|
/**
|
|
125
|
-
* Generates the identity commitment from
|
|
90
|
+
* Generates the identity commitment from trapdoor and nullifier.
|
|
126
91
|
* @returns identity commitment
|
|
127
92
|
*/
|
|
128
|
-
Identity.prototype.
|
|
129
|
-
return poseidon([this.
|
|
93
|
+
Identity.prototype.generateCommitment = function () {
|
|
94
|
+
return poseidon([poseidon([this._nullifier, this._trapdoor])]);
|
|
130
95
|
};
|
|
131
96
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
97
|
+
* Returns a JSON string with trapdoor and nullifier. It can be used
|
|
98
|
+
* to export the identity and reuse it later.
|
|
99
|
+
* @returns The string representation of the identity.
|
|
134
100
|
*/
|
|
135
|
-
Identity.prototype.
|
|
136
|
-
|
|
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);
|
|
101
|
+
Identity.prototype.toString = function () {
|
|
102
|
+
return JSON.stringify([this._trapdoor.toString(16), this._nullifier.toString(16)]);
|
|
142
103
|
};
|
|
143
104
|
return Identity;
|
|
144
105
|
}());
|
|
145
106
|
|
|
146
|
-
export { Identity
|
|
107
|
+
export { Identity };
|
package/dist/index.node.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @semaphore-protocol/identity
|
|
3
|
-
* @version 0.
|
|
3
|
+
* @version 0.2.0
|
|
4
4
|
* @file A library for managing Semaphore identities.
|
|
5
5
|
* @copyright Ethereum Foundation 2022
|
|
6
6
|
* @license MIT
|
|
7
|
-
* @see [Github]{@link https://github.com/
|
|
7
|
+
* @see [Github]{@link https://github.com/semaphore-protocol/semaphore.js/tree/main/packages/proof}
|
|
8
8
|
*/
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
@@ -19,6 +19,12 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
|
|
|
19
19
|
|
|
20
20
|
var _sha256__default = /*#__PURE__*/_interopDefaultLegacy(_sha256);
|
|
21
21
|
|
|
22
|
+
function checkParameter(value, name, type) {
|
|
23
|
+
if (typeof value !== type) {
|
|
24
|
+
throw new TypeError("Parameter '".concat(name, "' is not a ").concat(type));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
/**
|
|
23
29
|
* Returns an hexadecimal sha256 hash of the message passed as parameter.
|
|
24
30
|
* @param message The string to hash.
|
|
@@ -36,117 +42,72 @@ function sha256(message) {
|
|
|
36
42
|
function genRandomNumber(numberOfBytes) {
|
|
37
43
|
if (numberOfBytes === void 0) { numberOfBytes = 31; }
|
|
38
44
|
return bignumber.BigNumber.from(random.randomBytes(numberOfBytes)).toBigInt();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Checks if a string is a JSON.
|
|
48
|
+
* @param jsonString The JSON string.
|
|
49
|
+
* @returns True or false.
|
|
50
|
+
*/
|
|
51
|
+
function isJson(jsonString) {
|
|
52
|
+
try {
|
|
53
|
+
JSON.parse(jsonString);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
39
59
|
}
|
|
40
60
|
|
|
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
61
|
var Identity = /** @class */ (function () {
|
|
49
62
|
/**
|
|
50
63
|
* Initializes the class attributes based on the strategy passed as parameter.
|
|
51
|
-
* @param
|
|
52
|
-
* @param metadata Additional data needed to create identity for given strategy.
|
|
64
|
+
* @param identityOrMessage Additional data needed to create identity for given strategy.
|
|
53
65
|
*/
|
|
54
|
-
function Identity(
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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");
|
|
66
|
+
function Identity(identityOrMessage) {
|
|
67
|
+
if (identityOrMessage === undefined) {
|
|
68
|
+
this._trapdoor = genRandomNumber();
|
|
69
|
+
this._nullifier = genRandomNumber();
|
|
70
|
+
return;
|
|
102
71
|
}
|
|
72
|
+
checkParameter(identityOrMessage, "identityOrMessage", "string");
|
|
73
|
+
if (!isJson(identityOrMessage)) {
|
|
74
|
+
var messageHash = sha256(identityOrMessage);
|
|
75
|
+
this._trapdoor = bignumber.BigNumber.from("0x".concat(sha256("".concat(messageHash, "identity_trapdoor")))).toBigInt();
|
|
76
|
+
this._nullifier = bignumber.BigNumber.from("0x".concat(sha256("".concat(messageHash, "identity_nullifier")))).toBigInt();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
var _a = JSON.parse(identityOrMessage), trapdoor = _a[0], nullifier = _a[1];
|
|
80
|
+
this._trapdoor = bignumber.BigNumber.from("0x".concat(trapdoor)).toBigInt();
|
|
81
|
+
this._nullifier = bignumber.BigNumber.from("0x".concat(nullifier)).toBigInt();
|
|
103
82
|
}
|
|
104
83
|
/**
|
|
105
84
|
* Returns the identity trapdoor.
|
|
106
85
|
* @returns The identity trapdoor.
|
|
107
86
|
*/
|
|
108
87
|
Identity.prototype.getTrapdoor = function () {
|
|
109
|
-
return this.
|
|
88
|
+
return this._trapdoor;
|
|
110
89
|
};
|
|
111
90
|
/**
|
|
112
91
|
* Returns the identity nullifier.
|
|
113
92
|
* @returns The identity nullifier.
|
|
114
93
|
*/
|
|
115
94
|
Identity.prototype.getNullifier = function () {
|
|
116
|
-
return this.
|
|
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);
|
|
95
|
+
return this._nullifier;
|
|
131
96
|
};
|
|
132
97
|
/**
|
|
133
|
-
* Generates the identity commitment from
|
|
98
|
+
* Generates the identity commitment from trapdoor and nullifier.
|
|
134
99
|
* @returns identity commitment
|
|
135
100
|
*/
|
|
136
|
-
Identity.prototype.
|
|
137
|
-
return circomlibjs.poseidon([this.
|
|
101
|
+
Identity.prototype.generateCommitment = function () {
|
|
102
|
+
return circomlibjs.poseidon([circomlibjs.poseidon([this._nullifier, this._trapdoor])]);
|
|
138
103
|
};
|
|
139
104
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
105
|
+
* Returns a JSON string with trapdoor and nullifier. It can be used
|
|
106
|
+
* to export the identity and reuse it later.
|
|
107
|
+
* @returns The string representation of the identity.
|
|
142
108
|
*/
|
|
143
|
-
Identity.prototype.
|
|
144
|
-
|
|
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);
|
|
109
|
+
Identity.prototype.toString = function () {
|
|
110
|
+
return JSON.stringify([this._trapdoor.toString(16), this._nullifier.toString(16)]);
|
|
150
111
|
};
|
|
151
112
|
return Identity;
|
|
152
113
|
}());
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function checkParameter(value: any, name: string, type: string): void;
|
package/dist/types/identity.d.ts
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
import { SerializedIdentity } from "./types";
|
|
2
|
-
export declare enum Strategy {
|
|
3
|
-
RANDOM = 0,
|
|
4
|
-
MESSAGE = 1,
|
|
5
|
-
SERIALIZED = 2
|
|
6
|
-
}
|
|
7
1
|
export default class Identity {
|
|
8
|
-
private
|
|
9
|
-
private
|
|
10
|
-
private _secret;
|
|
2
|
+
private _trapdoor;
|
|
3
|
+
private _nullifier;
|
|
11
4
|
/**
|
|
12
5
|
* Initializes the class attributes based on the strategy passed as parameter.
|
|
13
|
-
* @param
|
|
14
|
-
* @param metadata Additional data needed to create identity for given strategy.
|
|
6
|
+
* @param identityOrMessage Additional data needed to create identity for given strategy.
|
|
15
7
|
*/
|
|
16
|
-
constructor(
|
|
8
|
+
constructor(identityOrMessage?: string);
|
|
17
9
|
/**
|
|
18
10
|
* Returns the identity trapdoor.
|
|
19
11
|
* @returns The identity trapdoor.
|
|
@@ -25,23 +17,14 @@ export default class Identity {
|
|
|
25
17
|
*/
|
|
26
18
|
getNullifier(): bigint;
|
|
27
19
|
/**
|
|
28
|
-
*
|
|
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.
|
|
20
|
+
* Generates the identity commitment from trapdoor and nullifier.
|
|
39
21
|
* @returns identity commitment
|
|
40
22
|
*/
|
|
41
|
-
|
|
23
|
+
generateCommitment(): bigint;
|
|
42
24
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
25
|
+
* Returns a JSON string with trapdoor and nullifier. It can be used
|
|
26
|
+
* to export the identity and reuse it later.
|
|
27
|
+
* @returns The string representation of the identity.
|
|
45
28
|
*/
|
|
46
|
-
|
|
29
|
+
toString(): string;
|
|
47
30
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import Identity
|
|
2
|
-
export { Identity
|
|
1
|
+
import Identity from "./identity";
|
|
2
|
+
export { Identity };
|
package/dist/types/utils.d.ts
CHANGED
|
@@ -10,3 +10,9 @@ export declare function sha256(message: string): string;
|
|
|
10
10
|
* @returns The generated random number.
|
|
11
11
|
*/
|
|
12
12
|
export declare function genRandomNumber(numberOfBytes?: number): bigint;
|
|
13
|
+
/**
|
|
14
|
+
* Checks if a string is a JSON.
|
|
15
|
+
* @param jsonString The JSON string.
|
|
16
|
+
* @returns True or false.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isJson(jsonString: string): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@semaphore-protocol/identity",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A library for managing Semaphore identities.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.node.js",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"LICENSE",
|
|
16
16
|
"README.md"
|
|
17
17
|
],
|
|
18
|
-
"repository": "https://github.com/
|
|
19
|
-
"homepage": "https://github.com/
|
|
18
|
+
"repository": "https://github.com/semaphore-protocol/semaphore.js",
|
|
19
|
+
"homepage": "https://github.com/semaphore-protocol/semaphore.js/tree/main/packages/proof",
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
|
|
22
22
|
"build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
|
|
@@ -37,6 +37,5 @@
|
|
|
37
37
|
"@ethersproject/random": "^5.5.1",
|
|
38
38
|
"circomlibjs": "0.0.8",
|
|
39
39
|
"crypto-js": "^4.1.1"
|
|
40
|
-
}
|
|
41
|
-
"gitHead": "6c1b469a4b5ffaac562f478fc2bbc78731734e81"
|
|
40
|
+
}
|
|
42
41
|
}
|
package/src/identity.test.ts
CHANGED
|
@@ -1,92 +1,102 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { BigNumber } from "@ethersproject/bignumber"
|
|
2
|
+
import Identity from "./identity"
|
|
2
3
|
|
|
3
4
|
describe("Identity", () => {
|
|
4
|
-
describe("
|
|
5
|
-
it("Should not create a identity if the
|
|
6
|
-
const
|
|
5
|
+
describe("# Identity", () => {
|
|
6
|
+
it("Should not create a identity if the parameter is not valid", () => {
|
|
7
|
+
const fun1 = () => new Identity(13 as any)
|
|
8
|
+
const fun2 = () => new Identity(true as any)
|
|
9
|
+
const fun3 = () => new Identity((() => true) as any)
|
|
10
|
+
|
|
11
|
+
expect(fun1).toThrow("Parameter 'identityOrMessage' is not a string")
|
|
12
|
+
expect(fun2).toThrow("Parameter 'identityOrMessage' is not a string")
|
|
13
|
+
expect(fun3).toThrow("Parameter 'identityOrMessage' is not a string")
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it("Should create random identities", () => {
|
|
17
|
+
const identity1 = new Identity()
|
|
18
|
+
const identity2 = new Identity()
|
|
7
19
|
|
|
8
|
-
expect(
|
|
20
|
+
expect(identity1.getTrapdoor()).not.toBe(identity2.getTrapdoor())
|
|
21
|
+
expect(identity1.getNullifier()).not.toBe(identity2.getNullifier())
|
|
9
22
|
})
|
|
10
23
|
|
|
11
|
-
it("Should create a
|
|
12
|
-
const
|
|
24
|
+
it("Should create deterministic identities from a message", () => {
|
|
25
|
+
const identity1 = new Identity("message")
|
|
26
|
+
const identity2 = new Identity("message")
|
|
13
27
|
|
|
14
|
-
expect(
|
|
28
|
+
expect(identity1.getTrapdoor()).toBe(identity2.getTrapdoor())
|
|
29
|
+
expect(identity1.getNullifier()).toBe(identity2.getNullifier())
|
|
15
30
|
})
|
|
16
31
|
|
|
17
|
-
it("Should not
|
|
18
|
-
const fun = () => new Identity(
|
|
32
|
+
it("Should not recreate an existing invalid identity", () => {
|
|
33
|
+
const fun = () => new Identity('[true, "01323"]')
|
|
19
34
|
|
|
20
|
-
expect(fun).toThrow("
|
|
35
|
+
expect(fun).toThrow("invalid BigNumber string")
|
|
21
36
|
})
|
|
22
37
|
|
|
23
|
-
it("Should
|
|
24
|
-
const
|
|
38
|
+
it("Should recreate an existing identity", () => {
|
|
39
|
+
const identity1 = new Identity("message")
|
|
25
40
|
|
|
26
|
-
|
|
41
|
+
const identity2 = new Identity(identity1.toString())
|
|
42
|
+
|
|
43
|
+
expect(identity1.getTrapdoor()).toBe(identity2.getTrapdoor())
|
|
44
|
+
expect(identity1.getNullifier()).toBe(identity2.getNullifier())
|
|
27
45
|
})
|
|
46
|
+
})
|
|
28
47
|
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
describe("# getTrapdoor", () => {
|
|
49
|
+
it("Should return the identity trapdoor", () => {
|
|
50
|
+
const identity = new Identity("message")
|
|
31
51
|
|
|
32
52
|
const trapdoor = identity.getTrapdoor()
|
|
33
|
-
const nullifier = identity.getNullifier()
|
|
34
|
-
const identitySecret = identity.getSecret()
|
|
35
53
|
|
|
36
|
-
expect(typeof identity).toBe("object")
|
|
37
54
|
expect(trapdoor).toBe(
|
|
38
55
|
BigInt("58952291509798197436757858062402199043831251943841934828591473955215726495831")
|
|
39
56
|
)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe("# getNullifier", () => {
|
|
61
|
+
it("Should return the identity nullifier", () => {
|
|
62
|
+
const identity = new Identity("message")
|
|
63
|
+
|
|
64
|
+
const nullifier = identity.getNullifier()
|
|
65
|
+
|
|
40
66
|
expect(nullifier).toBe(
|
|
41
67
|
BigInt("44673097405870585416457571638073245190425597599743560105244308998175651589997")
|
|
42
68
|
)
|
|
43
|
-
expect(identitySecret).toHaveLength(2)
|
|
44
|
-
expect(typeof identitySecret).toBe("object")
|
|
45
69
|
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe("# generateCommitment", () => {
|
|
73
|
+
it("Should generate an identity commitment", () => {
|
|
74
|
+
const identity = new Identity("message")
|
|
46
75
|
|
|
47
|
-
|
|
48
|
-
const identity = new Identity(Strategy.MESSAGE, "message")
|
|
49
|
-
const identityCommitment = identity.genIdentityCommitment()
|
|
76
|
+
const commitment = identity.generateCommitment()
|
|
50
77
|
|
|
51
|
-
expect(
|
|
78
|
+
expect(commitment).toBe(
|
|
52
79
|
BigInt("1720349790382552497189398984241859233944354304766757200361065203741879866188")
|
|
53
80
|
)
|
|
54
81
|
})
|
|
82
|
+
})
|
|
55
83
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
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
|
-
})
|
|
84
|
+
describe("# toString", () => {
|
|
85
|
+
it("Should return a string", () => {
|
|
86
|
+
const identity = new Identity("message")
|
|
68
87
|
|
|
69
|
-
|
|
70
|
-
const fun = () => new Identity(Strategy.SERIALIZED, "{a}")
|
|
88
|
+
const identityString = identity.toString()
|
|
71
89
|
|
|
72
|
-
expect(
|
|
90
|
+
expect(typeof identityString).toBe("string")
|
|
73
91
|
})
|
|
74
92
|
|
|
75
|
-
it("Should
|
|
76
|
-
const
|
|
77
|
-
new Identity(Strategy.SERIALIZED, {
|
|
78
|
-
identityTrapdoor: "234"
|
|
79
|
-
} as any)
|
|
93
|
+
it("Should return a valid identity string", () => {
|
|
94
|
+
const identity = new Identity("message")
|
|
80
95
|
|
|
81
|
-
|
|
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)
|
|
96
|
+
const [trapdoor, nullifier] = JSON.parse(identity.toString())
|
|
88
97
|
|
|
89
|
-
expect(
|
|
98
|
+
expect(BigNumber.from(`0x${trapdoor}`).toBigInt()).toBe(identity.getTrapdoor())
|
|
99
|
+
expect(BigNumber.from(`0x${nullifier}`).toBigInt()).toBe(identity.getNullifier())
|
|
90
100
|
})
|
|
91
101
|
})
|
|
92
102
|
})
|
package/src/identity.ts
CHANGED
|
@@ -1,82 +1,39 @@
|
|
|
1
1
|
import { BigNumber } from "@ethersproject/bignumber"
|
|
2
2
|
import { poseidon } from "circomlibjs"
|
|
3
|
-
import
|
|
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
|
-
}
|
|
3
|
+
import checkParameter from "./checkParameter"
|
|
4
|
+
import { genRandomNumber, isJson, sha256 } from "./utils"
|
|
12
5
|
|
|
13
6
|
export default class Identity {
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
private _secret: bigint[] = []
|
|
7
|
+
private _trapdoor: bigint
|
|
8
|
+
private _nullifier: bigint
|
|
18
9
|
|
|
19
10
|
/**
|
|
20
11
|
* Initializes the class attributes based on the strategy passed as parameter.
|
|
21
|
-
* @param
|
|
22
|
-
* @param metadata Additional data needed to create identity for given strategy.
|
|
12
|
+
* @param identityOrMessage Additional data needed to create identity for given strategy.
|
|
23
13
|
*/
|
|
24
|
-
constructor(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
}
|
|
14
|
+
constructor(identityOrMessage?: string) {
|
|
15
|
+
if (identityOrMessage === undefined) {
|
|
16
|
+
this._trapdoor = genRandomNumber()
|
|
17
|
+
this._nullifier = genRandomNumber()
|
|
36
18
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const messageHash = sha256(metadata)
|
|
19
|
+
return
|
|
20
|
+
}
|
|
42
21
|
|
|
43
|
-
|
|
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
|
-
}
|
|
22
|
+
checkParameter(identityOrMessage, "identityOrMessage", "string")
|
|
52
23
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
metadata = JSON.parse(metadata) as SerializedIdentity
|
|
56
|
-
} catch (error) {
|
|
57
|
-
throw new Error("The serialized identity cannot be parsed")
|
|
58
|
-
}
|
|
59
|
-
}
|
|
24
|
+
if (!isJson(identityOrMessage)) {
|
|
25
|
+
const messageHash = sha256(identityOrMessage)
|
|
60
26
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
!("identityTrapdoor" in metadata) ||
|
|
64
|
-
!("secret" in metadata)
|
|
65
|
-
) {
|
|
66
|
-
throw new Error("The serialized identity does not contain the right parameter")
|
|
67
|
-
}
|
|
27
|
+
this._trapdoor = BigNumber.from(`0x${sha256(`${messageHash}identity_trapdoor`)}`).toBigInt()
|
|
28
|
+
this._nullifier = BigNumber.from(`0x${sha256(`${messageHash}identity_nullifier`)}`).toBigInt()
|
|
68
29
|
|
|
69
|
-
|
|
30
|
+
return
|
|
31
|
+
}
|
|
70
32
|
|
|
71
|
-
|
|
72
|
-
this._identityTrapdoor = BigNumber.from(`0x${identityTrapdoor}`).toBigInt()
|
|
73
|
-
this._secret = secret.map((item) => BigNumber.from(`0x${item}`).toBigInt())
|
|
33
|
+
const [trapdoor, nullifier] = JSON.parse(identityOrMessage)
|
|
74
34
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
default:
|
|
78
|
-
throw new Error("The provided strategy is not supported")
|
|
79
|
-
}
|
|
35
|
+
this._trapdoor = BigNumber.from(`0x${trapdoor}`).toBigInt()
|
|
36
|
+
this._nullifier = BigNumber.from(`0x${nullifier}`).toBigInt()
|
|
80
37
|
}
|
|
81
38
|
|
|
82
39
|
/**
|
|
@@ -84,7 +41,7 @@ export default class Identity {
|
|
|
84
41
|
* @returns The identity trapdoor.
|
|
85
42
|
*/
|
|
86
43
|
public getTrapdoor(): bigint {
|
|
87
|
-
return this.
|
|
44
|
+
return this._trapdoor
|
|
88
45
|
}
|
|
89
46
|
|
|
90
47
|
/**
|
|
@@ -92,44 +49,23 @@ export default class Identity {
|
|
|
92
49
|
* @returns The identity nullifier.
|
|
93
50
|
*/
|
|
94
51
|
public getNullifier(): bigint {
|
|
95
|
-
return this.
|
|
52
|
+
return this._nullifier
|
|
96
53
|
}
|
|
97
54
|
|
|
98
55
|
/**
|
|
99
|
-
*
|
|
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.
|
|
56
|
+
* Generates the identity commitment from trapdoor and nullifier.
|
|
116
57
|
* @returns identity commitment
|
|
117
58
|
*/
|
|
118
|
-
public
|
|
119
|
-
return poseidon([this.
|
|
59
|
+
public generateCommitment(): bigint {
|
|
60
|
+
return poseidon([poseidon([this._nullifier, this._trapdoor])])
|
|
120
61
|
}
|
|
121
62
|
|
|
122
63
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
64
|
+
* Returns a JSON string with trapdoor and nullifier. It can be used
|
|
65
|
+
* to export the identity and reuse it later.
|
|
66
|
+
* @returns The string representation of the identity.
|
|
125
67
|
*/
|
|
126
|
-
public
|
|
127
|
-
|
|
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)
|
|
68
|
+
public toString(): string {
|
|
69
|
+
return JSON.stringify([this._trapdoor.toString(16), this._nullifier.toString(16)])
|
|
134
70
|
}
|
|
135
71
|
}
|
package/src/index.ts
CHANGED
package/src/utils.ts
CHANGED
|
@@ -21,3 +21,18 @@ export function sha256(message: string): string {
|
|
|
21
21
|
export function genRandomNumber(numberOfBytes = 31): bigint {
|
|
22
22
|
return BigNumber.from(randomBytes(numberOfBytes)).toBigInt()
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks if a string is a JSON.
|
|
27
|
+
* @param jsonString The JSON string.
|
|
28
|
+
* @returns True or false.
|
|
29
|
+
*/
|
|
30
|
+
export function isJson(jsonString: string) {
|
|
31
|
+
try {
|
|
32
|
+
JSON.parse(jsonString)
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return true
|
|
38
|
+
}
|