티스토리 뷰

반응형

Indy SDK에 오신 것을 환영합니다! 이곳은 Indy 생태계에 소개될 가장 좋은 곳입니다. Indy SDK를 사용하여 자기주권형 신원(self-sovereign identity) 및 검증 가능한 자격(credentials) 정보를 구현하는 방법을 먼저 이해하려면 먼저 IBM이 작성한 데모 비디오를 확인하십시오.

https://youtu.be/cz-6BldajiA

자기주권형 신원(self-sovereign identity)에 대해 배우기 시작했다면 이해를 높이기 위한 몇 가지 자료가 있습니다.

  • Hyperledger Indy Working Group 전화는 매주 목요일 오전 8시(PT), 오전 9시(MT), 오전 11시(ET), 오후 4시 (BST)에 이루어집니다. 캘린더에 추가하고 아무 장치나 이용해서 참여하세요 https://zoom.us/j/232861185
  • Hyperledger Indy 및 Sovrin을 사용하여 자기주권형 신원관리(self-sovereign identity)를 설명하는 최근 웨비나(webinar) : SSI Meetup Webinar
  • 코드 베이스, 유용한 리소스 및 최신 정보를 익히려면 "Indy"에 대한 메인 리소스를 방문하십시오.: Hyperledger Wiki-Indy
  • 다음 페이지에는 Indy를 소개하는 확장 튜토리얼, 전체 에코 시스템 작동 방식 및 SDK의 기능을 사용하여 다채로운 클라이언트를 구성하는 방법에 대해 설명합니다.

코드 작성을 시작하려면 Getting Started Guide with Docker를 실행하는 것이 좋습니다.

Indy를 사용하여 SSI(self-sovereign identity)에 대한 보다 철저한 기술 연습을 원한다면 우리가 작성한 Indy Story Walkthrough를 살펴보십시오.

이 가이드를 살펴본 후에는 SDK를 이해하기 위해 SDK Wrappers (Github folder)를 둘러본 다음 당신이 원하는 작업을 수행하기 위해 만든 How-to Guide를 사용하는 것이 좋습니다.

 

Indy Walkthrough(Indy 연습)

A Developer Guide for Building Indy Clients Using Libindy(Libindy를 사용하여 Indy 클라이언트를 구축하기 위한 개발자 가이드)

  • Indy Walkthrough
    • A Developer Guide for Building Indy Clients Using Libindy
    • What Indy and Libindy are and Why They Matter
    • What We’ll Cover
    • About Alice
    • Infrastructure Preparation
      • Step 1: Getting Trust Anchor Credentials for Faber, Acme, Thrift and Government
      • Step 2: Connecting to the Indy Nodes Pool
      • Step 3: Getting the ownership for Steward’s Verinym
      • Step 4: Onboarding Faber, Acme, Thrift and Government by Steward
        • Connecting the Establishment
        • Getting Verinym
      • Step 5: Credential Schemas Setup
      • Step 6: Credential Definition Setup
    • Alice Gets a Transcript
    • Apply for a Job
    • Apply for a Loan
    • Alice Quits her Job
    • Explore the Code

 

What Indy and Libindy are and Why They Matter(Indy와 Libindy란 무엇이며 왜 중요한가)

Indy는 개인, 안전 및 강력한 신원(identity)을 위한 소프트웨어 에코 시스템을 제공하며 libindy는 클라이언트를 지원합니다. Indy는 전통적으로 신원을 중앙 집중화하는 조직이 아닌 사람들이 자신의 개인 정보 및 공개에 대한 결정을 담당하게 합니다. 이를 통해 연결된 계약, 해지, 신규 지불 워크 플로우, 자산 및 문서 관리 기능, 창의적인 형태의 에스크로, 선별된 평판, 다른 멋진 기술과의 통합 등 모든 종류의 풍부한 혁신이 가능합니다.

Indy는 오픈 소스, 분산 원장 기술을 사용합니다. 이 원장은 중앙 관리자가 있는 거대한 데이터베이스 대신 참여자 풀이 공동으로 제공하는 데이터베이스 형식입니다. 데이터는 여러 곳에서 중복 존재하며 많은 시스템에서 조정된 트랜잭션에서 발생합니다. 강력한 업계 표준 암호화가 이를 보호합니다. 주요 관리 및 사이버 보안의 모범 사례는 디자인에 널리 퍼져 있습니다. 그 결과 단일 엔터티가 제어 할 수 없고, 시스템 장애에 강하고, 해킹에 대해 탄력적이며, 적대적인 엔터티에 의한 전복에 대한 면역이없는 신뢰할 수 있고 공개적인 진실의 근원입니다.

암호화 및 블록 체인 세부 사항의 개념이 모호해져도 두려워하지 마십시오. 이 안내서는 Indy의 주요 개념을 소개하는 데 도움이됩니다. 당신은 옳은 곳에서 시작하고 있습니다.

 

What We’ll Cover(우리가 다룰 내용)

우리의 목표는 Indy의 많은 개념을 소개하고 모든 기능을 수행하기 위해 무대 뒤에서 어떤 일이 발생하는지에 대한 아이디어를 제공하는 것입니다.

우리는 이야기와 함께 탐험을 구성할 것입니다. 가상의 대학 Faber College를 졸업한 Alice는 가상 회사 Acme Corp에 입사 지원을 원합니다. 일자리를 갖자마자 Thrift Bank에서 대출을 신청하여 자동차를 구입할 수 있습니다. 그녀는 대학 성적증명서를 취업 지원서에 대한 교육의 증거로 사용하고 싶었고, 일단 고용되면 Alice는 고용 사실을 대출에 대한 신용도의 증거로 사용하려고합니다.

이를 해결하기 위해 필요한 정체성과 신뢰의 상호 작용은 오늘날의 세상에서 지저분합니다. 그들은 느리고 개인 정보를 침해하며 사기에 취약합니다. 우리는 Indy가 어떻게 양자 도약인지 보여줄 것입니다.

준비 되었습니까?

 

About Alice(Alice 소개)

Faber College를 졸업한 Alice는 졸업생에게 알마 교인이 디지털 성적증명서를 제공하고 있음을 알게된 동문 뉴스 레터를 받습니다. 그녀는 대학 동창 웹 사이트에 로그인하고 성적증명서 가져오기(Get Transcript)를 클릭하여 그녀의 성적 증명서를 요청합니다. (이 요청을 시작하는 다른 방법에는 QR 코드 스캔, 게시된 URL에서 대화 내용 패키지 다운로드 등이 포함될 수 있습니다.)

Alice는 아직 모르지만 이 디지털 성적증명서를 사용하려면 Faber College가 캠퍼스 내 데이터베이스에서 그녀를 위해 구축한 전통적인 신원이 아니라 새로운 신원이 필요합니다. 그러나 그녀는 과거와 미래의 모든 관계와 무관한 새롭고 휴대가능한 자신의 신원으로 누구도 그녀의 허락없이 철회(revoke)하거나 공동 선택(co-opt)하거나 연관(correlate)시킬 수 없습니다. 이것이 자기주권형 신원(self-sovereign identity) 이며 Indy의 핵심 기능입니다.

일반적인 상황에서 자기주권형 신원(self-sovereign identity)을 관리하려면 데스크톱 또는 모바일 애플리케이션과 같은 도구가 필요합니다. 독립 실행형 앱이거나 원장이 에이전시(agency)라고 하는 타사 서비스 제공 업체를 활용할 수 있습니다. Sovrin Foundation은 이러한 도구의 참조 버전을 게시합니다. Faber College는 이러한 요구 사항을 연구했으며 Alice가 이러한 Indy 앱을 가지고있지 않은 경우, Alice에게 이 앱을 추천할 것입니다. 이 앱은 성적증명서 가져오기(Get Transcript) 버튼에서 워크 플로의 일부로 설치됩니다.

Alice가 성적증명서 가져오기(Get Transcript)를 클릭하면 Indy 연결 요청(connection request)이 포함된 파일을 다운로드합니다. 이 연결 요청(connection request) 파일은 .indy 확장자를 가지며 Indy 앱과 연결되어 원장 생태계의 다른 당사자인 Faber College와의 안전한 통신 채널을 설정할 수 있습니다.

따라서 Alice가 Get Transcript를 클릭하면 일반적으로 앱 설치(필요한 경우)를 시작한 다음 앱을 시작하고, Faber와의 연결 요청을 수락할 것인지 묻는 메시지가 나타납니다.

그러나 이 가이드에서는 앱 대신 Indy SDK API(libindy에서 제공)를 사용하므로 배후에서 어떤 일이 발생하는지 확인할 수 있습니다. 우리는 특히 호기심이 많고 기술적으로 모험적인 앨리스인 척 할 것입니다…

 

Infrastructure Preparation(인프라 준비)

Step 1: Getting Trust Anchor Credentials for Faber, Acme, Thrift and Government(Faber, Acme, Thrift 및 Government의 Trust Anchor 자격증명 얻기)

Faber College와 다른 배우들은 이 서비스를 Alice에게 제공하기 위해 약간의 준비를했습니다. 이러한 단계를 이해하기 위해 몇 가지 정의부터 시작하겠습니다.

원장은 원장 엔터티(Ledger Entity)를 설명하는 신원 레코드(Identity Records)를 저장하기 위한 것입니다. 신원 레코드는 공개 데이터이며 공개 키, 서비스 엔드 포인트, 자격 증명 스키마 및 자격 증명 정의를 포함할 수 있습니다. 모든 신원 레코드(Identity Recored)는 중앙 집중식 해결 기관 없이도 원장을 통해 전 세계적으로 고유하고 해결할 수 있는 정확히 하나의 DID(분산 식별자)와 연결됩니다. 프라이버시를 유지하기 위해 각 신원 소유자(Identity Owner)는 여러 DID를 소유할 수 있습니다.

이 튜토리얼에서는 두 가지 유형의 DID를 사용합니다. 첫 번째는 Verinym 입니다. Verinym신원 소유자(Identity Owner)법적인 신원(Legal Identity)과 연관되어 있습니다. 예를 들어, 모든 당사자는 일부 DID가 정부에서 일부 문서 유형에 대한 스키마를 게시하는 데 사용되는지 확인할 수 있어야합니다. 두 번째 유형은 Pseudonym - 지속적인 디지털 관계 (Connection) 와 관련하여 프라이버시를 유지하는 데 사용되는 블라인드 식별자(Blinded Identifier)입니다. Pseudonym을 하나의 디지털 관계만 유지하는 데 사용하는 경우 이를 Pairwise-Unique Identifier라고 합니다. 이 튜토리얼에서는 Pairwise-Unique Identifiers를 사용하여 액터 간 보안 연결을 유지합니다.

원장에게 알려진 DID 생성은 신원 레코드(Identity Record) 자체 (NYM 트랜잭션)입니다. NYM 트랜잭션은 해당 원장에게 알려진 새로운 DID 생성, 검증 키 설정 및 회전(rotation), 역할 설정 및 변경에 사용될 수 있습니다. 이 트랜잭션에서 가장 중요한 필드는 dest(대상 DID), role(사용자 NYM 레코드 역할) 및 verkey(대상 확인 키)입니다. 지원되는 원장 트랜잭션에 대한 자세한 정보는 요청(Requests) 을 참조하십시오.

DID 검증 키를 사용하여 발행하면 개인, 조직 또는 사물이 이 DID를 소유하고 있는지, 그 개인, 조직 또는 사물이 해당 서명 키와 이 키로 서명해야하는 DID 관련 작업을 알고있는 유일한 사람인지 여부를 확인할 수 있습니다.

우리의 원장은 공개 허가를 받았으며 DID를 게시하려는 사람은 원장에서 Trust Anchor의 역할을 수행해야합니다. Trust Anchor는 원장이 이미 알고있는 사람이나 조직에 다른 사람을 부트스트랩 할 수 있습니다. (사이버 보안 전문가가 "신뢰할 수 있는 제3자(trusted third party)"라고 부르는 것과는 다릅니다. 이것을 협력자라고 생각하십시오.). 역할에 대한 자세한 정보는 역할(Roles) 을 참조하십시오.

원장에 트랜잭션을 저장할 수 있는 첫 번째 단계는 원장에서 Trust Anchor의 역할을 얻는 것입니다. Faber College, Acme Corp 및 Thrift Bank는 원장에서 Trust Anchor의 역할을 가져 와서 Alice에게 서비스를 제공할 Verinyms 및 Pairwise-Unique Identifier를 만들 수 있습니다.

Trust Anchor가 되기 위해서는 원장에서 이미 Trust Anchor의 역할을 하는 사람이나 조직과의 연결이 필요합니다. 데모를 위해, 빈 테스트 원장에는 Steward 역할을 하는 NYMs만 있지만 모든 Steward들은 자동으로 Trust Anchor의 역할을 가집니다.

 

Step 2: Connecting to the Indy Nodes Pool(Indy 노드 풀에 연결)

우리는 이제 Alice의 사용 사례를 처음부터 끝까지 다루는 코드 작성을 시작할 준비가 되었습니다. 이것은 데모 목적으로 다른 에이전트에서 실행되도록 의도된 코드를 포함하는 단일 테스트라는 점에 유의해야 합니다. 우리는 항상 Agent가 각 코드 부분을 실행하기 위해 의도한 것에 초점을 둘 것입니다. 또한 다른 지갑을 사용하여 다른 Agent의 DID 및 키를 저장할 것입니다. 시작해봅시다.

첫 번째 코드 블록에는 Steward의 에이전트 코드가 포함됩니다.

적절한 역할을 얻은 후에 원장의 트랜잭션을 읽고 쓰기 위해서는 Indy 노드 풀에 연결해야합니다. Sovrin 풀 또는 이 튜토리얼의 일부로 우리 자신에 의해 시작한 로컬 풀과 같이 이미 존재하는 다른 풀에 연결하려면 풀 구성(pool configuration)을 설정해야합니다.

풀의 노드 목록은 원장에 NODE 트랜잭션으로 저장됩니다. Libindy를 사용하면 제네시스 트랜잭션(genesis transaction)이라고 하는 몇 가지 알려진 트랜잭션을 통해 실제 NODE 트랜잭션 목록을 복원할 수 있습니다. 각 풀 구성(Pool Configuration)은 풀 구성 이름과 풀 구성 JSON의 쌍으로 정의됩니다. 풀 구성 JSON에서 가장 중요한 필드는 제네시스 트랜잭션(genesis transaction) 목록이 있는 파일의 경로입니다. 이 경로가 올바른지 확인하십시오.

pool.create_pool_ledger_config 호출은 명명된 풀 풀 구성을 생성할 수 있습니다. 풀 구성이 생성된 후에는 pool.open_pool_ledger를 호출하여 이 구성이 설명하는 노드 풀에 연결할 수 있습니다. 이 호출은 향후 libindy 호출에서 열린 연결(opened connection)을 참조하는 데 사용할 수 있는 풀 핸들(pool handle)을 반환합니다.

아래 코드 블록에는 이러한 각 항목이 포함되어 있습니다. 어떻게 이것이 "Steward Agent"의 코드임을 나타내는지에 유의하십시오.

await pool.set_protocol_version(2)
  
pool_ = {'name': 'pool1'}
pool_['genesis_txn_path'] = get_pool_genesis_txn_path(pool_['name'])
pool_['config'] = json.dumps({"genesis_txn": str(pool_['genesis_txn_path'])})
await pool.create_pool_ledger_config(pool_['name'], pool_['config'])
pool_['handle'] = await pool.open_pool_ledger(pool_['name'], None)

 

Step 3: Getting the ownership for Steward’s Verinym(Steward 's Verinym의 소유권 확보)

다음으로, Steward의 에이전트는 원장의 Steward 역할과 일치하는 NYM 트랜잭션을 가진 DID에 대한 소유권을 가져야합니다.

우리가 사용하는 테스트 원장은 몇 개의 알려진 Steward NYM들을 저장하도록 사전 구성되었습니다. 또한 이러한 NYM에 대한 키를 생성하는 데 사용된 난수 생성기의 seed 값을 알고 있습니다. 이 seed 값을 사용하면 Steward의 에이전트 측에서 이러한 DID에 대한 서명 키를 복원할 수 있으며 결과적으로 DID 소유권을 얻을 수 있습니다.

Libindy는 지갑(Wallet) 개념을 가지고 있습니다. 지갑(Wallet)은 DID, 키, 등과 같은 암호화 자료를 위한 안전한 저장소입니다. Steward의 DID 및 해당 서명 키를 저장하려면, 에이전트는 먼저 wallet.create_wallet을 호출하여 명명된 지갑(wallet)을 만들어야 합니다. 그런 다음 wallet.open_wallet을 호출하여 이 명명된 지갑(wallet)을 오픈할 수 있습니다. 이 호출은 향후 libindy 호출에서 이 오픈된 지갑(wallet)을 참조하는 데 사용할 수 있는 지갑 핸들(wallet handle)을 리턴합니다.

지갑(wallet)을 오픈한 후에는 생성된 DID되 생선된 키의 verkey 부분을 리턴하는 did.create_and_store_my_did 호출을 통해 이 지갑(wallet)에 DID 레코드를 생성할 수 있습니다. 이 DID의 서명 키(signkey) 부분도 지갑(wallet)에 저장되지만 직접 읽을 수는 없습니다.

# Steward Agent
steward = {
    'name': "Sovrin Steward",
    'wallet_config': json.dumps({'id': 'sovrin_steward_wallet'}),
    'wallet_credentials': json.dumps({'key': 'steward_wallet_key'}),
    'pool': pool_['handle'],
    'seed': '000000000000000000000000Steward1'
}

await wallet.create_wallet(steward['wallet_config'], steward['wallet_credentials'])
steward['wallet'] = await wallet.open_wallet(steward['wallet_config'], steward['wallet_credentials'])

steward['did_info'] = json.dumps({'seed': steward['seed']})
steward['did'], steward['key'] = await did.create_and_store_my_did(steward['wallet'], steward['did_info'])

 

참고 : did.create_and_store_my_did는 오직 seed에 대한 정보만 제공하며, Steward의 DID에 대한 정보는 제공하지 않습니다. 기본적으로 DID는 verkey의 첫 16 바이트로 생성됩니다. 그러한 DID의 경우, DID와 verkey가 모두 필요한 작업을 처리할 때 verkey를 축약된 형식으로 사용할 수 있습니다. 이 형식에서 verkey는 물결표 '~'로 시작하고 22 또는 23 문자가 따라옵니다. 물결표는 DID 자체는 verkey의 처음 16 바이트를 나타내고 물결표 뒤의 문자열은 base58Check 인코딩 방식을 사용하여 verkey의 두 번째 16 바이트임을 나타내는 것임을 표시합니다.

 

Step 4: Onboarding Faber, Acme, Thrift and Government by Steward(Steward의 Faber, Acme, Thrift 및 Government와의 Onboarding)

Faber, Acme, Thrift 및 Gevernment는 이제 Steward와 connection을 설정해야 합니다.

각 connection은 실제로 한 쌍의 Pairwise-Unique Identifiers(DIDs) 입니다. 하나의 DID는 connection의 한 당사자가 소유하고 다른 하나는 나머지 당사자가 소유합니다.

두 당사자 모두 DID를 알고 있으며 이 쌍이 어떤 connection을 설명하는지에 대해 이해하고 있습니다.

그들 사이의 관계는 다른 사람들과 공유할 수 없습니다. 각 쌍별 관계가 다른 DID를 사용한다는 점에서 이것은 두 당사자에게 유니크합니다.

connection Onboarding 프로세스를 호출합니다.

이 튜토리얼에서는 간단한 버전의 onboarding 프로세스를 설명합니다. 우리의 경우, 한 당사자는 항상 Trust Anchor가 됩니다. 실제 엔터프라이즈 시나리오는 보다 복잡한 버전을 사용할 수 있습니다.

 

Connecting the Establishment(기관 연결)

StewardFaber College 간의 connection 설정 프로세스를 살펴보겠습니다.

1. onboarding 프로세스를 시작하기 위해 FaberSteward가 서로 연락을 해야합니다. 웹 시이트 또는 전화로 양식을 작성하는 것일 수 있습니다.

2. Steward는 오직 Faber와의 안전한 상호작용에만 사용하도록 did.create_and_store_my_did를 호출하여 지갑(wallet)에 새로운 DID 레코드를 생성합니다.

# Steward Agent
(steward['did_for_faber'], steward['key_for_faber']) = await did.create_and_store_my_did(steward['wallet'], "{}")

 

3. Steward는 지속적으로 ledger.build_nym_request를 호출하여 NYM request를 작성하고 ledger.sign_and_submit_request를 호출하여 작성된 request를 전송함으로써 원장에 NYM 트랜잭션을 작성합니다.

# Steward Agent
nym_request = await ledger.build_nym_request(steward['did'], steward['did_for_faber'], steward['key_for_faber'], None, role)
await ledger.sign_and_submit_request(steward['pool'], steward['wallet'], steward['did'], nym_request)

 

4. Steward는 작성된 DID 및 Nonce를 포함하는 connection request를 작성합니다. 이 Nonce는 유니크한 connection request를 추적하기 위해 생성된 임의의 큰 숫자입니다. nonce는 한 번만 사용할 수 있는 랜덤한 임의의 숫자입니다. connection request가 수락되면 초대 대상자는 nonce에 디지털 서명을 하여 초대자가 response를 이전 request와 매치시킬 수 있도록 합니다.

# Steward Agent
connection_request = {
    'did': steward['did_for_faber'],
    'nonce': 123456789
}

5. StewardFaber로 connection request를 보냅니다.

6. FaberSteward의 connection request를 수락합니다.

7. Faber는 아직 지갑이 생성되지 않았으면 지갑을 생성합니다.

# Faber Agent
await wallet.create_wallet(faber['wallet_config'], faber['wallet_credentials'])
faber['wallet'] = await wallet.open_wallet(faber['wallet_config'], faber['wallet_credentials'])

 

8. FaberSteward와의 안전한 상호 작용에만 사용하도록 did.create_and_store_my_did를 호출해 지갑에 새로운 DID 레코드를 생성합니다.

# Faber Agent
(faber['did_for_steward'], faber['key_for_steward']) = await did.create_and_store_my_did(faber['wallet'], "{}")

 

9. Faber는 수신된 connection request에서 생성된 DID, Verkey 및 Nonce를 포함하는 connection response를 작성합니다.

# Faber Agent
connection_response = json.dumps({
    'did': faber['did_for_steward'],
    'verkey': faber['key_for_steward'],
    'nonce': connection_request['nonce']
})

 

10. Faber는 did.key_for_did를 호출하여 원장에게 Steward의 DID 인증 키를 요청합니다.

# Faber Agent
faber['steward_key_for_faber'] = await did.key_for_did(faber['pool'], faber['wallet'], connection_request['did'])

 

11. FaberSteward verkey와 함께 crypto.anon_crypt를 호출하여 connection response를 익명으로 암호화합니다. 익명 암호화 스키마는 공개 키를 받은 수신자에게 메시지를 보내도록 설계되어 있습니다. 수신자만이 자신의 개인 키를 사용하여 이러한 메시지를 해독할 수 있습니다. 수신자는 메시지의 무결성을 확인할 수는 있지만 발신자의 신원을 확인할 수는 없습니다.

# Faber Agent
anoncrypted_connection_response = await crypto.anon_crypt(faber['steward_key_for_faber'], connection_response.encode('utf-8'))

 

12. Faber는 익명으로 암호화된 connection response를 Steward에게 보냅니다.

13. Steward는 crypto.anon_decrypt를 호출하여 connection response를 익명으로 해독합니다.

# Steward Agent
decrypted_connection_response = \
    (await crypto.anon_decrypt(steward['wallet'], steward['key_for_faber'], anoncrypted_connection_response)).decode("utf-8")

 

14. Steward는 Nonce를 비교하여 Faber를 인증합니다.

# Steward Agent
assert connection_request['nonce'] == decrypted_connection_response['nonce']

 

15. StewardFaber의 DID에 대한 NYM 트랜잭션을 원장에 보냅니다. Steward가 이 트랜잭션의 발신자임에도 불구하고 Faber가 제공한 verkey를 사용하므로 DID의 소유자는 Faber가 된다는 점을 유의하십시오.

# Steward Agent
nym_request = await ledger.build_nym_request(steward['did'], decrypted_connection_response['did'], decrypted_connection_response['verkey'], None, role)
await ledger.sign_and_submit_request(steward['pool'], steward['wallet'], steward['did'], nym_request)

 

이 시점에서 FaberSteward에 연결되어 안전한 P2P 방식으로 상호 작용할 수 있습니다. Faber는 다음과 같은 이유로 Steward의 응답을 신뢰할 수 있습니다. :

  • 현재 endpoint에 연결
  • 재생 금지 - 무작위 도전으로 공격 가능
  • Steward의 디지털 서명을 확인하는 데 사용되는 검증 키(verification key)가 원장에서 방금 확인했기 떄문에 올바른 키임을 알고 있습니다.

참고 : 모든 당사자가 다른 관계를 설정하기 위해 동일한 DID를 사용해서는 안됩니다. 독립적인 pairwise 관계를 가짐으로써 다른 사람들이 여러 상호 작용에서 활동을 상호 연관시키는 능력이 줄어듭니다.

 

Getting Verinym(Verinym 얻기)

초기에 생성된 Faber DID는 그 자체로 자기주권형 신원(self-sovereign identity)과 동일하지 않다는 것을 이해하는 것이 중요합니다. 이 DID는 Steward와의 안전한 상호 작용에만 사용해야합니다. connection이 설정되면 Faber는 원장에서 Verinym으로 사용할 새 DID 레코드를 작성해야합니다.

1. Faber는 did.create_and_store_my_did를 호출하여 지갑(wallet)에 새 DID를 생성합니다.

# Faber Agent
(faber['did'], faber['key']) = await did.create_and_store_my_did(faber['wallet'], "{}")

2. Faber는 생성된 DID 및 verkey를 포함할 메시지를 준비합니다.

# Faber Agent
faber['did_info'] = json.dumps({
    'did': faber['did'],
    'verkey': faber['key']
})

 

3. Faber는 인증된 암호화(authenticated-encryption) 스키마의 구현인 crypto.auth_crypt 함수를 호출하여 메시지를 인증하고 암호화합니다. 인증된 암호화(authenticated-encryption)는 특별히 수신자를 위해 기밀의 메시지를 보내도록 설계되어 있습니다. 발신자는 수신자의 공개 키(verkey)와 그의 비밀(signing) 키를 사용하여 공유 비밀 키를 계산할 수 있습니다. 수신자는 발신자의 공개 키(verkey)와 그의 비밀(signing) 키를 사용하여 정확히 동일한 공유 비밀 키를 계산할 수 있습니다. 이 공유 비밀 키를 사용하여 암호화 된 메시지가 변조되지 않았는지 확인하고, 결국 해독할 수 있습니다.

# Faber Agent
authcrypted_faber_did_info_json = \
    await crypto.auth_crypt(faber['wallet'], faber['key_for_steward'], faber['steward_key_for_faber, faber['did_info'].encode('utf-8'))

 

4. Faber는 암호화 된 메시지를 Steward에게 보냅니다.

5. Steward는 crypto.auth_decrypt를 호출하여 수신된 메시지를 해독합니다.

# Steward Agent
sender['faber_key_for_steward'], authdecrypted_faber_did_info_json = \
    await crypto.auth_decrypt(steward['wallet'], steward['key_for_faber'], authcrypted_faber_did_info_json)
faber_did_info = json.loads(authdecrypted_faber_did_info_json)

 

6. Steward는 did.key_for_did를 호출하여 원장에게 Faber의 DID 검증 키를 요청합니다.

# Steward Agent
steward['faber_key_for_steward'] = await did.key_for_did(steward['pool'], steward['wallet'], ['faber_did_for_steward'])

 

7. Steward는 메시지 발신자의 Verkey와 원장으로부터 받은 Faber의 Verkey를 비교하여 Faber를 인증합니다.

# Steward Agent
assert sender_verkey == steward['faber_key_for_steward']

 

8. Steward는 해당 NYM 트랜잭션을 TRUST ANCHOR 역할을 가진 원장에게 전송합니다. Steward가 이 트랜잭션의 발신자임에도 불구하고 Faber가 제공한 Verkey를 사용하기 때문에 DID의 소유자는 Faber가 된다는 점을 유의하십시오.

# Steward Agent
nym_request = await ledger.build_nym_request(steward['did'], decrypted_faber_did_info_json['did'],
                                             decrypted_faber_did_info_json['verkey'], None, 'TRUST_ANCHOR')
await ledger.sign_and_submit_request(steward['pool'], steward['wallet'], steward['did'], nym_request)

 

이 시점에수 Faber는 원장에 그의 신원과 관련된 DID를 가지고 있습니다.

Acme, Thrift Bank 및 GovernmentSteward와 동일한 Onboarding 프로세스 connection 설정을 통과해야 합니다.

 

Step 5: Credential Schemas Setup(Credential Schema 설정)

Credential Schema는 하나의 특정 Credential 정보가 포함할 수 있는 속성 목록을 설명하는 기본 의미(semantic) 구조입니다.

참고 : 기존의 스키마는 업데이트 할 수 없습니다. 따라서 스키마를 발전시켜야 하는 경우 새 버전 또는 이름을 가진 새로운 스키마를 생성해야합니다.

Credential Schema는 아무 Trust Anchor에 의해 생성되고 원장에 저장될 수 있습니다.

Government성적 증명서(Transcript) Credential Schema를 작성하여 원장에 게시하는 곳은 다음과 같습니다.:

1. Trust Anchor는 생성된 Credential Schema를 리턴하는 anoncreds.issuer_create_schema를 호출하여 Credential Schema를 생성합니다.

# Government Agent
transcript = {
    'name': 'Transcript',
    'version': '1.2',
    'attributes': ['first_name', 'last_name', 'degree', 'status', 'year', 'average', 'ssn']
}
(government['transcript_schema_id'], government['transcript_schema']) = \
    await anoncreds.issuer_create_schema(government['did'], transcript['name'], transcript['version'],
                                         json.dumps(transcript['attributes']))
transcript_schema_id = government['transcript_schema_id']

 

2. Trust Anchor는 ledger.build_schema_request를 지속적으로 호출하여 Schema request를 작성하고 ledger.sign_and_submit_request를 호출하여 생성된 request를 전송함으로써 해당 Schema 트랜잭션을 원장에 전송합니다.

# Government Agent
schema_request = await ledger.build_schema_request(government['did'], government['transcript_schema'])
await ledger.sign_and_submit_request(government['pool'], government['wallet'], government['did'], schema_request)

 

같은 방식으로 GovernmentJob-Certificate Credential Schema를 생성하고 원장에 게시합니다.:

  # Government Agent
    job_certificate = {
        'name': 'Job-Certificate',
        'version': '0.2',
        'attributes': ['first_name', 'last_name', 'salary', 'employee_status', 'experience']
    }
    (government['job_certificate_schema_id'], government['job_certificate_schema']) = \
        await anoncreds.issuer_create_schema(government['did'], job_certificate['name'], job_certificate['version'],
                                             json.dumps(job_certificate['attributes']))
    job_certificate_schema_id = government['job_certificate_schema_id']
    
    schema_request = await ledger.build_schema_request(government['did'], government['job_certificate_schema'])
    await ledger.sign_and_submit_request(government['pool'], government['wallet'], government['did'], schema_request)

 

이 시점에 우리는 Government에 의해 원장에 게시된 성적 증명서(Transcript)Job-Certificate의 Credential Schema를 가지고 있습니다.

 

Step 6: Credential Definition Setup(Credential Definition 설정)

Credential Definition는 발급자가 Credential의 서명에 사용하는 키가 특정 Credential Schema와도 충족한다는 점이 비슷합니다.

참고: 기존의 Credential Definition에 데이터를 업데이트하는 것은 불가능합니다. 따라서 CredDef를 발전시켜야하는 경우(예: 키를 회전해야하는 경우), 새로운 발급자의 DID로 새로운 Credential Definition을 생성해야합니다.

Credential Definition는 아무 Trust Anchor나 생성하고 원장에 저장할 수 있습니다. 여기서 Faber는 알고 있는 성적 증명서(Transcript)의 Credential Schema에 대한 Credential Definition을 생성하여 원장에 게시합니다.

1. Trust Anchor는 원장에서 특정 Credential Schema를 가져오기 위해 지속적으로 ledger.build_get_schema_request를 호출하여 GetSchema request를 작성하고, ledger.sign_and_submit_request로 생성된 request를 전송하며 ledger.parse_get_schema_response로 GetSchema response에서 Anoncreds API에 필요한 형식으로 Schema를 가져옵니다.

# Faber Agent
get_schema_request = await ledger.build_get_schema_request(faber['did'], transcript_schema_id)
get_schema_response = await ledger.submit_request(faber['pool'], get_schema_request) 
faber['transcript_schema_id'], faber['transcript_schema'] = await ledger.parse_get_schema_response(get_schema_response)

 

2. Trust Anchor는 작성된 public Credential Definition을 리턴하는 anoncreds.issuer_create_and_store_credential_def를 호출하여 수신된 Credential Schema와 관련된 Credential Definition을 생성합니다. 이 Credential Schema의 private Credential Definition 부분도 지갑(wallet)에 저장되지만 직접 읽을 수는 없습니다.

# Faber Agent
transcript_cred_def = {
    'tag': 'TAG1',
    'type': 'CL',
    'config': {"support_revocation": False}
}
(faber['transcript_cred_def_id'], faber['transcript_cred_def']) = \
    await anoncreds.issuer_create_and_store_credential_def(faber['wallet'], faber['did'],
                                                           faber['transcript_schema'], transcript_cred_def['tag'],
                                                           transcript_cred_def['type'],
                                                           json.dumps(transcript_cred_def['config']))

 

3. Trust Anchor는 ledger.build_cred_def_request를 지속적으로 호출하여 CredDef request를 작성하고 ledger.sign_and_submit_request로 생성된 request를 전송함으로써 해당 CredDef 트랜잭션을 원장에 전송합니다.

# Faber Agent     
cred_def_request = await ledger.build_cred_def_request(faber['did'], faber['transcript_cred_def'])
await ledger.sign_and_submit_request(faber['pool'], faber['wallet'], faber['did'], cred_def_request)

 

동일한 방법으로 Acme는 알려진 Job-Certificate Credential Schema에 대한 Credential Definition을 생성하고 원장에 게시합니다.

  # Acme Agent
  get_schema_request = await ledger.build_get_schema_request(acme['did'], job_certificate_schema_id)
  get_schema_response = await ledger.submit_request(acme['pool'], get_schema_request) 
  acme['job_certificate_schema_id'], acme['job_certificate_schema'] = await ledger.parse_get_schema_response(get_schema_response)
    
  job_certificate_cred_def = {
      'tag': 'TAG1',
      'type': 'CL',
      'config': {"support_revocation": False}
  }
  (acme['job_certificate_cred_def_id'], acme['job_certificate_cred_def']) = \
      await anoncreds.issuer_create_and_store_credential_def(acme['wallet'], acme['did'],
                                                             acme['job_certificate_schema'], job_certificate_cred_def['tag'],
                                                             job_certificate_cred_def['type'],
                                                             json.dumps(job_certificate_cred_def['config']))
  
  cred_def_request = await ledger.build_cred_def_request(acme['did'], acme['job_certificate_cred_def'])
  await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], cred_def_request)

 

Acme는 **Job-Certificate* credential을 해지할 것으로 예상됩니다. 해지 레지스트리를 생성하기로 결정합니다. Hyperledger Indy의 해지 레지스트리 유형 중 하나는 해지된 credential을 게시하기 위해 암호화 누산기를 사용합니다. (이 누산기의 내부 작업에 대한 자세한 내용을 알고싶으면 이곳을 보십시오.) 이러한 누산기를 사용하려면 원장 외부에 "validity tails"를 게시해야합니다. 이 데모의 목적 상 validity tails는 'blob storage'를 사용하여 파일에 기록됩니다.

    # Acme Agent
    acme['tails_writer_config'] = json.dumps({'base_dir': "/tmp/indy_acme_tails", 'uri_pattern': ''})
    tails_writer = await blob_storage.open_writer('default', acme['tails_writer_config'])

 

validity tail이 구성되면 Acme는 지정된 Credential Definition에 대해 새 해지 레지스트리를 만들 수 있습니다.

    # Acme Agent
    (acme['revoc_reg_id'], acme['revoc_reg_def'], acme['revoc_reg_entry']) = \
        await anoncreds.issuer_create_and_store_revoc_reg(acme['wallet'], acme['did'], 'CL_ACCUM', 'TAG1',
                                                          acme['job_certificate_cred_def_id'],
                                                          json.dumps({'max_cred_num': 5,
                                                                      'issuance_type': 'ISSUANCE_ON_DEMAND'}),
                                                          tails_writer)

    acme['revoc_reg_def_request'] = await ledger.build_revoc_reg_def_request(acme['did'], acme['revoc_reg_def'])
    await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], acme['revoc_reg_def_request'])

    acme['revoc_reg_entry_request'] = \
        await ledger.build_revoc_reg_entry_request(acme['did'], acme['revoc_reg_id'], 'CL_ACCUM',
                                                   acme['revoc_reg_entry'])
    await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], acme['revoc_reg_entry_request'])

 

이 시점에서 우리는 Acme에서 발행한 Job-Certificate Credential Schema에 대한 Credential Definition (해지 지원)과 Faber가 발행한 성적 증명서(Transcript) Credential Schema에 대한 Credential Definition을 가지게 됩니다.

 

Alice Gets a Transcript(Alice는 성적증명서를 얻는다)

credential은 이름, 나이, 신용 점수 등 신원에 대한 하나의 정보입니다. 이것은 사실이라고 주장하는 정보입니다. 여기에서 credential의 이름은 "Transcript"입니다.

Credential은 issuer에 의해 제공됩니다.

issuer는 원장에 알려진 어떤 신원의 소유자일 수 있으며 issuer는 자신이 식별할 수 있는 신원 소유자에 대한 credential을 발급할 수 있습니다.

credential의 유용성과 신뢰성은 현재 credential에 대한 issuer의 평판과 관련이 있습니다. Alice가 초콜릿 아이스크림을 좋아하는다는 credential을 자체 발급하는 것은 완벽하게 합리적일 수 있지만, Faber College를 졸업한 credential을 자체 발급하는 것은 누구에게도 인상깊지 않을 것입니다.

우리가 Alice에 대해 언급한 바와 같이, AliceFaber College를 졸업하였습니다. Faber College가 Alice와 connection을 맺은 후, Faber는 Alice에게 성적 증명서(Transcript) 발급에 대한 Credential Offer를 생성하였습니다.

  # Faber Agent
  faber['transcript_cred_offer'] = await anoncreds.issuer_create_credential_offer(faber['wallet'], faber['transcript_cred_def_id'])

 

