Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
fido2-net-lib https://github.com/abergs/fido2-net-lib を分析している (2019/03/03) 。
{
"rp": {
"id": "localhost",
"name": "Fido2 test"
},
"user": {
"name": "aaa@example.com",
"id": "YWFhQGV4YW1wbGUuY29t",
"displayName": "Display aaa@example.com"
},
"challenge": "U8iy6iV6nyXjWph2he3KwA",
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -257
},
{
"type": "public-key",
"alg": -37
},
{
"type": "public-key",
"alg": -35
},
{
"type": "public-key",
"alg": -258
},
{
"type": "public-key",
"alg": -38
},
{
"type": "public-key",
"alg": -36
},
{
"type": "public-key",
"alg": -259
},
{
"type": "public-key",
"alg": -39
},
{
"type": "public-key",
"alg": -65535
}
],
"timeout": 60000,
"attestation": "none",
"authenticatorSelection": {
"requireResidentKey": false,
"userVerification": "preferred"
},
"excludeCredentials": [],
"extensions": {
"exts": true,
"uvi": true,
"loc": true,
"uvm": true,
"biometricPerfBounds": {
"FAR": 3.40282347e+38,
"FRR": 3.40282347e+38
}
},
"status": "ok",
"errorMessage": ""
}
{
"rp": {
"id": "localhost",
"name": "Fido2 test"
},
"user": {
"name": "aaa@example.com",
"id": {ArrayBuffer(15)},
"displayName": "Display aaa@example.com"
},
"challenge": {ArrayBuffer(16)},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -257
},
{
"type": "public-key",
"alg": -37
},
{
"type": "public-key",
"alg": -35
},
{
"type": "public-key",
"alg": -258
},
{
"type": "public-key",
"alg": -38
},
{
"type": "public-key",
"alg": -36
},
{
"type": "public-key",
"alg": -259
},
{
"type": "public-key",
"alg": -39
},
{
"type": "public-key",
"alg": -65535
}
],
"timeout": 60000,
"attestation": "none",
"authenticatorSelection": {
"requireResidentKey": false,
"userVerification": "preferred"
},
"excludeCredentials": [],
"extensions": {
"exts": true,
"uvi": true,
"loc": true,
"uvm": true,
"biometricPerfBounds": {
"FAR": 3.40282347e+38,
"FRR": 3.40282347e+38
}
},
"status": "ok",
"errorMessage": ""
}
{
"id": "XgQL5lEqNiwJjR20PbJ2jeohu_C9IU8a03umfcXaHpF8qz_7oZx9bPgViOMWm7J7RaZYCo_oglDbDenGfwd5IA",
"rawId": "ArrayBuffer(64)",
"response(AuthenticatorAttestationResponse)": {
"attestationObject": "ArrayBuffer(226)",
"clientDataJSON": "ArrayBuffer(98)"
},
"type": "public-key",
}
{
"id": "XgQL5lEqNiwJjR20PbJ2jeohu_C9IU8a03umfcXaHpF8qz_7oZx9bPgViOMWm7J7RaZYCo_oglDbDenGfwd5IA",
"rawId": "XgQL5lEqNiwJjR20PbJ2jeohu_C9IU8a03umfcXaHpF8qz_7oZx9bPgViOMWm7J7RaZYCo_oglDbDenGfwd5IA",
"type": "public-key",
"extensions": {},
"response": {
"AttestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjESZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NBAAAAOwAAAAAAAAAAAAAAAAAAAAAAQF4EC-ZRKjYsCY0dtD2ydo3qIbvwvSFPGtN7pn3F2h6RfKs_-6GcfWz4FYjjFpuye0WmWAqP6IJQ2w3pxn8HeSClAQIDJiABIVggZYp4zvvIsAa45MBTfKoDArfr9G4JslQzHNBQiHoiIEQiWCA7Cs8aAvbCGX6IjPOf0zV8E7ynD-01sKg8boDbr4kLRQ",
"clientDataJson": "eyJjaGFsbGVuZ2UiOiJVOGl5NmlWNm55WGpXcGgyaGUzS3dBIiwib3JpZ2luIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMjkiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0"
}
}
{
"result": {
"publicKey": "pQECAyYgASFYIGWKeM77yLAGuOTAU3yqAwK36_RuCbJUMxzQUIh6IiBEIlggOwrPGgL2whl-iIzzn9M1fBO8pw_tNbCoPG6A26-JC0U",
"user": {
"name": "aaa@example.com",
"id": "YWFhQGV4YW1wbGUuY29t",
"displayName": "Display aaa@example.com"
},
"credType": "none",
"aaguid": "00000000-0000-0000-0000-000000000000",
"credentialId": "XgQL5lEqNiwJjR20PbJ2jeohu/C9IU8a03umfcXaHpF8qz/7oZx9bPgViOMWm7J7RaZYCo/oglDbDenGfwd5IA==",
"counter": 59,
"status": null,
"errorMessage": null
},
"status": "ok",
"errorMessage": ""
}
aaa@example.com (FormData)
{
"challenge": "w9PA4LdyXmYeYgC12J6_6Q",
"timeout": 60000,
"rpId": "localhost",
"allowCredentials": [
{
"type": "public-key",
"id": "XgQL5lEqNiwJjR20PbJ2jeohu_C9IU8a03umfcXaHpF8qz_7oZx9bPgViOMWm7J7RaZYCo_oglDbDenGfwd5IA"
}
],
"userVerification": "discouraged",
"extensions": {
"appid": "https://localhost:44329",
"txAuthSimple": "FIDO",
"txAuthGenericArg": {
"contentType": "text/plain",
"content": "RklETw=="
},
"uvi": true,
"loc": true,
"uvm": true
},
"status": "ok",
"errorMessage": ""
}
{
"challenge": {
"0": 195,
"1": 211,
"2": 192,
"3": 224,
"4": 183,
"5": 114,
"6": 94,
"7": 102,
"8": 30,
"9": 98,
"10": 0,
"11": 181,
"12": 216,
"13": 158,
"14": 191,
"15": 233
},
"timeout": 60000,
"rpId": "localhost",
"allowCredentials": [
{
"type": "public-key",
"id": {
"0": 94,
"1": 4,
"2": 11,
"3": 230,
"4": 81,
"5": 42,
"6": 54,
"7": 44,
"8": 9,
"9": 141,
"10": 29,
"11": 180,
"12": 61,
"13": 178,
"14": 118,
"15": 141,
"16": 234,
"17": 33,
"18": 187,
"19": 240,
"20": 189,
"21": 33,
"22": 79,
"23": 26,
"24": 211,
"25": 123,
"26": 166,
"27": 125,
"28": 197,
"29": 218,
"30": 30,
"31": 145,
"32": 124,
"33": 171,
"34": 63,
"35": 251,
"36": 161,
"37": 156,
"38": 125,
"39": 108,
"40": 248,
"41": 21,
"42": 136,
"43": 227,
"44": 22,
"45": 155,
"46": 178,
"47": 123,
"48": 69,
"49": 166,
"50": 88,
"51": 10,
"52": 143,
"53": 232,
"54": 130,
"55": 80,
"56": 219,
"57": 13,
"58": 233,
"59": 198,
"60": 127,
"61": 7,
"62": 121,
"63": 32
}
}
],
"userVerification": "discouraged",
"extensions": {
"appid": "https://localhost:44329",
"txAuthSimple": "FIDO",
"txAuthGenericArg": {
"contentType": "text/plain",
"content": "RklETw=="
},
"uvi": true,
"loc": true,
"uvm": true
},
"status": "ok",
"errorMessage": ""
}
{
"id": "XgQL5lEqNiwJjR20PbJ2jeohu_C9IU8a03umfcXaHpF8qz_7oZx9bPgViOMWm7J7RaZYCo_oglDbDenGfwd5IA",
"rawId": "ArrayBuffer(64) {}",
"response(AuthenticatorAssertionResponse)": {
"authenticatorData": "ArrayBuffer(37) {}",
"clientDataJSON": "ArrayBuffer(202) {}",
"signature": "ArrayBuffer(71) {}",
"userHandle": "ArrayBuffer(0) {}",
},
"type": "public-key",
}
{
"id": "XgQL5lEqNiwJjR20PbJ2jeohu_C9IU8a03umfcXaHpF8qz_7oZx9bPgViOMWm7J7RaZYCo_oglDbDenGfwd5IA",
"rawId": "XgQL5lEqNiwJjR20PbJ2jeohu_C9IU8a03umfcXaHpF8qz_7oZx9bPgViOMWm7J7RaZYCo_oglDbDenGfwd5IA",
"type": "public-key",
"extensions": {
"appid": false
},
"response": {
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAPQ",
"clientDataJson": "eyJjaGFsbGVuZ2UiOiJ3OVBBNExkeVhtWWVZZ0MxMko2XzZRIiwibmV3X2tleXNfbWF5X2JlX2FkZGVkX2hlcmUiOiJkbyBub3QgY29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuIFNlZSBodHRwczovL2dvby5nbC95YWJQZXgiLCJvcmlnaW4iOiJodHRwczovL2xvY2FsaG9zdDo0NDMyOSIsInR5cGUiOiJ3ZWJhdXRobi5nZXQifQ",
"signature": "MEUCIAOzC-3dbiDcyZtlIEP39EOctwQNe8XMXNtqKs71fOi3AiEAsOiKJr_qF7oCsDuYlAYdaN_nDot1EAAAwykbF-B8aXg"
}
}
{
"credentialId": "XgQL5lEqNiwJjR20PbJ2jeohu/C9IU8a03umfcXaHpF8qz/7oZx9bPgViOMWm7J7RaZYCo/oglDbDenGfwd5IA==",
"counter": 61,
"status": "ok",
"errorMessage": ""
}
@daisukenishino2

