【 FIDO Conformance Toolsメモ 】Server-ServerPublicKeyCredentialCreationOptions-Req-1 Test server generating ServerPublicKeyCredentialCreationOptionsRequest

はじめに

FIDO Conformance ToolsのServer-ServerPublicKeyCredentialCreationOptions-Req-1 Test server generating ServerPublicKeyCredentialCreationOptionsRequest という項目について書いていきます。
前回はv0.10.110(BETA)(BETA FIDO2)というバージョンを利用していましたが現在はv0.10.207を利用しています。
テスト項目についても以前の記事に追記しているのでよろければ確認ください。

kent056-n.hatenablog.com

Server-ServerPublicKeyCredentialCreationOptions-Req-1 Test server generating ServerPublicKeyCredentialCreationOptionsRequest

こちらのテスト項目については目次の通りになります。

P-1 Get ServerPublicKeyCredentialCreationOptionsResponse, and check that:

(a) response MUST contain "status" field, and it MUST be of type DOMString and set to "ok"
こちらはレスポンスにstatusフィールドがあるかどうかのチェックのようです。
成功時、失敗時それぞれ以下のようなレスポンスを返すようにします。

  • 成功時の例
{
    "status": "ok",
    "errorMessage": ""
}
  • 失敗時の例
    {
        "status": "failed",
        "errorMessage": "User does not exists!"
    }

(b) response MUST contain "errorMessage" field, and it MUST be of type DOMString and set to an empty string
こちらも(a)と同様にerrorMessageを全てのレスポンスで返すようにします。

  • 成功時の例
{
    "status": "ok",
    "errorMessage": ""
}
  • 失敗時の例
    {
        "status": "failed",
        "errorMessage": "User does not exists!"
    }

(c) response contains "user" field, of type Object and:
(1) check that user.name is not missing, and is of type DOMString
user.nameの有無をチェックしているかのテストのようです。
以下のように/attestation/optionsで返すオプションにuser.nameを含めて返します。

...
        user: {
            id: id,
            name: username,
            displayName: displayName
},
...

(2) check that user.displayName is not missing, and is of type DOMString
displayNameの有無をチェックしているかのテストのようです。 以下のように/attestation/optionsで返すオプションにuser.displayNameを含めて返します。

...
        user: {
            id: id,
            name: username,
            displayName: displayName
},
...

(3) check that user.id is not missing, and is of type DOMString, and is not empty. It MUST be base64url encoded byte sequence, and is not longer than 64 bytes.
user.idについてのテストのようです。
ここでは、そのidが存在しているか、typeがDOMStringか、base64url encodeされているか、64byteより長くないかどうかチェックされていそうです。
以下のように乱数を生成して、base64urlエンコードしたのちにレスポンスに含めるようにします。

    let buff = crypto.randomBytes(32);

    return base64url(buff);

(4)If user.icon is presented, check that it's is of type DOMString
もしiconが渡された時に型をチェックしているかどうかのテストのようです。
自分は全然チェックしたつもりないけど通っていました。そもそもオプションでiconを返していなければ問題なさそうでしょうか??

(d) response contains "rp" field, of type Object and:
(1) check that rp.name is not missing, and is of type DOMString
rp.nameに関するテストのようです。
/attestation/optionsのレスポンスでrpにnameを含めてレスポンスを返しておけば問題なさそうです。

    {
        "status": "ok",
        "errorMessage": "",
        "rp": {
            "name": "Example Corporation"
        },
    ...
    }

(2) check that rp.id is not missing, and is of type DOMString.
rp.idに関するテストのようです。
特に/attestation/optionsではrp.idを返すようにしていなかったはずだが通っていました。おそらく/attestation/resultの以下の処理でしょうか??

    if(!request.body       || !request.body.id
    || !request.body.rawId || !request.body.response
    || !request.body.type  || request.body.type !== 'public-key' ) {
        response.json({
            'status': 'failed',
            'errorMessage': 'Response missing one or more of id/rawId/response/type fields, or type is not public-key!'
        })
        return
    }

