-
Notifications
You must be signed in to change notification settings - Fork 64
BREAKING CHANGE: Fix digest method algorithm id #135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,22 @@ | ||
| var escapehtml = require('escape-html'); | ||
|
|
||
| module.exports = ({ encryptionPublicCert, encryptedKey, keyEncryptionMethod, keyEncryptionDigest }) => ` | ||
| const DIGEST_ALGORITHMS = { | ||
| // SHA-2 was published after 2000/09/xmldsig was locked, so sha256/sha512 live under 2001/04/xmlenc. | ||
| 'sha1': 'http://www.w3.org/2000/09/xmldsig#sha1', | ||
| 'sha256': 'http://www.w3.org/2001/04/xmlenc#sha256', | ||
| 'sha512': 'http://www.w3.org/2001/04/xmlenc#sha512' | ||
| }; | ||
|
|
||
| module.exports = ({ encryptionPublicCert, encryptedKey, keyEncryptionMethod, keyEncryptionDigest }) => { | ||
| const digestUri = DIGEST_ALGORITHMS[keyEncryptionDigest] || keyEncryptionDigest; | ||
|
|
||
| // RSA-1.5 doesn't hash the key, so it has no digest or DigestMethod. RSA-OAEP does. | ||
| const isOAEP = keyEncryptionMethod && keyEncryptionMethod.includes('rsa-oaep'); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a link to the relevant portion of the spec that says RSA-1.5 must not have a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There isn't really a relevant portion of the spec, but I updated the comment 8bc66ed
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that a breaking change ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know how anyone could rely on the DigestMethod when using RSA 1.5, but the shape of the XML is changing, so strictly speaking yes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a previous check on whether the key encryption method is on an allow list? The check here is pretty lax in that rsa-oaep can appear anywhere within the string.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| return ` | ||
| <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> | ||
| <e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#"> | ||
| <e:EncryptionMethod Algorithm="${escapehtml(keyEncryptionMethod)}"> | ||
| <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#${escapehtml(keyEncryptionDigest)}" /> | ||
| ${isOAEP ? `<DigestMethod Algorithm="${escapehtml(digestUri)}" />` : ''} | ||
| </e:EncryptionMethod> | ||
| <KeyInfo> | ||
| ${encryptionPublicCert} | ||
|
|
@@ -15,4 +27,4 @@ module.exports = ({ encryptionPublicCert, encryptedKey, keyEncryptionMethod, key | |
| </e:EncryptedKey> | ||
| </KeyInfo> | ||
| `; | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -255,10 +255,12 @@ function decryptKeyInfo(doc, options) { | |
| if (keyDigestMethod) { | ||
| const keyDigestMethodAlgorithm = keyDigestMethod.getAttribute('Algorithm'); | ||
| switch (keyDigestMethodAlgorithm) { | ||
| case 'http://www.w3.org/2000/09/xmldsig#sha256': | ||
| case 'http://www.w3.org/2001/04/xmlenc#sha256': | ||
| case 'http://www.w3.org/2000/09/xmldsig#sha256': // backwards compatibility for previous wrong usage | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this what we want ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is 👍 |
||
| oaepHash = 'sha256'; | ||
| break; | ||
| case 'http://www.w3.org/2000/09/xmldsig#sha512': | ||
| case 'http://www.w3.org/2001/04/xmlenc#sha512': | ||
| case 'http://www.w3.org/2000/09/xmldsig#sha512': // backwards compatibility for previous wrong usage | ||
| oaepHash = 'sha512'; | ||
| break; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| var assert = require('assert'); | ||
| var fs = require('fs'); | ||
| var xmlenc = require('../lib'); | ||
| var keyinfoTemplate = require('../lib/templates/keyinfo.tpl.xml'); | ||
|
|
||
| var RSA_OAEP = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we try both
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe there's a |
||
| var RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'; | ||
|
|
||
| describe('keyEncryptionDigest', function () { | ||
|
|
||
| describe('keyinfo template DigestMethod', function () { | ||
| function render(overrides) { | ||
| return keyinfoTemplate(Object.assign({ | ||
| encryptionPublicCert: '<X509Data></X509Data>', | ||
| encryptedKey: 'ZW5jcnlwdGVkS2V5', | ||
| keyEncryptionMethod: RSA_OAEP, | ||
| keyEncryptionDigest: 'sha1' | ||
| }, overrides)); | ||
| } | ||
|
|
||
| it('maps sha1 to the xmldsig sha1 URI', function () { | ||
| var xml = render({ keyEncryptionDigest: 'sha1' }); | ||
| assert(xml.includes('<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />')); | ||
| }); | ||
|
|
||
| it('maps sha256 to the xmlenc sha256 URI', function () { | ||
| var xml = render({ keyEncryptionDigest: 'sha256' }); | ||
| assert(xml.includes('<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />')); | ||
| }); | ||
|
|
||
| it('maps sha512 to the xmlenc sha512 URI', function () { | ||
| var xml = render({ keyEncryptionDigest: 'sha512' }); | ||
| assert(xml.includes('<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512" />')); | ||
| }); | ||
|
|
||
| it('passes through an unknown digest value unchanged', function () { | ||
| var custom = 'http://example.org/custom#sha3-256'; | ||
| var xml = render({ keyEncryptionDigest: custom }); | ||
| assert(xml.includes('<DigestMethod Algorithm="' + custom + '" />')); | ||
| }); | ||
|
|
||
| it('does NOT include a DigestMethod for RSA-1.5', function () { | ||
| var xml = render({ keyEncryptionMethod: RSA_1_5, keyEncryptionDigest: 'sha256' }); | ||
| assert(!xml.includes('<DigestMethod')); | ||
| }); | ||
| }); | ||
|
|
||
| describe('encrypt/decrypt round trip with digest', function () { | ||
| function baseOptions() { | ||
| return { | ||
| rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'), | ||
| pem: fs.readFileSync(__dirname + '/test-auth0.pem'), | ||
| key: fs.readFileSync(__dirname + '/test-auth0.key'), | ||
| encryptionAlgorithm: 'http://www.w3.org/2009/xmlenc11#aes256-gcm', | ||
| keyEncryptionAlgorithm: RSA_OAEP | ||
| }; | ||
| } | ||
|
|
||
| ['sha1', 'sha256', 'sha512'].forEach(function (digest) { | ||
| it('round trips with ' + digest, function (done) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this test have failed prior to this fix?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, but it seems like a reasonable regression test to have |
||
| var options = baseOptions(); | ||
| options.keyEncryptionDigest = digest; | ||
| var content = 'content encrypted with ' + digest; | ||
|
|
||
| xmlenc.encrypt(content, options, function (err, result) { | ||
| if (err) return done(err); | ||
| xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key') }, function (err, decrypted) { | ||
| if (err) return done(err); | ||
| assert.equal(decrypted, content); | ||
| done(); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| it('emits the correct xmldsig URI in the produced XML for sha1', function (done) { | ||
| var options = baseOptions(); | ||
| options.keyEncryptionDigest = 'sha1'; | ||
| xmlenc.encrypt('content', options, function (err, result) { | ||
| if (err) return done(err); | ||
| assert(result.includes('http://www.w3.org/2000/09/xmldsig#sha1')); | ||
| done(); | ||
| }); | ||
| }); | ||
|
|
||
| it('emits the correct xmlenc URI in the produced XML for sha256', function (done) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see similar tests for sha1/sha512?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added tests for sha1 and sha512 8bc66ed |
||
| var options = baseOptions(); | ||
| options.keyEncryptionDigest = 'sha256'; | ||
| xmlenc.encrypt('content', options, function (err, result) { | ||
| if (err) return done(err); | ||
| assert(result.includes('http://www.w3.org/2001/04/xmlenc#sha256')); | ||
| assert(!result.includes('http://www.w3.org/2000/09/xmldsig#sha256')); | ||
| done(); | ||
| }); | ||
| }); | ||
|
|
||
| it('emits the correct xmlenc URI in the produced XML for sha512', function (done) { | ||
| var options = baseOptions(); | ||
| options.keyEncryptionDigest = 'sha512'; | ||
| xmlenc.encrypt('content', options, function (err, result) { | ||
| if (err) return done(err); | ||
| assert(result.includes('http://www.w3.org/2001/04/xmlenc#sha512')); | ||
| assert(!result.includes('http://www.w3.org/2000/09/xmldsig#sha512')); | ||
| done(); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| describe('decrypt backwards compatibility with old (wrong) xmldsig digest URIs', function () { | ||
| function encryptWith(digest, cb) { | ||
| var options = { | ||
| rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'), | ||
| pem: fs.readFileSync(__dirname + '/test-auth0.pem'), | ||
| key: fs.readFileSync(__dirname + '/test-auth0.key'), | ||
| encryptionAlgorithm: 'http://www.w3.org/2009/xmlenc11#aes256-gcm', | ||
| keyEncryptionAlgorithm: RSA_OAEP, | ||
| keyEncryptionDigest: digest | ||
| }; | ||
| xmlenc.encrypt('legacy digest content', options, cb); | ||
| } | ||
|
|
||
| it('decrypts a document using the legacy xmldsig#sha256 URI', function (done) { | ||
| encryptWith('sha256', function (err, result) { | ||
| if (err) return done(err); | ||
| var legacy = result.replace( | ||
| 'http://www.w3.org/2001/04/xmlenc#sha256', | ||
| 'http://www.w3.org/2000/09/xmldsig#sha256' | ||
| ); | ||
| xmlenc.decrypt(legacy, { key: fs.readFileSync(__dirname + '/test-auth0.key') }, function (err, decrypted) { | ||
| if (err) return done(err); | ||
| assert.equal(decrypted, 'legacy digest content'); | ||
| done(); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| it('decrypts a document using the legacy xmldsig#sha512 URI', function (done) { | ||
| encryptWith('sha512', function (err, result) { | ||
| if (err) return done(err); | ||
| var legacy = result.replace( | ||
| 'http://www.w3.org/2001/04/xmlenc#sha512', | ||
| 'http://www.w3.org/2000/09/xmldsig#sha512' | ||
| ); | ||
| xmlenc.decrypt(legacy, { key: fs.readFileSync(__dirname + '/test-auth0.key') }, function (err, decrypted) { | ||
| if (err) return done(err); | ||
| assert.equal(decrypted, 'legacy digest content'); | ||
| done(); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment about why
sha1usesxmldsigand the others usexmlencwithin the URI.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment 8bc66ed