This comment has been minimized.

Copy link
Owner Author

commented Mar 3, 2019

追加の情報 (2019/03/01)

テスト画面に色々追加がされているのでそれぞれのパラメタがなんなのか?纏めた。
image

navigator.credentials.create() の param

  "attestation": "none",
  "authenticatorSelection":
  {
    "authenticatorAttachment":"platform",
    "userVerification":"discouraged",
    "requireResidentKey":false
  },

など、詳しくはコチラを参照。

Attestation Type

  • Authenticatorの出どころを検証するかどうか。
  • attestation に以下の様に以下の値を設定する ( enum AttestationConveyancePreference )。
    • None : "none"
    • Indirect : "indirect"
    • Direct : "direct"
 "attestation": "direct",

Authenticator Type

  • 優先Authenticator 接続方式を指定できる。
  • authenticatorSelection -> authenticatorAttachment に
    以下の様に以下の値を設定する ( enum AuthenticatorAttachment )。
    • Not specified : ""
    • Cross Platform (Token) : "cross-platform"
    • Platform (TPM) : "platform"
authenticatorSelection = { "authenticatorAttachment":"platform" }

Resident Credentials

  • FIDO2.0 から新しく追加された機能で、要はキーの中にユーザ情報を保存できる。
  • PublicKeyCredentialUserEntity と Credential ID を保存し、以降のログインで利用できる。
  • authenticatorSelection -> requireResidentKey に以下の様に設定する。
