Skip to content

constructing WKD urls  #314

Description

@tomholub

part of #275

We'll need a way to construct WKD URLs with an email address as an input. Signature:

classWkd{staticfunconstructUrl(email: string,mode: ADVANCED|DIRECT): string}

This will follow the following spec: https://tools.ietf.org/html/draft-koch-openpgp-webkey-service-11 - this uses z-base-32 which is different from base-32, and may need to be implemented by hand (unless you can find a library somewhere for ObjC, Swift or C).

A few test cases:

Existing implementation - typescript (we used a library for z-base32)

constparts=email.split('@');if(parts.length!==2){returnnull;}const[user,recipientDomain]=parts;if(!user||!recipientDomain){returnnull;}constdirectDomain=recipientDomain.toLowerCase();constadvancedDomainPrefix=(directDomain==='localhost') ? '' : 'openpgpkey.';consthu=opgp.util.encodeZBase32(awaitopgp.crypto.hash.digest(opgp.enums.hash.sha1,Buf.fromUtfStr(user.toLowerCase())));constdirectHost=(typeofthis.port==='undefined') ? directDomain : `${directDomain}:${this.port}`;constadvancedHost=`${advancedDomainPrefix}${directHost}`;constuserPart=`hu/${hu}?l=${encodeURIComponent(user)}`;constadvancedUrl=`https://${advancedHost}/.well-known/openpgpkey/${directDomain}`;constdirectUrl=`https://${directHost}/.well-known/openpgpkey`;// ...

Existing implementation - python, where we had to implement zbase32 ourselves:

classWkd(PubkeySource):
# https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/?include_text=1# https://www.sektioneins.de/en/blog/18-11-23-gnupg-wkd.html# https://metacode.biz/openpgp/web-key-directory__TIMEOUT= (3, 3)
deffind_pubkey(self, email:str=None):
user, domain=self._email_as_user_and_private_domain(email)
ifuserisNoneordomainisNone:
returnNonehu=self.__sha1_zbase32(user)
url_advanced=f'https://openpgpkey.{domain}/.well-known/openpgpkey/{domain}/hu/{hu}?l={user}'url_direct=f'https://{domain}/.well-known/openpgpkey/hu/{hu}?l={user}'# ...def__sha1_zbase32(self, user: str):
defchunk_string(string, length):
return (string[0+i:length+i] foriinrange(0, len(string), length))
defchars_from_int(int32):
result= []
shift=27for_inrange(4):
result.append("ybndrfg8ejkmcpqxot1uwisza345h769"[(int32>>shift) &31])
shift=shift-5returnresulthashed_bytes=hashlib.sha1(user.encode()).digest()
bits_string=bin(int.from_bytes(hashed_bytes, byteorder="big"))[2:]
chunks_of_20_bits=chunk_string(bits_string, 20)
offset_chunks_of_20_bits= [chunk+'000000000000'forchunkinchunks_of_20_bits]
ints= [int(bits, 2) forbitsinoffset_chunks_of_20_bits]
groups_of_4_chars= [chars_from_int(int32) forint32inints]
return"".join(sum(groups_of_4_chars, []))

This is a pre-requisite for #296

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions