Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions libp2p/autotls/acme/api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ type ACMECertificateResponse* = object
rawCertificate*: string
certificateExpiry*: DateTime

type ACMECertificate* = object
rawCertificate*: string
certificateExpiry*: DateTime
certKeyPair*: KeyPair

when defined(libp2p_autotls_support):
import options, sequtils, strutils, jwt, bearssl/pem

Expand Down Expand Up @@ -448,11 +453,16 @@ when defined(libp2p_autotls_support):
return await self.checkChallengeCompleted(chalURL, key, kid, retries = retries)

proc requestFinalize*(
self: ACMEApi, domain: Domain, finalize: Uri, key: KeyPair, kid: Kid
self: ACMEApi,
domain: Domain,
finalize: Uri,
certKeyPair: KeyPair,
key: KeyPair,
kid: Kid,
): Future[ACMEFinalizeResponse] {.async: (raises: [ACMEError, CancelledError]).} =
handleError("requestFinalize"):
let payload = await self.createSignedAcmeRequest(
finalize, %*{"csr": createCSR(domain)}, key, kid = Opt.some(kid)
finalize, %*{"csr": createCSR(domain, certKeyPair)}, key, kid = Opt.some(kid)
)
let acmeResponse = await self.post(finalize, payload)
# server responds with updated order response
Expand Down Expand Up @@ -484,11 +494,13 @@ when defined(libp2p_autotls_support):
domain: Domain,
finalize: Uri,
order: Uri,
certKeyPair: KeyPair,
key: KeyPair,
kid: Kid,
retries: int = DefaultFinalizeRetries,
): Future[bool] {.async: (raises: [ACMEError, CancelledError]).} =
let finalizeResponse = await self.requestFinalize(domain, finalize, key, kid)
let finalizeResponse =
await self.requestFinalize(domain, finalize, certKeyPair, key, kid)
# keep checking order until cert is valid (done)
return await self.checkCertFinalized(order, key, kid, retries = retries)

Expand Down
15 changes: 9 additions & 6 deletions libp2p/autotls/acme/client.nim
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,20 @@ when defined(libp2p_autotls_support):
await self.api.requestChallenge(domains, self.key, await self.getOrInitKid())

proc getCertificate*(
self: ACMEClient, domain: api.Domain, challenge: ACMEChallengeResponseWrapper
self: ACMEClient,
domain: api.Domain,
certKeyPair: KeyPair,
challenge: ACMEChallengeResponseWrapper,
): Future[ACMECertificateResponse] {.async: (raises: [ACMEError, CancelledError]).} =
let chalURL = parseUri(challenge.dns01.url)
let orderURL = parseUri(challenge.order)
let finalizeURL = parseUri(challenge.finalize)
trace "sending challenge completed notification"
trace "Sending challenge completed notification"
discard await self.api.sendChallengeCompleted(
chalURL, self.key, await self.getOrInitKid()
)

trace "checking for completed challenge"
trace "Checking for completed challenge"
let completed = await self.api.checkChallengeCompleted(
chalURL, self.key, await self.getOrInitKid()
)
Expand All @@ -80,15 +83,15 @@ when defined(libp2p_autotls_support):
ACMEError, "Failed to signal ACME server about challenge completion"
)

trace "waiting for certificate to be finalized"
trace "Waiting for certificate to be finalized"
let finalized = await self.api.certificateFinalized(
domain, finalizeURL, orderURL, self.key, await self.getOrInitKid()
domain, finalizeURL, orderURL, certKeyPair, self.key, await self.getOrInitKid()
)
if not finalized:
raise
newException(ACMEError, "Failed to finalize certificate for domain " & domain)

trace "downloading certificate"
trace "Downloading certificate"
await self.api.downloadCertificate(orderURL)

proc close*(self: ACMEClient) {.async: (raises: [CancelledError]).} =
Expand Down
29 changes: 15 additions & 14 deletions libp2p/autotls/acme/mockapi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,20 @@ proc new*(
acmeServerURL: parseUri(LetsEncryptURL),
)

