diff --git a/packages/phone-number-privacy/combiner/package.json b/packages/phone-number-privacy/combiner/package.json index 1a2ea203300..9f9b7f1ba3f 100644 --- a/packages/phone-number-privacy/combiner/package.json +++ b/packages/phone-number-privacy/combiner/package.json @@ -24,9 +24,9 @@ "db:migrate": "NODE_ENV=dev ts-node scripts/run-migrations.ts" }, "dependencies": { - "@celo/contractkit": "1.0.2", - "@celo/phone-number-privacy-common": "1.0.33", - "@celo/identity": "1.0.2", + "@celo/contractkit": "1.2.0", + "@celo/phone-number-privacy-common": "1.0.34", + "@celo/identity": "1.2.0", "@celo/utils": "1.0.2", "blind-threshold-bls": "https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a", "elliptic": "^6.5.4", diff --git a/packages/phone-number-privacy/combiner/src/match-making/get-contact-matches.ts b/packages/phone-number-privacy/combiner/src/match-making/get-contact-matches.ts index 6ad4fd27d43..1c10af3a68e 100644 --- a/packages/phone-number-privacy/combiner/src/match-making/get-contact-matches.ts +++ b/packages/phone-number-privacy/combiner/src/match-making/get-contact-matches.ts @@ -4,9 +4,9 @@ import { GetContactMatchesRequest, hasValidAccountParam, hasValidContactPhoneNumbersParam, + hasValidIdentifier, hasValidUserPhoneNumberParam, isVerified, - phoneNumberHashIsValidIfExists, WarningMessage, } from '@celo/phone-number-privacy-common' import Logger from 'bunyan' @@ -71,7 +71,6 @@ function isValidGetContactMatchesInput(requestBody: GetContactMatchesRequest): b hasValidAccountParam(requestBody) && hasValidUserPhoneNumberParam(requestBody) && hasValidContactPhoneNumbersParam(requestBody) && - !!requestBody.hashedPhoneNumber && - phoneNumberHashIsValidIfExists(requestBody) + hasValidIdentifier(requestBody) ) } diff --git a/packages/phone-number-privacy/combiner/src/signing/get-threshold-signature.ts b/packages/phone-number-privacy/combiner/src/signing/get-threshold-signature.ts index 9c93239f5f6..5401fdcc2ab 100644 --- a/packages/phone-number-privacy/combiner/src/signing/get-threshold-signature.ts +++ b/packages/phone-number-privacy/combiner/src/signing/get-threshold-signature.ts @@ -3,11 +3,10 @@ import { ErrorMessage, GetBlindedMessageSigRequest, hasValidAccountParam, - hasValidQueryPhoneNumberParam, - hasValidTimestamp, + hasValidBlindedPhoneNumberParam, + identifierIsValidIfExists, isBodyReasonablySized, MAX_BLOCK_DISCREPANCY_THRESHOLD, - phoneNumberHashIsValidIfExists, SignMessageResponse, SignMessageResponseFailure, SignMessageResponseSuccess, @@ -354,10 +353,9 @@ function getMajorityErrorCode(errorCodes: Map, logger: Logger) { function isValidGetSignatureInput(requestBody: GetBlindedMessageSigRequest): boolean { return ( hasValidAccountParam(requestBody) && - hasValidQueryPhoneNumberParam(requestBody) && - phoneNumberHashIsValidIfExists(requestBody) && - isBodyReasonablySized(requestBody) && - hasValidTimestamp(requestBody) + hasValidBlindedPhoneNumberParam(requestBody) && + identifierIsValidIfExists(requestBody) && + isBodyReasonablySized(requestBody) ) } diff --git a/packages/phone-number-privacy/combiner/test/end-to-end/get-blinded-sig.test.ts b/packages/phone-number-privacy/combiner/test/end-to-end/get-blinded-sig.test.ts index fb97ed41732..a9c599bac0c 100644 --- a/packages/phone-number-privacy/combiner/test/end-to-end/get-blinded-sig.test.ts +++ b/packages/phone-number-privacy/combiner/test/end-to-end/get-blinded-sig.test.ts @@ -36,7 +36,6 @@ describe('Running against a deployed service', () => { account: '0x1234', authenticationMethod: AuthenticationMethod.WALLET_KEY, blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER, - timestamp: Date.now(), version: 'ignore', sessionID: genSessionID(), } @@ -51,7 +50,6 @@ describe('Running against a deployed service', () => { account: ACCOUNT_ADDRESS, authenticationMethod: AuthenticationMethod.WALLET_KEY, blindedQueryPhoneNumber: '', - timestamp: Date.now(), version: 'ignore', sessionID: genSessionID(), } @@ -67,7 +65,6 @@ describe('Running against a deployed service', () => { account: ACCOUNT_ADDRESS, authenticationMethod: AuthenticationMethod.WALLET_KEY, blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER, - timestamp: Date.now(), version: 'ignore', } await expect( @@ -92,14 +89,12 @@ describe('Running against a deployed service', () => { describe('With enough quota', () => { // if these tests are failing, it may just be that the address needs to be fauceted: // celotooljs account faucet --account 0x1be31a94361a391bbafb2a4ccd704f57dc04d4bb --dollar 1 --gold 1 -e --verbose - const timestamp = Date.now() it('Returns sig when querying with unused and used request', async () => { await replenishQuota(ACCOUNT_ADDRESS, contractKit) const body: SignMessageRequest = { account: ACCOUNT_ADDRESS, authenticationMethod: AuthenticationMethod.WALLET_KEY, blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER, - timestamp, version: 'ignore', sessionID: genSessionID(), } diff --git a/packages/phone-number-privacy/combiner/test/index.test.ts b/packages/phone-number-privacy/combiner/test/index.test.ts index 38bb74886eb..43ec2be930b 100644 --- a/packages/phone-number-privacy/combiner/test/index.test.ts +++ b/packages/phone-number-privacy/combiner/test/index.test.ts @@ -1,4 +1,4 @@ -import { isVerified, REQUEST_EXPIRY_WINDOW_MS } from '@celo/phone-number-privacy-common' +import { isVerified } from '@celo/phone-number-privacy-common' import { Request, Response } from 'firebase-functions' import { BLSCryptographyClient } from '../src/bls/bls-cryptography-client' import { VERSION } from '../src/config' @@ -146,17 +146,6 @@ describe(`POST /getBlindedMessageSig endpoint`, () => { getBlindedMessageSig(req, invalidResponseExpected(done, 400)) }) - it('expired timestamp returns 400', (done) => { - const req = { - body: { - ...validRequest, - timestamp: Date.now() - REQUEST_EXPIRY_WINDOW_MS, - }, - headers: mockHeaders, - } as Request - - getBlindedMessageSig(req, invalidResponseExpected(done, 400)) - }) it('invalid blinded phone number returns 400', (done) => { const req = { body: { diff --git a/packages/phone-number-privacy/common/package.json b/packages/phone-number-privacy/common/package.json index 5b604a7efa4..2feccc5385f 100644 --- a/packages/phone-number-privacy/common/package.json +++ b/packages/phone-number-privacy/common/package.json @@ -1,6 +1,6 @@ { "name": "@celo/phone-number-privacy-common", - "version": "1.0.34-dev", + "version": "1.0.35-dev", "description": "Common library for the combiner and signer libraries", "author": "Celo", "license": "Apache-2.0", diff --git a/packages/phone-number-privacy/signer/package.json b/packages/phone-number-privacy/signer/package.json index cdba85d7659..dafd44d6f8f 100644 --- a/packages/phone-number-privacy/signer/package.json +++ b/packages/phone-number-privacy/signer/package.json @@ -1,6 +1,6 @@ { "name": "@celo/phone-number-privacy-signer", - "version": "1.1.6-dev", + "version": "1.1.8-dev", "description": "Signing participator of ODIS", "author": "Celo", "license": "Apache-2.0", @@ -19,9 +19,9 @@ }, "dependencies": { "@celo/base": "1.1.1", - "@celo/contractkit": "1.0.2", - "@celo/phone-number-privacy-common": "1.0.33", - "@celo/identity": "1.0.2", + "@celo/contractkit": "1.2.0", + "@celo/phone-number-privacy-common": "1.0.34", + "@celo/identity": "1.2.0", "@celo/utils": "1.0.2", "@celo/wallet-hsm-azure": "1.0.0-beta3", "@google-cloud/secret-manager": "3.0.0", diff --git a/packages/phone-number-privacy/signer/src/common/metrics.ts b/packages/phone-number-privacy/signer/src/common/metrics.ts index 97acb8aa2c3..809fcf5b153 100644 --- a/packages/phone-number-privacy/signer/src/common/metrics.ts +++ b/packages/phone-number-privacy/signer/src/common/metrics.ts @@ -22,8 +22,8 @@ export const Counters = { labelNames: ['endpoint', 'statusCode'], }), databaseErrors: new Counter({ - name: 'database_read_errors', - help: 'Counter for the number of database read errors', + name: 'database_errors', + help: 'Counter for the number of database errors', labelNames: ['type'], }), blockchainErrors: new Counter({ diff --git a/packages/phone-number-privacy/signer/src/database/models/request.ts b/packages/phone-number-privacy/signer/src/database/models/request.ts index 5e50f2b3d82..f8fea9db8cb 100644 --- a/packages/phone-number-privacy/signer/src/database/models/request.ts +++ b/packages/phone-number-privacy/signer/src/database/models/request.ts @@ -13,7 +13,7 @@ export class Request { constructor(request: GetBlindedMessagePartialSigRequest) { this[REQUESTS_COLUMNS.address] = request.account - this[REQUESTS_COLUMNS.timestamp] = new Date(request.timestamp as number) + this[REQUESTS_COLUMNS.timestamp] = new Date() this[REQUESTS_COLUMNS.blindedQuery] = request.blindedQueryPhoneNumber } } diff --git a/packages/phone-number-privacy/signer/src/database/wrappers/request.ts b/packages/phone-number-privacy/signer/src/database/wrappers/request.ts index 9a3b96b5742..6b4dc7b6f49 100644 --- a/packages/phone-number-privacy/signer/src/database/wrappers/request.ts +++ b/packages/phone-number-privacy/signer/src/database/wrappers/request.ts @@ -13,10 +13,6 @@ export async function getRequestExists( request: GetBlindedMessagePartialSigRequest, logger: Logger ): Promise { - if (!request.timestamp) { - logger.debug('request does not have timestamp') - return false - } logger.debug({ request }, 'Checking if request exists') const getRequestExistsMeter = Histograms.dbOpsInstrumentation .labels('getRequestExists') @@ -24,7 +20,6 @@ export async function getRequestExists( try { const existingRequest = await requests() .where({ - [REQUESTS_COLUMNS.timestamp]: new Date(request.timestamp as number), [REQUESTS_COLUMNS.address]: request.account, [REQUESTS_COLUMNS.blindedQuery]: request.blindedQueryPhoneNumber, }) @@ -42,10 +37,6 @@ export async function getRequestExists( } export async function storeRequest(request: GetBlindedMessagePartialSigRequest, logger: Logger) { - if (!request.timestamp) { - logger.debug('request does not have timestamp') - return true - } const storeRequestMeter = Histograms.dbOpsInstrumentation.labels('storeRequest').startTimer() logger.debug({ request }, 'Storing salt request') try { diff --git a/packages/phone-number-privacy/signer/src/signing/get-partial-signature.ts b/packages/phone-number-privacy/signer/src/signing/get-partial-signature.ts index 10b05aec65c..7e30aea1763 100644 --- a/packages/phone-number-privacy/signer/src/signing/get-partial-signature.ts +++ b/packages/phone-number-privacy/signer/src/signing/get-partial-signature.ts @@ -2,10 +2,9 @@ import { authenticateUser, ErrorMessage, hasValidAccountParam, - hasValidQueryPhoneNumberParam, - hasValidTimestamp, + hasValidBlindedPhoneNumberParam, + identifierIsValidIfExists, isBodyReasonablySized, - phoneNumberHashIsValidIfExists, SignMessageResponse, SignMessageResponseFailure, WarningMessage, @@ -203,10 +202,9 @@ export async function handleGetBlindedMessagePartialSig( function isValidGetSignatureInput(requestBody: GetBlindedMessagePartialSigRequest): boolean { return ( hasValidAccountParam(requestBody) && - hasValidQueryPhoneNumberParam(requestBody) && - phoneNumberHashIsValidIfExists(requestBody) && - isBodyReasonablySized(requestBody) && - hasValidTimestamp(requestBody) + hasValidBlindedPhoneNumberParam(requestBody) && + identifierIsValidIfExists(requestBody) && + isBodyReasonablySized(requestBody) ) } diff --git a/packages/phone-number-privacy/signer/src/signing/query-quota.ts b/packages/phone-number-privacy/signer/src/signing/query-quota.ts index bb50b01710d..7fac37179cb 100644 --- a/packages/phone-number-privacy/signer/src/signing/query-quota.ts +++ b/packages/phone-number-privacy/signer/src/signing/query-quota.ts @@ -6,9 +6,9 @@ import { FULL_NODE_TIMEOUT_IN_MS, GetQuotaRequest, hasValidAccountParam, + identifierIsValidIfExists, isBodyReasonablySized, isVerified, - phoneNumberHashIsValidIfExists, RETRY_COUNT, RETRY_DELAY_IN_MS, WarningMessage, @@ -68,7 +68,7 @@ export async function handleGetQuota( function isValidGetQuotaInput(requestBody: GetQuotaRequest): boolean { return ( hasValidAccountParam(requestBody) && - phoneNumberHashIsValidIfExists(requestBody) && + identifierIsValidIfExists(requestBody) && isBodyReasonablySized(requestBody) ) } diff --git a/packages/phone-number-privacy/signer/test/end-to-end/get-blinded-sig.test.ts b/packages/phone-number-privacy/signer/test/end-to-end/get-blinded-sig.test.ts index 5254e90d37a..d3ee0205eef 100644 --- a/packages/phone-number-privacy/signer/test/end-to-end/get-blinded-sig.test.ts +++ b/packages/phone-number-privacy/signer/test/end-to-end/get-blinded-sig.test.ts @@ -68,23 +68,16 @@ describe('Running against a deployed service', () => { }) it('With auth header signer mismatch', async () => { - const timestamp = Date.now() // Sign body with different account const body = JSON.stringify({ hashedPhoneNumber: '+1455556600', blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER.trim(), ACCOUNT_ADDRESS1, - timestamp, }) const signature = signMessage(JSON.stringify(body), PRIVATE_KEY2, ACCOUNT_ADDRESS2) const authHeader = serializeSignature(signature) - const response = await postToSignMessage( - BLINDED_PHONE_NUMBER, - ACCOUNT_ADDRESS1, - timestamp, - authHeader - ) + const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS1, authHeader) expect(response.status).toBe(401) }) @@ -102,50 +95,62 @@ describe('Running against a deployed service', () => { describe('When account address has enough quota', () => { // if these tests are failing, it may just be that the address needs to be fauceted: // celotooljs account faucet --account ACCOUNT_ADDRESS2 --dollar 1 --gold 1 -e --verbose - let initialQueryCount: number - let timestamp: number + beforeAll(async () => { console.log('ACCOUNT_ADDRESS1 ' + ACCOUNT_ADDRESS1) console.log('ACCOUNT_ADDRESS2 ' + ACCOUNT_ADDRESS2) console.log('ACCOUNT_ADDRESS3 ' + ACCOUNT_ADDRESS3) contractkit.defaultAccount = ACCOUNT_ADDRESS2 - - initialQueryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) - timestamp = Date.now() }) - it('Returns sig when querying succeeds with unused request', async () => { + it('Returns sig when querying succeeds', async () => { await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2, timestamp) + const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2) expect(response.status).toBe(200) }) - it('Returns count when querying with unused request increments query count', async () => { - const queryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) - expect(queryCount).toEqual(initialQueryCount + 1) + // Backwards compatibility check + it('Returns sig when querying succeeds w/ timestamp', async () => { + await replenishQuota(ACCOUNT_ADDRESS2, contractkit) + const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2, Date.now()) + expect(response.status).toBe(200) }) - it('Returns sig when querying succeeds with used request', async () => { + it('Increments query count when querying succeeds', async () => { await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2, timestamp) - expect(response.status).toBe(200) + const initialQueryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) + await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2) + expect(initialQueryCount).toEqual((await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER)) - 1) }) - it('Returns count when querying with used request does not increment query count', async () => { - const queryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) - expect(queryCount).toEqual(initialQueryCount + 1) + // Backwards compatibility check + it('Increments query count when querying succeeds w/ timestamp', async () => { + await replenishQuota(ACCOUNT_ADDRESS2, contractkit) + const initialQueryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) + await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2, Date.now()) + expect(initialQueryCount).toEqual((await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER)) - 1) }) - it('Returns sig when querying succeeds with missing timestamp', async () => { + it('Returns sig when querying succeeds with replayed request without incrementing query count', async () => { await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2) - expect(response.status).toBe(200) + const res1 = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2) + expect(res1.status).toBe(200) + const queryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) + const res2 = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2) + expect(res2.status).toBe(200) + expect(queryCount).toEqual(await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER)) }) - it('Returns count when querying with missing timestamp increments query count', async () => { + // Backwards compatibility check + it('Returns sig when querying succeeds with replayed request without incrementing query count w/ timestamp', async () => { + await replenishQuota(ACCOUNT_ADDRESS2, contractkit) + const res1 = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2, Date.now()) + expect(res1.status).toBe(200) const queryCount = await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER) - expect(queryCount).toEqual(initialQueryCount + 2) + const res2 = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS2, Date.now()) + expect(res2.status).toBe(200) + expect(queryCount).toEqual(await getQueryCount(ACCOUNT_ADDRESS2, IDENTIFIER)) }) }) @@ -155,7 +160,6 @@ describe('Running against a deployed service', () => { // NOTE: DO NOT FAUCET ACCOUNT_ADDRESS3 let initialQuota: number let initialQueryCount: number - let timestamp: number beforeAll(async () => { contractkit.defaultAccount = ACCOUNT_ADDRESS3 await registerWalletAddress(ACCOUNT_ADDRESS3, ACCOUNT_ADDRESS2, PRIVATE_KEY2, contractkit) @@ -163,7 +167,6 @@ describe('Running against a deployed service', () => { // and ACCOUNT_ADDRESS3 is account address (does not have quota on it's own, only bc of walletAddress) initialQuota = await getQuota(ACCOUNT_ADDRESS3, IDENTIFIER) initialQueryCount = await getQueryCount(ACCOUNT_ADDRESS3, IDENTIFIER) - timestamp = Date.now() }) it('Check that accounts are set up correctly', async () => { @@ -173,7 +176,7 @@ describe('Running against a deployed service', () => { it('Returns sig when querying succeeds with unused request', async () => { await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS3, timestamp) + const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS3) expect(response.status).toBe(200) }) @@ -184,7 +187,7 @@ describe('Running against a deployed service', () => { it('Returns sig when querying succeeds with used request', async () => { await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS3, timestamp) + const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS3) expect(response.status).toBe(200) }) @@ -192,17 +195,6 @@ describe('Running against a deployed service', () => { const queryCount = await getQueryCount(ACCOUNT_ADDRESS3, IDENTIFIER) expect(queryCount).toEqual(initialQueryCount + 1) }) - - it('Returns sig when querying succeeds with missing timestamp', async () => { - await replenishQuota(ACCOUNT_ADDRESS2, contractkit) - const response = await postToSignMessage(BLINDED_PHONE_NUMBER, ACCOUNT_ADDRESS3) - expect(response.status).toBe(200) - }) - - it('Returns count when querying with missing timestamp increments query count', async () => { - const queryCount = await getQueryCount(ACCOUNT_ADDRESS3, IDENTIFIER) - expect(queryCount).toEqual(initialQueryCount + 2) - }) }) }) diff --git a/packages/phone-number-privacy/signer/test/index.test.ts b/packages/phone-number-privacy/signer/test/index.test.ts index 0755b618239..05dcb7b2b16 100644 --- a/packages/phone-number-privacy/signer/test/index.test.ts +++ b/packages/phone-number-privacy/signer/test/index.test.ts @@ -1,4 +1,4 @@ -import { authenticateUser, REQUEST_EXPIRY_WINDOW_MS } from '@celo/phone-number-privacy-common' +import { authenticateUser } from '@celo/phone-number-privacy-common' import BigNumber from 'bignumber.js' import request from 'supertest' import { ErrorMessage, WarningMessage } from '../../common/src/interfaces/error-utils' @@ -78,7 +78,6 @@ describe(`POST /getBlindedMessageSignature endpoint`, () => { blindedQueryPhoneNumber: BLINDED_PHONE_NUMBER, hashedPhoneNumber: '0x5f6e88c3f724b3a09d3194c0514426494955eff7127c29654e48a361a19b4b96', account: '0x78dc5D2D739606d31509C31d654056A45185ECb6', - timestamp: Date.now(), } describe('with valid input', () => { @@ -102,6 +101,27 @@ describe(`POST /getBlindedMessageSignature endpoint`, () => { done ) }) + // Backwards compatibility check + it('provides signature w/ expired timestamp', (done) => { + mockGetRemainingQueryCount.mockResolvedValue({ performedQueryCount: 0, totalQuota: 10 }) + mockGetBlockNumber.mockResolvedValue(10000) + request(app) + .post('/getBlindedMessagePartialSig') + .send({ ...validRequest, timestamp: Date.now() - 10 * 60 * 1000 }) // 10 minutes ago + .expect('Content-Type', /json/) + .expect( + 200, + { + success: true, + signature: BLS_SIGNATURE, + version: getVersion(), + performedQueryCount: 1, + totalQuota: 10, + blockNumber: 10000, + }, + done + ) + }) it('returns 403 on query count 0', (done) => { mockGetRemainingQueryCount.mockResolvedValue({ performedQueryCount: 10, totalQuota: 10 }) request(app) @@ -213,15 +233,6 @@ describe(`POST /getBlindedMessageSignature endpoint`, () => { request(app).post('/getBlindedMessagePartialSig').send(mockRequestData).expect(400, done) }) - it('expired timestamp returns 400', (done) => { - const mockRequestData = { - ...validRequest, - timestamp: Date.now() - REQUEST_EXPIRY_WINDOW_MS, - } - - request(app).post('/getBlindedMessagePartialSig').send(mockRequestData).expect(400, done) - }) - it('invalid blinded phone number returns 400', (done) => { const mockRequestData = { ...validRequest, diff --git a/packages/sdk/identity/src/odis/bls-blinding-client.ts b/packages/sdk/identity/src/odis/bls-blinding-client.ts index 9f0c688bc4b..c406b6ca1ec 100644 --- a/packages/sdk/identity/src/odis/bls-blinding-client.ts +++ b/packages/sdk/identity/src/odis/bls-blinding-client.ts @@ -1,7 +1,7 @@ import { randomBytes } from 'crypto' export interface BlsBlindingClient { - blindMessage: (base64PhoneNumber: string) => Promise + blindMessage: (base64PhoneNumber: string, seed?: Uint8Array) => Promise unblindAndVerifyMessage: (blindedMessage: string) => Promise } @@ -36,8 +36,16 @@ export class WasmBlsBlindingClient implements BlsBlindingClient { } } - async blindMessage(base64PhoneNumber: string): Promise { - const userSeed = randomBytes(32) + async blindMessage(base64PhoneNumber: string, seed?: Uint8Array): Promise { + let userSeed + if (!seed) { + userSeed = randomBytes(32) + console.warn( + 'Warning: Use a private deterministic seed (i.e. DEK private key) to preserve user quota when requests are replayed.' + ) + } else { + userSeed = seed + } this.rawMessage = Buffer.from(base64PhoneNumber, 'base64') this.blindedValue = await this.thresholdBls.blind(this.rawMessage, userSeed) const blindedMessage = this.blindedValue.message diff --git a/packages/sdk/identity/src/odis/phone-number-identifier.ts b/packages/sdk/identity/src/odis/phone-number-identifier.ts index 542a148bf38..5315af5669b 100644 --- a/packages/sdk/identity/src/odis/phone-number-identifier.ts +++ b/packages/sdk/identity/src/odis/phone-number-identifier.ts @@ -49,6 +49,7 @@ export async function getPhoneNumberIdentifier( throw new Error(`Invalid phone number: ${e164Number}`) } // Fallback to using Wasm version if not specified + if (!blsBlindingClient) { debug('No BLSBlindingClient found, using WasmBlsBlindingClient') blsBlindingClient = new WasmBlsBlindingClient(context.odisPubKey) @@ -98,7 +99,6 @@ export async function getBlindedPhoneNumberSignature( ): Promise { const body: SignMessageRequest = { account, - timestamp: Date.now(), blindedQueryPhoneNumber: base64BlindedMessage, hashedPhoneNumber: selfPhoneHash, version: clientVersion ? clientVersion : 'unknown', diff --git a/packages/sdk/identity/src/odis/query.ts b/packages/sdk/identity/src/odis/query.ts index d3a02c89edf..4989d3d8b33 100644 --- a/packages/sdk/identity/src/odis/query.ts +++ b/packages/sdk/identity/src/odis/query.ts @@ -43,7 +43,6 @@ export interface PhoneNumberPrivacyRequest { export interface SignMessageRequest extends PhoneNumberPrivacyRequest { blindedQueryPhoneNumber: string - timestamp?: number hashedPhoneNumber?: string } diff --git a/yarn.lock b/yarn.lock index f9f02e1d89e..8a5a432ab3d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1549,6 +1549,26 @@ is-base64 "^1.1.0" libphonenumber-js "^1.9.11" +"@celo/phone-number-privacy-common@1.0.34": + version "1.0.34" + resolved "https://registry.yarnpkg.com/@celo/phone-number-privacy-common/-/phone-number-privacy-common-1.0.34.tgz#59b3b4d03a0482c967f26f7638d1afd326d4115b" + integrity sha512-xAIusO9N66lBG11/21ro0w8BZTLcRs6sWluam2qlTCf5e1vSPAxQeDx8GaqpcAJ2500zSsvmFOzlAA1iZiAD8g== + dependencies: + "@celo/base" "1.1.1" + "@celo/contractkit" "1.2.0" + "@celo/identity" "1.2.0" + "@celo/utils" "1.0.2" + bignumber.js "^9.0.0" + blind-threshold-bls "https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a" + btoa "1.2.1" + bunyan "1.8.12" + bunyan-debug-stream "2.0.0" + bunyan-gke-stackdriver "0.1.2" + dotenv "^8.2.0" + elliptic "^6.5.4" + is-base64 "^1.1.0" + libphonenumber-js "^1.9.11" + "@celo/typechain-target-web3-v1-celo@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@celo/typechain-target-web3-v1-celo/-/typechain-target-web3-v1-celo-0.2.0.tgz#2560b470e348d2628debe899885724ce5b218bc3"