관리 메뉴

평행우주 : world 1

[실습 | Solidity] 블록체인을 사용한 DID 백신 접종 인증서 개발 본문

텃밭 1 : BlockChain/SSI | DID

[실습 | Solidity] 블록체인을 사용한 DID 백신 접종 인증서 개발

parallelworlds 2022. 4. 7. 15:55

 

 

💁  SSI, DID가 매력적인 이유

 

 

우리는 신원과 식별자를 종종 혼동하는 경우가 있다.

우리가 신원하면 흔히 떠올리는 신분증, 여권, 인증서 등은 신원이 아닌 식별자다.

 

신원은 보다 더 개인적이고, 자기주권적 인간인 내 자신의 통제하에 있는 것이다.

문제는, 우리의 신원과 관련된 대부분의 것들이 조직의 관리 편리성을 위한 방법대로

우리 자신의 통제가 없는 상태에서 이뤄지고 있다는 것이다.

 

우리는 우리의 신원을 통제할 필요가 있다 

SSI의 발전은 바로 그 시작점이다 

 

 

 

✍🏻 SSI, DID에 대한 짧은 정리글

 

[SSI | DID] SSI 자기주권 신원증명 인증

SSI 자기주권 신원증명 대부분의 SSI 플랫폼은 블록체인 기술을 기반으로 개발 모든 SSI 플랫폼이 블록체인을 통해 개발될 필요는 없다 블록체인 분산원장의 특징 블록체인에 참여하는 모든 사

1parallelworlds.tistory.com

 

[SSI | DID] DID(Decentralized IDentifier) 구성요소, 생성과 사용과정

DID(Decentralized IDentifier) 사용하는 사람 스스로 생성하고 제어할 수 있는 분산형 식별자 DID를 사용하는 객체에 대한 식별자로 사용될 뿐만 아니라, 인증 수단인 DID document를 참조할 수 있는 URI 역

1parallelworlds.tistory.com

 

[SSI | DID] DID Document 구성요소 : id

DID Document DID Document에는 DID의 소유권을 증명할 수 있는 인증 수단이 포함된다 +) 대부분의 DID 플랫폼은 DID와 DID doc 생성 시 비대칭키를 함께 생성 생성한 비대칭키의 비밀키는 본인이 보관하고,

1parallelworlds.tistory.com

 

 

 

 

 

🧑🏽‍💻 만들어 볼 것 : 백신 접종 증명서

코로나 19 백신 접종을 받았음을 검증 가능한 자격 증명을 DID로 구현해보자

 

W3C의 권고사항에 따르면 다음과 같은 사항을 설계 시 참고해야 한다

 

>> 발급자가 선택적 공개를 지원하는 소프트웨어가 없는 경우에도,

선택적 공개를 지원하는 검증가능한 크리덴셜을 발급할 수 있는 수단 제공

 

>> 발급자는 선택적 공개를 지원하는 검증가능한 크리덴셜을 발급 가능

검증은 어떤 발급자에게도 검증자의 신원을 공개하지 않아야 함

 

>> issuanceDate

크리덴셜이 유효한 날짜와 시간을 나타내는 문자열 값

 

>> proof

변조를 감지하고 크리덴셜 또는 프레젠테이션의 소유권을 확인하는 데 사용할 수 있는 암호화 프루프

 

>> credentialStatus

id , type 등이 포함되어야 한다

 

 

이보다 더더 많은데,, 일단 도전해볼 수 있을만한 것들만 추려보았다.

일단 이번에는 위의 참고사항도 다 포함하지 않은

아주 간단한 DID 개발을 해볼 예정이다

 

 

 

 

 

 

 👀  코드 분석

 

OwnerHelper

// Owner만 실행할 수 있는 컨트랙트
abstract contract OwnerHelper {
    address private owner;

    event OwnerTransferPropose(address indexed _from, address indexed _to);

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    // 함수 호출 시 owner = msg.sender로 자동 설정
    constructor() {
        owner = msg.sender;
    }
     // owner 권한을 가진 자가 다른 주소로 권한 위임
    function transferOwnership(address _to) public onlyOwner {
        require(_to != owner);
        require(_to != address(0x0));
        owner = _to;
        emit OwnerTransferPropose(owner, _to);
    }
}

 

 

IssuerHelper

