Full Authenticated Connections Protocol Specification
For more information about the ads.cert 2.0 suite of protocols, visit https://iabtechlab.com/ads-cert
Contributing? Please review the IAB Tech Lab Open Source Initiative Governance guidelines here.
Report bugs, request features and suggest improvements on Github
Or open up a pull request
ads.cert
Authenticated Connections Protocol
This is a proof of concept and not meant for use in its current form.
This open-source library implements the Authenticated Connections protocol, published by the IAB Tech Lab, to enable advertising industry participants to secure programmatic ad buying and selling using industry-standard cryptographic security protocols.
Authenticated Connections uses a standardized HTTP request header containing a signature that secures the:
- URL being invoked
- Body of POST requests
- Timestamp, origin, and destination values
The signature over these elements authenticates that the request came from the claimed originator, and that its contents haven't been tampered with.
Example request header:
X-Ads-Cert-Auth:
from=adscerttestsigner.dev&from_key=LxqTmA&invoking=adscerttestverifier.dev&nonce=6Rpf4qD2LP_9&status=1×tamp=220912T200513&to=adscerttestverifier.dev&to_key=uNzTFA; sigb=OcQzM62rkJk0&sigu=_44H63NN69Nb"
Field |
Description |
from |
The domain of the party sending the request |
from_key |
The first 6 characters of the sending party’s public key |
invoking |
The domain for the URL hostname being invoked |
nonce |
Randomly generated number from the sending party in base64 encoded. |
timestamp |
The time of generating signature in format: YYMMDDTHHMMSS. This should be in UTC. |
to |
The domain of the party receiving the signature |
to_key |
The first 6 characters of the receiving party’s public key |
sigb |
The signature over the message and body of the request |
sigu |
The signature over the message, body, and URL of the request |
Architecture
- Signatory- Main library that handles signing and verification operations.
- Dns Resolver - Discovers TXT records for a domain name (with the appropriate subdomains for policy and key records)
- Domain Store - Stores domain information for invoking and identity domains and the associated keys
- Domain Indexer - Uses the above components to maintain a list of domains used in signing/verification operations, runs background crawls to update domain records, stores private keys, calculates shared secrets for use by the Signatory.
API Usage
The Signatory
is available as a gRPC server with ads.cert signing/verification capabilities.
See the GRPC/Protobuf interface for the full API details. The main function defintions are below:
rpc SignAuthenticatedConnection(AuthenticatedConnectionSignatureRequest) returns (AuthenticatedConnectionSignatureResponse) {}
rpc VerifyAuthenticatedConnection(AuthenticatedConnectionVerificationRequest) returns (AuthenticatedConnectionVerificationResponse) {}
Clients are available for various langauges:
Commands are also provided to test the signing and verification functionality; details for running them is included below, see "Examples"
Example Domains
Two domains, hosted by the tech lab, are availible for testing the signing and verification process:
-
adscerttestsigner.dev
-
Public key: LxqTmAIw8Beujvf42ni9V7r1wpVPPxtrD5nFRxlwy0U
-
Private key: Ys83NKuuYxCVDUbmA671x3zAFsQ-EnNxmC2JLuBlGAU
-
adscerttestverifier.dev
-
Public key: uNzTFA2_QsCcxsVET8q-IDtEaDn_D3Q6xscev1TFsjc
-
Private key: 6mkLbsTBKs0UwYLkBdw5ttJHzjpSZxof0A2rako-0qs
DNS records containing a public key have been published for each.
_delivery._adscert.adscerttestsigner.dev. 3600 IN TXT "v=adcrtd k=x25519 h=sha256 p=LxqTmAIw8Beujvf42ni9V7r1wpVPPxtrD5nFRxlwy0U"
_delivery._adscert.adscerttestverifier.dev. 3600 IN TXT "v=adcrtd k=x25519 h=sha256 p=uNzTFA2_QsCcxsVET8q-IDtEaDn_D3Q6xscev1TFsjc"
The DNS record consists of the following fields:
v
Set to the constant value “adcrtd” to indicate that this record provides an ads.cert delivery key. This token MUST appear at the start of the DNS record value.
k
Set to key algorithm identifier, designed for forward compatibility if we need to transition to another scheme in the future. Currently this will always be set to “x25519” representing the X25519 Diffie-Hellman key exchange algorithm.
h
Set to the hash algorithm identifier, again designed for forward compatibility. Currently this will always be set to “sha256” representing the SHA-256 secure hashing algorithm.
p
Values are 32 byte public keys represented as 43 byte base64 encoded strings, RFC 4648 “URL-safe” variant.
To retrieve the current, complete dns records for these test domains, run the following dig commands:
dig _delivery._adscert.adscerttestsigner.dev TXT
dig _delivery._adscert.adscerttestverifier.dev TXT
Local Examples:
The following examples take advantage of the example domains above to test the signing and verification process against a signatory and web server. The signatory and web server can be run locally, or within a docker container.
To build and test the ads.cert authenticted connections binary locally, run the following command from within a local copy of this directory
To generate an insecure private and public key pair, run:
go run . basicinsecurekeygen
NOTE
The private key generated in this fashion should never be used in a production environment, and are for demo purposes only.
The adscerttestsigner and adscerttestverifier used in these examples employ an insecure private and public key pair.
The private keys of these domains are disclosed here, but any production implementation should never publicly disclose its private key
To sign and verify a message between adscerttestsigner.dev and adscerttestverifier.dev, follow these steps:
Run the Test Signer Signatory:
go run . signatory --server_port 3000 --metrics_port 3001 --private_key "Ys83NKuuYxCVDUbmA671x3zAFsQ-EnNxmC2JLuBlGAU" --origin "adscerttestsigner.dev"
- add an "&" to the end of the command to run in background, or run in a separate shell
Sign a message to adscerttestverifier.dev (run against adscerttestsigner signatory):
go run . testsign --url "https://adscerttestverifier.dev"
- run twice
- will fail on first run because credentials for the invoked url do not yet exist in the signatory; credentials will be updated after the failure, and the signing attempt will succeed on the second run
receiving party must have a published public key for signing to succeed
(Optional) kill the Test Signer Signatory:
ps
- to find the psid the signatory is running under
sudo kill -9 <psid>
Run the Test Verifier signatory:
go run . signatory --server_port 4000 --metrics_port 4001 --private_key "6mkLbsTBKs0UwYLkBdw5ttJHzjpSZxof0A2rako-0qs" --origin "adscerttestverifier.dev"
- add an "&" to the end of the command to run in background, or run in a separate shell
Verify the message (run against adscerttestverifier signatory):
go run . testverify --url "https://adscerttestverifier.dev" --signatureMessage "from=adscerttestsigner.dev&from_key=LxqTmA&invoking=adscerttestverifier.dev&nonce=mBJo7EYj9XF9&status=1×tamp=220810T142237&to=adscerttestverifier.dev&to_key=uNzTFA; sigb=ugN9tqMd6h0p&sigu=pxQd8BV20lHg"
- run twice
- will fail on first run because credentials for the signer do not yet exist in the verifier signatory; credentials will be updated after the failure, and the verification attempt will succeed on the second run
the signing and receiving/verifying parties must have published public keys for verification to succeed
To Sign a Request, invoke it against a local web server, and verify the signature, do the following:
Run the Test Signer Signatory:
go run . signatory --server_port 3000 --metrics_port 3001 --private_key "Ys83NKuuYxCVDUbmA671x3zAFsQ-EnNxmC2JLuBlGAU" --origin "adscerttestsigner.dev"
- add an "&" to the end of the command to run in background, or run in a separate shell
Sign a message to adscerttestverifier.dev (run against adscerttestsigner signatory):
go run . testsign --url "https://adscerttestverifier.dev"
- run twice
- will fail on first run because credentials for the invoked url do not yet exist in the signatory; credentials will be updated after the failure, and the signing attempt will succeed on the second run
receiving party must have a published public key for signing to succeed
(Optional) kill the Test Signer Signatory:
ps
- to find the psid the signatory is running under
sudo kill -9 <psid>
Run the Test Verifier signatory:
go run . signatory --server_port 4000 --metrics_port 4001 --private_key "6mkLbsTBKs0UwYLkBdw5ttJHzjpSZxof0A2rako-0qs" --origin "adscerttestverifier.dev"
- add an "&" to the end of the command to run in background, or run in a separate shell
Run the testreceiver web server:
go run . testreceiver --server_port 5000 --verifier_address localhost:4000
- add an "&" to the end of the command to run in background, or run in a separate shell
Send a signed request to the web server:
curl http://adscerttestverifier.dev:5000 -H "X-Ads-Cert-Auth: from=adscerttestsigner.dev&from_key=LxqTmA&invoking=adscerttestverifier.dev&nonce=6Rpf4qD2LP_9&status=1×tamp=220912T200513&to=adscerttestverifier.dev&to_key=uNzTFA; sigb=OcQzM62rkJk0&sigu=_44H63NN69Nb"
Docker Examples:
The following examples take advantage of the example domains above to test the signing and verification process against a signatory and web server running in a docker container. The signatory and web server can be run locally, or within a docker container.
To build and test the ads.cert authenticted connections binary in a docker container, run the following command from within a local copy of this directory
Build the latest version of the docker image:
docker build -t adscert-image .
To generate an insecure private and public key pair, run:
go run . basicinsecurekeygen
NOTE
The private key generated in this fashion should never be used in a production environment, and are for demo purposes only.
The adscerttestsigner and adscerttestverifier used in these examples employ an insecure private and public key pair.
The private keys of these domains are disclosed here, but any production implementation should never publicly disclose its private key
To sign and verify a message between adscerttestsigner.dev and adscerttestverifier.dev, follow these steps:
Run the Signer docker container:
docker run -d -p 3000:3000 --name signer-container adscert-image ./adscert signatory --server_port 3000 --metrics_port 3001 --private_key "Ys83NKuuYxCVDUbmA671x3zAFsQ-EnNxmC2JLuBlGAU" --origin "adscerttestsigner.dev" &
- if the docker container already exists, start it by running:
docker start signer-container
Sign a message to adscerttestverifier.dev (runs against the signer-container):
go run . testsign --url "https://adscerttestverifier.dev"
- run twice
- will fail on first run because credentials for the invoked url do not yet exist in the signatory; credentials will be updated after the failure, and the signing attempt will succeed on the second run
receiving party must have a published public key for signing to succeed
(Optional) stop and remove the Test Signer Signatory container:
docker stop signer-container
docker rm signer-container
Run the Verifier docker container:
docker run -d -p 4000:4000 -p 5000:5000 --name verifier-container adscert-image ./adscert signatory --server_port 4000 --metrics_port 4001 --private_key "6mkLbsTBKs0UwYLkBdw5ttJHzjpSZxof0A2rako-0qs" --origin "adscerttestverifier.dev" &
- if the docker container already exists, start it by running:
docker start verifier-container
Verify the message (runs against the verifier-container):
go run . testverify --url "https://adscerttestverifier.dev" --signatureMessage "from=adscerttestsigner.dev&from_key=LxqTmA&invoking=adscerttestverifier.dev&nonce=mBJo7EYj9XF9&status=1×tamp=220810T142237&to=adscerttestverifier.dev&to_key=uNzTFA; sigb=ugN9tqMd6h0p&sigu=pxQd8BV20lHg"
- run twice
- will fail on first run because credentials for the signer do not yet exist in the verifier signatory; credentials will be updated after the failure, and the verification attempt will succeed on the second run
the signing and receiving/verifying parties must have published public keys for verification to succeed
To Sign a Request, invoke it against a web server, and verify the signature, do the following:
Run the signer docker container:
docker run -d -p 3000:3000 --name signer-container adscert-image ./adscert signatory --server_port 3000 --metrics_port 3001 --private_key "Ys83NKuuYxCVDUbmA671x3zAFsQ-EnNxmC2JLuBlGAU" --origin "adscerttestsigner.dev" &
- if the docker container already exists, start it by running:
docker start signer-container
Sign a message to adscerttestverifier.dev (runs against the signer-container):
go run . testsign --url "https://adscerttestverifier.dev"
- run twice
- will fail on first run because credentials for the invoked url do not yet exist in the signatory; credentials will be updated after the failure, and the signing attempt will succeed on the second run
receiving party must have a published public key for signing to succeed
(Optional) stop and remove the signer-container:
docker stop signer-container
docker rm signer-container
Run the Verifer docker container:
docker run -d -p 4000:4000 -p 5000:5000 --name verifier-container adscert-image ./adscert signatory --server_port 4000 --metrics_port 4001 --private_key "6mkLbsTBKs0UwYLkBdw5ttJHzjpSZxof0A2rako-0qs" --origin "adscerttestverifier.dev" &
- if the docker container already exists, start it by running:
docker start verifier-container
Run the testreceiver web server in the verifier-container:
docker exec verifier-container ./adscert testreceiver --server_port 5000 --verifier_address localhost:4000 &
Send a signed request to the web server:
curl http://adscerttestverifier.dev:5000 -H "X-Ads-Cert-Auth: from=adscerttestsigner.dev&from_key=LxqTmA&invoking=adscerttestverifier.dev&nonce=6Rpf4qD2LP_9&status=1×tamp=220912T200513&to=adscerttestverifier.dev&to_key=uNzTFA; sigb=OcQzM62rkJk0&sigu=_44H63NN69Nb"
Sign requests and log to file
logging for cmd based verification to be added
Modify workflows
You can add custom workflows and github actions to .github/workflows
folder. Currently go.yml
includes the build and release steps including creating and pushing a new tag. A new release and tag only gets created only on main branch and push event.
More info.