openlang

NFC lookup keys and JCS signatures

Signing complicates kim_dh's earlier question about NFC lookup keys, which separates the submitted name from an NFC lookup key. A JSON payload holds a name spelled as e followed by U+0301, and that string goes into a JCS-based signature.

RFC 8785 keeps parsed Unicode string data as is, with no normalization. JCS still serializes the JSON canonically, so what gets signed is not simply the original source bytes. Swap that name for U+00E9 before canonicalization and the string data the protocol preserves has changed.

My plan is to leave the payload values handed to JCS untouched and derive the NFC lookup key into its own application field. One more representation to carry, but the lookup transformation stays visible. Two payloads can then share an application key while keeping different Unicode string data for JCS.

What I haven't worked out is where that derivation should live so lookup code can't quietly put its key into the payload going to JCS.

后续讨论 4

顺着彼此的依据读下去
  1. 固定链接

    Put the derivation in a constructor and make it the only way to get a lookup key. A LookupKey type whose only factory takes the original name and stores its NFC form means lookup code holds a LookupKey, never a bare string, and the payload type has no field of that type at all. In C# that's a record with a private constructor and a static From that calls Normalize(NormalizationForm.FormC). It won't stop someone calling Normalize by hand on a payload value, but that line stands out in review.

  2. 固定链接

    From the form side, it goes wrong where both strings leave together. In my lookup-key thread the submit sent the name as typed and the NFC key side by side in one object. Then the code that builds the signed payload grabs nameKey instead of name, because autocomplete offered it first. The screen still shows the same name. JCS gets U+00E9, the signature covers that, and it's a perfectly valid signature over a spelling the user never typed.

    Since then lookup derives its own key from the typed text, so the form doesn't send nameKey at all anymore. That's my answer to where it lives. On the server, after the signature check, and nowhere near code that builds a payload.

  3. 固定链接

    Went with ordering instead of a type. The handler parses the body, verifies the JCS signature over the parsed value, and only then calls deriveLookupKey(payload.name). Until verification the payload is typed unknown, so there's nothing for lookup code to write into, and after verification nothing writes back to it.

    JSON.parse keeps e\u0301 as two code points, so the string JCS canonicalizes is the one the client sent. ed_fern's key type would still be nice inside the lookup module. I haven't added it, because the ordering already covers the mix-up I was worried about.

同一个问题的其他角度

  1. Is an NFC key a sensible default for name lookup?

    Okay, a user pastes their name and it ends in e followed by U+0301. Later they type the same name and the keyboard gives U+00…

    回复 6最近活动 typescriptjavascript
  2. Mode line columns drift as soon as a buffer name has Hangul in it

    Padded the buffer name in my mode line to a fixed width so the bits after it line up, and it worked for a long while (well, i…

    回复 1最近活动 emacs-lisp
  3. Truncating by bytes like the old C++, and Rust panics

    Porting a chunk of old C++ to Rust that cuts a std::string down to a byte limit with a plain s.substr(0, n), and I figured it…

    回复 6最近活动 rustcpp