Skip to content

Commit

Permalink
feat(iam): build working with mishtiwasm
Browse files Browse the repository at this point in the history
  • Loading branch information
lucianHymer committed Dec 30, 2024
1 parent f811af0 commit b602c8d
Show file tree
Hide file tree
Showing 20 changed files with 151 additions and 85 deletions.
4 changes: 2 additions & 2 deletions iam/__tests__/additional_signer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import request from "supertest";

// ---- Test subject
import { app } from "../src/index";
import { getEip712Issuer } from "../src/issuers";
import { app } from "../src/index.js";
import { getEip712Issuer } from "../src/issuers.js";

const issuer = getEip712Issuer();

Expand Down
2 changes: 1 addition & 1 deletion iam/__tests__/autoVerification.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Request, Response } from "express";
import { isAddress } from "ethers";
import axios from "axios";
import { autoVerificationHandler } from "../src/utils/autoVerification";
import { autoVerificationHandler } from "../src/utils/autoVerification.js";
import { ApiError } from "../src/utils/helpers.js";
import { checkConditionsAndIssueCredentials } from "../src/utils/credentials.js";
import { VerifiableCredential } from "@gitcoin/passport-types";
Expand Down
2 changes: 1 addition & 1 deletion iam/__tests__/challenge.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RequestPayload } from "@gitcoin/passport-types";
import { getChallenge } from "../src/utils/challenge";
import { getChallenge } from "../src/utils/challenge.js";