참고: actor간에 전송된 모든 메시지는 Authenticated-encryption schema를 사용하여 암호화됩니다.

성적 증명서(Transcript) Credential의 가치는 Faber College에서 발급한 것이라는 점입니다.

AliceTranscript Credential에 포함된 속성을 보기를 원합니다. 이 속성들은 Transcript에 대한 Credential Schema가 원장에 작성되어있기 때문에 알려져 있습니다.

  # Alice Agent
  get_schema_request = await ledger.build_get_schema_request(alice['did_for_faber'], alice['transcript_cred_offer']['schema_id'])
  get_schema_response = await ledger.submit_request(alice['pool'], get_schema_request)
  transcript_schema = await ledger.parse_get_schema_response(get_schema_response)

  print(transcript_schema['data'])
  # Transcript Schema:
  {
      'name': 'Transcript',
      'version': '1.2',
      'attr_names': ['first_name', 'last_name', 'degree', 'status', 'year', 'average', 'ssn']
  }

 

그러나 Transcript는 아직 사용 가능한 형태로 Alice에게 전달된 것은 아닙니다. Alice는 해당 Credential을 사용하려고 합니다. 그것을 얻으려면 Alice는 요청을 해야하지만, 그보다 먼저 Master Secret을 생성해야 합니다.

참고: Master Secret은 credential이 유니크하게 적용된다는 것을 보증하기 위해 Prover가 사용하는 Private Data 항목입니다. Master Secret은 여러 Credential의 데이터를 결합하여 Credential에 공통 주제(Prover)가 있음을 증명하는 input입니다. Master Secret은 Prover에게만 알려야합니다.

Alice는 지갑(wallet)에 Master Secret을 생성합니다.

  # Alice Agent
  alice['master_secret_id'] = await anoncreds.prover_create_master_secret(alice['wallet'], None)

Alice는 또한 Transcript Credential Offer에서 cred_def_id에 해당하는 Credential Definition을 가져와야합니다.

  # Alice Agent
  get_cred_def_request = await ledger.build_get_cred_def_request(alice['did_for_faber'], alice['transcript_cred_offer']['cred_def_id'])
  get_cred_def_response = await ledger.submit_request(alice['pool'], get_cred_def_request)
  alice['transcript_cred_def'] = await ledger.parse_get_cred_def_response(get_cred_def_response)

 

이제 Alice는 Faber Transcript Credential 발급에 대한 Credential Request를 생성하기 위한 모든 것을 갖추고 있습니다.

  # Alice Agent
    (alice['transcript_cred_request'], alice['transcript_cred_request_metadata']) = \
        await anoncreds.prover_create_credential_req(alice['wallet'], alice['did_for_faber'], alice['transcript_cred_offer'],
                                                     alice['transcript_cred_def'], alice['master_secret_id'])

 

FaberTranscript Credential Schema의 각 속성에 대해 가공되지 않은 값과 인코딩된 값을 모두 준비합니다. Faber는 Alice의 Transcript Credential을 생성합니다.

  # Faber Agent
  # note that encoding is not standardized by Indy except that 32-bit integers are encoded as themselves. IS-786
  transcript_cred_values = json.dumps({
      "first_name": {"raw": "Alice", "encoded": "1139481716457488690172217916278103335"},
      "last_name": {"raw": "Garcia", "encoded": "5321642780241790123587902456789123452"},
      "degree": {"raw": "Bachelor of Science, Marketing", "encoded": "12434523576212321"},
      "status": {"raw": "graduated", "encoded": "2213454313412354"},
      "ssn": {"raw": "123-45-6789", "encoded": "3124141231422543541"},
      "year": {"raw": "2015", "encoded": "2015"},
      "average": {"raw": "5", "encoded": "5"}
  })

  faber['transcript_cred_def'], _, _ = \
      await anoncreds.issuer_create_credential(faber['wallet'], faber['transcript_cred_offer'], faber['transcript_cred_request'],
                                               transcript_cred_values, None, None)

 

이제 Transcript Credential이 발급되었습니다. Alice는 이를 지갑(wallet)에 저장합니다.

  # Alice Agent
  await anoncreds.prover_store_credential(alice['wallet'], None, faber['transcript_cred_request'], faber['transcript_cred_request_metadata'],
                                          alice['transcript_cred'], alice['transcript_cred_def'], None)

 

Alice는 자신에게 우편으로 발송된 실제 transcript를 보관하는 것과 거의 같은 방식으로 그것을 소유하고 있습니다.

 

Apply for a Job(구직 신청)

