-
Notifications
You must be signed in to change notification settings - Fork 15
encrypted_card
This document describes a proposal for a payment handler that behaves like basic card, but provides additional security, by ensuring that the card information is not transmitted in the clear to the merchant.
This reduces PCI scope for the merchant, especially v3+
We'd like to consider this colloquially as "BasicCard++". In that sense, there are a few things that a similar to BasicCard
and a few differences.
Similarities:
- Form Fills are used to populate payment instruments
- Payment Request invocation is similar
Differences:
- The PAN is encrypted following an exchange with a key provider
- The returned value from the
PaymentRequest
API does not contain the PAN, but contains an opaque blob - The returned blob can only be used to process the transaction with the key provider that created it
- Additional constraints may be put by the key provider on the blob (could be a nonce, time bounded, or permanent)
The payment handler can be specified using the same syntax as BasicCard
with a notable difference: a key provider will be specified.
This parameter will be provided in addition to those specified in basic-card
:
dictionary EncryptedCardRequest {
sequence<DOMString> supportedNetworks; // Defined in Basic Card
sequence<CardType> supportedTypes; // Defined in Basic Card
DOMString keyProviderURL;
};
The Javascript would look as follows:
var supportedInstruments = [
{
supportedMethods: ['encrypted-card']
data: {
supportedNetworks: ['amex', 'discover', 'mastercard', 'visa'],
supportedTypes: ['credit', 'debit'],
keyProviderURL: 'https://www.bobpay.com/encrypted-cards'
}
},
...,
];
var payment = new PaymentRequest(
supportedInstruments,
details,
options
);
The public key is specified using some concepts from the WebCrypto API, specifically, a subset of the [importKey](https://www.w3.org/TR/WebCryptoAPI/#dfn-SubtleCrypto-method-importKey)
argument specification.
The payment handler will issue a request to the key provider URL and expect a valid public key to be provided. The response is expected to be as follows:
dictionary PublicKey {
KeyFormat format;
(BufferSource or JsonWebKey) keyData;
}
An alternative is to use public key cryptography. The merchant specifies the public key in the payment method specification and that data is used in the encryption process later on. As above, the data is specified similar to basic-card
.
The public key is specified using some concepts from the WebCrypto API, specifically, a subset of the [importKey](https://www.w3.org/TR/WebCryptoAPI/#dfn-SubtleCrypto-method-importKey)
argument specification.
dictionary PublicKey {
KeyFormat format;
(BufferSource or JsonWebKey) keyData;
}
dictionary EncryptedCardRequest {
sequence<DOMString> supportedNetworks; // Defined in Basic Card
sequence<CardType> supportedTypes; // Defined in Basic Card
PublicKey publicKey;
};
The Javascript would look as follows:
var supportedInstruments = [{
supportedMethods: 'encrypted-card',
data: {
publicKey: {
format: 'jwk',
keyData: {
alg: 'RSA-OAEP-256',
e: 'AQAB',
ext: true,
key_ops: ['encrypt'],
kty: 'RSA',
n: 'vVf4OM-GXBF_r-mtGfNVuMiF99x55p-q2ZYYhNKMoDo3PgVWwdq3lYRyWLro0mRomYWcETlitE_L69P-bnOe65_DJs1y6_kp7m1jU2hNreZ7fmxv4osFNBOC7VOL0UK8LTTt4la0OBDx2omGwtt-UHBvYOkO_RhZI_JpGQfcHZbCYXA4qdk6URbLtJ-DMfm4RRk7OTpTgBkbtX7kC-r_L-5uMPndCzXkp7roEjVVt6CDo0hp_OeRWIverrNub5f6vxFM0WlR309ulWaq9shDKioAvB6aSwLhCBPNaRM40PHkzY9WLw2QF7tfzX3XpREnmufOLyq3KHyyIQ6N8xEOIIXdekAP4lJFUIZ4YfwtgK71N-aPukMlIRn0FDMq8RX8rmDFqxuxaty1rmP-uKe282Cna4M4cTJdprcjnurYaRig2BbCIlTBDBx0onLbIr0TQGtqSNQzxttHADqIcSMuOKTnmWI3dUvj8YJ-eaNw6Ge_Z7Z1x0tIwM424-6cYKwz4UROXL0ifzgzaeezaFd_t4p1oV9hQRbqVdilLOXuZ25rIBmWUzEzWm8XHo4sDPdyv8YulziBNvOhipObOBe9LWMVpDzulk2gotm7L7_uFxBf4kKA3oEFVxR3r9n0Eljrxr8os19db3z59dsfEDMwSLAkARyfll0KECVZAsZ8HFE'
},
algo: {name: 'RSA-OAEP', hash: {name: 'SHA-256'}},
}
}
}];
The exact same data contained in the BasicCardResponse
dictionary, is encrypted.
dictionary EncryptedCardData {
required DOMString cardNumber;
DOMString cardholderName;
DOMString cardSecurityCode;
DOMString expiryMonth;
DOMString expiryYear;
PaymentAddress? billingAddress;
};
This data is returned as part of the PaymentResponse
as follows:
dictionary EncryptedCardResponse {
DOMString cardholderName; // data from BasicCardResponse
optional DOMString suffix; // Last four digits of card number
required DOMString encryptedCardData; // base64-encoded string
};
The encryption can occur in one of these ways:
through the public key in the TLS certificate of the key provider if a manifest is not found or the parameter is not found in the manifestthrough a Web App Manifest parameter on the key provider's origin, specifying a public key and list of ciphers as defined by WebCrypto- through a public key provided as part of the
PaymentMethodData
dictionary - Key provided in key request API call
There's an inherent advantage to re-use the public key in the TLS certificate. This is because a lot can be cached/short circuited.
If there has already been a cipher exchange, the public key is potentially in the Browser's cache, and the result of the TLS handshaking will have selected a cipher suite for the key exchange step in TLS. The same cipher suite can be used to encrypt the card data.
That said, re-using the public key, where TLS expressly chooses not to for Forward Secrecy for transport makes this method less ideal for that reason.
A web manifest file can be provided with additional parameters specifying cryptographic parameters. Here's a quick example:
{
encryptedCardKey: {
format,
keyData,
algo,
extractable,
usages
}
}
This data will be fed directly in SubtleCrypto's
importKey IDL
function.
note: feedback incorporated in proposal above
Alternatively, encryption configuration can be specified in PaymentMethodData
. It can use the same parameters specified above in the web app manifest, as defined by importKey IDL
.
dictionary EncryptedCardRequest {
sequence<DOMString> supportedNetworks; // Defined in Basic Card
sequence<CardType> supportedTypes; // Defined in Basic Card
KeyFormat format;
(BufferSource or JsonWebKey) keyData;
AlgorithmIdentifier algorithm;
boolean extractable;
sequence<KeyUsage> keyUsages;
}
note: feedback incorporated in proposal above
When making a GET
request to the keyProviderURL
, the response will be a public key as defined in Web Crypto.