006. FIDO 2.0 Key Attestation Format - Attestation Statement(증명서) - (3)

FIDO 2.0 Key Attestation Format

FIDO의 3가지 스펙 문서 중 Key Attestation Format부터 정리한다.

블로그 제목의 길이가 길어지면 가독성이 떨어지기 때문에 편의상 KAF로 줄여서 제목을 붙이도록하겠다.

참고 FIDO alliance : FIDO 2.0 Key Attestation Format 문서

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”

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 문서에서 확인

      참고 Object identifier의 2.23.133.8.3 형식 문서

    • 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, 배포지점

      참고 FIDO Metadata Service Document

Android Attestation

의심스러운 Authenticator가 Android Platform의 Platform-provided Authenticator인 경우엔 SafetyNet API기반으로 attestation statement가 작성된다.

참고 SafetyNet in Android Document

AndroidAttestationClientData 는 Relying Party Application이 attestation object에 public key를 저장하도록 하기 때문에 Android attestation statement는 항상 AndroidAttestationClientData 와 결합한 상태에서 사용해야 한다.

참고 AndroidAttestationClientData 는 본 포스팅 아래에서 다루고 있다.

  • Attestation rawData (type = “android”)
    Android의 SafetyNet은 Compact Serialization에서 JWS Object를 반환한다.

    참고 JSON Web Signature in RFC 7515

    Compact Serialization의 JWS는 dot(.)으로 구분되는 세 개의 segment로 구성되어 있으며 이 segment들은 각각 base64url로 인코딩된 문자열이다.

    rawData 는 아래의 세그먼트들의 연속적인 데이터로 구성되어 있다.

    • first segment
      JWS Protected Header이다. UTF-8로 인코딩 되어 있다.
    • a dot (.)
    • the second segment
      JWS Payload이다. UTF-8로 인코딩되어 있다.

    packedtpm 유형과 다르게 android 유형은 rawData 필드와 rawData 객체는 동일한 문자열이다.

    참고 packedtpm 유형의 rawData 필드는 rawData객체의 base64url 인코딩이다.

  • 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를 작성한다.

굳이 아래 나와 있는 알고리즘이 아니더라도 같은 결과가 나온다면 대체할 수 있다.

  1. AndroidAttestationClientData 유형의 clientDataJSON 을 생성한 후 clientData를 base64url로 인코딩하여 clientDataJSON으로 계산한다.
  2. SafetyNet attestation을 요청할 때 clientData의 hash 값으로 계산된 clientDataHash 를 Nonce 값으로 제공한다.
  3. JWS 객체인 SafetyNet response snr 을 받는다.
  4. 위에서 받은 snr 로부터 base64url로 인코딩된 hdr 헤더를 추출한다.
  5. snr 로부터 base64url로 인코딩된 payload p 를 추출한다.
  6. snr 로부터 base64url로 인코딩된 signature s 를 추출한다.
  7. AttestationStatement.core.rawData 값으로 hdr | “.” | p 을 설정한다.
  8. AttestationStatement.signature 값으로 s 를 설정한다.
  9. hdr 을 base64url로 디코딩하여 hdr-d 생성한다.
  10. AttestationStatement.header.alg 값으로 hdr-d.alg 를 설정한다.
  11. hdr-d.x5c 값이 존재하면 AttestationStatement.header.x5xhdr-d.x5x 로 설정한다.
  12. hdr-d.x5u 값이 존재하면 URL을 확인하면 검색된 인증서 체인을 AttestationStatement.header.x5c 에 추가한다.
  13. AttestationStatement.core.type 값으로 “android” 를 설정한다.
  14. AttestationStatement.core.version 은 SafetyNet API를 제공하는 Google Play Service의 버전 번호로 설정한다.
  • AndroidAttestationClientData
    ClientData dictionary는 아래와 같이 확장될 수 있다.

    1
    2
    3
    4
    5
    6
    dictionary AndroidAttestationClientData : ClientData {
    JsonWebKey publicKey;
    boolean isInsideSecureHardware;
    DOMString userAuthentication;
    optional unsigned long userAuthenticationValidityDurationSeconds;
    };
    • JsonWebKey publicKey
      Authenticator가 JsonWebKey 객체로 생성한 Public Key

      참고 Web Cryptography API Document

    • 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가 사용되도록 하는 상태
  • Verifying AndroidClientData Specific Contextual Bindings
    Relying Party는 AndroidClientData의 아래와 같은 특정 Contextual 바인딩을 검증해야 한다.

    참고 AttestationStatement의 검증은 다음 포스팅에서 다룬다.

    • AndroidAttestationClientData.challenge 검증
      makeCredential이 호출하여 전달된 attestationChallenge 값과 동일한지 확인한다.

      참고 Section 3.1 in FIDO 2.0 Web API Specification

    • facettokenBinding 검증
      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와 일치하는 지 확인한다.