メインコンテンツへスキップ

go-webauthn version 11でのエラー

·252 文字·2 分
技術記事 Go言語 WebAuthn コードリーディング

go-webauthnを使っていたところ以下のエラーに遭遇した。

Unable to validate attestation signature statement during attestation validation: invalid certificate chain from MDS: x509: unhandled critical extension

ここでエラーを追跡してみた。

前半の文面からここがエラーだとわかる。どうやら証明書の検証エラーのようだ https://github.com/go-webauthn/webauthn/blob/cf1758ab00a77bbe97e9315946f78e6874a8d708/protocol/attestation.go#L257-L259

ここで検証に使っているx5cの出どころはここ https://github.com/go-webauthn/webauthn/blob/cf1758ab00a77bbe97e9315946f78e6874a8d708/protocol/attestation.go#L240-L250

x5csの出どころはここ https://github.com/go-webauthn/webauthn/blob/cf1758ab00a77bbe97e9315946f78e6874a8d708/protocol/attestation.go#L169

formatHandlerの出どころであるattestationRegistryに登録されているのはRegisterAttestationFormat関数 https://github.com/go-webauthn/webauthn/blob/cf1758ab00a77bbe97e9315946f78e6874a8d708/protocol/attestation.go#L87-L89

今回は Windows Hello の tpm 形式であるため tpm のハンドラーがRegisterAttestationFormatで登録されているのはここで、formatHandlerの実態はverifyTPMFormatだとわかる https://github.com/go-webauthn/webauthn/blob/cf1758ab00a77bbe97e9315946f78e6874a8d708/protocol/attestation_tpm.go#L18-L20

verifyTPMFormat 内では証明書(x5cs)がある場合、独自に extension を解析しているようだ Subject Alternative Name extension https://github.com/go-webauthn/webauthn/blob/9ca2faef6e4bbc88bfbaaccca846ee420b142e17/protocol/attestation_tpm.go#L175-L182 Extended Key Usage https://github.com/go-webauthn/webauthn/blob/9ca2faef6e4bbc88bfbaaccca846ee420b142e17/protocol/attestation_tpm.go#L199 Basic Constraints extension https://github.com/go-webauthn/webauthn/blob/9ca2faef6e4bbc88bfbaaccca846ee420b142e17/protocol/attestation_tpm.go#L222

なお標準では以下の processExtensions 関数でハンドリングされる https://github.com/golang/go/blob/72735094660a475a69050b7368c56b25346f5406/src/crypto/x509/parser.go#L664

ここでデバッグ用のサーバーを立ち上げ tpm 形式の attestation object を取得してその中からx5cにあたる証明書をダンプして、 python でをパースして x509 証明書の extension を確認したところ以下のようであった。

Signature Algorithm: <ObjectIdentifier(oid=1.2.840.113549.1.1.11, name=sha256WithRSAEncryption)>
Extensions:
  Type: <ObjectIdentifier(oid=2.5.29.15, name=keyUsage)>
  Value: <KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False)>
  Critical: True
  Type: <ObjectIdentifier(oid=2.5.29.19, name=basicConstraints)>
  Value: <BasicConstraints(ca=False, path_length=None)>
  Critical: True
  Type: <ObjectIdentifier(oid=2.5.29.32, name=certificatePolicies)>
  Value: <CertificatePolicies([<PolicyInformation(policy_identifier=<ObjectIdentifier(oid=1.3.6.1.4.1.311.21.31, name=Unknown OID)>, policy_qualifiers=[<UserNotice(notice_reference=None, explicit_text='TCPA  Trusted  Platform  Identity')>])>])>
  Critical: True
  Type: <ObjectIdentifier(oid=2.5.29.37, name=extendedKeyUsage)>
  Value: <ExtendedKeyUsage([<ObjectIdentifier(oid=2.23.133.8.3, name=Unknown OID)>])>
  Critical: False
  Type: <ObjectIdentifier(oid=2.5.29.17, name=subjectAltName)>
  Value: <SubjectAlternativeName(<GeneralNames([<DirectoryName(value=<Name(2.23.133.2.1=id:494E5443,2.23.133.2.2=ADL,2.23.133.2.3=id:02580012)>)>])>)>
  Critical: True
  Type: <ObjectIdentifier(oid=2.5.29.35, name=authorityKeyIdentifier)>
  Value: <AuthorityKeyIdentifier(key_identifier=b'0\xa3\x12\xbc\x94$q\xc8\xcb#\x9c\xf3\xc2,@N\x19\x1e\x8f\xa9', authority_cert_issuer=None, authority_cert_serial_number=None)>
  Critical: False
  Type: <ObjectIdentifier(oid=2.5.29.14, name=subjectKeyIdentifier)>
  Value: <SubjectKeyIdentifier(digest=b'\xab#\xb6\xd1z\xb92\x16\xd3\xcf\x92%\xa1\xdct\xd2\x96\x961t')>
  Critical: False
  Type: <ObjectIdentifier(oid=1.3.6.1.5.5.7.1.1, name=authorityInfoAccess)>
  Value: <AuthorityInformationAccess([<AccessDescription(access_method=<ObjectIdentifier(oid=1.3.6.1.5.5.7.48.2, name=caIssuers)>, access_location=<UniformResourceIdentifier(value='http://azcsprodncuaikpublish.blob.core.windows.net/ncu-intc-keyid-134d03d6581dabea3bf82eb2e34bf98192826962/e069c79b-9b7d-4ae6-afc6-23d396df1a4c.cer')>)>])>
  Critical: False