(3) If rp.icon is presented, check that it's is of type DOMString.
rp.iconに関するテストのようです。
チェックした記憶がないが通っていました。そもそもrp.iconをオプションとして返していなければ問題なさそうでしょうか??

(e) response contains "challenge" field, of type String, base64url encoded and not less than 16 bytes. challengeに関するテストのようです。
以下のように乱数を生成してbase64urlエンコード後レスポンスに含めて返します。

    let buff = crypto.randomBytes(32);

    return base64url(buff)

(f) response contains "pubKeyCredParams" field, of type Array and: /attestation/optionsのレスポンスのpubKeyCredParamsのチェックのようです。   pubKeyCredParamsのtypeがArrayになっていれば問題なさそうです。

    {
        "status": "ok",
        "errorMessage": "",
        ...
        "pubKeyCredParams": [
            {
                "type": "public-key",
                "alg": -7
            }
        ],
     ...
    }

(1) each member MUST be of type Object
pubKeyCredParamsの中身がObjectかどうかのチェックのようです。 以下のように/attestation/optionsでレスポンス生成時にpubKeyCredParamsのArrayの中身がObjectになっていれば問題なさそうです。

 ...
        pubKeyCredParams: [
            {
                type: "public-key",
                alg: -7
            }
        ]
 ...

(2) each member MUST contain "type" field of type DOMString pubKeyCredParamsに含まれるObjectがtypeという値を含むかどうかのチェックのようです。
以下のようにtyepを含めてレスポンスを返します。

 ...
        pubKeyCredParams: [
            {
                type: "public-key",
                ...
            }
        ]
 ...

(3) each member MUST contain "alg" field of type Number
pubKeyCredParamsに含まれるObjectがalgという値を含むかどうかのチェックのようです。
以下のように型に注意してalgを含めてレスポンスを返します。

 ...
        pubKeyCredParams: [
            {
                ...
                alg: -7
            }
        ]
 ...

(4) MUST contain one member with type set to "public-key" and alg set to an algorithm that is supported by the authenticator
pubKeyCredParamsに含まれるObjectのtypeがpublic-keyであるかどうか、algがサポートされているアルゴリズムかどうかのテストのようです。
以下のようなレスポンスを返すようにします。

 ...
        pubKeyCredParams: [
            {
                type: "public-key",
                alg: -7
            }
        ]
 ...

(g) If response contains "timeout" field, check that it's of type Number and is bigger than 0
もしレスポンスにtimeoutを含む場合のパラメーターに関するてチェックのようです。
そもそもオプションでtimeoutを返さなければ問題なさそうです。

(h) response contains "extensions" field, with "example.extension" key presented
extensionsに関するテストのようです。
このテストに関しては正直あまりよくわかっていないです。
該当するテストの/attestation/optionsに対するリクエストにextensionsのフィールドがありました。
結局 /attestation/optionsにリクエストにextensionsがあったらそのままオプションに含めて返す処理を追加しました。

   ... 
    challengeMakeCred.extensions = request.body.extensions;
   ...
    response.json(challengeMakeCred)
})

P-2 Request from server ServerPublicKeyCredentialCreationOptionsResponse with "none" attestation, and check that server, and check that ServerPublicKeyCredentialCreationOptionsResponse.attestation is set to "none"

該当するテストの/attestation/optionsに対するリクエストにattestationのフィールドがあり、そこに"none"が指定されていました。 リクエストでattestationが指定された場合はそれを優先的にレスポンスに含めてオプションを返すようにします。

   }
   ...

        attestation: request.body.attestation || 'direct',

    ...
    }

P-3 Get two ServerPublicKeyCredentialCreationOptionsResponses, and check that challenge in Request1 is different to challenge in Request2

ServerPublicKeyCredentialCreationOptionsResponsesのchallengeに関するテストのようです。
リクエストごとに毎回異なるchallengeを利用していれば問題なさそうです。

   if(clientData.challenge !== request.session.challenge) {
        response.json({
            'status': 'failed',
            'errorMessage': 'Challenges don\'t match!'
        })
    }

参考

https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-server-v2.0-rd-20180702.html