method requestNonce*(
self: MockACMEApi
): Future[Nonce] {.async: (raises: [ACMEError, CancelledError]).} =
return $self.acmeServerURL & "/acme/1234"
when defined(libp2p_autotls_support):
method requestNonce*(
self: MockACMEApi
): Future[Nonce] {.async: (raises: [ACMEError, CancelledError]).} =
return $self.acmeServerURL & "/acme/1234"

method post*(
self: MockACMEApi, uri: Uri, payload: string
): Future[HTTPResponse] {.async: (raises: [ACMEError, HttpError, CancelledError]).} =
result = self.mockedResponses[0]
self.mockedResponses.delete(0)
method post*(
self: MockACMEApi, uri: Uri, payload: string
): Future[HTTPResponse] {.async: (raises: [ACMEError, HttpError, CancelledError]).} =
result = self.mockedResponses[0]
self.mockedResponses.delete(0)

method get*(
self: MockACMEApi, uri: Uri
): Future[HTTPResponse] {.async: (raises: [ACMEError, HttpError, CancelledError]).} =
result = self.mockedResponses[0]
self.mockedResponses.delete(0)
method get*(
self: MockACMEApi, uri: Uri
): Future[HTTPResponse] {.async: (raises: [ACMEError, HttpError, CancelledError]).} =
result = self.mockedResponses[0]
self.mockedResponses.delete(0)
18 changes: 10 additions & 8 deletions libp2p/autotls/acme/utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,21 @@ when defined(libp2p_autotls_support):
ACMEError, "Unexpected error occurred while getting body bytes", exc
)

proc createCSR*(domain: string): string {.raises: [ACMEError].} =
proc createCSR*(
domain: string, certKeyPair: KeyPair
): string {.raises: [ACMEError].} =
var certKey: cert_key_t
var certCtx: cert_context_t
var derCSR: ptr cert_buffer = nil

let personalizationStr = "libp2p_autotls"
if cert_init_drbg(
personalizationStr.cstring, personalizationStr.len.csize_t, certCtx.addr
) != CERT_SUCCESS:
raise newException(ACMEError, "Failed to initialize certCtx")
if cert_generate_key(certCtx, certKey.addr) != CERT_SUCCESS:
raise newException(ACMEError, "Failed to generate cert key")
# convert KeyPair to cert_key_t
let rawSeckey: seq[byte] = certKeyPair.seckey.getRawBytes.valueOr:
raise newException(ACMEError, "Failed to get seckey raw bytes (DER)")
let seckeyBuffer = rawSeckey.toCertBuffer()
if cert_new_key_t(seckeyBuffer.unsafeAddr, certKey.addr) != CERT_SUCCESS:
raise newException(ACMEError, "Failed to convert key pair to cert_key_t")

# create CSR
if cert_signing_req(domain.cstring, certKey, derCSR.addr) != CERT_SUCCESS:
raise newException(ACMEError, "Failed to create CSR")

Expand Down
33 changes: 33 additions & 0 deletions libp2p/autotls/mockservice.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
when defined(libp2p_autotls_support):
import ./service, ./acme/client, ../peeridauth/client

import ../crypto/crypto, ../crypto/rsa, websock/websock

type MockAutotlsService* = ref object of AutotlsService
mockedCert*: TLSCertificate
mockedKey*: TLSPrivateKey

proc new*(
T: typedesc[MockAutotlsService],
rng: ref HmacDrbgContext = newRng(),
config: AutotlsConfig = AutotlsConfig.new(),
): T =
T(
acmeClient:
ACMEClient.new(api = ACMEApi.new(acmeServerURL = config.acmeServerURL)),
brokerClient: PeerIDAuthClient.new(),
bearer: Opt.none(BearerToken),
cert: Opt.none(AutotlsCert),
certReady: newAsyncEvent(),
running: newAsyncEvent(),
config: config,
rng: rng,
)

method getCertWhenReady*(
self: MockAutotlsService
): Future[AutotlsCert] {.async: (raises: [AutoTLSError, CancelledError]).} =
AutotlsCert.new(self.mockedCert, self.mockedKey, Moment.now)

method setup*(self: MockAutotlsService) {.base, async.} =
self.running.fire()
Loading
Loading