"서명한 사람 이름이 같다고 인증서가 같은 건 아니야."
requirement가 뭐냐면
서명에는 코드 요구 조건이라는 작은 언어가 들어 있어. 그중 designated requirement(DR)는 "나중에 온 이 바이너리가 같은 앱이야?"에 답하는 조건이야. 직접 쓰지 않으면 codesign이 서명 방식에 따라 알아서 만들어. 인증서로 서명한 앱이면 번들 식별자에 인증서 체인 조건을 더해서 만들고, 애드혹 코드면 코드 자체의 해시로 만들어. TCC, 키체인, 시스템 서비스 몇 개는 앱 이름이나 경로나 버전이 아니라 이 DR을 저장해 뒀다가, 새 코드가 오면 거기에 대조해.
codesign -dr - Spark.app으로 읽어. 출력 맨 앞에 #이 붙어 있으면(# designated => cdhash H"…") 서명에 적힌 게 아니라 암묵적으로 만들어진 조건이라는 뜻이야.
눈으로 보지 말고 시험해
codesign --verify -R='<requirement>' Spark.app은 네가 쓴 조건 무엇에든 바이너리를 대조해 봐. 안 맞으면 test-requirement: code failed to satisfy specified code requirement(s)를 찍고 0이 아닌 코드로 끝나. "새 빌드가 TCC 입장에서 여전히 같은 앱이야?"라는 질문을, 스크립트가 판정할 수 있는 명령으로 바꿔주는 거지.
바꾸기 전에 비교해
가족의 큐 런처는 Mac마다 설치되는 작은 서명된 헬퍼야. 그 설치기는 설치된 런처의 designated requirement가 새로 빌드한 것과 다르면 교체를 거부하고, 두 requirement를 다 찍어 보여줘. 이유는 맨 위 인용 그대로야. 같은 공통 이름으로 인증서를 다시 만들면, 서명은 전부 같은 신원에서 온 것처럼 "보여". 하지만 leaf 해시가 달라지고, 그러면 requirement가 달라지고, 결국 그 Mac의 손쉬운 사용 권한이 전부 조용히 주인 잃은 권한이 돼. requirement 문자열을 비교하는 건 비용이 거의 안 들어. 그리고 바꿔 끼우기 전에, 사용자가 권한 창을 보고서야 눈치채기 전에 문제를 잡아줘.
안정적인 requirement는 필요조건이지 충분조건이 아니야
가족이 비싸게 배운 구분이 하나 더 있어. 인증서로 제대로 서명했고 requirement도 안정적인 런처가, 어떤 Mac에서 손쉬운 사용 검사에 실패했어. 그 Mac엔 예전에 애드혹 빌드가 깔려 있었고, 복구하는 과정에서 권한 행이 지워졌거든. DR이 안정적이라는 건 앞으로 받을 권한이 다시 빌드해도 살아남는다는 뜻이지, 지금 권한이 있다는 증거가 아니야. 권한을 정직하게 확인하는 방법은 돌고 있는 앱이 직접 물어보는 것뿐이야. 다음 레슨들에서 방법을 보여줄게.