언젠가 Alice는 가상의 회사 Acme Corp에서 근무하고 싶습니다. 일반적으로 그녀는 웹 사이트를 탐색하고, 하이퍼링크를 클릭하여 일자리에 지원합니다. 그녀의 브라우저는 Indy 앱을 열 수 있는 connection request를 다운로드합니다. 이렇게 하면 Alice에게 Acme Corp와의 connection을 수락하라는 메시지가 표시됩니다. Indy-SDK를 사용하고 있기 때문에 프로세스는 다르지만 단계는 동일합니다. connection 설정 프로세스는 Faber가 Steward의 connection request를 수락할 때와 동일합니다.

Alice가 Acme와 connection을 설정한 후 Job-Application Proof Request를 받았습니다. proof request는 특정 속성을 가지고 있고, 다른 검증된 credential로 제공될 수 있는 속성을 풀어낼 수 있다는 것에 대해 검증 가능한 증거가 필요한 party에 의해 만들어진 요청입니다.

이 경우 Acme Corp는 Alice에게 Job Application 제공을 요청하고 있습니다. Job Application에는 이름, 학위, 상태, SSN 및 평균 점수에 대한 조건의 만족도도 필요합니다.

이 경우 Job-Application의 Proof Request는 다음과 같습니다.:

  # Acme Agent
  acme['job_application_proof_request'] = json.dumps({
      'nonce': '1432422343242122312411212',
      'name': 'Job-Application',
      'version': '0.1',
      'requested_attributes': {
          'attr1_referent': {
              'name': 'first_name'
          },
          'attr2_referent': {
              'name': 'last_name'
          },
          'attr3_referent': {
              'name': 'degree',
              'restrictions': [{'cred_def_id': faber['transcript_cred_def_id']}]
          },
          'attr4_referent': {
              'name': 'status',
              'restrictions': [{'cred_def_id': faber['transcript_cred_def_id']}]
          },
          'attr5_referent': {
              'name': 'ssn',
              'restrictions': [{'cred_def_id': faber['transcript_cred_def_id']}]
          },
          'attr6_referent': {
              'name': 'phone_number'
          }
      },
      'requested_predicates': {
          'predicate1_referent': {
              'name': 'average',
              'p_type': '>=',
              'p_value': 4,
              'restrictions': [{'cred_def_id': faber['transcript_cred_def_id']}]
          }
      }
  })

 

일부 속성은 검증 가능하고 일부는 불가능합니다.

proof request에 따르면 Credential 내의 SSN, 학위(degree) 및 졸업 상태(graduate status)는 issuer와 schema_key에 의해 공식적으로 주장해야합니다. 또한 이름(first_name), 성(last_name) 및 핸드폰 번호(phone_number)는 검증할 필요가 없습니다. Acme의 credential request는 이러한 credential에 검증 가능한 상태로 태그를 지정하지 않음으로써 이름과 전화번호에 대한 Alice의 credential을 수락할 것이라고 말하고 있습니다.

Alice가 Job-Application Proof Request에 대한 Proof 생성에 사용할 수 있는 Credential들을 표시하려면 anoncreds.prover_get_credentials_for_proof_req를 호출합니다.

  # Alice Agent
    creds_for_job_application_proof_request = json.loads(
        await anoncreds.prover_get_credentials_for_proof_req(alice['wallet'], alice['job_application_proof_request']))

 

Alice에게는 이 Job Application의 요구사항을 충족시키는 credential이 한 개 존재합니다.

  # Alice Agent
  {
    'referent': 'Transcript Credential Referent',
    'attrs': {
        'first_name': 'Alice',
        'last_name': 'Garcia',
        'status': 'graduated',
        'degree': 'Bachelor of Science, Marketing',
        'ssn': '123-45-6789',
        'year': '2015',
        'average': '5'
    },
    'schema_id': job_certificate_schema_id,
    'cred_def_id': faber_transcript_cred_def_id,
    'rev_reg_id': None,
    'cred_rev_id': None
  }

 

이제 Alice는 이러한 속성을 세 그룹으로 나눌 수 있습니다.:

  1. 공개될 속성 값
  2. 공개되지 않을 속성 값
  3. 검증 가능한 proof를 생성할 필요가 없는 속성

Job-Application Proof Request에서 Alice는 다음과 같이 속성들을 구분할 수 있습니다.

  # Alice Agent
    alice['job_application_requested_creds'] = json.dumps({
        'self_attested_attributes': {
            'attr1_referent': 'Alice',
            'attr2_referent': 'Garcia',
            'attr6_referent': '123-45-6789'
        },
        'requested_attributes': {
            'attr3_referent': {'cred_id': cred_for_attr3['referent'], 'revealed': True},
            'attr4_referent': {'cred_id': cred_for_attr4['referent'], 'revealed': True},
            'attr5_referent': {'cred_id': cred_for_attr5['referent'], 'revealed': True},
        },
        'requested_predicates': {'predicate1_referent': {'cred_id': cred_for_predicate1['referent']}}
    })

 

또한, Alice는 Credential Request를 생성하는 데 사용된 단계와 동일한 방식으로 사용된 각 Credential에 대한 Credential Schema 및 해당 Credential Definition을 가져와야합니다.

이제 Alice는 Acme Job-Application Proof Request에 대한 Proof를 생성하기 위한 모든 것을 갖추고 있습니다.

  # Alice Agent
  alice['apply_job_proof'] = \
        await anoncreds.prover_create_proof(alice['wallet'], alice['job_application_proof_request'], alice['job_application_requested_creds'],
                                            alice['master_secret_id'], alice['schemas'], alice['cred_defs'], alice['revoc_states'])

 

Acme는 수신된 Proof를 검사할 때 다음과 같은 구조를 보게 됩니다.:

  # Acme Agent
  {
      'requested_proof': {
          'revealed_attrs': {
              'attr4_referent': {'sub_proof_index': 0, 'raw':'graduated', 'encoded':'2213454313412354'},
              'attr5_referent': ['sub_proof_index': 0, 'raw':'123-45-6789', 'encoded':'3124141231422543541'},
              'attr3_referent': ['sub_proof_index': 0, 'raw':'Bachelor of Science, Marketing', 'encoded':'12434523576212321'}
          },
          'self_attested_attrs': {
              'attr1_referent': 'Alice',
              'attr2_referent': 'Garcia',
              'attr6_referent': '123-45-6789'
          },
          'unrevealed_attrs': {},
          'predicates': {
              'predicate1_referent': {'sub_proof_index': 0}
          }
      },
      'proof' : [] # Validity Proof that Acme can check
      'identifiers' : [ # Identifiers of credentials were used for Proof building
          {
            'schema_id': job_certificate_schema_id,
            'cred_def_id': faber_transcript_cred_def_id,
            'rev_reg_id': None,
            'timestamp': None
          }
      }
  }

 

Acme는 요청된 모든 속성을 가지고 있습니다. 이제 Acme는 Validity Proof를 확인하려고 합니다. 이를 위해 Acme는 먼저 Alice가 수행한 것과 같은 방식으로 Proof에 제시된 각 식별자에 대한 모든 Credential Schema 및 해당 Credential Definition을 가져와야합니다. 이제 Acme는 Alice의 Job-Application Proof를 확인하기 위한 모든 것을 갖추고 있습니다.

 # Acme Agent
 assert await anoncreds.verifier_verify_proof(acme['job_application_proof_request'], acme['apply_job_proof'],
                                              acme['schemas'], acme['cred_defs'], acme['revoc_ref_defs'], acme['revoc_regs'])

 

여기서는 apllication이 승인되고 결국 Alice가 작업을 시작한다고 가정합니다. Acme는 Alice에게 새로운 Credential Offer를 생성합니다.

  # Acme Agent
  acme['job_certificate_cred_offer'] = await anoncreds.issuer_create_credential_offer(acme['wallet'], acme['job_certificate_cred_def_id'])

 

Alice가 Acme와의 connection을 검사하면 새로운 Credential Offer가 제공되는 것을 볼 수 있습니다.

 

Apply for a Loan(대출 신청)

이제 Alice는 직업이 생겼기 때문에 대출을 신청하고 싶습니다. 이를 위해서는 고용 증명이 필요합니다. 그녀는 Acme가 제공하는 Job-Certificate credential에서 이를 얻을 수 있습니다. Alice는 친숙한 일련의 상호 작용을 거칩니다.

1. 먼저 Credential Request를 생성합니다.

 # Alice Agent
   (alice['job_certificate_cred_request'], alice['job_certificate_cred_request_metadata']) = \
       await anoncreds.prover_create_credential_req(alice['wallet'], alice['did_for_acme'], alice['job_certificate_cred_offer'],
                                                    alice['acme_job_certificate_cred_def'], alice['master_secret_id'])

 

