Check S/MIME signatures, and remember who signed
A signed message now says whether that holds up, as it is read. This is
verification only: nothing here signs, encrypts or decrypts, and the
private-key question that blocks those is untouched. Verifying needed
none of it, because the certificate travels inside the message -- which
is why this is the half that could be built.
What it checks. For multipart/signed carrying PKCS#7, the exact bytes of
the signed part -- headers included, canonicalised to CRLF -- are hashed
against the messageDigest attribute, and the signature over the signed
attributes is verified with WebCrypto against the certificate inside the
message. RSA PKCS#1 v1.5 and ECDSA over P-256/384/521, with SHA-256, 384
or 512.
The trust model is the design, and it is deliberately small. A browser
has no system trust store, and the certificate arrives inside the
message, so anyone can self-sign as anyone: on its own a good signature
shows only that the sender held the key they attached. So the word
"verified" is never rendered, and the reassuring case is not the loud
one. What carries the weight is remembering -- the first signed message
from an address pins its fingerprint, later ones are compared, and a
signer that changed is reported with both names and told to check by
another route. Trust on first use, no certificate authority anywhere.
The pins live in the account's settings rather than the browser: one
that only a single device knew would greet the same correspondent as new
everywhere else, which is how people are trained to click past the one
warning that matters. A pin records the message that created it, so the
message that established a signer keeps saying so instead of appearing
to be corroborated by itself -- without that, the very first signed
message anybody receives reads as "the same signer as before", where
before is itself. A changed, mismatched or expired signer is never
pinned, since writing the anomaly into the baseline makes every later
message agree with it.
Three things are declined rather than attempted, and all three say
"could not check" rather than "does not check out", because ignorance
and an accusation are different claims:
- OpenPGP, by name. The signature carries no key and there is nowhere
to get the sender's: x:PublicKey is the account's OWN registry, and
a keyserver or WKD lookup would tell a third party who you
correspond with -- the leak the image proxy exists to close.
- SHA-1. Not forgeable in practice today, still not something to put a
tick beside.
- RSA-PSS, whose salt length lives in parameters this does not read.
Guessing wrong would report a good signature as bad.
Nothing validates a chain: no CA bundle is shipped and revocation is not
checked. "Issued by" reports what the certificate claims, and a
self-signed one claims itself.
The DER, CMS, X.509 and MIME readers are hand-written and deliberately
narrow -- no new dependency, and the whole verifier is a lazily imported
8.6 kB chunk that a reader of unsigned mail never downloads. The one
place this is easy to get quietly wrong has its own function and its own
test: signed attributes are signed as a SET OF, not as the [0] IMPLICIT
they arrive as, and hashing the message instead would make every
signature "pass".
Tested against real `openssl smime -sign` output rather than hand-built
fixtures -- RSA, ECDSA, a tampered copy, and a valid signature by a
certificate for somebody else -- because a signed message written by
hand only agrees with whatever its author believed the format to be.
Also driven in a browser against the mock, which now serves three real
signed messages so every branch of the banner is reachable.
Translations: 34 new strings in all nine catalogues, 306 entries.
Falling back to English is unchanged at 24 per language.
This commit is contained in:
@@ -1311,6 +1311,41 @@ export const catalog: Catalog = {
|
||||
"Empty “{name}”?": "清空“{name}”?",
|
||||
"Delete them": "删除",
|
||||
"Nothing was deleted": "未删除任何内容",
|
||||
// ── Checking an S/MIME signature, and what may be said about it ──
|
||||
"Certificate covers": "证书适用于",
|
||||
"Details": "详情",
|
||||
"Earlier messages from this address were signed by {previous}. This one is signed by {current}.": "此地址以前的邮件由 {previous} 签名,而这封由 {current} 签名。",
|
||||
"Fingerprint": "指纹",
|
||||
"Hide details": "隐藏详情",
|
||||
"Issued by": "颁发者",
|
||||
"It is signed with OpenPGP, and ihasmail has no way to fetch the sender's public key.": "该邮件使用 OpenPGP 签名,而 ihasmail 无法获取发件人的公钥。",
|
||||
"It uses a signature algorithm ihasmail cannot check yet.": "它使用了 ihasmail 尚不能校验的签名算法。",
|
||||
"It was made with a certificate belonging to {name}, which does not cover this address.": "签名使用的是 {name} 的证书,该证书并不包含此地址。",
|
||||
"Previous fingerprint": "以前的指纹",
|
||||
"Signed at": "签名时间",
|
||||
"Signed by {name} — the same signer as before.": "由 {name} 签名——与此前是同一签名者。",
|
||||
"Signed by {name}, seen here for the first time.": "由 {name} 签名,这是首次在此见到。",
|
||||
"Signer": "签名者",
|
||||
"That can mean a renewed certificate, and it can mean somebody else. Check with them by some other route before trusting it.": "这可能只是更换了证书,也可能是另一个人。信任之前,请通过其他途径向对方核实。",
|
||||
"The certificate has expired.": "证书已过期。",
|
||||
"The certificate is not valid yet.": "证书尚未生效。",
|
||||
"The message does not match what was signed — it was altered after signing, or damaged on the way.": "邮件与所签名的内容不一致——它在签名后被改动,或在传输中损坏。",
|
||||
"The signature carries no certificate that can be read.": "签名中没有可读取的证书。",
|
||||
"The signature could not be read.": "无法读取签名。",
|
||||
"The signature does not match the certificate sent with it.": "签名与随附的证书不匹配。",
|
||||
"The signature is not for this sender.": "该签名不属于此发件人。",
|
||||
"The signed part is missing either the message or the signature.": "签名部分缺少邮件正文或签名。",
|
||||
"The signer has changed.": "签名者已更换。",
|
||||
"This message is signed, and ihasmail could not check the signature.": "此邮件带有签名,但 ihasmail 无法校验该签名。",
|
||||
"This signature does not check out.": "此签名不成立。",
|
||||
"Valid until": "有效期至",
|
||||
"a different certificate": "另一份证书",
|
||||
"an unnamed signer": "未具名的签名者",
|
||||
"as claimed by the signer": "据签名者声称",
|
||||
"first seen {date}": "首次见于 {date}",
|
||||
"ihasmail will tell you if a later message from this address is signed by anybody else.": "如果此地址之后的邮件由他人签名,ihasmail 会提醒您。",
|
||||
"itself, or an issuer it does not name": "其自身,或一个未具名的颁发者",
|
||||
"no address": "无地址",
|
||||
},
|
||||
plurals: {
|
||||
// ── Third pass ─────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user