authenticatorSelection = { "requireResidentKey": true }

navigator.credentials.get() の param

 "userVerification": "discouraged"

など、詳しくはコチラを参照。

User Verification

  • ユーザー検証要件を指定できる。
  • userVerification に以下の様に以下の値を設定する ( enum UserVerificationRequirement )。
    • Required : "required"
    • Preferred : "preferred"
    • Discouraged : "discouraged"
 "userVerification": "discouraged"
@daisukenishino2

This comment has been minimized.

Copy link
Owner Author

commented Mar 6, 2019

ちょっと疑問(ArrayBuffer と Uint8Array)

概要

2. Parameter of navigator.credentials.create()の直前で、

  • $.challenge
  • $.allowCredentials.id

を、ArrayBufferではなく、Uint8Arrayに変換しているが、
Uint8ArrayだとJSON.stringify() したとき、少々、冗長な出方になる。

確認

このため、webauthn.jsに定義されている、coerceToArrayBuffer関数を使用するように処理を変更して、問題なく動作するか、動作検証してみることにした。

コードの修正

            const challenge = makeAssertionOptions.challenge.replace(/-/g, "+").replace(/_/g, "/");
            makeAssertionOptions.challenge = Uint8Array.from(atob(challenge), c => c.charCodeAt(0));

            makeAssertionOptions.allowCredentials.forEach(function (listItem) {
                var fixedId = listItem.id.replace(/\_/g, "/").replace(/\-/g, "+");
                listItem.id = Uint8Array.from(atob(fixedId), c => c.charCodeAt(0));
            });