2. Acme는 Alice에 대한 Job-Certificate Credential을 발급합니다.

 # Acme Agent
 alice_job_certificate_cred_values_json = json.dumps({
     "first_name": {"raw": "Alice", "encoded": "245712572474217942457235975012103335"},
     "last_name": {"raw": "Garcia", "encoded": "312643218496194691632153761283356127"},
     "employee_status": {"raw": "Permanent", "encoded": "2143135425425143112321314321"},
     "salary": {"raw": "2400", "encoded": "2400"},
     "experience": {"raw": "10", "encoded": "10"}
 })

 

Faber의 Transcript를 사용하는 것과의 한 가지 차이점은 Job-Certificate를 취소할 수 있고 credential 생성시 Acme에서 생성한 해지 레지스트리의 ID와 validity tails가 포함된 blob 저장소에 대한 핸들을 사용한다는 것입니다.

    # Acme Agent
    acme['blob_storage_reader_cfg_handle'] = await blob_storage.open_reader('default', acme['tails_writer_config'])
    acme['job_certificate_cred'], acme['job_certificate_cred_rev_id'], acme['alice_cert_rev_reg_delta'] = \
        await anoncreds.issuer_create_credential(acme['wallet'], acme['job_certificate_cred_offer'],
                                                 acme['job_certificate_cred_request'],
                                                 acme['job_certificate_cred_values'],
                                                 acme['revoc_reg_id'],
                                                 acme['blob_storage_reader_cfg_handle'])

 

또한 Acme는 다른 사용자가 나중에 credential의 해지 상태를 확인할 수 있도록 원장에 해지 레지스트리 항목을 게시해야합니다.

    # Acme agent
    acme['revoc_reg_entry_req'] = \
        await ledger.build_revoc_reg_entry_request(acme['did'], acme['revoc_reg_id'], 'CL_ACCUM',
                                                   acme['alice_cert_rev_reg_delta'])
    await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], acme['revoc_reg_entry_req'])

 

AliceAcme로부터 Job-Certificate credential을 받으려면 credential을 저장하기 전에 원장에게 해지 레지스트리 정의를 요청해야합니다.

    # Alice Agent
    alice['acme_revoc_reg_des_req'] = \
        await ledger.build_get_revoc_reg_def_request(alice['did_for_acme'],
                                                     alice_job_certificate_cred['rev_reg_id'])
    alice['acme_revoc_reg_des_resp'] = await ledger.submit_request(alice['pool'], alice['acme_revoc_reg_des_req'])
    (alice['acme_revoc_reg_def_id'], alice['acme_revoc_reg_def_json']) = \
        await ledger.parse_get_revoc_reg_def_response(alice['acme_revoc_reg_des_resp'])

 

이제 Job-Certificate Credential이 발급되었으며, Alice가 그것을 보유하고 있습니다. Alice는 Job-Certificate Credential을 자신의 지갑(wallet)에 저장합니다.

  # Alice Agent
  await anoncreds.prover_store_credential(alice['wallet'], None, alice['job_certificate_cred_request_metadata'],
                                          alice['job_certificate_cred'], alice['acme_job_certificate_cred_def'], alice['acme_revoc_reg_def_json'])

 

그녀는 일자리를 신청할 때 Transcript를 사용한 것과 거의 같은 방식으로 대출 신청을 할 수 있습니다.

그러나 데이터 공유에 대한 이러한 접근 방식에는 단점이 있습니다. 이는 꼭 필요한 것보다 더 많은 데이터를 공개할 수 있다는 것입니다. Alice가 해야할 모든 것이 고용 증명을 제공하는 것이라면 익명의 credential로 이를 대신 수행할 수 있습니다. 익명의 credential은 실제 값을 공개하지 않고 특정 술어를 증명할 수 있습니다.(예: Alice는 정규직으로 고용되며, 급여는 X보다 크고 고용 날짜는 있지만 실제 급여는 숨겨져있습니다.) Faber College와 Acme Corp의 credential을 사용하여 필요한 내용만 공개하는 복합 proof를 만들 수 있습니다.

Alice는 이제 Thrift Bank와 connection을 맺습니다.

Alice는 Thrift Bank로부터 다음과 같은 Loan-Application-Basic Proof Request를 받습니다.:

  # Thrift Agent
  thrift['apply_loan_proof_request'] = json.dumps({
      'nonce': '123432421212',
      'name': 'Loan-Application-Basic',
      'version': '0.1',
      'requested_attributes': {
          'attr1_referent': {
              'name': 'employee_status',
              'restrictions': [{'cred_def_id': acme_job_certificate_cred_def_id}]
          }
      },
      'requested_predicates': {
          'predicate1_referent': {
              'name': 'salary',
              'p_type': '>=',
              'p_value': 2000,
              'restrictions': [{'cred_def_id': acme_job_certificate_cred_def_id}]
          },
          'predicate2_referent': {
              'name': 'experience',
              'p_type': '>=',
              'p_value': 1,
              'restrictions': [{'cred_def_id': acme_job_certificate_cred_def_id}]
          }
      },
      'non_revoked': {'to': int(time.time())}
  })

 

마지막 행은 제공된 Job-Certificate가 응용 프로그램 시간에 의해 취소되지 않아야 함을 나타냅니다.

Alice는 이 Loan-Application-Basic Proof Request의 증명 요구 사항을 충족하는 credential을 하나만 가지고 있습니다.

  # Alice Agent
  {
      'referent': 'Job-Certificate Credential Referent',
      'revoc_reg_seq_no': None,
      'schema_id': job_certificate_schema_id,
      'cred_def_id': acme_job_certificate_cred_def_id,
      'attrs': {
          'employee_status': 'Permanent',
          'last_name': 'Garcia',
          'experience': '10',
          'first_name': 'Alice',
           'salary': '2400'
      }
  }

 

Loan-Application-Basic Proof Request의 경우 Alice는 다음과 같이 특성을 나누었습니다. 원장에서 조회한 해지 상태에서 각 속성에 대한 유효 timestamp를 얻을 수 있습니다.

  # Alice Agent
  revoc_states_for_loan_app = json.loads(alice['revoc_states_for_loan_app'])
        timestamp_for_attr1 = await get_timestamp_for_attribute(cred_for_attr1, revoc_states_for_loan_app)
        timestamp_for_predicate1 = await get_timestamp_for_attribute(cred_for_predicate1, revoc_states_for_loan_app)
        timestamp_for_predicate2 = await get_timestamp_for_attribute(cred_for_predicate2, revoc_states_for_loan_app)
        alice['apply_loan_requested_creds'] = json.dumps({
            'self_attested_attributes': {},
            'requested_attributes': {
                'attr1_referent': {'cred_id': cred_for_attr1['referent'], 'revealed': True, 'timestamp': timestamp_for_attr1}
            },
            'requested_predicates': {
                'predicate1_referent': {'cred_id': cred_for_predicate1['referent'], 'timestamp': timestamp_for_predicate1},
                'predicate2_referent': {'cred_id': cred_for_predicate2['referent'], 'timestamp': timestamp_for_predicate2}
            }
        })

 

Alice는 Loan-Application-Basic Proof Request에 대한 Proof를 생성합니다.

  # Alice Agent
  alice['apply_loan_proof'] = \
            await anoncreds.prover_create_proof(alice['wallet'], alice['apply_loan_proof_request'],
                                                alice['apply_loan_requested_creds'], alice['master_secret_id'],
                                                alice['schemas_for_loan_app'], alice['cred_defs_for_loan_app'],
                                                alice['revoc_states_for_loan_app'])

 

Alice는 Loan-Application-Basic proof만 은행해 보냅니다. 이를 통해 현재 수행하려는 모든 것이 기본 자격이 될 때 공유해야하는 PII (personally identifiable information: 개인 식별 정보)를 최소화할 수 있습니다.

Thrift는 수신된 Proof를 검사할 때 다음과 같은 구조를 보게됩니다.:

  # Thrift Agent
  {
      'requested_proof': {
          'revealed_attrs': {
              'attr1_referent': {'sub_proof_index': 0, 'raw': 'Permanent', 'encoded':'2143135425425143112321314321'},
          },
          'self_attested_attrs': {},
          'unrevealed_attrs': {},
          'predicates': {
              'predicate1_referent': {'sub_proof_index': 0},
              'predicate2_referent': {'sub_proof_index': 0}
          }
      },
      'proof' : [] # Validity Proof that Thrift can check
      'identifiers' : [ # Identifiers of credentials were used for Proof building
          'schema_id': acme['job_certificate_schema_id'],
          'cred_def_id': acme['job_certificate_cred_def_id'],
          'rev_reg_id': acme['revoc_reg_id'],
          'timestamp': 1550503925 # A integer timestamp
      ]
  }

 

Thrift Bank는 Alice의 Loan-Application-Basic Proof를 성공적으로 검증하였습니다.

  # Thrift Agent
  assert await anoncreds.verifier_verify_proof(thrift['apply_loan_proof_request'],
                                                 thrift['alice_apply_loan_proof'],
                                                 thrift['schemas_for_loan_app'],
                                                 thrift['cred_defs_for_loan_app'],
                                                 thrift['revoc_defs_for_loan_app'],
                                                 thrift['revoc_regs_for_loan_app'])

 

