SM-DP+
The SM-DP+ is the server half of a consumer profile download. It holds the profile inventory, turns an operator’s order into an activation code, proves its identity to an eUICC, and hands over a profile that only that eUICC can install.
It exposes two interfaces that could not differ more in who is allowed to call them, on one hostname, in front of one Lambda. That asymmetry is the whole architecture, so it is the first picture.
Source: diagrams/smdp-plus/architecture.drawio. Paths, the
AWS_IAM Function URL and the guard behaviour are taken from
AminSM-DPplus-lambda/template.yml.
Who may call what
| Interface | Caller | Authentication | Enforced by |
|---|---|---|---|
ES9+ /gsma/rsp2/es9plus/* | an LPA on a device | none, deliberately | nothing — the edge passes it through |
ES2+ /gsma/rsp2/es2plus/* | the operator’s BSS | refused at the edge | Es2PlusGuard, a CloudFront Function |
| ES2+ via the console | a signed-in operator | OIDC token, then a role check | API Gateway → shared authorizer → console BFF |
Two of those rows surprise people, so both are worth stating plainly.
ES9+ is unauthenticated on purpose. An LPA sitting in a handset has no credential to present to a server it has never met — mutual authentication happens inside the protocol, between the eUICC and the SM-DP+ certificates, not around it in a transport header. Requiring a token here would make the interface unusable by the only client it exists for.
ES2+ is refused at the edge rather than secured. In a real deployment ES2+ sits behind mutually authenticated TLS between operator and SM-DP+. This implementation has no such relationship to offer, so instead of pretending, the distribution rejects the path outright:
function handler(event) { var uri = event.request.uri.toLowerCase(); if (uri.indexOf('/gsma/rsp2/es2plus') === 0) { return { statusCode: 403, statusDescription: 'Forbidden', headers: { 'content-type': { value: 'application/json' }, 'cache-control': { value: 'no-store' } }, body: '{"error":"forbidden"}' }; } return event.request;}The guard exists because of a real failure, not a hypothetical one. The
distribution originally forwarded every path to the function, which meant
downloadOrder and confirmOrder were callable by anyone who knew the
hostname — a stranger could create orders in the inventory and mint working
activation codes for them. The template records it being verified live on
2026-08-25, and notes that this is why the inventory filled with ICCIDs nobody
ordered.
The sanctioned route to ES2+ is now the console: a signed-in operator, a token checked by the shared authorizer, a role check, and then a direct Lambda invoke that never traverses the public hostname at all.
Interfaces
- ES9+ — profile download — the three calls that move a profile from the inventory onto a card, with the exact request and response fields.
Where it runs
| Hostname | smdpplus.rsplab.click |
| Repository | AminSM-DPplus-lambda (private) |
| Stack | amin-smdpp-dev, us-east-1 |
| Front door | CloudFront → Lambda Function URL, AuthType: AWS_IAM |
| Console | smdpplus.rsplab.click/console (AminSmDpPlusAdmin) |
| Specifications | GSMA SGP.22, interfaces ES2+, ES8+, ES9+ |