FIDO 2.0 Key Attestation Format
FIDO의 3가지 스펙 문서 중 Key Attestation Format부터 정리한다.
블로그 제목의 길이가 길어지면 가독성이 떨어지기 때문에 편의상 KAF로 줄여서 제목을 붙이도록하겠다.
Attestation Statement(증명서) - (3)
Attestation Raw Data Type
Attestation Raw Data (rawData) 는 증명서의 서명된 객체를 말한다.
FIDO는 플러그형 Attestation Data Type을 지원하며, 이를 통해 TPM에서 생성된 Attestation Data와 다른 FIDO Authenticator를 지원할 수 있다.
Attestation Data의 내용은 Authenticator가 스스로 생성하거나 검증하는 등의 제어를 수행하여야 한다.
TPM Attestation
Attestation rawData (type = “tpm”)
rawData 값은 base64url 인코딩 방식의 binary object이다.
이 binary object는 만약 attestationStatement.core.version 값이 1이라면 TPM_CERTIFY_INFO 혹은 TPM_CERTIFY_INFO2의 구조를 가지게 되며 값이 2일 경우엔 base64url로 인코딩된 TPMS_ATTEST 구조로 정의된다.
TPM_CERTIFY_INFO 혹은 TPM_CERTIFY_INFO2의 경우엔 data 필드를, TPMS_ATTEST의 경우엔 extraData 필드를 가지게 되며 이 두 개의 필드는 clientDataHash 값을 포함하고 있어야 한다.Signature
attestationStatement.core.version 값이 1인 경우 FIDO Authenticator가 RSASSA-PKCS1-v1_5 서명 알고리즘을 사용할 수 있다.
즉, attestationStatement.header.alg=”RS256” 의 값을 가지게 된다는 뜻이다.참고 RS256 Digital Signature with RSASSA-PKCS1-v1_5 in RFC 7518
attestationStatement.core.version 값이 2인 경우 FIDO Authenticator는 아래와 같은 알고리즘을 사용한다.
- TPM_ALG_RSASSA (0x14)
attestationStatement.header.alg=”RS256”
이 알고리즘은 위의 RSASSA-PKCS1-v1_5와 동일한 알고리즘이지만
version 값이 1인 경우에도 사용된다. - TPM_ALG_RSAPSS (0x16)
attestationStatement.header.alg=”PS256” - TPM_ALG_ECDSA (0x18)
attestationStatement.header.alg=”ES256” - TPM_ALG_ECDAA (0x1A)
attestationStatement.header.alg=”ED256” - TPM_ALG_SM2 (0x1B)
attestationStatement.header.alg=”SM256”
- TPM_ALG_RSASSA (0x14)
Signature는 base64url로 디코딩된 rawData 필드를 통해 계산된다.
TPM Attestation statement certificate requirements
TPM의 Attestation statement certificate의 요구사항을 알아보자.TPM Attestation certificate는 아래와 같은 필드와 확장자가 꼭 있어야 한다.
Version은 3으로 설정한다.
Subject 필드는 무조건 비어있어야 한다.
Subject Alternative Name extension
Version이 2인 경우 Subject Alternative Name extension은 Credential_Profile_EK_V2 의 3.2.9 섹션에 정의되어 있다.
Version이 1인 경우 Subject Alternative Name extension은 Credential_Profile_V1.2의 3.2.9 섹션에 정의되어 있다.Extended Key Usage extension은 “joint-iso-itu-t(2) internationalorganizations(23) 133tcp-kp(8) tcg-kp-AIKCertificate(3)” OID를 포함한다.
참고 OID : Object Identifier
참고 joint-iso-itu-t : OID tree의 루트 노드가 가진 세 개의 호 중 하나 자세한 건 위키피디아의 Object identifier 문서에서 확인
Basic Constrains extension(기본적인 확장 제약 조건)은 CA 구성요소를 false로 설정해야 한다.
id-ad-ocsp 엔트리와 CRL DP extension을 가진 AIA의 확장은 Attestation certificate의 status가 FIDO Metadata Service를 통해 사용 가능하다.
참고 CRL : Certificate Revocation List, 인증서 폐기 목록
참고 DP : Distribution Point, 배포지점
Android Attestation
의심스러운 Authenticator가 Android Platform의 Platform-provided Authenticator인 경우엔 SafetyNet API기반으로 attestation statement가 작성된다.
AndroidAttestationClientData 는 Relying Party Application이 attestation object에 public key를 저장하도록 하기 때문에 Android attestation statement는 항상 AndroidAttestationClientData 와 결합한 상태에서 사용해야 한다.
참고 AndroidAttestationClientData 는 본 포스팅 아래에서 다루고 있다.
Attestation rawData (type = “android”)
Android의 SafetyNet은 Compact Serialization에서 JWS Object를 반환한다.Compact Serialization의 JWS는 dot(.)으로 구분되는 세 개의 segment로 구성되어 있으며 이 segment들은 각각 base64url로 인코딩된 문자열이다.
rawData 는 아래의 세그먼트들의 연속적인 데이터로 구성되어 있다.
- first segment
JWS Protected Header이다. UTF-8로 인코딩 되어 있다. - a dot (.)
- the second segment
JWS Payload이다. UTF-8로 인코딩되어 있다.
packed 와 tpm 유형과 다르게 android 유형은 rawData 필드와 rawData 객체는 동일한 문자열이다.
참고 packed와 tpm 유형의 rawData 필드는 rawData객체의 base64url 인코딩이다.
- first segment
Signature
Signature는 TPM Attestation 과 마찬가지로 rawData 필드를 통해 계산된다.
SafetyNet이 반환하는 JWS의 세 번째 segment는 Signature의 base64url 인코딩이며 AttestationStatement.signature 의 값이 된다.Converting SafetyNet response to attestationStatement
SafetyNet response를 attestationStatement로 변환하는 방법을 알아보자.Authenticator 및 Platform은 아래 나와있는 절차대로 Android SafetyNet response로부터 attestationStatement를 작성한다.
굳이 아래 나와 있는 알고리즘이 아니더라도 같은 결과가 나온다면 대체할 수 있다.
- AndroidAttestationClientData 유형의 clientDataJSON 을 생성한 후 clientData를 base64url로 인코딩하여 clientDataJSON으로 계산한다.
- SafetyNet attestation을 요청할 때 clientData의 hash 값으로 계산된 clientDataHash 를 Nonce 값으로 제공한다.
- JWS 객체인 SafetyNet response snr 을 받는다.
- 위에서 받은 snr 로부터 base64url로 인코딩된 hdr 헤더를 추출한다.
- snr 로부터 base64url로 인코딩된 payload p 를 추출한다.
- snr 로부터 base64url로 인코딩된 signature s 를 추출한다.
- AttestationStatement.core.rawData 값으로 hdr | “.” | p 을 설정한다.
- AttestationStatement.signature 값으로 s 를 설정한다.
- hdr 을 base64url로 디코딩하여 hdr-d 생성한다.
- AttestationStatement.header.alg 값으로 hdr-d.alg 를 설정한다.
- hdr-d.x5c 값이 존재하면 AttestationStatement.header.x5x 를 hdr-d.x5x 로 설정한다.
- hdr-d.x5u 값이 존재하면 URL을 확인하면 검색된 인증서 체인을 AttestationStatement.header.x5c 에 추가한다.
- AttestationStatement.core.type 값으로 “android” 를 설정한다.
- AttestationStatement.core.version 은 SafetyNet API를 제공하는 Google Play Service의 버전 번호로 설정한다.
AndroidAttestationClientData
ClientData dictionary는 아래와 같이 확장될 수 있다.1
2
3
4
5
6dictionary AndroidAttestationClientData : ClientData {
JsonWebKey publicKey;
boolean isInsideSecureHardware;
DOMString userAuthentication;
optional unsigned long userAuthenticationValidityDurationSeconds;
};- JsonWebKey publicKey
Authenticator가 JsonWebKey 객체로 생성한 Public Key - boolean isInsideSecureHardware
key가 장치 내의 기기에 존재하는 경우엔 true의 값을 가진다참고 대표적으로 아래의 형태의 하드웨어를 말한다.
TEE : Trusted Execution Environment
SE : Secure Element - DOMString userAuthentication
“none”, “keyguard”, “fingerprint” 중 한 가지의 값을 가진다.
none : 지문을 등록하지않았거나 잠금 화면을 설정하여 key가 사용자 인증에 연결되지 않은 상태
keyguard : 사용자가 잠금 화면을 해제해야 생성된 key가 사용되는 상태
fingerprint : 생성된 key를 포함하는 각각의 연산이 지문을 필요로 하여 각각 사용자에게 인증을 받아야하는 상태 - unsigned long userAuthenticationValidityDurationSeconds
userAuthentication이 keyguard 로 설정된 경우 key의 승인 시간을 정하는 변수로 사용자가 성공적으로 인증된 후에 key가 사용되도록 하는 상태
- JsonWebKey publicKey
Verifying AndroidClientData Specific Contextual Bindings
Relying Party는 AndroidClientData의 아래와 같은 특정 Contextual 바인딩을 검증해야 한다.참고 AttestationStatement의 검증은 다음 포스팅에서 다룬다.
AndroidAttestationClientData.challenge 검증
makeCredential이 호출하여 전달된 attestationChallenge 값과 동일한지 확인한다.facet 과 tokenBinding 검증
AndroidAttestationClientData의 facet 및 tokenBinding 파라미터가 Relying Party Application과 일치하는 지 확인한다.AndroidAttestationClientData.publicKey 검증
makeCredential이 호출한 FIDOCredentialInfo에서 반환된 것과 동일한 key인지 확인한다.clientDataJSON 의 hash 검증
safetynetResponse의 JWS payload에 있는 nonce 값과 일치하는 지 확인한다.ctsProfileMatch 검증
safetynetResponse의 payload에 있는 ctsProfileMatch 속성값이 true인지 확인한다.apkPackageName 검증
safetynetResponse의 payload에 있는 apkPackageName 속성값이 SafetyNet API를 호출하는 Application의 package name과 일치하는 지 확인한다.apkDigestSha256 검증
safetynetResponse의 payload에 있는 apkDigestSha256 속성값이 SafetyNet API를 호출하는 Application의 package hash와 일치하는지 확인한다.apkCertificateDigestSha256 검증
safetynetResponse의 payload에 있는 apkCertificateDigestSha256 속성값이 SafetyNet API를 호출하는 Application의 Signing 인증서의 hash와 일치하는 지 확인한다.