// IssuerHelper는 Issuer을 추가 또는 삭제할 수 있다
// 그 기능은 onlyOwner로 제한되어 Owner만 제어 가능
abstract contract IssuerHelper is OwnerHelper {
    mapping(address => bool) public issuers;

    event AddIssuer(address indexed _issuer);
    event DelIssuer(address indexed _issuer);

    modifier onlyIssuer() {
        require(isIssuer(msg.sender) == true);
        _;
    }

    constructor() {
        issuers[msg.sender] = true;
    }

    function isIssuer(address _addr) public view returns (bool) {
        return issuers[_addr];
    }

    function addIssuer(address _addr) public onlyOwner returns (bool) {
        require(issuers[_addr] == false);
        issuers[_addr] = true;
        emit AddIssuer(_addr);
        return true;
    }

    function delIssuer(address _addr) public onlyOwner returns (bool) {
        require(issuers[_addr] == true);
        issuers[_addr] = false;
        emit DelIssuer(_addr);
        return true;
    }
}

 

 

 

CredentialBox : mapping, struct

// Credential 발행, 확인
contract CredentialBox is IssuerHelper {
    uint256 private idCount; // Credential이 한 주소에 하나씩만 발급받게 한다
    mapping(uint8 => string) private vaccineEnum; // 백신 종류
    mapping(uint8 => string) private statusEnum; // 접종 상태 (횟수)

    // VC를 구현하기 위한 구조체
    struct Credential {
        uint256 id; // index 순서 표기하는 idCount
        address issuer; // 발행자
        uint8 vaccineType; // 백신 증명서 타입
        uint8 statusType; //접종 상태 (횟수)
        string value; // 크리덴셜에 포함되어야 하는 암호화된 정보.
        // 중앙화된 서버에서 제공하는 신원, 서명 등이 JSON 형태로 저장됨
        uint256 createDate;
    }

    // 주소값으로 발급된 크리덴셜 확인
    mapping(address => Credential) private credentials;

 

 

CredentialBox : constructor

    constructor() {
        idCount = 1;
        vaccineEnum[0] = "미접종 권고 대상자"; //약물알러지, 기저질환 등
        vaccineEnum[1] = "PFI"; //화이자
        vaccineEnum[2] = "JOH"; //얀센
        vaccineEnum[3] = "AST"; //아스트라제네카
        vaccineEnum[4] = "NOV"; //노바백스
        vaccineEnum[5] = "MOD"; //모더나
        statusEnum[0] = "미접종"; // 접종 상태 (횟수)
        statusEnum[1] = "1회차";
        statusEnum[2] = "2회차";
        statusEnum[3] = "3회차";
        statusEnum[4] = "4회차";
    }

 

 

CredentialBox : claimCredential

// claimCredential 함수로 Credential 발행
    function claimCredential(
        address _vaccineAddress,
        uint8 _vaccineType,
        uint8 _vaccineStatusType,
        string calldata _value
    ) public onlyIssuer returns (bool) {
        Credential storage credential = credentials[_vaccineAddress];

        // credential의 id가 0일 경우에만 함수 작동
        require(credential.id == 0);
        credential.id = idCount;
        credential.issuer = msg.sender;
        credential.vaccineType = _vaccineType;
        credential.statusType = 0;
        credential.value = _value;
        credential.createDate = block.timestamp; //block.timestamp를 활용해 크리덴셜을 클레임한 시간 저장

        idCount += 1;

        return true;
    }

 

 

 

CredentialBox : getCredential, addStatusType, changeStatus

// getCredential 함수로 Credential을 발행한 주소에서 VC확인
    function getCredential(address _vaccineAddress)
        public
        view
        returns (Credential memory)
    {
        return credentials[_vaccineAddress];
    }

    function addVaccineType(uint8 _type, string calldata _value)
        public
        onlyIssuer
        returns (bool)
    {
        require(bytes(vaccineEnum[_type]).length == 0);
        vaccineEnum[_type] = _value;
        return true;
    }

    function getVaccineType(uint8 _type) public view returns (string memory) {
        return vaccineEnum[_type];
    }

    // bytes로 변환하여 길이로 String이 null인지 검사
    // 내부 statusEnum의 Type이 중복되는 타입이 존재하는지 검사
    function addStatusType(uint8 _type, string calldata _value)
        public
        onlyIssuer
        returns (bool)
    {
        require(bytes(statusEnum[_type]).length == 0);
        statusEnum[_type] = _value;
        return true;
    }

    function getStatusType(uint8 _type) public view returns (string memory) {
        return statusEnum[_type];
    }

    // 특정 사용자의 상태 변경
    // statusType의 값을 가져와 변경한다
    function changeStatus(address _vaccine, uint8 _type)
        public
        onlyIssuer
        returns (bool)
    {
        require(credentials[_vaccine].id != 0);
        require(bytes(statusEnum[_type]).length != 0);
        credentials[_vaccine].statusType = _type;
        return true;
    }
}

 

 

 

 

 

🧩 전체 코드

 

GitHub - potterpeter/vaccine-DID: vaccine DID

vaccine DID. Contribute to potterpeter/vaccine-DID development by creating an account on GitHub.

github.com

 

 

Comments