↓ ↓ ↓

            const challenge = makeAssertionOptions.challenge.replace(/-/g, "+").replace(/_/g, "/");
            //makeAssertionOptions.challenge = Uint8Array.from(atob(challenge), c => c.charCodeAt(0));
            makeAssertionOptions.challenge = coerceToArrayBuffer(challenge);

            makeAssertionOptions.allowCredentials.forEach(function (listItem) {
                var fixedId = listItem.id.replace(/\_/g, "/").replace(/\-/g, "+");
                //listItem.id = Uint8Array.from(atob(fixedId), c => c.charCodeAt(0));
                listItem.id = coerceToArrayBuffer(fixedId);
            });

出力の確認

適切に動作し、出力も以下の様になった。

{
	"challenge": {ArrayBuffer(16)},
	"timeout": 60000,
	"rpId": "localhost",
	"allowCredentials": [
		{
			"type": "public-key",
			"id": {ArrayBuffer(64)}
		}
	],
	"userVerification": "discouraged",
	"extensions": {
		"appid": "https://localhost:44329",
		"txAuthSimple": "FIDO",
		"txAuthGenericArg": {
			"contentType": "text/plain",
			"content": "RklETw=="
		},
		"uvi": true,
		"loc": true,
		"uvm": true
	},
	"status": "ok",
	"errorMessage": ""
}

結論

Credential Management API(navigator.credentials...)のBufferSourceの引数は、

  • Uint8Arrayでも、
  • Uint8Array.bufferの返すArrayBufferでも、

どちらでも適切に動作するもよう。

因みに、BufferSourceとは、

4.2. BufferSource
typedef (ArrayBufferView or ArrayBuffer) BufferSource;

...良く解らん。

@daisukenishino2

This comment has been minimized.

Copy link
Owner Author

commented Mar 15, 2019

ちょっと疑問(2つ以上のカギの登録)

概要

fido2-net-libのストア設計を見ると、
公開鍵が複数登録可能に見えるが本当か?

確認

重複登録

認証器を重複して登録仕様とすると、
FIDO2Serverは、excludeCredentialsに以下の様に値を設定する。

"excludeCredentials": [
    {
        "type": "public-key",
        "id": "xzJdD_Fl8_MlGjM00owaGLIdAklEnNktohB5wuaylsWlzAxbuOcIks7eXN4yVFy4or1ZyVKCgEAv6iSGNqOJAg"
    }
],

例外発生

この公開鍵は、当該認証器が生成したものであると判別できるようで、Chromeのnavigator.credentials.create()メソッドから「The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.」というメッセージが返される。
image

結論

異なる認証器を使用すれば、
2つ以上のカギを登録可能であるため、
削除処理はリスト形式にする必要がある。

※ ただし、Googleなんかは1つしか登録できない(Microsoftは不明)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.