diff --git a/src/cleartext.js b/src/cleartext.js index 45100f35..c716fc6b 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -43,7 +43,7 @@ export function CleartextMessage(text, signature) { return new CleartextMessage(text, signature); } // normalize EOL to canonical form - this.text = util.canonicalizeEOL(util.removeTrailingSpaces(text)); + this.text = util.removeTrailingSpaces(text).replace(/\r\n/g, '\n').replace(/[\r\n]/g, '\r\n'); if (signature && !(signature instanceof Signature)) { throw new Error('Invalid signature input'); } @@ -124,7 +124,7 @@ CleartextMessage.prototype.verifyDetached = function(signature, keys, date = new */ CleartextMessage.prototype.getText = function() { // normalize end of line to \n - return util.nativeEOL(this.text); + return this.text.replace(/\r\n/g, '\n'); }; /** diff --git a/src/packet/literal.js b/src/packet/literal.js index 3e7e5ef5..68395fb6 100644 --- a/src/packet/literal.js +++ b/src/packet/literal.js @@ -64,7 +64,7 @@ Literal.prototype.setText = function(text, format = 'utf8') { */ Literal.prototype.getText = function(clone = false) { if (this.text === null || util.isStream(this.text)) { // Assume that this.text has been read - this.text = util.nativeEOL(util.decode_utf8(this.getBytes(clone))); + this.text = util.decode_utf8(util.nativeEOL(this.getBytes(clone))); } return this.text; }; @@ -88,8 +88,8 @@ Literal.prototype.setBytes = function(bytes, format) { */ Literal.prototype.getBytes = function(clone = false) { if (this.data === null) { - // normalize EOL to \r\n and encode UTF8 - this.data = util.encode_utf8(util.canonicalizeEOL(this.text)); + // encode UTF8 and normalize EOL to \r\n + this.data = util.canonicalizeEOL(util.encode_utf8(this.text)); } if (clone) { return stream.passiveClone(this.data); diff --git a/src/packet/signature.js b/src/packet/signature.js index 6b487bff..69263f1f 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -577,11 +577,9 @@ Signature.prototype.toSign = function (type, data) { return data.getBytes(true); case t.text: { - let text = data.getText(true); + const bytes = data.getBytes(true); // normalize EOL to \r\n - text = util.canonicalizeEOL(text); - // encode UTF8 - return util.encode_utf8(text); + return util.canonicalizeEOL(bytes); } case t.standalone: return new Uint8Array(0); diff --git a/src/util.js b/src/util.js index e9bd9bdd..3c6cf767 100644 --- a/src/util.js +++ b/src/util.js @@ -689,27 +689,48 @@ export default { }, /** - * Normalize line endings to \r\n + * Normalize line endings to + * Support any encoding where CR=0x0D, LF=0x0A */ - canonicalizeEOL: function(text) { - return stream.transform(util.nativeEOL(text), value => value.replace(/\r/g, "\n").replace(/\n/g, "\r\n")); + canonicalizeEOL: function(data) { + const CR = 13; + const LF = 10; + + return stream.transform(util.nativeEOL(data, true), bytes => { + const normalized = []; + for (let i = 0; i < bytes.length; i++){ + const x = bytes[i]; + if (x === LF || x === CR) { + normalized.push(CR, LF); + } else { + normalized.push(x); + } + } + return new Uint8Array(normalized); + }); }, /** - * Convert line endings from canonicalized \r\n to native \n + * Convert line endings from canonicalized to native + * Support any encoding where CR=0x0D, LF=0x0A */ - nativeEOL: function(text) { - let lastChar = ''; - return stream.transform(text, value => { - value = lastChar + value; - if (value[value.length - 1] === '\r') { - lastChar = '\r'; - value = value.slice(0, -1); + nativeEOL: function(data) { + const CR = 13; + const LF = 10; + let carryOverCR = false; + + return stream.transform(data, bytes => { + bytes = carryOverCR ? [CR].concat(Array.from(bytes)) : Array.from(bytes); + + if (bytes[bytes.length - 1] === CR) { + carryOverCR = true; + bytes.pop(); } else { - lastChar = ''; + carryOverCR = false; } - return value.replace(/\r\n/g, '\n'); - }, () => lastChar); + + return new Uint8Array(bytes.filter((x, i, xs) => (x !== CR || (i < xs.length - 1 && xs[i + 1] !== LF)))); + }, () => new Uint8Array(carryOverCR ? [CR] : [])); }, /** diff --git a/test/general/signature.js b/test/general/signature.js index 6e47b40e..17de1de9 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -303,6 +303,360 @@ describe("Signature", function() { '=X7/F', '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); + const pub_latin1_msg = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +eGlmAABNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEA +AABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEA +AqACAAQAAAABAAAAfaADAAQAAAABAAAAlgAAAAD/4QkhaHR0cDovL25zLmFkb2Jl +LmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENl +aGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5z +Om1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQuMCI+IDxyZGY6UkRGIHhtbG5z +OnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5z +IyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiLz4gPC9yZGY6UkRGPiA8 +L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8P3hw +YWNrZXQgZW5kPSJ3Ij8+AP/tADhQaG90b3Nob3AgMy4wADhCSU0EBAAAAAAAADhC +SU0EJQAAAAAAENQdjNmPALIE6YAJmOz4Qn7/4gzgSUNDX1BST0ZJTEUAAQEAAAzQ +YXBwbAIQAABtbnRyUkdCIFhZWiAH3wABAAEACwA7AC1hY3NwQVBQTAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFkZXNjAAABUAAAAGJkc2Nt +AAABtAAAAbhjcHJ0AAADbAAAACN3dHB0AAADkAAAABRyWFlaAAADpAAAABRnWFla +AAADuAAAABRiWFlaAAADzAAAABRyVFJDAAAD4AAACAxhYXJnAAAL7AAAACB2Y2d0 +AAAMDAAAADBuZGluAAAMPAAAAD5jaGFkAAAMfAAAACxtbW9kAAAMqAAAAChiVFJD +AAAD4AAACAxnVFJDAAAD4AAACAxhYWJnAAAL7AAAACBhYWdnAAAL7AAAACBkZXNj +AAAAAAAAAAhEaXNwbGF5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +bWx1YwAAAAAAAAAiAAAADGhySFIAAAAQAAABqGtvS1IAAAAQAAABqG5iTk8AAAAQ +AAABqGlkAAAAAAAQAAABqGh1SFUAAAAQAAABqGNzQ1oAAAAQAAABqGRhREsAAAAQ +AAABqHVrVUEAAAAQAAABqGFyAAAAAAAQAAABqGl0SVQAAAAQAAABqHJvUk8AAAAQ +AAABqGVzRVMAAAAQAAABqGhlSUwAAAAQAAABqG5sTkwAAAAQAAABqGZpRkkAAAAQ +AAABqHpoVFcAAAAQAAABqHZpVk4AAAAQAAABqHNrU0sAAAAQAAABqHpoQ04AAAAQ +AAABqHJ1UlUAAAAQAAABqGZyRlIAAAAQAAABqG1zAAAAAAAQAAABqGNhRVMAAAAQ +AAABqHRoVEgAAAAQAAABqGVzWEwAAAAQAAABqGRlREUAAAAQAAABqGVuVVMAAAAQ +AAABqHB0QlIAAAAQAAABqHBsUEwAAAAQAAABqGVsR1IAAAAQAAABqHN2U0UAAAAQ +AAABqHRyVFIAAAAQAAABqGphSlAAAAAQAAABqHB0UFQAAAAQAAABqABTAE0AQgAx +ADkANAAwAFd0ZXh0AAAAAENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDE1AABYWVog +AAAAAAAA89gAAQAAAAEWCFhZWiAAAAAAAABwAAAAOREAAAPCWFlaIAAAAAAAAGI3 +AAC3mgAAGRFYWVogAAAAAAAAJJ8AAA9VAAC2WmN1cnYAAAAAAAAEAAAAAAUACgAP +ABQAGQAeACMAKAAtADIANgA7AEAARQBKAE8AVABZAF4AYwBoAG0AcgB3AHwAgQCG +AIsAkACVAJoAnwCjAKgArQCyALcAvADBAMYAywDQANUA2wDgAOUA6wDwAPYA+wEB +AQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGLAZIBmgGh +AakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJx +AnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNy +A34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSo +BLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYW +BicGNwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/ +B9IH5Qf4CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmk +CboJzwnlCfsKEQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvI +C+EL+QwSDCoMQwxcDHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4u +DkkOZA5/DpsOtg7SDu4PCQ8lD0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDX +EPURExExEU8RbRGMEaoRyRHoEgcSJhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPF +E+UUBhQnFEkUahSLFK0UzhTwFRIVNBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6 +Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3 +Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzMHPUdHh1HHXAdmR3DHeweFh5A +HmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAhHCFIIXUhoSHOIfsiJyJV +IoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl9yYnJlcmhya3 +JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1KmgqmyrPKwIrNitp +K50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/HL/4wNTBs +MKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXC +Nf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtr +O6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFq +QaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fA +SAVIS0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5u +TrdPAE9JT5NP3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1 +VcJWD1ZcVqlW91dEV5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzW +XSddeF3JXhpebF69Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSU +ZOllPWWSZedmPWaSZuhnPWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yv +bQhtYG25bhJua27Ebx5veG/RcCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUo +dYV14XY+dpt2+HdWd7N4EXhueMx5KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4B +fmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6hB2EgITjhUeFq4YOhnKG14c7 +h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0xjZiN/45mjs6PNo+ekAaQbpDW +kT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX4JhMmLiZJJmQmfyaaJrV +m0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKWowajdqPmpFakx6U4 +pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1ErbiuLa6hrxavi7AA +sHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnCuju6tbsu +u6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjGRsbD +x0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB +00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p +36/gNuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv7 +7IbtEe2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4 ++cf6V/rn+3f8B/yY/Sn9uv5L/tz/bf//cGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1Z +AAAT0AAACg52Y2d0AAAAAAAAAAEAAQAAAAAAAAABAAAAAQAAAAAAAAABAAAAAQAA +AAAAAAABAABuZGluAAAAAAAAADYAAKPAAABUQAAATMAAAJmAAAAmgAAAD0AAAFBA +AABUQAACMzMAAjMzAAIzMwAAAAAAAAAAc2YzMgAAAAAAAQu3AAAFlv//81cAAAcp +AAD91///+7f///2mAAAD2gAAwPZtbW9kAAAAAAAATC0AAAaVVjg5RchujAAAAAAA +AAAAAAAAAAAAAAAA/8AAEQgAlgB9AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAA +AAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFB +BhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNE +RUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqi +o6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz +9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIB +AgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy +0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpz +dHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG +x8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICAwICAwQD +AwMEBQQEBAQFBwUFBQUFBwgHBwcHBwcICAgICAgICAoKCgoKCgsLCwsLDQ0NDQ0N +DQ0NDf/bAEMBAgICAwMDBgMDBg0JBwkNDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0N +DQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDf/dAAQACP/aAAwDAQACEQMRAD8A/HD4 +zajC3iQ6RHO91PYZFzMQETzWAO1FGcBRjJYkkmvMNE0271jV7PTLH/j5up0iiycA +Ox4JI6Y65r07Ufh5df8ACuJ/iXqs8n2i9nSWGHaMPHNLhpHY5PzZyoBHqa4Pwbf3 +Ol+J9P1G0tTezW82+OBesj7WAH65/CvUx6lPE+0qqylqvS//AADzcDKCwzp0Hdwu +n25luP8AGWhXnhvxBe6NfSiee2kAeVc4kLgPuG4k98cntXNFMw+duX72zZn5umc4 +9O2fWtrxBrGo69qdzq2qPvubiUs+BgDHAUAdAoGBWdfadfaZKkF/A8DyRrMqyDDb +JM7TjtnHQ8159VQ55ez+G+h20edU4qr8VtfXrYZaTR213BcSp5qRSI7J/eCkHH44 +xX1n+x3d/avjqt1sC/aLLUX2L0UMY2AGOw6V8taRBZSXiNqZZbdQWbb1ZgPlXnoC +eprvPA3i6fwT4wk8SaCWRtk8carxtSYAHp9KylrFo6IRfNdI/o48FR/6JKPu7Zbg +4PB/1zGuukVFUt145Ir8SfDv7bnxS8N6XNp6JZXZYsyXE0REqb2LEcEK3J9BUlh+ +3Z8Z3Eo1K70+dHA25tBFsOeuQ7Z/IVjBJbmc8JUm9D9ibyWE65YbWwRbXB68HlO9 +S2cE1x44NwqIEis7ZZJGwzOu2V9qYIK7SRycivw6vv2r/jHLr8OrWOqmBkDIIRGr +2xV+o2H5ucDOGFeq+AP23/HPhzV21jxTp1lrs0nyjaWtmjXaEwmS+FwPuknnvWVR +X2Khgqkdz9KPjWAt9p7ettKPwDivnXTYw/iCGEp/rVnUd8lonAryXxP+2vo3jW5s +5dR8OXemtDE6OUmjuEO5gcqFO7AxzkZ9K6bwF8Q/C3izxLpE+jX6YluVR1c+XJGz +Kw+ZWAwf6GsKnNzeR0RpSjHU/QL9jqPzvAfiXaP+YlCck8k/Z04Fe++MY/LgsXON +xMgOO3ArwP8AYyJn+HniqOPrFqtuDt5CkW0fB+n519GeNoQlhYSEffll59to4rvS +Tos4G39Y/rseYtuxzyOnFVy3zHrVgk524wCeKpkDPXFeZys9FH//0Pz5+M0sEPw5 +vPD2k2zvFbRQhtmPLhigZeWJx6dBzk18O2l3c2V0l3ayNDLCSyuvBXggn8jX3z8X +Dbj4c63HAuxni3/KM5+YFieP1Nfn8khjkEi4JU7hkZGQcjPtX1nFlL2eJhFfynyf +CE+bCTuvtP8AJHaeI/Cl94Wgsm1NlW5voGuBAPvxLwF3npubOaXxnq8XiXxKbyF9 +8f2e3jB/65xgEfgc1b+IHiJ/Ger/ANtWccht4LW2gkO0hY5CDke2W4Hris/w9pkH +nCS5y+QQy9ACTgcjrmvnMVOFJyhSfuu3zt/TPp8HTnVUZ1V7yv8AK/8ASMwiIgKW +wOBkdcVqoIbO3+0REFs7do5bBHU8cZrqo/AXiG9zJptk88J5G3PA981uR/D/AFlQ +M2TqR88gcYx+HPT2rxpYqmt2fQwwdRrSL+482dH+zrcvGQpOCccfXp1/StG0ayGd +ke4jAG7+ua9KtfAfiTWJVtBallBwqqp45xzxnmu6j+BHiMxtG9sUJACPtO1s9m4y +Pyrnq5hRjo5HXSyzES2iz56+2RxyOFhVsjJ52/kRQltNfSObcbTnIGeT3/GvfZP2 +evG8B3fZBsxjdu/TpXHXXwi8Z6W7SfZ2DKSMqcDI69etKGPoSdlJFzyvFR1cGeSB +ruJ2Rjk8ZPbHSuqsNSn0WNb23eSOYMCjIcYI9CPzrb1HwV4mlgiuprUpHF1JwAMe +w5rC1i2kS3igRS2cEqUKYbHPXJNbxrQnomctTD1afxqx+k/7Ev7ZOl/DWXUPBnj0 +BdK8QXcMo1MsQba4SJYQJFwQEYLkuehr9lNc17R/FHg/R9d0K9hvrC5ncxTQOJI3 +Bj6hl4/Kv5L9LLIC5wm0kEDqQfX8K/S79i39oJfCl1F8JPEUn/Eo1W5M+mTu+Ft7 +l4wpiG44CSYyAP4jj0rplVUafKeVVwvNU9pHc/V+XOT2qkzbegFXGYONy9D71SOA +TgVyIlp7H//R/OX4ma8q+CtWjkUnzYDECfWQ7R+pr4mPWvpr4w3saeHI7ZX3G4uE +GP8Ac+Y/yr5kr63jGonjFFLZf5nyvCFLlwcp95P8kd5o9/Yp4L1XTpTi4luYJU56 +iPBHH4H866n4b+HZ9c1UXcn/AB7xHkdQWGOK8ntF3fLk4LDgV9yfCfwylvYxJCoU +gKWz3Y9fxr4bOMa4UU7bafmz9H4dyxVq7S2buz2Twroapbx28EWABz2z9a9ftfD1 +nIY0mtkYqOSRuPNWfC+gxpboUBMh4B46CvSrbSMIAFCtkcjnPv0r83qYqUpto/Ya +eEpRppM5ay0C0i2iKBUC9ABj8q0bnTBDJlkwR17kZ7cV3dnpqqAfvvzwBzRNpsnL +MpB7Z/zxXNNTuzrg4RsrHmEtszKRzgdufzrltS0yOc5dAR1GRxzXtTaL5Sng5Y89 ++v1rltS0xGDgD6exFY++tb6nXFU2rM8G1fw5ZXMbRvGFx047en0r5p+JvgZUsxqN +imdvyMOmCO34Y/EV9oalZvDE24ZY8GuE1bRorrT7qGYBlkBJ3DI5zn8jXpZdjp05 +pnh5zl1OrTkrWPy9ae4troxlMBj29R3rodE1W90+/jkgdomjkWWJ16oy4IZfcHBr +qPGGix6Frky7PlcnZkAqUP8ASuUhmt1iVSoVgTg4yR6Y/H9K+9hUU43PyatDkk0f +vz+zP8TZvif8LdP1XUf+QnabrS8Oc+ZLDwZB/dDDkA17yxJOelflp/wT68YzLr3i +LwdIQyXNvBex/OQA6FkcbfoAc9fwr9TicE8EnviiL1szz60bO5//0vxu8ayf2tqk +emkny7O2muX57gfL+teOV6TeahH5viTURz5gW0hPYbuCK82PtXvZ/WVXEe06u/3J +2X5Hj5JTlTw6ptbJfe1d/mbmhRi4v7aDGd0qj9a/Sn4a6ckFgM4yQpx06AH8K+Av +BsUF74ptXVVUSEPtXoGVef5Zr7O8PfEfR9Iga3lJ+Q4LY+XIx69a+Cz2LnanHU/T ++FasKN6k3Y+0fDIJCFeEAGf8/WvRbVWkcMOeDjHHFfOvw8+Lvg688u1uZ2hmJ24k +GFJ9j0/OvpzTha6mUn05gyMuQQcrivjauGlCXvI/QaWLp1o+6zSstsaGTYMnPXoK +laYSOAfQAADqazZmnUi0i4O8q7dvlPNVb3UtB0uEy3t9Ch3bQzuACR2FEU3shuUV +o3Y0LzbIG3EcdAMD+Vcte2oC+YR74qi3izQxJmC9hkHJyHB6cnmqyeMfD1xlBeQg +46Fxms50pLWSOmOJitIs5DWbRmPmDqG5GK861tvJt3L8ZBwTx0HQ17JfyRTHerBo +2GQV55/+vXlni+wF3p7RRD5zkjGBz2zXPRhaorl16zcGkfCHxPgV7mSeZTgAgOoy +o+oB/WvDnCRxRujDByQT1JPb6Zr3HxrpeqQ3M8UwwyE/LnGR2PuK+f71ZAsZI2qe +AB0zX6BgLOkrH5JmcGqzbR9rfsP332H452k8jMkdxYXKHaNylyUADHsD2Jr9u3DE +46Ee9fk//wAE6/CT6hrHivxdIg8iyhttNAZVYF5AZm68g4YdK/WEAINozgcCtpN8 +7PHr20P/0/wZkupGt2gJ+V5TKfUt05qsIZWV3VSVjALEdga0dJtVvL5Ef7iAu2fR +a3NAgW5s74sP9b8o/In+tetRwzxE0pPv+BxYjExoRbS2t+LLPw/MjeIoBF97ZJj2 +OP8A69fengDwV4RFpG2v20M7uMHzxv35HJ28/pXxN8Ibbz/GAtnXcVhkY/VSB7+t +fefhXTohr7/24gk09EEWw52p8uQ+FOWIbt6V8RnLfP7O9j9D4bivZubjzHUX3w3+ +HaoJdIEmnd9qBxEPXG4HA/KvSPhlrs/hm/j0d7gzwllVCx/hbp9a8N8DeANctPFT +HVLqS20o3kr3N/HcyNFLaHcQiQYZcg9DjjOO1dJfRW9v4vtrTRJpJYobnPmMhjEs +S85A9iMeh6189iKEk/4nN+h9hhHGSbUOV/1+J9qS/arnTp723ZImErD5+h56fjXz +V4w8GX+tzGWXURahVcAAZ4JLE9ffH0FeiXviua20eOHp+8G4j1PXNeZa5c3N9Ypc +CbyhcylGPJwnrxyPpXHCrKL00Z6SwftHyyOc0L4R+GJm3atrk15KXyVWTyoyPQsO +fyNdJe/BTTEgI0u8kiAGIxkMvXuec1ynjrw5qnhvwnoniPwrqNy/m3bx6jcQ+XcC +3i2nywtqcHlgMk5NZPhHxX4xg0KDV9eRD50zJG9uDE7Iv8UsOSMMeARgg9sV3OE1 +T53M8iE6DrOlCLOq0CbWPB97/ZGpytc2MrbYmdixj+jHgj27V11/cLKjFRuO0nGc +DFWp2ttes1MkeG27g2M449Kwpl8ljbuBwgGRXm1nGTuj16UJx0Z83/F5LZJLUKwE +oUkvjkKeDn8xXx5qVnMsUsTYYrKcfnn8M19V/Gt7mK4iKjIOSTjpj0rP+FHwri8U +w2WteIAv9n73d4znfNz8vToD1r6bA11QwyqTPjMwws8VjXSgtT7u/wCCfPhOXQvh +Rq3iC4+Q+INVMsS5GTBbRLCpx6FkYj2NfdbEZ6+/pX5zaNaDwtr1q2gpJp504xzR +KGYARqw3LgHGCMgjHev0TJ80K45DKGGOeGGa6cJivbJu2p4uf5JLAOEnK6l5W2P/ +1Pws0yNl068uBwduFPrjrW54cT/iXkjPzO36Vtpoa2/hVZlyXktjK2OcM3OMVR8J +2wl01pX3bVkI45z0Jr6rC03TlSX91nzOLxCqUqrXSSR6N8DLJT8RbtcYBts49MsD +/MV+iVvoUbxRXEJ2SKAGOMqcdM5718MfBewMHju+YZDtZ2zKepw7vnPocCv0n8Pa +V9os187IGBn8q/K+LJOGPmkfuPAlFVMshUe5wNz58MTqgBbGWwNqgdcnnpXEeDre +S/1271BxvIbYp69PrXqfj20XTNHuGhH3Vwxxy3OBx+NN+FOjbLKNWiMk0h4UDLFm +PtzxXgKpem09z7aFNfWFfZFrU4H8j7NKSEcZ/E/X07U7SfC51uKSOFiJY0DbQeHC +/wDs1dp4msXjgkXyiWjyMEYOaxPAryXerGzjYwyL83fGK4FU5aiZ6GJpKUW4mlpn +g6KLCShXKZwrjP14P171NfeELadiXVIhgDEYAGB9K9Xn0eW4TMi7Zf7y55+tc9fa +fLYgs5DKfWu6c21rseVSoTlUumeZtYfYnEMafu1OM98VyWqqBNiMYANd1f3MRLgZ +UqSM9ef8K8w8Q3pttjBsnPJHsf1riT5vdOmUFT1kzyH4h2Nrq7mzvIw0Z+UjuM9w +a9h8JeGha6SltbQBILKOBhHyPMi4XjHIx1z614/qrS3upRY+dpJFAHXksAMV9Kpr +0GlS3NhCrSyQQR2MqIMgsFDFc9AckV3tSajBvQ8vDqLqyklqRTaLet4z0zTFJf8A +tSNYkJHIUyKSc/7ua+6ztT5AeFAA+gGBXzR8KbCfxH4hh8Q3ce2HRbeSNN3I+0Ts +Nqg99igk/UV9JlgD619FltK0Ls+M4yxrq4iOHb+Ba+rP/9X8dtLvJrfw6sVxIse+ +HALMSxQoRtwOxrB8Hahf2umyW0LEQs5LJ6kqAecZ59Oldto1hpNx4Fhvpo90yWjF +iD3QGqXw70ayuvD6XVyG8x5XyQxGQDgV9vGlKc6SXWLf5HxtSulRrXX2rfmek/CL +WEi8c2wlTy1mhaBiRxkEMv8AWv0w0TUYo7dEJXBAIPSvzDig0rw/IuuGR4/sbCXe +zH5QtfYfh3xBJqVpbJby53gbW67uN36jpX51xxlbjio1v5l+R+s+HGfRWDeHf2X+ +Z33j7WrOWM2jMJCGWQrnAO0g4P16UeCvGGoWWrN/Z8DxwKgdWBxIknoME5H5V5KD +NrGtTxyghVfGST0H17fjXvHhTTdOtU8tgApIO4kbs+1fHezjBJH6T9YqV3z007Fr +WNb8V3F5JPNarNBIA27dhySeS2easeEdUj0zWpLvUrfymZFVQhzjJ6k4rf06K1W4 +ntrmeGJGOV82VRz+JrD1vTrf5hbyrIy5w6uCPzB9qwqU1e510q1VLlnFnt9n4hR4 +vNhcMrg8HkDmsPUr/wA/Bc5xk8CvGdM1e/s42idxjI6dcZ961l1mViyHnZjv29ai +cJNKz0M6WNhTqNNalTWHEasVJABPPuK8V8RXvmDY2flOePUe9eja1eMxKIwLbucn +19K8f1+TEuwY4J3YHrWtGkcGLx3O+Ud4ZgXVfF+nQNIEjWQyOScBRHz1+uK+mItF +ufEU6+HrSPfLcTI4CjAznl3ZRwoHJJ6jgV4v8EPg0vx18W6t4TGuNokOj6ZHqlzL +HF5ks0LymMRJyApJUnJBr9ZNK8G+HfAnwwGg6HFtW3eziaeQbp5Nr4JdzyT+Neth +sslUlzdD5zMuJYYVujFXn+R5t4e8P2PhbRrfRdPAWOEfMQMeY5+85+p/StAkFiev +vU8h5qi7Anjj8K9inT5Vyo+Hq1ZVJupPVvc//9b8f7a9sIvDN5oqbhGYGRTkbh5h +ySOenYZp9lqDaBpUVlp8kUpjCbQ+VJSQklicDkH2rhLOeKazeykl278BuCec9Ovr ++FVNTurgymF3DbAAM4OBjgA+lZYfG42FRctR6fl2PKeXUZJxnHRu79To/EPiqfWL +H+zZIhFmXLlSSGVOgGR6819V/s9eK7bUYotIv5dstoFiBY5yijMZz34yp+lfDpaT +emedo6+tdp4P8S3PhrXYNQiZlQfLIq9Sp6/ljNa5mquMp3qO8j28m5MHJKkrK5+l +nxAtTpOlyazoDKt0pHmFlDRlWGQcf1qDwB418LS3NrF4stJBKI5PNNycWjdNu1lO +AevUD61m2erLr2lW6h/Miu41YcgocCrll4aswGWZMDAK4UHjr+VfB3cU4M/Y8vdG +rFQqTcVbodhb+I/htDC0zC2lMF1n73nPIinIUbiMgjj0riNf8YWl3LfHwrYXayST +nyIwwW3CEDPOSFGc4UA13Wk+GvConW5ljSbAI2GAsuRnO4cV2OneG9Llma5MRZUY +kK4wBjpge1YwvTTa1b7npVKOEg7yrSfktDy7wdpXjx4Jr/WNRiEQxsh+zjv1+fIJ +x7AV3iO9tbSSuw+Tr6D1IrotVubeCL7PF8ufkAXoM9jXmmr3hEU9vvAGAAT03fXr +jFVTlKrJyeh4GOrRi/dX4lea6NxI7s2Ad2COn4mvNtWuUPmO7Yxx37d63rm+MMJj +LcEY4PTFYOsWDReH73UpsBmhZYlxyNwxv+vp+ddsIJNI85Tm05JbK59If8E+NSWb +4yeMJpeRceGlVSSPux3J9epOfwr9QvEro3hCYooXNzbkjqSN4x9K/KD/AIJ9MI/i +54jw0akeGjgSNjP7/tnqa/UfXJt/hiYb2/18PGAAf3gr6nDe7TaR+cZi+bEqT8jz +l3znPPt61WwCT1NTyHGR1xxVfnqDjP4VhG9zQ//X/B+4guLGYxOSQp4PY01H4B6/ +Wr961w3lpc455AAxx71VVDjnjms4zW6NqlBxepKkjNwcYFINwk3qc+wqIttyFFb/ +AIe0u813WLPRbBA13eyrFHGepJ6sR12gAkn0FdHNZXMoJ3sfX/wQ1PUZvCsKahGT +HFLItrI2cMsRG4A/7J4r6TtLu4vmigJG1jnKjpz6123gz4TW9r8D7rwxpsSvqemw +RahZXO0FxcQHdLjjIEqZVwOqmvO9LvXsTGLtRbOANpbDRnPXa/Q/Q4NfKZhhf3vO +up93k+NtRs+mh6LZaJbWUiSiWcFskrv3Lu7DH0711FtqEsLJbxp+7bIZj/8AW615 +tD4iMd0zv+8QgbWPAHoRitNfEcTObh5UUbc7ck7Tj1Aya4p4dNanqSx19InRaqwZ +TIMMuM8HOSM89q8a1OaN5SyZXI4zyBxyB35roL3xBPPEVtUZEBIz2GeehrlFtJJn +LvkkdwaIUeQ5ZT52UltnvJY42J5OevYcmvQ9P07TNS1vSdJ1qD7Tp1zMsFxCM5Mc +isDjBzleG/CqWlaXtzeSLkAbVB7D/wDXVHVrr7LqVgyyGJ/tUCI46h2YKuMc8sQP +xqObmqJHq0aHLh5p7tP8j1T9j7wxceBv2j/FPhW5YyNaaHMIpDyZIGlRopBjg7kI +z2yDX6S698ugTF2AYzwDaBk48wc54AH5mvjj4d6PPpX7VL3tyoSS78EC3lOMDzrR +494+v7wZr6u1ueOTS2jVsnzYWOO3z/y9hX11L4WflGOVqyOVlJ7c1UYqT8wJ/CrD +HDc/iKrM3PBxXO3roaM//9D8TNX0/EtidwAeEN9Mk1758J/2Y/EXxXdTaarYadak +jdJKJZpceyKEXP1c/SvFtY+/p3/Xuv8ANq/Tn9jz/jyX6j+Qry8G26SbPbzCKVWV +jvfBn7APwPsNHdvE02sa7eqCXZ7trWDgc7Ut/LOPQZ/GrVx8J/h58O7qPRfBmiW2 +nA/vJJgoeZ88AGRst9ea+19O/wCPKf8A3W/lXzZ48/5GVP8Arkv8664awTZ5MHqd +54PhjsLa3kjAKLwVPQr3H5cV4P8AETwnbeHfGGp6RFsa3WVZYwBjEVwodVI6ZXOP +TAr3zw9/yD4v89q8z+NP/JQ9S/65WX/ooVyZgkqNz6LIW/rDj0sfO0dn9lvXhgI2 +nkBhwB6YrTEKhlLKvyeg9f51E/8AyEfw/wAatt1P4V4/Q9yrFcwsekRyjrxw3v06 +VALKJpTAoAHf8K37b7v/AAEf1rMj/wCPs/Q/zFc9Rs7cDCLd2uprXMSWsCgDgr0H +Aq98IvCln4z+K1gNUCtZ6Ah1WSI8+dJBzEvpgSEMc/3RVXVP9Qn0Ndt+zl/yU3V/ ++wPL/wChLWWDV66TPSx83HC1JR3sz3nwmqTfFO31aYbpp4b1MjqPNUEj6fIK9q1l +gdOOc4aSMge+4da8W8I/8j/p/wDuXP8A6Aa9l1n/AJBq/wDXRP8A0IV9bQe6PyDG +/wAWPoc02Sciq7A9uOtWe1Qt/U1EUjY//9mJAj0EEwEKACcFAlS6eugCGwMFCRLM +A3EFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQ2xGHud1faTsejg/9GuUpmB71 +M9hl4W5lesOnXPSwIWIhE8AofDHhAGq7PdbXoGK95+IS20MtU2T4dNMgmBmu997w +m4cQVLBjO9DaFiihgYNxIS2UEPnbCk+kLwEwdqmoW4Arl3KSasqBcJSXtPYZpr1Q +kJUn238kPYWeqdKlI1AHnmd4KzNwM9FosDFcTYALNCkiR/BeN0GTq8drpgGxzTHn +nWzmHzM3vefFVP4reJaLq5u8n00lXJVxQZeaSUMp0ABRPU+HqM61oL0t2sJnTbsM +u0Nmv/Xccu3h5jGm6yW8EJMzyUjPsj2L3NqJ+11t+Uh8KW+6BwmI6dhiPMT9gc3+ +87bVhhFauKx3KgpO7xff++JkneQYoZ67atfmtpgnL1ae9tBUeld5VBjq6aOZ1tva +Nskmraduzbmv9bLMd9eLydf4C10tSyVsn0EYGy/mee6nxW3XHiXTfqG19Qe2gkFB +/NY/iIbOjEU8tq+RVRIjFcAuQj1ozsGb3kFxA/EnB7KEMjcELev4U9DQxz7t8UHJ +laBHcaKWPbCvA8C1kyvxLp22sxXYT+/qWmrLAEGbfUi0bloPZ0yrPpb//FrJy8Sh +8afKqU9upr2PXRhQcn14fTrmjaAfx+e42MfUpLh02EmF2hdqirN+Vo82Cr8etNP0 +QU+oJKqppUyfFOT6UYBut0OcZKL8OkIz8va5Ag0EWl76BwEQAL0XJbYJDygnfCrp +mfY37RmVOjaptW6Y4Qg146BfymgaU1Bw5AOSUHEPx0aIKzMkPd9qVd3YgIujtblr +hn1FmxB5tAdQa53MOa9kNa261TAKy2kThDcUNjBLirDC6TInpl7bpCdW6+slYLx5 +HtP3sKUtR+jmSa7YBBsROB1J1eYeMp5QrOh4CllXOcKaT47bGgOAHbw+WYZUfiDR +exrawOvlNmH8akfKsJ55I8epFPoXieibsHtPD1aDORD9ey+p8KsZGYZ9n9XzDswH +5lLVQF+3iXs97dKub1aqPZwQZPdB4U6ZQ8rF1jMYK70lgbiTX/aL6sW/jwHw62TL +5mWBRLZ/qKzxdCu5lWCWOX/p/DwWcPyD5Jvt++/B5Dq2//XpBcF8KgeVxPAi9tQ7 +OscuW9ypoMooxAum5umNYcyeBwAg2YKQ5O6jRMK3VqAFPKkhMbYIf1aEfOzO/BS5 +wFOQLXKW8/U3UGnvOtbRjjtNxiiStUt0I7v/amXTnuhAnoWyXzh+ULu0MV9d+X8m +Vxoa4o7+wngCJzOgnK4eMcpo2GTqF5dvPQtynPaYZ9N4qeAviwvt+Haw+zZGBrU7 +Z4fKxUKKvSsQURlQMC5ek/hokQINggmuFEQpkoc9K65TLUcf2UmWVPVi9YkDqJjY +X4TDgzIQQulaaGfAV7VUH3f/nvChABEBAAGJAjwEGAEKACYWIQRPn4n1UFrB0aJg +YxzbEYe53V9pOwUCWl76BwIbDAUJB4TOAAAKCRDbEYe53V9pO0E3EACNx026NV5q +mb0Rbq51beMzqOtDwmxHLaWjJspYhMWGQqvvkCuHCQzBQhMonEfDWF6thev3UnHI +fDWs5imwsxbunacnwX9gZQKmva0pM7rgngY4oOfuPu2rrrhfHBBqfOwVyk3T8nRf +OBVMxEPmF+8tKokZzZfc7cOfuCVDQde8qfyD6FBvuXoUGf4uILZnS+yaF3GjcwJj +YZSYTxRTiTv5VMNJrLC+ynaBmUhVbUaIlm3Ne1zi3QmCjzvSGDOUNtAhdvGHk4Sc +qPxvR/hXprb6YHr/RlvV1OfUc/pgspjInblNtEW1/NjKlpTUg/XO+RtZdoktjbHE +lq8z6SvfygdCq5FId6beulKmIMnBMOS5I8tC/pemwzn/aNBgd7i6FGMVmDZBeb5d +m7ikmg7u4Gr8Dp4TuD7/ardI4zqffWdnSV1xyar1GEmyA/cWJOuv9Uc9RpjiKBe3 +15l5UrmuJf3Bt/3cinmPPFpablC342GWEmszNdlmsVJ5NChWFyyTBHlr4CrmhvMF +gR1JcKmZZe++oshGB/7AQpHIbsMJohZP7e8M2NVmCYSPI7ryO0CVTqL5ndQgR4Y/ +WPVMYDzj6X7I1A+nWeNiPlp2PoUUUvdCLisY1aU1wyTJa7wBsLARsrhXk5/R1pQt +Blk+CJ7ytHy6En8542bB/yC+Z9/zWbVuhg== +=jmT1 +-----END PGP PUBLIC KEY BLOCK-----`; + const msg_sig_expired = ['-----BEGIN PGP MESSAGE-----', 'Comment: GPGTools - https://gpgtools.org', @@ -713,6 +1067,39 @@ vwjE8mqJXetNMfj8r2SCyvkEnlVRYR+/mnge+ib56FdJ8uKtqSxyvgA= }); }); + it('Verify latin-1 signed message', async function() { + const latin1Binary = openpgp.util.hex_to_Uint8Array('48e46c6cf62057e86c74'); + const message = openpgp.message.fromBinary(latin1Binary); + + message.appendSignature(`-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCAAdFiEET5+J9VBawdGiYGMc2xGHud1faTsFAl5lE/AACgkQ2xGHud1f +aTtIuw//YWrVaXLyP8sGBc0uUSLxQbmfQQYV8Oq8Vsg+jV4orc73wmEy8+Nj5m2g +fFEPaWy07dfDBtv874XCsZmCM+ZhwkGaT9lwcCxkxNZeywTE5JRS1/6Ky3G4gDZ/ +QozTXr/ZNPXF6bBENqhfqeO2xkD577bjiPu5wLcu3/RR39YnWp5zQu9ynJbpwobz +HHQW5TgSUgi/9tInQ+cc7vMkHzfe2Zg45HkyaStBW1x7Fm9FLv8GNw1R2jVbUlsX +SEL7yPrsZAmgyu1ifMrTTY4vunuUNUFiGZN9UC75b7s4tjKkvJ0sfdQusC0vmcE0 +Wq7dAoeX72B94TITuXldDGIfL01smycm+mEvf/kXxQg81wj4la72IzhdWeC6r4PF +558QDEtZy984iL8RJKpbjwgsxyvfM9zf9qtmuNdscbZaZsx9LRe21uaKhX8yJsAU +jCgyjM5e7CMdvBUyxrpGzS+mP/rh77r2TXkg6u2+8Nj4osxwkfxvDIGpJfaIWl2a +sNQ4Bsgm6rcNYaxUVbOsBFJddBb503KpTX5aK8TuC6AINg6bV6rmfj2Jc/QX4eHb +Xgm6EyJo74z5R7YvA3XdUZf1sfwfoMLbbUqpeHEZOcWy4vrS79Omx1MyPxKHPiXU +PAAeuQTUrcJdZeJ86eQ9cCUB216HCwSKOWTQRzL+hBWKXij4WD4= +=ZEFm +-----END PGP SIGNATURE-----`); + + const pubKey = (await openpgp.key.readArmored(pub_latin1_msg)).keys[0]; + + return message.verify([pubKey]).then(async verifiedSig => { + expect(await openpgp.stream.readToEnd(message.getLiteralData())).to.equal(latin1Binary); + expect(verifiedSig).to.exist; + expect(verifiedSig).to.have.length(1); + expect(await verifiedSig[0].verified).to.be.true; + expect((await verifiedSig[0].signature).packets.length).to.equal(1); + }); + }); + + it('Verify cleartext signed message with trailing spaces from GPG', async function() { const { reject_message_hash_algorithms } = openpgp.config; Object.assign(openpgp.config, { reject_message_hash_algorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) }); @@ -781,7 +1168,7 @@ yYDnCgA= return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(function(cleartextSig) { expect(cleartextSig).to.exist; - expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(cleartextSig.data))).to.equal(plaintext); + expect(openpgp.util.Uint8Array_to_str(openpgp.util.nativeEOL(cleartextSig.data))).to.equal(plaintext); expect(cleartextSig.signatures).to.have.length(1); expect(cleartextSig.signatures[0].valid).to.equal(!openpgp.config.reject_message_hash_algorithms.has(openpgp.enums.hash.sha1)); expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); @@ -818,7 +1205,7 @@ yYDnCgA= return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) { expect(cleartextSig).to.exist; - expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); + expect(openpgp.util.Uint8Array_to_str(openpgp.util.nativeEOL(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); expect(cleartextSig.signatures).to.have.length(1); if (!openpgp.config.reject_message_hash_algorithms.has(openpgp.enums.hash.sha1)) { expect(await cleartextSig.signatures[0].verified).to.be.true; @@ -851,7 +1238,7 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) { expect(cleartextSig).to.exist; - expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); + expect(openpgp.util.Uint8Array_to_str(openpgp.util.nativeEOL(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); expect(cleartextSig.signatures).to.have.length(0); }); }); @@ -884,7 +1271,7 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) { expect(cleartextSig).to.exist; - expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); + expect(openpgp.util.Uint8Array_to_str(openpgp.util.nativeEOL(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); expect(cleartextSig.signatures).to.have.length(1); await expect(cleartextSig.signatures[0].verified).to.be.rejectedWith('Corresponding signature packet missing'); expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(0);