Thrift Bank는 Alice가 자신의 개인 정보를 은행과 공유해야하는 두 번째 Proof Request를 보냅니다.

  # Thrift Agent
  thrift['apply_loan_kyc_proof_request'] = json.dumps({
      'nonce': '123432421212',
      'name': 'Loan-Application-KYC',
      'version': '0.1',
      'requested_attributes': {
          'attr1_referent': {'name': 'first_name'},
          'attr2_referent': {'name': 'last_name'},
          'attr3_referent': {'name': 'ssn'}
      },
      'requested_predicates': {}
  })

 

Alice는 이 Loan-Application-KYC Proof Request에 대한 증명 요구 사항을 충족하는 두 가지 credential을 가지고 있습니다.

  # Alice Agent
  {
    'referent': 'Transcript Credential Referent',
    'schema_id': transcript_schema_id,
    'cred_def_id': faber_transcript_cred_def_id,
    'attrs': {
        'first_name': 'Alice',
        'last_name': 'Garcia',
        'status': 'graduated',
        'degree': 'Bachelor of Science, Marketing',
        'ssn': '123-45-6789',
        'year': '2015',
        'average': '5'
    },
    'rev_reg_id': None,
    'cred_rev_id': None
  },
  {
      'referent': 'Job-Certificate Credential Referent',
      'schema_key': job_certificate_schema_id,
      'cred_def_id': acme_job_certificate_cred_def_id,
      'attrs': {
          'employee_status': 'Permanent',
          'last_name': 'Garcia',
          'experience': '10',
          'first_name': 'Alice',
          'salary': '2400'
      },
      'rev_reg_id': None,
      'revoc_reg_seq_no': None
  }

 

Loan-Application-KYC Proof Request의 경우 Alice는 다음과 같이 속성을 구분했습니다.

  # Alice Agent
  alice['apply_loan_kyc_requested_creds'] = json.dumps({
      'self_attested_attributes': {},
      'requested_attributes': {
          'attr1_referent': {'cred_id': cred_for_attr1['referent'], 'revealed': True},
          'attr2_referent': {'cred_id': cred_for_attr2['referent'], 'revealed': True},
          'attr3_referent': {'cred_id': cred_for_attr3['referent'], 'revealed': True}
      },
      'requested_predicates': {}
  })

 

Alice는 Loan-Application-KYC Proof Request에 대한 Proof를 생성합니다.

  # Alice Agent
  alice['apply_loan_kyc_proof'] = \
      await anoncreds.prover_create_proof(alice['wallet'], alice['apply_loan_kyc_proof_request'], alice['apply_loan_kyc_requested_creds'],
                                          alice['alice_master_secret_id'], alice['schemas'], alice['cred_defs'], alice['revoc_states'])

 

Thrift는 수신된 Proof를 검사할 때 다음과 같은 구조를 보게 됩니다.:

  # Thrift Agent
  {
      'requested_proof': {
          'revealed_attributes': {
              'attr1_referent': {'sub_proof_index': 0, 'raw':'123-45-6789', 'encoded':'3124141231422543541'},
              'attr1_referent': {'sub_proof_index': 1, 'raw':'Alice', 'encoded':'245712572474217942457235975012103335'},
              'attr1_referent': {'sub_proof_index': 1, 'raw':'Garcia', 'encoded':'312643218496194691632153761283356127'},
          },
          'self_attested_attrs': {},
          'unrevealed_attrs': {},
          'predicates': {}
      },
      'proof' : [] # Validity Proof that Thrift can check
      'identifiers' : [ # Identifiers of credentials were used for Proof building
          {
            'schema_id': transcript_schema_id,
            'cred_def_id': faber['transcript_cred_def_id'],
            'rev_reg_id': None,
            'timestamp': None
          },
          {
            'schema_key': job_certificate_schema_id,
            'cred_def_id': acme['job_certificate_cred_def_id'],
            'rev_reg_id': None,
            'timestamp': None
          }
      ]
  }

 

Thrift Bank는 Alice의 Loan-Application-KYC Proof를 성공적으로 검증했습니다.

  # Thrift Agent
  assert await anoncreds.verifier_verify_proof(thrift['apply_loan_kyc_proof_request'], thrift['alice_apply_loan_kyc_proof'],
                                               thrift['schemas'], thrift['cred_defs'], thrift['revoc_defs'], thrift['revoc_regs'])

 

Alice의 Proof는 모두 성공적으로 검증되었으며 Thrift Bank에서 대출을 받았습니다.

 

Alice Quits her Job(앨리스가 직장을 그만둔다)

시간이 지난 뒤, Alice는 그녀의 직업을 그만두기로 결정하여 AcmeJob-Certificate credential을 해지합니다.

    # Acme Agent
    await anoncreds.issuer_revoke_credential(acme['wallet'],
                                             acme['blob_storage_reader_cfg_handle'],
                                             acme['revoc_reg_id'],
                                             acme['job_certificate_cred_rev_id'])

 

그러면 Acme는 ledger.build_revoc_reg_entry_request 및 ledger.sign_and_submit_request를 호출해 원장에 해지를 게시하면 됩니다.

Alice가 대출(Loan-Application-Basic)을 다시 신청하려고 하면 Proof 검증이 실패합니다.

 

Explore the Code(코드 탐색)

이제 Libindy 구현이 외부에서 어떻게 작동하는지 볼 수 있는 기회를 얻었으므로 코드 아래에서 어떻게 작동하는지 알고 싶습니까? 그렇다면, Simulating Getting Started in the Jupiter를 실행하십시오. 이 링크를 보려면 GitHub에 로그인해야 할 수도 있습니다. 또한 여기에서 소스 코드를 찾을 수 있습니다.

데모 실행 시 오류가 발생하면 Trouble Shooting Guide를 확인하십시오..

 

Getting Started with Docker

Running getting-started with docker-compose

Prerequisites

indy-sdk 복제 : git clone https://github.com/hyperledger/indy-sdk.git

시작 폴더 cd indy-sdk/docs/getting-started로 이동합니다.

docker 및 docker-compose가 설치되어 있어야 합니다.

Run

getting-started 폴더에서 docker를 실행하십시오: docker-compose up

위의 명령은 getting-started(jupyter notebook)및 indy_pool(유효성 검사 노드 컬렉션) 이미지가 없는 경우 이미지를 생성하고, 컨테이너를 생성하고 실행합니다. 유효성 검사는 기본적으로 IP 10.0.0.2에서 실행됩니다. docker-compose 파일에서 pool_ip를 수정하여 변경할 수 있습니다. Jupyter를 얻으려면 출력되는 링크를 클릭하십시오. (반드시 다음과 같은 포맷으로 이루어져야합니다.: http://0.0.0.0:8888/?token= )

Stop

docker-compose down 명령은 생성된 네트워크 및 컨테이너를 중지하고 삭제합니다.

Trouble Shooting

Jupyter에서 실행할 때 데모에 오류가 발생하면 문제 해결 가이드를 확인하십시오.

 

Troubleshooting

데모를 설정하는 데 307 오류가 발생하면 다음 단계를 수행하여 정리하고 다시 시작하는 것이 좋습니다. 원장과의 통신에 영향을 미치며 데모를 실행할 수 없습니다. 다른 오류에 대한 몇 가지 권장 사항은 다음과 같습니다.

  • 306: 이미 원장을 구성했습니다. clean start를 수행하십시오.
  • 301: 원장을 만들려고 했지만 이미 구성되어 있습니다. 원장을 열 때 단일 실패로 인해 문제가 발생할 수 있습니다. clean install을 수행하십시오.
  • 212: 지갑을 찾을 수 없습니다. 이 문제가 발생하면 컨테이너를 중지하고 다시 시작하십시오: Ctrl-C, docker-compose down, docker-compose up

 

Overview steps for clean start(clean start 단계 개요)

  1. 기존 인스턴스 제거
  2. 소스 파일 리셋
  3. 새로운 빌드 수행
  4. 데모 시작

Steps in detail(상세 단계)

1. 컨테이너가 closed 상태인지 확인

docker-compose down   # to make sure containers are closed
docker image ls       # find image names that need to be removed in next step
docker image rm getting-started
docker image rm indy_pool
docker volume ls      # find volume name that needs to be removed in next step
docker volume rm gettingstarted_sandbox

2. 소스 파일 리셋

git checkout <branch_name>
git reset --hard     **WARNING** : make copies of any changes you want to keep prior to taking this step
git fetch --all
git pull

3. 새로운 빌드 수행

docker-compose build --no-cache    # adding no cache to make clean build

4. 데모 시작

docker-compose up

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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 31
글 보관함