describe("getChallenge", () => {
it("returns a challenge for SignerChallenge", () => {
Expand Down
4 changes: 2 additions & 2 deletions iam/__tests__/credential_issuance.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios from "axios";
import { checkCredentialBans } from "../src/utils/bans";
import { checkCredentialBans } from "../src/utils/bans.js";
import { ErrorResponseBody } from "@gitcoin/passport-types";
import { ApiError } from "../src/utils/helpers";
import { ApiError } from "../src/utils/helpers.js";

jest.mock("axios");
const mockedAxios = axios as jest.Mocked<typeof axios>;
Expand Down
2 changes: 1 addition & 1 deletion iam/__tests__/easFees.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getEASFeeAmount } from "../src/utils/easFees";
import { getEASFeeAmount } from "../src/utils/easFees.js";
import { parseEther } from "ethers";
import Moralis from "moralis";
import { PassportCache } from "@gitcoin/passport-platforms";
Expand Down
4 changes: 2 additions & 2 deletions iam/__tests__/easPassportSchema.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as easPassportModule from "../src/utils/easPassportSchema";
import * as easStampModule from "../src/utils/easStampSchema";
import * as easPassportModule from "../src/utils/easPassportSchema.js";
import * as easStampModule from "../src/utils/easStampSchema.js";
import onchainInfo from "../../deployments/onchainInfo.json";

import { VerifiableCredential } from "@gitcoin/passport-types";
Expand Down
2 changes: 1 addition & 1 deletion iam/__tests__/easStampSchema.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as easStampModule from "../src/utils/easStampSchema";
import * as easStampModule from "../src/utils/easStampSchema.js";
import { VerifiableCredential } from "@gitcoin/passport-types";
import { NO_EXPIRATION, ZERO_BYTES32 } from "@ethereum-attestation-service/eas-sdk";
import { SchemaEncoder } from "@ethereum-attestation-service/eas-sdk";
Expand Down
18 changes: 9 additions & 9 deletions iam/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import * as DIDKit from "@spruceid/didkit-wasm-node";
import { PassportCache, providers } from "@gitcoin/passport-platforms";

// ---- Test subject
import { app } from "../src/index";
import { getAttestationDomainSeparator } from "../src/utils/attestations";
import { app } from "../src/index.js";
import { getAttestationDomainSeparator } from "../src/utils/attestations.js";

// ---- Types
import {
Expand All @@ -21,14 +21,14 @@ import {
import { MultiAttestationRequest, ZERO_BYTES32, NO_EXPIRATION } from "@ethereum-attestation-service/eas-sdk";

import { parseEther } from "ethers";
import * as easFeesMock from "../src/utils/easFees";
import * as easFeesMock from "../src/utils/easFees.js";
import * as identityMock from "@gitcoin/passport-identity";
import * as easSchemaMock from "../src/utils/easStampSchema";
import * as easPassportSchemaMock from "../src/utils/easPassportSchema";
import { IAMError } from "../src/utils/scorerService";
import { VerifyDidChallengeBaseError, verifyDidChallenge } from "../src/utils/verifyDidChallenge";
import { getEip712Issuer } from "../src/issuers";
import { toJsonObject } from "../src/utils/json";
import * as easSchemaMock from "../src/utils/easStampSchema.js";
import * as easPassportSchemaMock from "../src/utils/easPassportSchema.js";
import { IAMError } from "../src/utils/scorerService.js";
import { VerifyDidChallengeBaseError, verifyDidChallenge } from "../src/utils/verifyDidChallenge.js";
import { getEip712Issuer } from "../src/issuers.js";
import { toJsonObject } from "../src/utils/json.js";

const issuer = getEip712Issuer();

Expand Down
10 changes: 5 additions & 5 deletions iam/__tests__/index_eas_score.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import request from "supertest";
import { PassportCache } from "@gitcoin/passport-platforms";

// ---- Test subject
import { app } from "../src/index";
import { app } from "../src/index.js";

import { MultiAttestationRequest, ZERO_BYTES32, NO_EXPIRATION } from "@ethereum-attestation-service/eas-sdk";

import * as identityMock from "@gitcoin/passport-identity";
import * as easSchemaMock from "../src/utils/easStampSchema";
import * as easPassportSchemaMock from "../src/utils/easPassportSchema";
import { IAMError } from "../src/utils/scorerService";
import { serializeJson, toJsonObject } from "../src/utils/json";
import * as easSchemaMock from "../src/utils/easStampSchema.js";
import * as easPassportSchemaMock from "../src/utils/easPassportSchema.js";
import { IAMError } from "../src/utils/scorerService.js";
import { serializeJson, toJsonObject } from "../src/utils/json.js";

jest.mock("@gitcoin/passport-identity", () => ({
...jest.requireActual("@gitcoin/passport-identity"),
Expand Down
4 changes: 2 additions & 2 deletions iam/__tests__/scorerService.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { fetchPassportScore } from "../src/utils/scorerService";
import { fetchPassportScore } from "../src/utils/scorerService.js";

// Import the entire module to help with typing
import * as scorerService from "../src/utils/scorerService";
import * as scorerService from "../src/utils/scorerService.js";

// Type for the mocked function (adjust as needed based on the actual implementation)
type MockedFunction = jest.MockedFunction<typeof scorerService.fetchPassportScore>;
Expand Down
8 changes: 4 additions & 4 deletions iam/__tests__/scrollDevBadge.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Request, Response } from "express";
import { Signature, Contract } from "ethers";
import { scrollDevBadgeHandler, getScrollRpcUrl } from "../src/utils/scrollDevBadge";
import { getAttestationSignerForChain } from "../src/utils/attestations";
import { hasValidIssuer } from "../src/issuers";
import { getEASFeeAmount } from "../src/utils/easFees";
import { scrollDevBadgeHandler, getScrollRpcUrl } from "../src/utils/scrollDevBadge.js";
import { getAttestationSignerForChain } from "../src/utils/attestations.js";
import { hasValidIssuer } from "../src/issuers.js";
import { getEASFeeAmount } from "../src/utils/easFees.js";

// Mock external dependencies
jest.mock("@spruceid/didkit-wasm-node");
Expand Down
2 changes: 1 addition & 1 deletion iam/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"@holonym-foundation/mishtiwasm": "^0.3.0-robustnet",
"@ipld/dag-cbor": "^7.0.3",
"@spruceid/didkit-wasm-node": "^0.2.1",
"axios": "^0.27.2",
"axios": "^1.7.9",
"brightid_sdk": "^1.0.1",
"cors": "^2.8.5",
"dids": "^5.0.2",
Expand Down
42 changes: 42 additions & 0 deletions iam/src/missingTypes.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
declare module "ceramic-cacao" {
export declare type Cacao = {
h: Header;
p: Payload;
s?: Signature;
};

export declare namespace Cacao {
function fromBlockBytes(bytes: Uint8Array): Promise<Cacao>;
}
}

declare module "multiformats/cid" {
export class CID {
static decode(bytes: Uint8Array): CID;
toString(): string;
}
}

declare module "multiformats/block" {
export class CID {
static decode(bytes: Uint8Array): CID;
toString(): string;
}

export class Block {
cid: CID;
}

export function encode<T, Code extends number, Algorithm_1 extends number>({

Check warning on line 30 in iam/src/missingTypes.d.ts

View workflow job for this annotation

GitHub Actions / Build and Test

'Algorithm_1' is defined but never used

Check warning on line 30 in iam/src/missingTypes.d.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

'Algorithm_1' is defined but never used
value,
codec,
hasher,
}: {
value: T;
codec: import("./codecs/interface").BlockEncoder<Code, T>;
hasher: import("./hashes/interface").MultihashHasher<number>;
}): Promise<Block<T>>;
}

declare module "multiformats/hashes/sha2";
declare module "@ipld/dag-cbor";
2 changes: 1 addition & 1 deletion iam/src/scripts/buildProviderBitMapInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { writeFileSync } from "fs";
import { join } from "path";
import axios from "axios";

import { StampMetadata, mapBitMapInfo } from "../utils/easPassportSchema";
import { StampMetadata, mapBitMapInfo } from "../utils/easPassportSchema.js";

dotenv.config();

Expand Down
54 changes: 35 additions & 19 deletions iam/src/utils/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import {

import { getIssuerKey } from "../issuers.js";

// Need to do this here instead of in the identity package
// so that this isn't loaded in the browser
import initMishtiWasm, { generate_oprf } from "@holonym-foundation/mishtiwasm";
let mishtiWasmInitialized = false;

// ---- Generate & Verify methods
import * as DIDKit from "@spruceid/didkit-wasm-node";
import { issueHashedCredential, verifyCredential } from "@gitcoin/passport-identity";
Expand All @@ -21,30 +26,24 @@ import { providers, platforms } from "@gitcoin/passport-platforms";
import { ApiError } from "./helpers.js";
import { checkCredentialBans } from "./bans.js";

const providerTypePlatformMap = Object.entries(platforms).reduce(
(acc, [platformName, { providers }]) => {
providers.forEach(({ type }) => {
acc[type] = platformName;
});
const providerTypePlatformMap = Object.entries(platforms).reduce((acc, [platformName, { providers }]) => {

Check failure on line 29 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Insert `⏎··`

Check failure on line 29 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Insert `⏎··`
providers.forEach(({ type }) => {

Check failure on line 30 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Insert `··`

Check failure on line 30 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Insert `··`
acc[type] = platformName;

Check failure on line 31 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Replace `····` with `······`

Check failure on line 31 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Replace `····` with `······`
});

Check failure on line 32 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Insert `··`

Check failure on line 32 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Insert `··`

return acc;
},
{} as { [k: string]: string }
);
return acc;

Check failure on line 34 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Insert `··`

Check failure on line 34 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Insert `··`
}, {} as { [k: string]: string });

Check failure on line 35 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Replace `},·{}·as·{·[k:·string]:·string·}` with `··},⏎··{}·as·{·[k:·string]:·string·}⏎`

Check failure on line 35 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Replace `},·{}·as·{·[k:·string]:·string·}` with `··},⏎··{}·as·{·[k:·string]:·string·}⏎`

function groupProviderTypesByPlatform(types: string[]): string[][] {
return Object.values(
types.reduce(
(groupedProviders, type) => {
const platform = providerTypePlatformMap[type] || "generic";
types.reduce((groupedProviders, type) => {

Check failure on line 39 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Insert `⏎······`

Check failure on line 39 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Insert `⏎······`
const platform = providerTypePlatformMap[type] || "generic";

Check failure on line 40 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Insert `··`

Check failure on line 40 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Insert `··`

if (!groupedProviders[platform]) groupedProviders[platform] = [];
groupedProviders[platform].push(type);
if (!groupedProviders[platform]) groupedProviders[platform] = [];

Check failure on line 42 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Insert `··`

Check failure on line 42 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Insert `··`
groupedProviders[platform].push(type);

Check failure on line 43 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Replace `······` with `········`

Check failure on line 43 in iam/src/utils/credentials.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Replace `······` with `········`

return groupedProviders;
},
{} as { [k: keyof typeof platforms]: string[] }
)
return groupedProviders;
}, {} as { [k: keyof typeof platforms]: string[] })
);
}

Expand All @@ -54,6 +53,10 @@ const issueCredentials = async (
address: string,
payload: RequestPayload
): Promise<CredentialResponseBody[]> => {
if (!mishtiWasmInitialized) {
await initMishtiWasm();
mishtiWasmInitialized = true;
}
// if the payload includes an additional signer, use that to issue credential.
if (payload.signer) {
// We can assume that the signer is a valid address because the challenge was verified within the /verify endpoint
Expand Down Expand Up @@ -89,7 +92,20 @@ const issueCredentials = async (
address,
record,
verifyResult.expiresInSeconds,
payload.signatureType
payload.signatureType,
async () => {
const nullifier = await generate_oprf(
process.env.TMP_PRIVATE_KEY,
// JSON.stringify(objToSortedArray(record)),
"usr:1234",
"OPRFSecp256k1",
"http://192.168.0.33:8081"
);

console.log("nullifier", nullifier);

return nullifier;
}
));
}
} catch {
Expand Down
2 changes: 1 addition & 1 deletion iam/src/utils/scorerService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios from "axios";
import { Score } from "./easStampSchema";
import { Score } from "./easStampSchema.js";
import { handleAxiosError } from "@gitcoin/passport-platforms";

export class IAMError extends Error {
Expand Down
9 changes: 6 additions & 3 deletions iam/src/utils/verifyDidChallenge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import { Cacao } from "ceramic-cacao";
import { CID } from "multiformats/cid";
import { SignedDidChallenge } from "@gitcoin/passport-types";
import * as dagCBOR from "@ipld/dag-cbor";
import { encode } from "multiformats/block";
import { encode, Block } from "multiformats/block";
import { sha256 } from "multiformats/hashes/sha2";
import { MAX_VALID_DID_SESSION_AGE } from "@gitcoin/passport-identity";

export class VerifyDidChallengeBaseError extends Error {}

Expand Down Expand Up @@ -36,7 +35,11 @@ const verifyMatchesExpectedChallenge = async (
expectedChallenge: string
): Promise<void> => {
try {
const expectedBlock = await encode({ value: expectedChallenge, codec: dagCBOR, hasher: sha256 });
const expectedBlock: Block = await encode({
value: expectedChallenge,
codec: dagCBOR,
hasher: sha256,
});

const signedCID = CID.decode(new Uint8Array(signedChallenge.cid));

Expand Down
4 changes: 2 additions & 2 deletions iam/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"extends": "../tsconfig.settings.json",
"compilerOptions": {
"module": "esnext",
"module": "NodeNext",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"target": "es6",
"noImplicitAny": true,
"moduleResolution": "node",
"moduleResolution": "nodenext",
"sourceMap": true,
"outDir": "dist",
"baseUrl": ".",
Expand Down
47 changes: 19 additions & 28 deletions identity/src/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ import * as base64 from "@ethersproject/base64";
// --- Crypto lib for hashing
import { createHash } from "crypto";

import initMishtiWasm, { InitOutput, generate_oprf } from "@holonym-foundation/mishtiwasm";
let mishti: InitOutput | undefined;

// Keeping track of the hashing mechanism (algo + content)
export const VERSION = "v0.0.0";

Expand Down Expand Up @@ -203,39 +200,33 @@ export const issueChallengeCredential = async (
} as IssuedCredential;
};

const getNullifier = ({ key, record, oprf }: { key: string; record: ProofRecord; oprf?: () => Promise<string> }) => {
if (oprf) {
return oprf();
} else {
// Generate a hash like SHA256(IAM_PRIVATE_KEY+PII), where PII is the (deterministic) JSON representation
// of the PII object after transforming it to an array of the form [[key:string, value:string], ...]
// with the elements sorted by key
return base64.encode(
createHash("sha256")
.update(key, "utf-8")
.update(JSON.stringify(objToSortedArray(record)))
.digest()
);
}
};

// Return a verifiable credential with embedded hash
export const issueHashedCredential = async (
DIDKit: DIDKitLib,
key: string,
address: string,
record: ProofRecord,
expiresInSeconds: number = CREDENTIAL_EXPIRES_AFTER_SECONDS,
signatureType?: string
signatureType?: string,
oprf?: () => Promise<string>
): Promise<IssuedCredential> => {
// Generate a hash like SHA256(IAM_PRIVATE_KEY+PII), where PII is the (deterministic) JSON representation
// of the PII object after transforming it to an array of the form [[key:string, value:string], ...]
// with the elements sorted by key
// important to call init -- wasm is initialized asynchronously.
// without it, the other functions won't work
if (mishti === undefined) {
mishti = await initMishtiWasm();
}

const nullifier = await generate_oprf(
"0xde7642ddc5c6315505dd4ecd2d82a93cf2bc81ae1b1ca505f1a0647308a5da61",
// JSON.stringify(objToSortedArray(record)),
"usr:1234",
"OPRFSecp256k1",
"http://192.168.0.33:8081"
);

console.log("nullifier", nullifier);
const hash = base64.encode(
createHash("sha256")
.update(key, "utf-8")
.update(JSON.stringify(objToSortedArray(record)))
.digest()
);
const hash = getNullifier({ key, record, oprf });

let credential: VerifiableCredential;
if (signatureType === "EIP712") {
Expand Down
Loading

0 comments on commit b602c8d

Please sign in to comment.