比較してみるとわかるのだが実は標準でハンドリングされるものばかりなのである。 では何が原因かとデバッガを使って調べてみると以下のコードに到達した。

https://github.com/golang/go/blob/72735094660a475a69050b7368c56b25346f5406/src/crypto/x509/parser.go#L691

if len(out.DNSNames) == 0 && len(out.EmailAddresses) == 0 && len(out.IPAddresses) == 0 && len(out.URIs) == 0 {
	// If we didn't parse anything then we do the critical check, below.
	unhandled = true
}

つまり証明書がドメイン名にもメールアドレスにも IP アドレスにも URI にも関連していないのでハンドリングされない扱いになっているのであった。 ちなみに tpm 用の独自のロジックのほうでは tpm 用のメタデータ(manufacturer,model,version)などが解析されていた https://github.com/go-webauthn/webauthn/blob/9ca2faef6e4bbc88bfbaaccca846ee420b142e17/protocol/attestation_tpm.go#L320-L328

そしてこの unhandled フラグにより以下で UnhandledCriticalExtensions が設定されえる。 https://github.com/golang/go/blob/72735094660a475a69050b7368c56b25346f5406/src/crypto/x509/parser.go#L819-L821

そしてこの箇所で UnhandledCriticalExtensions との比較が行われエラーが発生する。 https://github.com/golang/go/blob/72735094660a475a69050b7368c56b25346f5406/src/crypto/x509/verify.go#L565

なお UnhandledCriticalExtension エラー型の定義は以下でありこれはx509: unhandled critical extensionの文面に一致する。 https://github.com/golang/go/blob/72735094660a475a69050b7368c56b25346f5406/src/crypto/x509/x509.go#L969-L973

なおこの一連の検証を行うかは GetValidateTrustAnchor によって判定されている。 https://github.com/go-webauthn/webauthn/blob/cf1758ab00a77bbe97e9315946f78e6874a8d708/protocol/attestation.go#L229

GetValidateTrustAnchor の実態は anchors である。 https://github.com/go-webauthn/webauthn/blob/cf1758ab00a77bbe97e9315946f78e6874a8d708/metadata/providers/memory/provider.go#L71-72

anchors はここでデフォルトの true に設定され https://github.com/go-webauthn/webauthn/blob/cf1758ab00a77bbe97e9315946f78e6874a8d708/metadata/providers/memory/provider.go#L17

ここのオプションで変更可能であると思われ https://github.com/go-webauthn/webauthn/blob/master/metadata/providers/memory/provider.go#L23

そしてこの関数をオプションとして与えることで検証をスキップさせとりあえずの成功にはできそうである。 https://github.com/go-webauthn/webauthn/blob/master/metadata/providers/memory/options.go#L44

またもうひとつの手段としてはブラウザの publicKey オブジェクトに渡す attestation プロパティの値を none に設定することで tpm 形式の証明書ではないものにするという手もある。

https://developer.mozilla.org/ja/docs/Web/API/CredentialsContainer/create#attestation

だが検証できるのに検証できないのはもどかしいので Issue を立てた。どうなるかは知らない。

https://github.com/go-webauthn/webauthn/issues/275