mirror of
https://github.com/selfxyz/self.git
synced 2026-01-10 23:27:56 -05:00
refactor: store pubkeys as string
also add some comment code for registerPubkey function
This commit is contained in:
@@ -27,6 +27,13 @@ interface IIdentityRegistrySelfricaV1 {
|
||||
*/
|
||||
function checkPubkeyCommitment(uint256 pubkeyCommitment) external view returns (bool);
|
||||
|
||||
/**
|
||||
* @notice Checks if the provided pubkey string is stored in the registry.
|
||||
* @param pubkey The pubkey string to verify.
|
||||
* @return True if the pubkey is stored in the registry, false otherwise.
|
||||
*/
|
||||
function checkPubkey(string calldata pubkey) external view returns (bool);
|
||||
|
||||
/**
|
||||
* @notice Checks if the identity commitment Merkle tree contains the specified root.
|
||||
* @param root The Merkle tree root to check.
|
||||
|
||||
@@ -25,44 +25,18 @@ library GCPJWTHelper {
|
||||
return 0;
|
||||
}
|
||||
|
||||
function extractPubkeyCommitment(uint256 p0, uint256 p1, uint256 p2) internal pure returns (uint256) {
|
||||
function unpackPubkeyString(uint256 p0, uint256 p1, uint256 p2) internal pure returns (string memory) {
|
||||
bytes memory b64 = new bytes(93);
|
||||
uint256 idx;
|
||||
for (; p0 > 0 && idx < 31; idx++) { b64[idx] = bytes1(uint8(p0 & 0xff)); p0 >>= 8; }
|
||||
for (; p1 > 0 && idx < 62; idx++) { b64[idx] = bytes1(uint8(p1 & 0xff)); p1 >>= 8; }
|
||||
for (; p2 > 0 && idx < 93; idx++) { b64[idx] = bytes1(uint8(p2 & 0xff)); p2 >>= 8; }
|
||||
|
||||
bytes memory decoded = _decodeBase64Url(b64, idx);
|
||||
uint256 result;
|
||||
for (uint256 i; i < decoded.length && i < 32; i++) {
|
||||
result = (result << 8) | uint8(decoded[i]);
|
||||
// Trim to actual length
|
||||
bytes memory result = new bytes(idx);
|
||||
for (uint256 i; i < idx; i++) {
|
||||
result[i] = b64[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function _decodeBase64Url(bytes memory data, uint256 len) private pure returns (bytes memory) {
|
||||
if (len == 0) return new bytes(0);
|
||||
uint256 outLen = (len * 3) / 4;
|
||||
bytes memory result = new bytes(outLen);
|
||||
uint256 j;
|
||||
for (uint256 i; i < len; i += 4) {
|
||||
uint8 a = _b64(uint8(data[i]));
|
||||
uint8 b = i + 1 < len ? _b64(uint8(data[i + 1])) : 0;
|
||||
uint8 c = i + 2 < len ? _b64(uint8(data[i + 2])) : 0;
|
||||
uint8 d = i + 3 < len ? _b64(uint8(data[i + 3])) : 0;
|
||||
if (j < outLen) result[j++] = bytes1((a << 2) | (b >> 4));
|
||||
if (j < outLen && i + 2 < len) result[j++] = bytes1((b << 4) | (c >> 2));
|
||||
if (j < outLen && i + 3 < len) result[j++] = bytes1((c << 6) | d);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function _b64(uint8 c) private pure returns (uint8) {
|
||||
if (c >= 65 && c <= 90) return c - 65;
|
||||
if (c >= 97 && c <= 122) return c - 71;
|
||||
if (c >= 48 && c <= 57) return c + 4;
|
||||
if (c == 45) return 62;
|
||||
if (c == 95) return 63;
|
||||
return 0;
|
||||
return string(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,9 @@ abstract contract IdentityRegistrySelfricaStorageV1 is ImplRoot {
|
||||
|
||||
/// @notice Address of the GCP JWT verifier contract.
|
||||
address internal _gcpJwtVerifier;
|
||||
|
||||
/// @notice Pubkey strings registered for Selfrica (via GCP JWT proof).
|
||||
mapping(string => bool) internal _isRegisteredPubkey;
|
||||
}
|
||||
|
||||
interface IGCPJWTVerifier {
|
||||
@@ -110,10 +113,10 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
uint256 imtRoot,
|
||||
uint256 imtIndex
|
||||
);
|
||||
/// @notice Emitted when a public key commitment is successfully registered.
|
||||
event PubkeyRegistered(uint256 indexed commitment);
|
||||
/// @notice Emitted when a public key commitment is removed.
|
||||
event PubkeyRemoved(uint256 indexed commitment);
|
||||
/// @notice Emitted when a public key commitment is successfully registered (owner).
|
||||
event PubkeyCommitmentRegistered(uint256 indexed commitment);
|
||||
/// @notice Emitted when a public key is successfully registered (via GCP JWT proof).
|
||||
event PubkeyRegistered(string pubkey);
|
||||
|
||||
/// @notice Emitted when a identity commitment is added by dev team.
|
||||
event DevCommitmentRegistered(
|
||||
@@ -222,13 +225,20 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
return _rootTimestamps[root];
|
||||
}
|
||||
|
||||
/// @notice Checks if the pubkey commitment is registered.
|
||||
/// @notice Checks if the pubkey commitment is registered (owner-registered).
|
||||
/// @param pubkeyCommitment The pubkey commitment to check.
|
||||
/// @return True if the pubkey commitment is registered, false otherwise.
|
||||
function isRegisteredPubkeyCommitment(uint256 pubkeyCommitment) external view onlyProxy returns (bool) {
|
||||
return _isRegisteredPubkeyCommitment[pubkeyCommitment];
|
||||
}
|
||||
|
||||
/// @notice Checks if the pubkey string is registered (via GCP JWT proof).
|
||||
/// @param pubkey The pubkey string to check.
|
||||
/// @return True if the pubkey is registered, false otherwise.
|
||||
function isRegisteredPubkey(string calldata pubkey) external view onlyProxy returns (bool) {
|
||||
return _isRegisteredPubkey[pubkey];
|
||||
}
|
||||
|
||||
/// @notice Retrieves the total number of identity commitments in the Merkle tree.
|
||||
/// @return The size (i.e., count) of the identity commitment Merkle tree.
|
||||
function getIdentityCommitmentMerkleTreeSize() external view virtual onlyProxy returns (uint256) {
|
||||
@@ -273,7 +283,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Checks if the provided pubkey commitment is stored in the registry.
|
||||
* @notice Checks if the provided pubkey commitment is stored in the registry (owner-registered).
|
||||
* @param pubkeyCommitment The pubkey commitment to verify.
|
||||
* @return True if the pubkey commitment is stored in the registry, false otherwise.
|
||||
*/
|
||||
@@ -281,6 +291,15 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
return _isRegisteredPubkeyCommitment[pubkeyCommitment];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Checks if the provided pubkey string is stored in the registry (via GCP JWT proof).
|
||||
* @param pubkey The pubkey string to verify.
|
||||
* @return True if the pubkey is stored in the registry, false otherwise.
|
||||
*/
|
||||
function checkPubkey(string calldata pubkey) external view onlyProxy returns (bool) {
|
||||
return _isRegisteredPubkey[pubkey];
|
||||
}
|
||||
|
||||
// ====================================================
|
||||
// External Functions - Registration
|
||||
// ====================================================
|
||||
@@ -348,10 +367,10 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
*/
|
||||
function registerPubkeyCommitment(uint256 pubkeyCommitment) external virtual onlyProxy onlyOwner {
|
||||
_isRegisteredPubkeyCommitment[pubkeyCommitment] = true;
|
||||
emit PubkeyRegistered(pubkeyCommitment);
|
||||
emit PubkeyCommitmentRegistered(pubkeyCommitment);
|
||||
}
|
||||
|
||||
/// @notice Registers a pubkey commitment via GCP JWT proof.
|
||||
/// @notice Registers a pubkey via GCP JWT proof.
|
||||
/// @dev Verifies the proof, checks root CA hash matches constant, validates image hash against PCR0Manager.
|
||||
/// @param pA Groth16 proof element A.
|
||||
/// @param pB Groth16 proof element B.
|
||||
@@ -363,15 +382,20 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
uint256[2] calldata pC,
|
||||
uint256[7] calldata pubSignals
|
||||
) external onlyProxy {
|
||||
// Check if the proof is valid
|
||||
if (!IGCPJWTVerifier(_gcpJwtVerifier).verifyProof(pA, pB, pC, pubSignals)) revert INVALID_PROOF();
|
||||
|
||||
// Check if the root CA pubkey hash is valid
|
||||
if (pubSignals[0] != GCP_ROOT_CA_PUBKEY_HASH) revert INVALID_ROOT_CA();
|
||||
|
||||
// Check if the TEE image hash is valid
|
||||
bytes memory imageHash = GCPJWTHelper.unpackAndConvertImageHash(pubSignals[4], pubSignals[5], pubSignals[6]);
|
||||
if (!IPCR0Manager(_PCR0Manager).isPCR0Set(imageHash)) revert INVALID_IMAGE();
|
||||
|
||||
uint256 pubkeyCommitment = GCPJWTHelper.extractPubkeyCommitment(pubSignals[1], pubSignals[2], pubSignals[3]);
|
||||
_isRegisteredPubkeyCommitment[pubkeyCommitment] = true;
|
||||
emit PubkeyRegistered(pubkeyCommitment);
|
||||
// Unpack the pubkey and register it
|
||||
string memory pubkey = GCPJWTHelper.unpackPubkeyString(pubSignals[1], pubSignals[2], pubSignals[3]);
|
||||
_isRegisteredPubkey[pubkey] = true;
|
||||
emit PubkeyRegistered(pubkey);
|
||||
}
|
||||
|
||||
/// @notice Updates the GCP JWT verifier contract address.
|
||||
|
||||
@@ -24,18 +24,18 @@ contract TestGCPJWTHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Exposes extractPubkeyCommitment for testing
|
||||
* @notice Exposes unpackPubkeyString for testing
|
||||
* @param p0 First packed field element (up to 31 bytes of base64url chars)
|
||||
* @param p1 Second packed field element (up to 31 bytes of base64url chars)
|
||||
* @param p2 Third packed field element (remaining base64url chars)
|
||||
* @return The extracted pubkey commitment as uint256
|
||||
* @return The unpacked pubkey as a string
|
||||
*/
|
||||
function testExtractPubkeyCommitment(
|
||||
function testUnpackPubkeyString(
|
||||
uint256 p0,
|
||||
uint256 p1,
|
||||
uint256 p2
|
||||
) external pure returns (uint256) {
|
||||
return GCPJWTHelper.extractPubkeyCommitment(p0, p1, p2);
|
||||
) external pure returns (string memory) {
|
||||
return GCPJWTHelper.unpackPubkeyString(p0, p1, p2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,7 +116,7 @@ describe("GCPJWTHelper", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractPubkeyCommitment", function () {
|
||||
describe("unpackPubkeyString", function () {
|
||||
// Known test vectors from TypeScript implementation
|
||||
// These values pack to the base64url string: "AmtPnrcj3vuhOo10QXjKfsQ2JZsLt7DqeeTHyLlicfUe"
|
||||
const testPubkey = {
|
||||
@@ -125,34 +125,34 @@ describe("GCPJWTHelper", function () {
|
||||
p2: 0n,
|
||||
};
|
||||
|
||||
// Expected pubkey commitment (from TypeScript verification)
|
||||
const expectedCommitment = 1094227850017695624326559586424504982480957966087397748000597471445507011061n;
|
||||
// Expected pubkey string (base64url encoded)
|
||||
const expectedPubkeyString = "AmtPnrcj3vuhOo10QXjKfsQ2JZsLt7DqeeTHyLlicfUe";
|
||||
|
||||
it("should correctly decode base64url and extract commitment", async function () {
|
||||
const result = await testHelper.testExtractPubkeyCommitment(
|
||||
it("should correctly unpack to the expected string", async function () {
|
||||
const result = await testHelper.testUnpackPubkeyString(
|
||||
testPubkey.p0,
|
||||
testPubkey.p1,
|
||||
testPubkey.p2,
|
||||
);
|
||||
|
||||
expect(result).to.equal(expectedCommitment);
|
||||
expect(result).to.equal(expectedPubkeyString);
|
||||
});
|
||||
|
||||
it("should handle zeros correctly", async function () {
|
||||
// All zeros should produce zero output
|
||||
const result = await testHelper.testExtractPubkeyCommitment(0n, 0n, 0n);
|
||||
expect(result).to.equal(0n);
|
||||
// All zeros should produce empty string
|
||||
const result = await testHelper.testUnpackPubkeyString(0n, 0n, 0n);
|
||||
expect(result).to.equal("");
|
||||
});
|
||||
|
||||
it("should produce consistent results", async function () {
|
||||
// Call multiple times to ensure deterministic behavior
|
||||
const result1 = await testHelper.testExtractPubkeyCommitment(
|
||||
const result1 = await testHelper.testUnpackPubkeyString(
|
||||
testPubkey.p0,
|
||||
testPubkey.p1,
|
||||
testPubkey.p2,
|
||||
);
|
||||
|
||||
const result2 = await testHelper.testExtractPubkeyCommitment(
|
||||
const result2 = await testHelper.testUnpackPubkeyString(
|
||||
testPubkey.p0,
|
||||
testPubkey.p1,
|
||||
testPubkey.p2,
|
||||
@@ -166,15 +166,24 @@ describe("GCPJWTHelper", function () {
|
||||
// Uppercase: A-Z
|
||||
// Lowercase: a-z
|
||||
// Numbers: 0-9
|
||||
// Special: - and _ (though not in this particular test string)
|
||||
// The fact that we get the correct commitment proves all chars are decoded correctly
|
||||
const result = await testHelper.testExtractPubkeyCommitment(
|
||||
// The fact that we get the correct string proves all chars are unpacked correctly
|
||||
const result = await testHelper.testUnpackPubkeyString(
|
||||
testPubkey.p0,
|
||||
testPubkey.p1,
|
||||
testPubkey.p2,
|
||||
);
|
||||
|
||||
expect(result).to.equal(expectedCommitment);
|
||||
expect(result).to.equal(expectedPubkeyString);
|
||||
});
|
||||
|
||||
it("should return correct length string", async function () {
|
||||
const result = await testHelper.testUnpackPubkeyString(
|
||||
testPubkey.p0,
|
||||
testPubkey.p1,
|
||||
testPubkey.p2,
|
||||
);
|
||||
|
||||
expect(result.length).to.equal(expectedPubkeyString.length);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user