일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 체인의정석
- ethers
- git rebase
- 러스트 기초
- 컨트렉트 배포 자동화
- ethers v6
- Vue
- chainlink 설명
- ethers type
- ethers typescript
- 스마트컨트렉트테스트
- ethers websocket
- nestjs 튜토리얼
- SBT표준
- 스마트컨트렉트프록시
- 스마트컨트렉트 함수이름 중복 호출
- nest.js설명
- 깃허브명령어
- 스마트컨트렉트 예약어 함수이름 중복
- multicall
- vue기초
- 스마트 컨트렉트 함수이름 중복
- ambiguous function description
- Vue.js
- 러스트기초
- 머신러닝기초
- 프록시배포구조
- 러스트 기초 학습
- 컨트렉트 동일한 함수이름 호출
- rust 기초
- Today
- Total
체인의정석
ERC4337 + Pass Key, 계정추상화에 패스키 적용하기 본문
패스키는 일반적인 블록체인상의 검증 방식인 ECDSA서명과 다른 서명 로직이 들어가게 된다.
계정추상화의 계정추상화는 ECDSA서명을 통한 예제를 지원한다. 하지만 각 유저들이 보유하는 SmartAccount에 P256 서명을 검증하는 로직을 만들어야 계정추상화에 패스키까지 적용할 수 있다.
다행히 P256Verifer는 오딧 받은 유명한 공개 라이브러리가 있다. (https://p256.eth.limo/)
mport "p256-verifier/P256.sol";
bytes32 hash; // message hash
uint256 r, s; // signature
uint256 x, y; // public key
bool valid = P256.verifySignature(hash, r, s, x, y);
해당 라이브러리를 사용하여 P256.verifySignature를 하면 되는데, 동일한 분이 작성한 아래 코드에 구현이 되어있다.
https://github.com/daimo-eth/p256-verifier/blob/master/src/WebAuthn.sol
해당 내용의 입력값을 보기 위하여 테스트 코드를 보았다.
https://github.com/daimo-eth/p256-verifier/blob/master/test/WebAuthn.t.sol
uint256[2] publicKey = [
0x80d9326e49eb6314d03f58830369ea5bafbc4e2709b30bff1f4379586ca869d9,
0x806ed746d8ac6c2779a472d8c1ed4c200b07978d9d8d8d862be8b7d4b7fb6350
];
string clientDataJSON =
'{"type":"webauthn.get","challenge":"dGVzdA","origin":"https://funny-froyo-3f9b75.netlify.app"}';
bytes challenge = hex"74657374";
bytes authenticatorData =
hex"e0b592a7dd54eedeec65206e031fc196b8e5915f9b389735860c83854f65dc0e1d00000000";
uint256 r =
0x32e005a53ae49a96ac88c715243638dd5c985fbd463c727d8eefd05bee4e2570;
uint256 s =
0x7a4fef4d0b11187f95f69eefbb428df8ac799bbd9305066b1e9c9fe9a5bcf8c4;
uint256 challengeLocation = 23;
uint256 responseTypeLocation = 1;
해당 값이 VerifySignature의 입력값으로 들어갔는데,
ECDSA서명의 경우 PaymasterAndData에 들어가는 서명하는 원본 메세지가 validUnitil, validAfter, userOp hash였다.
이에 맞추어서 challenge에는 해당 값을 인코딩한 값이 들어가면 될 것이고
나머지 값들의 경우에도 챗지피티로 대략적인 느낌을 찾아보았다. 내용은 다음과 같다.
이 코드에서 사용된 값들은 WebAuthn 인증 과정에서 생성된 실제 데이터로, 웹 브라우저와 인증기(Authenticator) 사이에서 주고받은 정보를 테스트 목적으로 고정된 값으로 사용하고 있습니다. 각 값은 WebAuthn 인증 데이터의 특정 구성 요소를 나타냅니다. 자세히 설명해 드리겠습니다:
- publicKey (공개키):
- 이 값은 P-256 공개키의 두 좌표 (X, Y)를 나타냅니다. WebAuthn에서 공개키는 사용자의 인증 기기에 저장된 개인 키에 해당하는 공개 키입니다.
- 이 공개키는 credential public key로 사용되며, WebAuthn 검증 중에 서명된 데이터와 대조하여 서명이 올바른지 확인하는 데 사용됩니다.
- 0x80d9326e...와 0x806ed746...은 타원 곡선의 두 좌표값(X, Y)이며, P-256 타원 곡선에서 유효한 공개키 좌표입니다.
- clientDataJSON:
- 클라이언트 측에서 생성된 JSON 데이터를 나타냅니다. 이 JSON 데이터에는 인증 요청 또는 로그인의 세부 사항이 포함되어 있으며, 이 값은 서명 검증 과정에서 중요한 역할을 합니다.
- {"type":"webauthn.get","challenge":"dGVzdA","origin":"https://funny-froyo-3f9b75.netlify.app"}는 사용자가 인증하는 중에 발생한 이벤트를 설명합니다.
- type: webauthn.get은 사용자가 로그인을 시도하고 있다는 것을 나타냅니다.
- challenge: 이 값은 서버가 제공한 도전값(challenge)으로, 이는 dGVzdA라는 base64로 인코딩된 데이터입니다. 디코딩하면 "test"라는 문자열입니다.
- origin: 인증 요청을 발생시킨 웹사이트의 URL입니다. 여기서는 https://funny-froyo-3f9b75.netlify.app입니다.
- challenge (도전값):
- 이 값은 실제로 서명 검증 과정에서 사용된 도전값을 바이트 형식으로 나타낸 것입니다. 이는 clientDataJSON에서 설명된 challenge와 일치해야 합니다.
- 여기서는 hex"74657374"으로, 이는 ASCII로 "test"라는 문자열입니다.
- authenticatorData:
- 이 값은 WebAuthn 인증기(Authenticator)가 생성한 데이터로, 서명에 포함됩니다.
- authenticatorData에는 사용자의 인증 상태, RP ID 해시, 서명 카운터 등의 정보가 포함됩니다. 이 데이터는 WebAuthn에서 매우 중요한 역할을 합니다.
- 여기서 사용된 값은 e0b592a7...로, WebAuthn 인증기의 상태를 나타냅니다.
- r, s (서명 값):
- 이 값들은 ECDSA 서명의 두 구성 요소인 r과 s 값을 나타냅니다. WebAuthn에서 개인 키를 사용하여 생성된 서명입니다.
- r과 s는 타원 곡선 디지털 서명 알고리즘(ECDSA)에서 생성된 값으로, 이 두 값이 서명이 유효한지 검증하는 데 사용됩니다.
- r과 s의 값은 다음과 같습니다:
- r: 0x32e005a53ae49a96...
- s: 0x7a4fef4d0b11187f...
- challengeLocation 및 responseTypeLocation:
- 이 값들은 clientDataJSON 내에서 도전값(challenge)와 응답 유형(response type)이 위치하는 바이트 오프셋을 나타냅니다.
- challengeLocation = 23은 JSON 데이터의 23번째 바이트부터 challenge가 시작된다는 것을 의미하며, 이는 dGVzdA로 시작하는 도전값의 위치입니다.
- responseTypeLocation = 1은 responseType이 JSON 데이터의 첫 번째 바이트에 위치함을 나타내며, 이는 "type": "webauthn.get"의 시작 부분입니다.
이 값들의 생성 과정
이 값들은 WebAuthn 프로토콜을 사용하여 클라이언트와 서버 간의 실제 인증 과정에서 생성되며, 서명 검증을 위해 필요한 데이터입니다. 테스트 코드에서 이러한 값들을 하드코딩하여 실제 WebAuthn 인증의 결과를 모방하여 검증할 수 있도록 설정한 것입니다.
해당 컨트렉트를 통해서 만든 AA지갑의 구현체는 다음과 같다.
function _validateSignature(
bytes memory message,
bytes calldata signature
) private view returns (bool) {
Signature memory sig = abi.decode(signature, (Signature));
return
WebAuthn.verifySignature({
challenge: message,
authenticatorData: sig.authenticatorData,
requireUserVerification: false,
clientDataJSON: sig.clientDataJSON,
challengeLocation: sig.challengeLocation,
responseTypeLocation: sig.responseTypeLocation,
r: sig.r,
s: sig.s,
x: uint256(publicKey.X),
y: uint256(publicKey.Y)
});
}
서명 검증은 다음과 같이 진행하며 되며 여기에 들어가는 signature를 어떤 식으로 대입하였는지 보기 위하여 테스트 코드를 보았다.
해당 코드를 보면
// get userOp hash (with signature == 0x) by calling the entry point contract
const userOpHash = await this._getUserOpHash(userOp);
// version = 1 and validUntil = 0 in msgToSign are hardcoded
const msgToSign = encodePacked(["uint8", "uint48", "bytes32"], [1, 0, userOpHash]);
// get signature from webauthn
const signature = await this.getSignature(msgToSign, keyId);
userOp 해시를 먼저 구한 후에 version, validUntil, userOpHash를 넣어서 서명할 메세지를 만들었다.
https://github.com/FuelLabs/authn-sign
위의 깃허브를 사용하면 자바스크립트로 P256서명을 만들어서 사용할 수 있다.
위와 같은 서명로직을 이용하여 오프체인에서 입력값만 잘 뽑아내면 여기서 잘 풀리겠지만 문제는 해당 부분의 작동하는 실 구현체를 찾기는 어려운것 같아 직접 구현해야 할 것 같다.
참고할만한 글
https://www.stackup.sh/blog/passkeys-webauthn-erc4337
구현 예제
https://github.com/passkeys-4337/smart-wallet
P256 서명 컨트렉트
'블록체인 > 퍼블릭 블록체인' 카테고리의 다른 글
smart contract에서 동일한 함수 이름이나 예약어가 있을 경우 (0) | 2024.09.30 |
---|---|
smart contract에서 동일한 함수 이름이나 예약어가 있을 경우 (0) | 2024.09.30 |
다중 체인 환경 hardhat써서 로컬에서 테스트하기 (0) | 2024.06.24 |
폴리곤 Polygon amoy 테스트넷 사용방법 (0) | 2024.06.05 |
AVAX 아발란체 테스트넷 사용법 (0) | 2024.06.05 |