├── .editorconfig ├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── README.md ├── docs ├── images │ ├── bg_hr.png │ ├── blacktocat.png │ ├── icon_download.png │ └── sprite_download.png ├── index.html ├── javascripts │ └── main.js ├── params.json └── stylesheets │ ├── github-light.css │ └── stylesheet.css ├── examples ├── encrypt.js ├── mac.js ├── sign-rs.js └── sign.js ├── lib ├── common.js ├── encrypt.js ├── index.js ├── mac.js └── sign.js ├── package-lock.json ├── package.json └── test ├── aes-ccm-examples.js ├── aes-gcm-examples.js ├── cbc-mac-examples.js ├── common.js ├── ecdh-direct-examples.js ├── encrypted-tests.js ├── enveloped-tests.js ├── hmac-examples.js ├── mac-tests.js ├── mac0-tests.js ├── rsa-pkcs-examples.js ├── rsa-pkcs-examples └── rsa-pkcs-01.json ├── sign-performance-tests.js ├── sign-tests.js ├── sign1-tests.js └── util.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | npm-debug.log 4 | .nyc_output/ 5 | coverage/ 6 | tmp/ 7 | .vscode/ 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/Examples"] 2 | path = test/Examples 3 | url = https://github.com/cose-wg/Examples.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - '12' 5 | env: 6 | - CXX=g++-4.8 7 | addons: 8 | apt: 9 | sources: 10 | - ubuntu-toolchain-r-test 11 | packages: 12 | - g++-4.8 13 | install: 14 | - npm install 15 | - npm install coveralls 16 | script: 17 | - npm run coverage 18 | after_success: 19 | - npm run coveralls 20 | cache: 21 | directories: 22 | - node_modules 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://app.travis-ci.com/erdtman/cose-js.svg?branch=master)](https://app.travis-ci.com/erdtman/cose-js) 2 | [![Coverage Status](https://coveralls.io/repos/github/erdtman/cose-js/badge.svg?branch=master)](https://coveralls.io/github/erdtman/cose-js?branch=master) 3 | # cose-js 4 | JavaScript implementation of [COSE](https://tools.ietf.org/html/rfc8152), [RFC8152](https://tools.ietf.org/html/rfc8152) 5 | ## MAC 6 | ```js 7 | const cose = require('cose-js'); 8 | try { 9 | const plaintext = 'Important message!'; 10 | const headers = { 11 | p: { alg: 'SHA-256_64' }, 12 | u: { kid: 'our-secret' } 13 | }; 14 | const recipent = { 15 | key: Buffer.from('231f4c4d4d3051fdc2ec0a3851d5b383', 'hex') 16 | }; 17 | const buf = await cose.mac.create(headers, plaintext, recipent); 18 | console.log('MACed message: ' + buf.toString('hex')); 19 | } catch (error) { 20 | console.log(error); 21 | } 22 | ``` 23 | ## Verify MAC 24 | ```js 25 | const cose = require('cose-js'); 26 | try { 27 | const key = Buffer.from('231f4c4d4d3051fdc2ec0a3851d5b383', 'hex'); 28 | const COSEMessage = Buffer.from('d18443a10104a1044a6f75722d73656372657472496d706f7274616e74206d65737361676521488894981d4aa5d614', 'hex'); 29 | const buf = await cose.mac.read(COSEMessage, key); 30 | console.log('Verified message: ' + buf.toString('utf8')); 31 | } catch (error) { 32 | console.log(error); 33 | } 34 | ``` 35 | ## Sign 36 | ```js 37 | const cose = require('cose-js'); 38 | try { 39 | const plaintext = 'Important message!'; 40 | const headers = { 41 | p: { alg: 'ES256' }, 42 | u: { kid: '11' } 43 | }; 44 | const signer = { 45 | key: { 46 | d: Buffer.from('6c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c19', 'hex') 47 | } 48 | }; 49 | const buf = await cose.sign.create(headers, plaintext, signer); 50 | console.log('Signed message: ' + buf.toString('hex')); 51 | } catch (error) { 52 | console.log(error); 53 | } 54 | ``` 55 | ## Verify Signature 56 | ```js 57 | const cose = require('cose-js'); 58 | try { 59 | const verifier = { 60 | key: { 61 | x: Buffer.from('143329cce7868e416927599cf65a34f3ce2ffda55a7eca69ed8919a394d42f0f', 'hex'), 62 | y: Buffer.from('60f7f1a780d8a783bfb7a2dd6b2796e8128dbbcef9d3d168db9529971a36e7b9', 'hex') 63 | } 64 | }; 65 | const COSEMessage = Buffer.from('d28443a10126a10442313172496d706f7274616e74206d6573736167652158404c2b6b66dfedc4cfef0f221cf7ac7f95087a4c4245fef0063a0fd4014b670f642d31e26d38345bb4efcdc7ded3083ab4fe71b62a23f766d83785f044b20534f9', 'hex'); 66 | const buf = await cose.sign.verify(COSEMessage, verifier); 67 | console.log('Verified message: ' + buf.toString('utf8')); 68 | } catch (error) { 69 | console.log(error); 70 | } 71 | ``` 72 | ## Encrypt 73 | ```js 74 | const cose = require('cose-js'); 75 | try { 76 | const plaintext = 'Secret message!'; 77 | const headers = { 78 | p: { alg: 'A128GCM' }, 79 | u: { kid: 'our-secret' } 80 | }; 81 | const recipient = { 82 | key: Buffer.from('231f4c4d4d3051fdc2ec0a3851d5b383', 'hex') 83 | }; 84 | const buf = await cose.encrypt.create(headers, plaintext, recipient); 85 | console.log('Encrypted message: ' + buf.toString('hex')); 86 | } catch (error) { 87 | console.log(error); 88 | } 89 | ``` 90 | ## Decrypt 91 | ```js 92 | const cose = require('cose-js'); 93 | try { 94 | const key = Buffer.from('231f4c4d4d3051fdc2ec0a3851d5b383', 'hex'); 95 | const COSEMessage = Buffer.from('d8608443a10101a2044a6f75722d736563726574054c291a40271067ff57b1623c30581f23b663aaf9dfb91c5a39a175118ad7d72d416385b1b610e28b3b3fd824a397818340a040', 'hex'); 96 | const buf = await cose.encrypt.read(COSEMessage, key); 97 | console.log('Protected message: ' + buf.toString('utf8')); 98 | } catch (error) { 99 | console.log(error); 100 | } 101 | ``` 102 | ## Install 103 | ``` 104 | npm install cose-js --save 105 | ``` 106 | ## Test 107 | ``` 108 | npm test 109 | ``` 110 | -------------------------------------------------------------------------------- /docs/images/bg_hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdtman/cose-js/c9e6d4d288392aa20fd9b8b434123f905e7449b0/docs/images/bg_hr.png -------------------------------------------------------------------------------- /docs/images/blacktocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdtman/cose-js/c9e6d4d288392aa20fd9b8b434123f905e7449b0/docs/images/blacktocat.png -------------------------------------------------------------------------------- /docs/images/icon_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdtman/cose-js/c9e6d4d288392aa20fd9b8b434123f905e7449b0/docs/images/icon_download.png -------------------------------------------------------------------------------- /docs/images/sprite_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdtman/cose-js/c9e6d4d288392aa20fd9b8b434123f905e7449b0/docs/images/sprite_download.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | cose-js 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | View on GitHub 20 | 21 |

cose-js

22 |

JavaScript implementation of COSE

23 | 24 |
25 | Download this project as a .zip file 26 | Download this project as a tar.gz file 27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |

Build Status 35 | Coverage Status

36 | 37 |

38 | cose-js

39 | 40 |

JavaScript implementation of COSE

41 | 42 |

43 | install

44 | 45 |
npm install cose-js --save
46 | 
47 |
48 |
49 | 50 | 51 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /docs/javascripts/main.js: -------------------------------------------------------------------------------- 1 | console.log('This would be the main JS file.'); 2 | -------------------------------------------------------------------------------- /docs/params.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cose-js", 3 | "tagline": "JavaScript implementation of COSE", 4 | "body": "[![Build Status](https://travis-ci.org/erdtman/cose-js.svg?branch=master)](https://travis-ci.org/erdtman/cose-js)\r\n[![Coverage Status](https://coveralls.io/repos/github/erdtman/cose-js/badge.svg?branch=master)](https://coveralls.io/github/erdtman/cose-js?branch=master)\r\n# cose-js\r\nJavaScript implementation of [COSE](https://tools.ietf.org/html/draft-ietf-cose-msg)\r\n\r\n## install\r\n```\r\nnpm install cose-js --save\r\n```\r\n", 5 | "note": "Don't delete this file! It's used internally to help with page regeneration." 6 | } -------------------------------------------------------------------------------- /docs/stylesheets/github-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 GitHub, Inc. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | .pl-c /* comment */ { 27 | color: #969896; 28 | } 29 | 30 | .pl-c1 /* constant, variable.other.constant, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header */, 31 | .pl-s .pl-v /* string variable */ { 32 | color: #0086b3; 33 | } 34 | 35 | .pl-e /* entity */, 36 | .pl-en /* entity.name */ { 37 | color: #795da3; 38 | } 39 | 40 | .pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */, 41 | .pl-s .pl-s1 /* string source */ { 42 | color: #333; 43 | } 44 | 45 | .pl-ent /* entity.name.tag */ { 46 | color: #63a35c; 47 | } 48 | 49 | .pl-k /* keyword, storage, storage.type */ { 50 | color: #a71d5d; 51 | } 52 | 53 | .pl-s /* string */, 54 | .pl-pds /* punctuation.definition.string, string.regexp.character-class */, 55 | .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, 56 | .pl-sr /* string.regexp */, 57 | .pl-sr .pl-cce /* string.regexp constant.character.escape */, 58 | .pl-sr .pl-sre /* string.regexp source.ruby.embedded */, 59 | .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ { 60 | color: #183691; 61 | } 62 | 63 | .pl-v /* variable */ { 64 | color: #ed6a43; 65 | } 66 | 67 | .pl-id /* invalid.deprecated */ { 68 | color: #b52a1d; 69 | } 70 | 71 | .pl-ii /* invalid.illegal */ { 72 | color: #f8f8f8; 73 | background-color: #b52a1d; 74 | } 75 | 76 | .pl-sr .pl-cce /* string.regexp constant.character.escape */ { 77 | font-weight: bold; 78 | color: #63a35c; 79 | } 80 | 81 | .pl-ml /* markup.list */ { 82 | color: #693a17; 83 | } 84 | 85 | .pl-mh /* markup.heading */, 86 | .pl-mh .pl-en /* markup.heading entity.name */, 87 | .pl-ms /* meta.separator */ { 88 | font-weight: bold; 89 | color: #1d3e81; 90 | } 91 | 92 | .pl-mq /* markup.quote */ { 93 | color: #008080; 94 | } 95 | 96 | .pl-mi /* markup.italic */ { 97 | font-style: italic; 98 | color: #333; 99 | } 100 | 101 | .pl-mb /* markup.bold */ { 102 | font-weight: bold; 103 | color: #333; 104 | } 105 | 106 | .pl-md /* markup.deleted, meta.diff.header.from-file */ { 107 | color: #bd2c00; 108 | background-color: #ffecec; 109 | } 110 | 111 | .pl-mi1 /* markup.inserted, meta.diff.header.to-file */ { 112 | color: #55a532; 113 | background-color: #eaffea; 114 | } 115 | 116 | .pl-mdr /* meta.diff.range */ { 117 | font-weight: bold; 118 | color: #795da3; 119 | } 120 | 121 | .pl-mo /* meta.output */ { 122 | color: #1d3e81; 123 | } 124 | 125 | -------------------------------------------------------------------------------- /docs/stylesheets/stylesheet.css: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Slate Theme for GitHub Pages 3 | by Jason Costello, @jsncostello 4 | *******************************************************************************/ 5 | 6 | @import url(github-light.css); 7 | 8 | /******************************************************************************* 9 | MeyerWeb Reset 10 | *******************************************************************************/ 11 | 12 | html, body, div, span, applet, object, iframe, 13 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 14 | a, abbr, acronym, address, big, cite, code, 15 | del, dfn, em, img, ins, kbd, q, s, samp, 16 | small, strike, strong, sub, sup, tt, var, 17 | b, u, i, center, 18 | dl, dt, dd, ol, ul, li, 19 | fieldset, form, label, legend, 20 | table, caption, tbody, tfoot, thead, tr, th, td, 21 | article, aside, canvas, details, embed, 22 | figure, figcaption, footer, header, hgroup, 23 | menu, nav, output, ruby, section, summary, 24 | time, mark, audio, video { 25 | margin: 0; 26 | padding: 0; 27 | border: 0; 28 | font: inherit; 29 | vertical-align: baseline; 30 | } 31 | 32 | /* HTML5 display-role reset for older browsers */ 33 | article, aside, details, figcaption, figure, 34 | footer, header, hgroup, menu, nav, section { 35 | display: block; 36 | } 37 | 38 | ol, ul { 39 | list-style: none; 40 | } 41 | 42 | table { 43 | border-collapse: collapse; 44 | border-spacing: 0; 45 | } 46 | 47 | /******************************************************************************* 48 | Theme Styles 49 | *******************************************************************************/ 50 | 51 | body { 52 | box-sizing: border-box; 53 | color:#373737; 54 | background: #212121; 55 | font-size: 16px; 56 | font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif; 57 | line-height: 1.5; 58 | -webkit-font-smoothing: antialiased; 59 | } 60 | 61 | h1, h2, h3, h4, h5, h6 { 62 | margin: 10px 0; 63 | font-weight: 700; 64 | color:#222222; 65 | font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif; 66 | letter-spacing: -1px; 67 | } 68 | 69 | h1 { 70 | font-size: 36px; 71 | font-weight: 700; 72 | } 73 | 74 | h2 { 75 | padding-bottom: 10px; 76 | font-size: 32px; 77 | background: url('../images/bg_hr.png') repeat-x bottom; 78 | } 79 | 80 | h3 { 81 | font-size: 24px; 82 | } 83 | 84 | h4 { 85 | font-size: 21px; 86 | } 87 | 88 | h5 { 89 | font-size: 18px; 90 | } 91 | 92 | h6 { 93 | font-size: 16px; 94 | } 95 | 96 | p { 97 | margin: 10px 0 15px 0; 98 | } 99 | 100 | footer p { 101 | color: #f2f2f2; 102 | } 103 | 104 | a { 105 | text-decoration: none; 106 | color: #007edf; 107 | text-shadow: none; 108 | 109 | transition: color 0.5s ease; 110 | transition: text-shadow 0.5s ease; 111 | -webkit-transition: color 0.5s ease; 112 | -webkit-transition: text-shadow 0.5s ease; 113 | -moz-transition: color 0.5s ease; 114 | -moz-transition: text-shadow 0.5s ease; 115 | -o-transition: color 0.5s ease; 116 | -o-transition: text-shadow 0.5s ease; 117 | -ms-transition: color 0.5s ease; 118 | -ms-transition: text-shadow 0.5s ease; 119 | } 120 | 121 | a:hover, a:focus {text-decoration: underline;} 122 | 123 | footer a { 124 | color: #F2F2F2; 125 | text-decoration: underline; 126 | } 127 | 128 | em { 129 | font-style: italic; 130 | } 131 | 132 | strong { 133 | font-weight: bold; 134 | } 135 | 136 | img { 137 | position: relative; 138 | margin: 0 auto; 139 | max-width: 739px; 140 | padding: 5px; 141 | margin: 10px 0 10px 0; 142 | border: 1px solid #ebebeb; 143 | 144 | box-shadow: 0 0 5px #ebebeb; 145 | -webkit-box-shadow: 0 0 5px #ebebeb; 146 | -moz-box-shadow: 0 0 5px #ebebeb; 147 | -o-box-shadow: 0 0 5px #ebebeb; 148 | -ms-box-shadow: 0 0 5px #ebebeb; 149 | } 150 | 151 | p img { 152 | display: inline; 153 | margin: 0; 154 | padding: 0; 155 | vertical-align: middle; 156 | text-align: center; 157 | border: none; 158 | } 159 | 160 | pre, code { 161 | width: 100%; 162 | color: #222; 163 | background-color: #fff; 164 | 165 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; 166 | font-size: 14px; 167 | 168 | border-radius: 2px; 169 | -moz-border-radius: 2px; 170 | -webkit-border-radius: 2px; 171 | } 172 | 173 | pre { 174 | width: 100%; 175 | padding: 10px; 176 | box-shadow: 0 0 10px rgba(0,0,0,.1); 177 | overflow: auto; 178 | } 179 | 180 | code { 181 | padding: 3px; 182 | margin: 0 3px; 183 | box-shadow: 0 0 10px rgba(0,0,0,.1); 184 | } 185 | 186 | pre code { 187 | display: block; 188 | box-shadow: none; 189 | } 190 | 191 | blockquote { 192 | color: #666; 193 | margin-bottom: 20px; 194 | padding: 0 0 0 20px; 195 | border-left: 3px solid #bbb; 196 | } 197 | 198 | 199 | ul, ol, dl { 200 | margin-bottom: 15px 201 | } 202 | 203 | ul { 204 | list-style-position: inside; 205 | list-style: disc; 206 | padding-left: 20px; 207 | } 208 | 209 | ol { 210 | list-style-position: inside; 211 | list-style: decimal; 212 | padding-left: 20px; 213 | } 214 | 215 | dl dt { 216 | font-weight: bold; 217 | } 218 | 219 | dl dd { 220 | padding-left: 20px; 221 | font-style: italic; 222 | } 223 | 224 | dl p { 225 | padding-left: 20px; 226 | font-style: italic; 227 | } 228 | 229 | hr { 230 | height: 1px; 231 | margin-bottom: 5px; 232 | border: none; 233 | background: url('../images/bg_hr.png') repeat-x center; 234 | } 235 | 236 | table { 237 | border: 1px solid #373737; 238 | margin-bottom: 20px; 239 | text-align: left; 240 | } 241 | 242 | th { 243 | font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif; 244 | padding: 10px; 245 | background: #373737; 246 | color: #fff; 247 | } 248 | 249 | td { 250 | padding: 10px; 251 | border: 1px solid #373737; 252 | } 253 | 254 | form { 255 | background: #f2f2f2; 256 | padding: 20px; 257 | } 258 | 259 | /******************************************************************************* 260 | Full-Width Styles 261 | *******************************************************************************/ 262 | 263 | .outer { 264 | width: 100%; 265 | } 266 | 267 | .inner { 268 | position: relative; 269 | max-width: 640px; 270 | padding: 20px 10px; 271 | margin: 0 auto; 272 | } 273 | 274 | #forkme_banner { 275 | display: block; 276 | position: absolute; 277 | top:0; 278 | right: 10px; 279 | z-index: 10; 280 | padding: 10px 50px 10px 10px; 281 | color: #fff; 282 | background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%; 283 | font-weight: 700; 284 | box-shadow: 0 0 10px rgba(0,0,0,.5); 285 | border-bottom-left-radius: 2px; 286 | border-bottom-right-radius: 2px; 287 | } 288 | 289 | #header_wrap { 290 | background: #212121; 291 | background: -moz-linear-gradient(top, #373737, #212121); 292 | background: -webkit-linear-gradient(top, #373737, #212121); 293 | background: -ms-linear-gradient(top, #373737, #212121); 294 | background: -o-linear-gradient(top, #373737, #212121); 295 | background: linear-gradient(top, #373737, #212121); 296 | } 297 | 298 | #header_wrap .inner { 299 | padding: 50px 10px 30px 10px; 300 | } 301 | 302 | #project_title { 303 | margin: 0; 304 | color: #fff; 305 | font-size: 42px; 306 | font-weight: 700; 307 | text-shadow: #111 0px 0px 10px; 308 | } 309 | 310 | #project_tagline { 311 | color: #fff; 312 | font-size: 24px; 313 | font-weight: 300; 314 | background: none; 315 | text-shadow: #111 0px 0px 10px; 316 | } 317 | 318 | #downloads { 319 | position: absolute; 320 | width: 210px; 321 | z-index: 10; 322 | bottom: -40px; 323 | right: 0; 324 | height: 70px; 325 | background: url('../images/icon_download.png') no-repeat 0% 90%; 326 | } 327 | 328 | .zip_download_link { 329 | display: block; 330 | float: right; 331 | width: 90px; 332 | height:70px; 333 | text-indent: -5000px; 334 | overflow: hidden; 335 | background: url(../images/sprite_download.png) no-repeat bottom left; 336 | } 337 | 338 | .tar_download_link { 339 | display: block; 340 | float: right; 341 | width: 90px; 342 | height:70px; 343 | text-indent: -5000px; 344 | overflow: hidden; 345 | background: url(../images/sprite_download.png) no-repeat bottom right; 346 | margin-left: 10px; 347 | } 348 | 349 | .zip_download_link:hover { 350 | background: url(../images/sprite_download.png) no-repeat top left; 351 | } 352 | 353 | .tar_download_link:hover { 354 | background: url(../images/sprite_download.png) no-repeat top right; 355 | } 356 | 357 | #main_content_wrap { 358 | background: #f2f2f2; 359 | border-top: 1px solid #111; 360 | border-bottom: 1px solid #111; 361 | } 362 | 363 | #main_content { 364 | padding-top: 40px; 365 | } 366 | 367 | #footer_wrap { 368 | background: #212121; 369 | } 370 | 371 | 372 | 373 | /******************************************************************************* 374 | Small Device Styles 375 | *******************************************************************************/ 376 | 377 | @media screen and (max-width: 480px) { 378 | body { 379 | font-size:14px; 380 | } 381 | 382 | #downloads { 383 | display: none; 384 | } 385 | 386 | .inner { 387 | min-width: 320px; 388 | max-width: 480px; 389 | } 390 | 391 | #project_title { 392 | font-size: 32px; 393 | } 394 | 395 | h1 { 396 | font-size: 28px; 397 | } 398 | 399 | h2 { 400 | font-size: 24px; 401 | } 402 | 403 | h3 { 404 | font-size: 21px; 405 | } 406 | 407 | h4 { 408 | font-size: 18px; 409 | } 410 | 411 | h5 { 412 | font-size: 14px; 413 | } 414 | 415 | h6 { 416 | font-size: 12px; 417 | } 418 | 419 | code, pre { 420 | min-width: 320px; 421 | max-width: 480px; 422 | font-size: 11px; 423 | } 424 | 425 | } 426 | -------------------------------------------------------------------------------- /examples/encrypt.js: -------------------------------------------------------------------------------- 1 | const cose = require('../'); 2 | 3 | async function run () { 4 | try { 5 | const plaintext = 'Secret message!'; 6 | const headers = { 7 | p: { alg: 'A128GCM' }, 8 | u: { kid: 'our-secret' } 9 | }; 10 | const recipient = { 11 | key: Buffer.from('231f4c4d4d3051fdc2ec0a3851d5b383', 'hex') 12 | }; 13 | const buf = await cose.encrypt.create(headers, plaintext, recipient); 14 | console.log('Encrypted message: ' + buf.toString('hex')); 15 | } catch (error) { 16 | console.log(error); 17 | } 18 | 19 | try { 20 | const key = Buffer.from('231f4c4d4d3051fdc2ec0a3851d5b383', 'hex'); 21 | const COSEMessage = Buffer.from('d8608443a10101a2044a6f75722d736563726574054c291a40271067ff57b1623c30581f23b663aaf9dfb91c5a39a175118ad7d72d416385b1b610e28b3b3fd824a397818340a040', 'hex'); 22 | const buf = await cose.encrypt.read(COSEMessage, key); 23 | console.log('Protected message: ' + buf.toString('utf8')); 24 | } catch (error) { 25 | console.log(error); 26 | } 27 | } 28 | run(); 29 | -------------------------------------------------------------------------------- /examples/mac.js: -------------------------------------------------------------------------------- 1 | const cose = require('../'); 2 | 3 | async function run () { 4 | try { 5 | const plaintext = 'Important message!'; 6 | const headers = { 7 | p: { alg: 'SHA-256_64' }, 8 | u: { kid: 'our-secret' } 9 | }; 10 | const recipent = { 11 | key: Buffer.from('231f4c4d4d3051fdc2ec0a3851d5b383', 'hex') 12 | }; 13 | const buf = await cose.mac.create(headers, plaintext, recipent); 14 | console.log('MACed message: ' + buf.toString('hex')); 15 | } catch (error) { 16 | console.log(error); 17 | } 18 | 19 | try { 20 | const key = Buffer.from('231f4c4d4d3051fdc2ec0a3851d5b383', 'hex'); 21 | const COSEMessage = Buffer.from('d18443a10104a1044a6f75722d73656372657472496d706f7274616e74206d65737361676521488894981d4aa5d614', 'hex'); 22 | const buf = await cose.mac.read(COSEMessage, key); 23 | console.log('Verified message: ' + buf.toString('utf8')); 24 | } catch (error) { 25 | console.log(error); 26 | } 27 | } 28 | run(); 29 | -------------------------------------------------------------------------------- /examples/sign-rs.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | const cose = require('../lib'); 3 | 4 | async function sample () { 5 | const plaintext = 'Important message!'; 6 | const headers = { 7 | p: { alg: 'RS256' }, 8 | u: { kid: '11' } 9 | }; 10 | const keys = crypto.generateKeyPairSync('rsa', { 11 | modulusLength: 2048, 12 | publicKeyEncoding: { 13 | type: 'spki', 14 | format: 'pem' 15 | }, 16 | privateKeyEncoding: { 17 | type: 'pkcs8', 18 | format: 'pem' 19 | } 20 | }); 21 | const signer = { 22 | key: keys.privateKey 23 | }; 24 | 25 | const msg = await cose.sign.create(headers, plaintext, signer); 26 | console.log('Signed message: ' + msg.toString('hex')); 27 | 28 | const verifier = { 29 | key: keys.publicKey 30 | }; 31 | 32 | const plaintext2 = await cose.sign.verify(msg, verifier); 33 | console.log('Verified message: ' + plaintext2.toString('utf8')); 34 | } 35 | sample(); 36 | -------------------------------------------------------------------------------- /examples/sign.js: -------------------------------------------------------------------------------- 1 | const cose = require('../'); 2 | 3 | async function run () { 4 | try { 5 | const plaintext = 'Important message!'; 6 | const headers = { 7 | p: { alg: 'ES256' }, 8 | u: { kid: '11' } 9 | }; 10 | const signer = { 11 | key: { 12 | d: Buffer.from('6c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c19', 'hex') 13 | } 14 | }; 15 | const buf = await cose.sign.create(headers, plaintext, signer); 16 | console.log('Signed message: ' + buf.toString('hex')); 17 | } catch (error) { 18 | console.log(error); 19 | } 20 | 21 | try { 22 | const verifier = { 23 | key: { 24 | x: Buffer.from('143329cce7868e416927599cf65a34f3ce2ffda55a7eca69ed8919a394d42f0f', 'hex'), 25 | y: Buffer.from('60f7f1a780d8a783bfb7a2dd6b2796e8128dbbcef9d3d168db9529971a36e7b9', 'hex') 26 | } 27 | }; 28 | const COSEMessage = Buffer.from('d28443a10126a10442313172496d706f7274616e74206d6573736167652158404c2b6b66dfedc4cfef0f221cf7ac7f95087a4c4245fef0063a0fd4014b670f642d31e26d38345bb4efcdc7ded3083ab4fe71b62a23f766d83785f044b20534f9', 'hex'); 29 | const buf = await cose.sign.verify(COSEMessage, verifier); 30 | console.log('Verified message: ' + buf.toString('utf8')); 31 | } catch (error) { 32 | console.log(error); 33 | } 34 | } 35 | run(); 36 | -------------------------------------------------------------------------------- /lib/common.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const AlgToTags = { 6 | PS512: -39, 7 | PS384: -38, 8 | PS256: -37, 9 | RS512: -259, 10 | RS384: -258, 11 | RS256: -257, 12 | 'ECDH-SS-512': -28, 13 | 'ECDH-SS': -27, 14 | 'ECDH-ES-512': -26, 15 | 'ECDH-ES': -25, 16 | ES256: -7, 17 | ES384: -35, 18 | ES512: -36, 19 | direct: -6, 20 | A128GCM: 1, 21 | A192GCM: 2, 22 | A256GCM: 3, 23 | 'SHA-256_64': 4, 24 | 'SHA-256-64': 4, 25 | 'HS256/64': 4, 26 | 'SHA-256': 5, 27 | HS256: 5, 28 | 'SHA-384': 6, 29 | HS384: 6, 30 | 'SHA-512': 7, 31 | HS512: 7, 32 | 'AES-CCM-16-64-128': 10, 33 | 'AES-CCM-16-128/64': 10, 34 | 'AES-CCM-16-64-256': 11, 35 | 'AES-CCM-16-256/64': 11, 36 | 'AES-CCM-64-64-128': 12, 37 | 'AES-CCM-64-128/64': 12, 38 | 'AES-CCM-64-64-256': 13, 39 | 'AES-CCM-64-256/64': 13, 40 | 'AES-MAC-128/64': 14, 41 | 'AES-MAC-256/64': 15, 42 | 'AES-MAC-128/128': 25, 43 | 'AES-MAC-256/128': 26, 44 | 'AES-CCM-16-128-128': 30, 45 | 'AES-CCM-16-128/128': 30, 46 | 'AES-CCM-16-128-256': 31, 47 | 'AES-CCM-16-256/128': 31, 48 | 'AES-CCM-64-128-128': 32, 49 | 'AES-CCM-64-128/128': 32, 50 | 'AES-CCM-64-128-256': 33, 51 | 'AES-CCM-64-256/128': 33 52 | }; 53 | 54 | const Translators = { 55 | kid: (value) => { 56 | return Buffer.from(value, 'utf8'); 57 | }, 58 | alg: (value) => { 59 | if (!(AlgToTags[value])) { 60 | throw new Error('Unknown \'alg\' parameter, ' + value); 61 | } 62 | return AlgToTags[value]; 63 | } 64 | }; 65 | 66 | const HeaderParameters = { 67 | partyUNonce: -22, 68 | static_key_id: -3, 69 | static_key: -2, 70 | ephemeral_key: -1, 71 | alg: 1, 72 | crit: 2, 73 | content_type: 3, 74 | ctyp: 3, // one could question this but it makes testing easier 75 | kid: 4, 76 | IV: 5, 77 | Partial_IV: 6, 78 | counter_signature: 7, 79 | x5chain: 33 80 | }; 81 | 82 | exports.EMPTY_BUFFER = Buffer.alloc(0); 83 | 84 | exports.TranslateHeaders = function (header) { 85 | const result = new Map(); 86 | for (const param in header) { 87 | if (!HeaderParameters[param]) { 88 | throw new Error('Unknown parameter, \'' + param + '\''); 89 | } 90 | let value = header[param]; 91 | if (Translators[param]) { 92 | value = Translators[param](header[param]); 93 | } 94 | if (value !== undefined && value !== null) { 95 | result.set(HeaderParameters[param], value); 96 | } 97 | } 98 | return result; 99 | }; 100 | 101 | const KeyParameters = { 102 | crv: -1, 103 | k: -1, 104 | x: -2, 105 | y: -3, 106 | d: -4, 107 | kty: 1 108 | }; 109 | 110 | const KeyTypes = { 111 | OKP: 1, 112 | EC2: 2, 113 | RSA: 3, 114 | Symmetric: 4 115 | }; 116 | 117 | const KeyCrv = { 118 | 'P-256': 1, 119 | 'P-384': 2, 120 | 'P-521': 3, 121 | X25519: 4, 122 | X448: 5, 123 | Ed25519: 6, 124 | Ed448: 7 125 | }; 126 | 127 | const KeyTranslators = { 128 | kty: (value) => { 129 | if (!(KeyTypes[value])) { 130 | throw new Error('Unknown \'kty\' parameter, ' + value); 131 | } 132 | return KeyTypes[value]; 133 | }, 134 | crv: (value) => { 135 | if (!(KeyCrv[value])) { 136 | throw new Error('Unknown \'crv\' parameter, ' + value); 137 | } 138 | return KeyCrv[value]; 139 | } 140 | }; 141 | 142 | exports.TranslateKey = function (key) { 143 | const result = new Map(); 144 | for (const param in key) { 145 | if (!KeyParameters[param]) { 146 | throw new Error('Unknown parameter, \'' + param + '\''); 147 | } 148 | let value = key[param]; 149 | if (KeyTranslators[param]) { 150 | value = KeyTranslators[param](value); 151 | } 152 | result.set(KeyParameters[param], value); 153 | } 154 | return result; 155 | }; 156 | 157 | module.exports.xor = function (a, b) { 158 | const buffer = Buffer.alloc(Math.max(a.length, b.length)); 159 | for (let i = 1; i <= buffer.length; ++i) { 160 | const av = (a.length - i) < 0 ? 0 : a[a.length - i]; 161 | const bv = (b.length - i) < 0 ? 0 : b[b.length - i]; 162 | buffer[buffer.length - i] = av ^ bv; 163 | } 164 | return buffer; 165 | }; 166 | 167 | exports.HeaderParameters = HeaderParameters; 168 | 169 | exports.runningInNode = function () { 170 | return Object.prototype.toString.call(global.process) === '[object process]'; 171 | }; 172 | -------------------------------------------------------------------------------- /lib/encrypt.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cbor = require('cbor'); 6 | const crypto = require('crypto'); 7 | const Promise = require('any-promise'); 8 | const common = require('./common'); 9 | const HKDF = require('node-hkdf-sync'); 10 | 11 | const Tagged = cbor.Tagged; 12 | 13 | const EMPTY_BUFFER = common.EMPTY_BUFFER; 14 | const EncryptTag = exports.EncryptTag = 96; 15 | const Encrypt0Tag = exports.Encrypt0Tag = 16; 16 | 17 | const runningInNode = common.runningInNode; 18 | 19 | const TagToAlg = { 20 | 1: 'A128GCM', 21 | 2: 'A192GCM', 22 | 3: 'A256GCM', 23 | 10: 'AES-CCM-16-64-128', 24 | 11: 'AES-CCM-16-64-256', 25 | 12: 'AES-CCM-64-64-128', 26 | 13: 'AES-CCM-64-64-256', 27 | 30: 'AES-CCM-16-128-128', 28 | 31: 'AES-CCM-16-128-256', 29 | 32: 'AES-CCM-64-128-128', 30 | 33: 'AES-CCM-64-128-256' 31 | }; 32 | 33 | const COSEAlgToNodeAlg = { 34 | A128GCM: 'aes-128-gcm', 35 | A192GCM: 'aes-192-gcm', 36 | A256GCM: 'aes-256-gcm', 37 | 38 | 'AES-CCM-16-64-128': 'aes-128-ccm', 39 | 'AES-CCM-16-64-256': 'aes-256-ccm', 40 | 'AES-CCM-64-64-128': 'aes-128-ccm', 41 | 'AES-CCM-64-64-256': 'aes-256-ccm', 42 | 'AES-CCM-16-128-128': 'aes-128-ccm', 43 | 'AES-CCM-16-128-256': 'aes-256-ccm', 44 | 'AES-CCM-64-128-128': 'aes-128-ccm', 45 | 'AES-CCM-64-128-256': 'aes-256-ccm' 46 | }; 47 | 48 | const isNodeAlg = { 49 | 1: true, // A128GCM 50 | 2: true, // A192GCM 51 | 3: true // A256GCM 52 | }; 53 | 54 | const isCCMAlg = { 55 | 10: true, // AES-CCM-16-64-128 56 | 11: true, // AES-CCM-16-64-256 57 | 12: true, // AES-CCM-64-64-128 58 | 13: true, // AES-CCM-64-64-256 59 | 30: true, // AES-CCM-16-128-128 60 | 31: true, // AES-CCM-16-128-256 61 | 32: true, // AES-CCM-64-128-128 62 | 33: true // AES-CCM-64-128-256 63 | }; 64 | 65 | const authTagLength = { 66 | 1: 16, 67 | 2: 16, 68 | 3: 16, 69 | 10: 8, // AES-CCM-16-64-128 70 | 11: 8, // AES-CCM-16-64-256 71 | 12: 8, // AES-CCM-64-64-128 72 | 13: 8, // AES-CCM-64-64-256 73 | 30: 16, // AES-CCM-16-128-128 74 | 31: 16, // AES-CCM-16-128-256 75 | 32: 16, // AES-CCM-64-128-128 76 | 33: 16 // AES-CCM-64-128-256 77 | }; 78 | 79 | const ivLenght = { 80 | 1: 12, // A128GCM 81 | 2: 12, // A192GCM 82 | 3: 12, // A256GCM 83 | 10: 13, // AES-CCM-16-64-128 84 | 11: 13, // AES-CCM-16-64-256 85 | 12: 7, // AES-CCM-64-64-128 86 | 13: 7, // AES-CCM-64-64-256 87 | 30: 13, // AES-CCM-16-128-128 88 | 31: 13, // AES-CCM-16-128-256 89 | 32: 7, // AES-CCM-64-128-128 90 | 33: 7 // AES-CCM-64-128-256 91 | }; 92 | 93 | const keyLength = { 94 | 1: 16, // A128GCM 95 | 2: 24, // A192GCM 96 | 3: 32, // A256GCM 97 | 10: 16, // AES-CCM-16-64-128 98 | 11: 32, // AES-CCM-16-64-256 99 | 12: 16, // AES-CCM-64-64-128 100 | 13: 32, // AES-CCM-64-64-256 101 | 30: 16, // AES-CCM-16-128-128 102 | 31: 32, // AES-CCM-16-128-256 103 | 32: 16, // AES-CCM-64-128-128 104 | 33: 32, // AES-CCM-64-128-256 105 | 'P-521': 66, 106 | 'P-256': 32 107 | }; 108 | 109 | const HKDFAlg = { 110 | 'ECDH-ES': 'sha256', 111 | 'ECDH-ES-512': 'sha512', 112 | 'ECDH-SS': 'sha256', 113 | 'ECDH-SS-512': 'sha512' 114 | }; 115 | 116 | const nodeCRV = { 117 | 'P-521': 'secp521r1', 118 | 'P-256': 'prime256v1' 119 | }; 120 | 121 | function createAAD (p, context, externalAAD) { 122 | p = (!p.size) ? EMPTY_BUFFER : cbor.encode(p); 123 | const encStructure = [ 124 | context, 125 | p, 126 | externalAAD 127 | ]; 128 | return cbor.encode(encStructure); 129 | } 130 | 131 | function _randomSource (bytes) { 132 | return crypto.randomBytes(bytes); 133 | } 134 | 135 | function nodeEncrypt (payload, key, alg, iv, aad, ccm = false) { 136 | const nodeAlg = COSEAlgToNodeAlg[TagToAlg[alg]]; 137 | const chiperOptions = ccm ? { authTagLength: authTagLength[alg] } : null; 138 | const aadOptions = ccm ? { plaintextLength: Buffer.byteLength(payload) } : null; 139 | const cipher = crypto.createCipheriv(nodeAlg, key, iv, chiperOptions); 140 | cipher.setAAD(aad, aadOptions); 141 | return Buffer.concat([ 142 | cipher.update(payload), 143 | cipher.final(), 144 | cipher.getAuthTag() 145 | ]); 146 | } 147 | 148 | function createContext (rp, alg, partyUNonce) { 149 | return cbor.encode([ 150 | alg, // AlgorithmID 151 | [ // PartyUInfo 152 | null, // identity 153 | (partyUNonce || null), // nonce 154 | null // other 155 | ], 156 | [ // PartyVInfo 157 | null, // identity 158 | null, // nonce 159 | null // other 160 | ], 161 | [ 162 | keyLength[alg] * 8, // keyDataLength 163 | rp // protected 164 | ] 165 | ]); 166 | } 167 | 168 | exports.create = function (headers, payload, recipients, options) { 169 | return new Promise((resolve, reject) => { 170 | options = options || {}; 171 | const externalAAD = options.externalAAD || EMPTY_BUFFER; 172 | const randomSource = options.randomSource || _randomSource; 173 | let u = headers.u || {}; 174 | let p = headers.p || {}; 175 | 176 | p = common.TranslateHeaders(p); 177 | u = common.TranslateHeaders(u); 178 | 179 | const alg = p.get(common.HeaderParameters.alg) || u.get(common.HeaderParameters.alg); 180 | 181 | if (!alg) { 182 | throw new Error('Missing mandatory parameter \'alg\''); 183 | } 184 | 185 | if (Array.isArray(recipients)) { 186 | if (recipients.length === 0) { 187 | throw new Error('There has to be at least one recipent'); 188 | } 189 | if (recipients.length > 1) { 190 | throw new Error('Encrypting with multiple recipents is not implemented'); 191 | } 192 | 193 | let iv; 194 | if (options.contextIv) { 195 | const partialIv = randomSource(2); 196 | iv = common.xor(partialIv, options.contextIv); 197 | u.set(common.HeaderParameters.Partial_IV, partialIv); 198 | } else { 199 | iv = randomSource(ivLenght[alg]); 200 | u.set(common.HeaderParameters.IV, iv); 201 | } 202 | 203 | const aad = createAAD(p, 'Encrypt', externalAAD); 204 | 205 | let key; 206 | let recipientStruct; 207 | // TODO do a more accurate check 208 | if (recipients[0] && recipients[0].p && 209 | (recipients[0].p.alg === 'ECDH-ES' || 210 | recipients[0].p.alg === 'ECDH-ES-512' || 211 | recipients[0].p.alg === 'ECDH-SS' || 212 | recipients[0].p.alg === 'ECDH-SS-512')) { 213 | const recipient = crypto.createECDH(nodeCRV[recipients[0].key.crv]); 214 | const generated = crypto.createECDH(nodeCRV[recipients[0].key.crv]); 215 | recipient.setPrivateKey(recipients[0].key.d); 216 | let pk = randomSource(keyLength[recipients[0].key.crv]); 217 | if (recipients[0].p.alg === 'ECDH-ES' || 218 | recipients[0].p.alg === 'ECDH-ES-512') { 219 | pk = randomSource(keyLength[recipients[0].key.crv]); 220 | pk[0] = (recipients[0].key.crv !== 'P-521' || pk[0] === 1) ? pk[0] : 0; 221 | } else { 222 | pk = recipients[0].sender.d; 223 | } 224 | 225 | generated.setPrivateKey(pk); 226 | const senderPublicKey = generated.getPublicKey(); 227 | const recipientPublicKey = Buffer.concat([ 228 | Buffer.from('04', 'hex'), 229 | recipients[0].key.x, 230 | recipients[0].key.y 231 | ]); 232 | 233 | const generatedKey = common.TranslateKey({ 234 | crv: recipients[0].key.crv, 235 | x: senderPublicKey.slice(1, keyLength[recipients[0].key.crv] + 1), // TODO slice based on key length 236 | y: senderPublicKey.slice(keyLength[recipients[0].key.crv] + 1), 237 | kty: 'EC2' // TODO use real value 238 | }); 239 | const rp = cbor.encode(common.TranslateHeaders(recipients[0].p)); 240 | const ikm = generated.computeSecret(recipientPublicKey); 241 | let partyUNonce = null; 242 | if (recipients[0].p.alg === 'ECDH-SS' || recipients[0].p.alg === 'ECDH-SS-512') { 243 | partyUNonce = randomSource(64); // TODO use real value 244 | } 245 | const context = createContext(rp, alg, partyUNonce); 246 | const nrBytes = keyLength[alg]; 247 | const hkdf = new HKDF(HKDFAlg[recipients[0].p.alg], undefined, ikm); 248 | key = hkdf.derive(context, nrBytes); 249 | let ru = recipients[0].u; 250 | 251 | if (recipients[0].p.alg === 'ECDH-ES' || 252 | recipients[0].p.alg === 'ECDH-ES-512') { 253 | ru.ephemeral_key = generatedKey; 254 | } else { 255 | ru.static_key = generatedKey; 256 | } 257 | 258 | ru.partyUNonce = partyUNonce; 259 | ru = common.TranslateHeaders(ru); 260 | 261 | recipientStruct = [[rp, ru, EMPTY_BUFFER]]; 262 | } else { 263 | key = recipients[0].key; 264 | const ru = common.TranslateHeaders(recipients[0].u); 265 | recipientStruct = [[EMPTY_BUFFER, ru, EMPTY_BUFFER]]; 266 | } 267 | 268 | let ciphertext; 269 | if (isNodeAlg[alg]) { 270 | ciphertext = nodeEncrypt(payload, key, alg, iv, aad); 271 | } else if (isCCMAlg[alg] && runningInNode()) { 272 | ciphertext = nodeEncrypt(payload, key, alg, iv, aad, true); 273 | } else { 274 | throw new Error('No implementation for algorithm, ' + alg); 275 | } 276 | 277 | if (p.size === 0 && options.encodep === 'empty') { 278 | p = EMPTY_BUFFER; 279 | } else { 280 | p = cbor.encode(p); 281 | } 282 | 283 | const encrypted = [p, u, ciphertext, recipientStruct]; 284 | resolve(cbor.encode(options.excludetag ? encrypted : new Tagged(EncryptTag, encrypted))); 285 | } else { 286 | let iv; 287 | if (options.contextIv) { 288 | const partialIv = randomSource(2); 289 | iv = common.xor(partialIv, options.contextIv); 290 | u.set(common.HeaderParameters.Partial_IV, partialIv); 291 | } else { 292 | iv = randomSource(ivLenght[alg]); 293 | u.set(common.HeaderParameters.IV, iv); 294 | } 295 | 296 | const key = recipients.key; 297 | 298 | const aad = createAAD(p, 'Encrypt0', externalAAD); 299 | let ciphertext; 300 | if (isNodeAlg[alg]) { 301 | ciphertext = nodeEncrypt(payload, key, alg, iv, aad); 302 | } else if (isCCMAlg[alg] && runningInNode()) { 303 | ciphertext = nodeEncrypt(payload, key, alg, iv, aad, true); 304 | } else { 305 | throw new Error('No implementation for algorithm, ' + alg); 306 | } 307 | 308 | if (p.size === 0 && options.encodep === 'empty') { 309 | p = EMPTY_BUFFER; 310 | } else { 311 | p = cbor.encode(p); 312 | } 313 | const encrypted = [p, u, ciphertext]; 314 | resolve(cbor.encode(options.excludetag ? encrypted : new Tagged(Encrypt0Tag, encrypted))); 315 | } 316 | }); 317 | }; 318 | 319 | function nodeDecrypt (ciphertext, key, alg, iv, tag, aad, ccm = false) { 320 | const nodeAlg = COSEAlgToNodeAlg[TagToAlg[alg]]; 321 | const chiperOptions = ccm ? { authTagLength: authTagLength[alg] } : null; 322 | const aadOptions = ccm ? { plaintextLength: Buffer.byteLength(ciphertext) } : null; 323 | const decipher = crypto.createDecipheriv(nodeAlg, key, iv, chiperOptions); 324 | decipher.setAuthTag(tag); 325 | decipher.setAAD(aad, aadOptions); 326 | return Buffer.concat([decipher.update(ciphertext), decipher.final()]); 327 | } 328 | 329 | exports.read = async function (data, key, options) { 330 | options = options || {}; 331 | const externalAAD = options.externalAAD || EMPTY_BUFFER; 332 | let obj = await cbor.decodeFirst(data); 333 | let msgTag = options.defaultType ? options.defaultType : EncryptTag; 334 | if (obj instanceof Tagged) { 335 | if (obj.tag !== EncryptTag && obj.tag !== Encrypt0Tag) { 336 | throw new Error('Unknown tag, ' + obj.tag); 337 | } 338 | msgTag = obj.tag; 339 | obj = obj.value; 340 | } 341 | 342 | if (!Array.isArray(obj)) { 343 | throw new Error('Expecting Array'); 344 | } 345 | 346 | if (msgTag === EncryptTag && obj.length !== 4) { 347 | throw new Error('Expecting Array of lenght 4 for COSE Encrypt message'); 348 | } 349 | 350 | if (msgTag === Encrypt0Tag && obj.length !== 3) { 351 | throw new Error('Expecting Array of lenght 4 for COSE Encrypt0 message'); 352 | } 353 | 354 | let [p, u, ciphertext] = obj; 355 | 356 | p = (p.length === 0) ? EMPTY_BUFFER : cbor.decodeFirstSync(p); 357 | p = (!p.size) ? EMPTY_BUFFER : p; 358 | u = (!u.size) ? EMPTY_BUFFER : u; 359 | 360 | const alg = (p !== EMPTY_BUFFER) ? p.get(common.HeaderParameters.alg) : (u !== EMPTY_BUFFER) ? u.get(common.HeaderParameters.alg) : undefined; 361 | if (!TagToAlg[alg]) { 362 | throw new Error('Unknown or unsupported algorithm ' + alg); 363 | } 364 | 365 | let iv = u.get(common.HeaderParameters.IV); 366 | const partialIv = u.get(common.HeaderParameters.Partial_IV); 367 | if (iv && partialIv) { 368 | throw new Error('IV and Partial IV parameters MUST NOT both be present in the same security layer'); 369 | } 370 | if (partialIv && !options.contextIv) { 371 | throw new Error('Context IV must be provided when Partial IV is used'); 372 | } 373 | if (partialIv && options.contextIv) { 374 | iv = common.xor(partialIv, options.contextIv); 375 | } 376 | 377 | const tagLength = authTagLength[alg]; 378 | const tag = ciphertext.slice(ciphertext.length - tagLength, ciphertext.length); 379 | ciphertext = ciphertext.slice(0, ciphertext.length - tagLength); 380 | 381 | const aad = createAAD(p, (msgTag === EncryptTag ? 'Encrypt' : 'Encrypt0'), externalAAD); 382 | if (isNodeAlg[alg]) { 383 | return nodeDecrypt(ciphertext, key, alg, iv, tag, aad); 384 | } else if (isCCMAlg[alg] && runningInNode()) { 385 | return nodeDecrypt(ciphertext, key, alg, iv, tag, aad, true); 386 | } else { 387 | throw new Error('No implementation for algorithm, ' + alg); 388 | } 389 | }; 390 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | exports.common = require('./common'); 6 | exports.mac = require('./mac'); 7 | exports.sign = require('./sign'); 8 | exports.encrypt = require('./encrypt'); 9 | -------------------------------------------------------------------------------- /lib/mac.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cbor = require('cbor'); 6 | const aesCbcMac = require('aes-cbc-mac'); 7 | const crypto = require('crypto'); 8 | const Promise = require('any-promise'); 9 | const common = require('./common'); 10 | const Tagged = cbor.Tagged; 11 | const EMPTY_BUFFER = common.EMPTY_BUFFER; 12 | 13 | const MAC0Tag = exports.MAC0Tag = 17; 14 | const MACTag = exports.MACTag = 97; 15 | 16 | const AlgFromTags = { 17 | 4: 'SHA-256_64', 18 | 5: 'SHA-256', 19 | 6: 'SHA-384', 20 | 7: 'SHA-512', 21 | 14: 'AES-MAC-128/64', 22 | 15: 'AES-MAC-256/64', 23 | 25: 'AES-MAC-128/128', 24 | 26: 'AES-MAC-256/128' 25 | }; 26 | 27 | const COSEAlgToNodeAlg = { 28 | 'SHA-256_64': 'sha256', 29 | 'SHA-256': 'sha256', 30 | HS256: 'sha256', 31 | 'SHA-384': 'sha384', 32 | 'SHA-512': 'sha512', 33 | 'AES-MAC-128/64': 'aes-cbc-mac-64', 34 | 'AES-MAC-128/128': 'aes-cbc-mac-128', 35 | 'AES-MAC-256/64': 'aes-cbc-mac-64', 36 | 'AES-MAC-256/128': 'aes-cbc-mac-128' 37 | }; 38 | 39 | const CutTo = { 40 | 4: 8, 41 | 5: 32, 42 | 6: 48, 43 | 7: 64 44 | }; 45 | 46 | const context = {}; 47 | context[MAC0Tag] = 'MAC0'; 48 | context[MACTag] = 'MAC'; 49 | 50 | function doMac (context, p, externalAAD, payload, alg, key) { 51 | return new Promise((resolve, reject) => { 52 | const MACstructure = [ 53 | context, // 'MAC0' or 'MAC1', // context 54 | p, // protected 55 | externalAAD, // bstr, 56 | payload // bstr 57 | ]; 58 | 59 | const toBeMACed = cbor.encode(MACstructure); 60 | if (alg === 'aes-cbc-mac-64') { 61 | const mac = aesCbcMac.create(key, toBeMACed, 8); 62 | resolve(mac); 63 | } else if (alg === 'aes-cbc-mac-128') { 64 | const mac = aesCbcMac.create(key, toBeMACed, 16); 65 | resolve(mac); 66 | } else { 67 | const hmac = crypto.createHmac(alg, key); 68 | hmac.end(toBeMACed, function () { 69 | resolve(hmac.read()); 70 | }); 71 | } 72 | }); 73 | } 74 | 75 | exports.create = async function (headers, payload, recipents, externalAAD, options) { 76 | options = options || {}; 77 | externalAAD = externalAAD || EMPTY_BUFFER; 78 | let u = headers.u || {}; 79 | let p = headers.p || {}; 80 | 81 | p = common.TranslateHeaders(p); 82 | u = common.TranslateHeaders(u); 83 | 84 | const alg = p.get(common.HeaderParameters.alg) || u.get(common.HeaderParameters.alg); 85 | 86 | if (!alg) { 87 | throw new Error('Missing mandatory parameter \'alg\''); 88 | } 89 | 90 | if (recipents.length === 0) { 91 | throw new Error('There has to be at least one recipent'); 92 | } 93 | 94 | const predictableP = (!p.size) ? EMPTY_BUFFER : cbor.encode(p); 95 | if (p.size === 0 && options.encodep === 'empty') { 96 | p = EMPTY_BUFFER; 97 | } else { 98 | p = cbor.encode(p); 99 | } 100 | // TODO check crit headers 101 | if (Array.isArray(recipents)) { 102 | if (recipents.length > 1) { 103 | throw new Error('MACing with multiple recipents is not implemented'); 104 | } 105 | const recipent = recipents[0]; 106 | let tag = await doMac('MAC', predictableP, externalAAD, payload, COSEAlgToNodeAlg[AlgFromTags[alg]], recipent.key); 107 | tag = tag.slice(0, CutTo[alg]); 108 | const ru = common.TranslateHeaders(recipent.u); 109 | const rp = EMPTY_BUFFER; 110 | const maced = [p, u, payload, tag, [[rp, ru, EMPTY_BUFFER]]]; 111 | return cbor.encode(options.excludetag ? maced : new Tagged(MACTag, maced)); 112 | } else { 113 | let tag = await doMac('MAC0', predictableP, externalAAD, payload, COSEAlgToNodeAlg[AlgFromTags[alg]], recipents.key); 114 | tag = tag.slice(0, CutTo[alg]); 115 | const maced = [p, u, payload, tag]; 116 | return cbor.encode(options.excludetag ? maced : new Tagged(MAC0Tag, maced)); 117 | } 118 | }; 119 | 120 | exports.read = async function (data, key, externalAAD, options) { 121 | options = options || {}; 122 | externalAAD = externalAAD || EMPTY_BUFFER; 123 | 124 | let obj = await cbor.decodeFirst(data); 125 | 126 | let type = options.defaultType ? options.defaultType : MAC0Tag; 127 | if (obj instanceof Tagged) { 128 | if (obj.tag !== MAC0Tag && obj.tag !== MACTag) { 129 | throw new Error('Unexpected cbor tag, \'' + obj.tag + '\''); 130 | } 131 | type = obj.tag; 132 | obj = obj.value; 133 | } 134 | 135 | if (!Array.isArray(obj)) { 136 | throw new Error('Expecting Array'); 137 | } 138 | 139 | if (type === MAC0Tag && obj.length !== 4) { 140 | throw new Error('Expecting Array of lenght 4'); 141 | } 142 | if (type === MACTag && obj.length !== 5) { 143 | throw new Error('Expecting Array of lenght 5'); 144 | } 145 | 146 | let [p, u, payload, tag] = obj; 147 | p = (!p.length) ? EMPTY_BUFFER : cbor.decode(p); 148 | p = (!p.size) ? EMPTY_BUFFER : p; 149 | u = (!u.size) ? EMPTY_BUFFER : u; 150 | 151 | // TODO validate protected header 152 | const alg = (p !== EMPTY_BUFFER) ? p.get(common.HeaderParameters.alg) : (u !== EMPTY_BUFFER) ? u.get(common.HeaderParameters.alg) : undefined; 153 | p = (!p.size) ? EMPTY_BUFFER : cbor.encode(p); 154 | if (!AlgFromTags[alg]) { 155 | throw new Error('Unknown algorithm, ' + alg); 156 | } 157 | if (!COSEAlgToNodeAlg[AlgFromTags[alg]]) { 158 | throw new Error('Unsupported algorithm, ' + AlgFromTags[alg]); 159 | } 160 | 161 | let calcTag = await doMac(context[type], p, externalAAD, payload, COSEAlgToNodeAlg[AlgFromTags[alg]], key); 162 | calcTag = calcTag.slice(0, CutTo[alg]); 163 | if (tag.toString('hex') !== calcTag.toString('hex')) { 164 | throw new Error('Tag mismatch'); 165 | } 166 | return payload; 167 | }; 168 | -------------------------------------------------------------------------------- /lib/sign.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cbor = require('cbor'); 6 | const EC = require('elliptic').ec; 7 | const crypto = require('crypto'); 8 | const NodeRSA = require('node-rsa'); 9 | const common = require('./common'); 10 | const EMPTY_BUFFER = common.EMPTY_BUFFER; 11 | const Tagged = cbor.Tagged; 12 | 13 | const SignTag = exports.SignTag = 98; 14 | const Sign1Tag = exports.Sign1Tag = 18; 15 | 16 | const AlgFromTags = {}; 17 | AlgFromTags[-7] = { sign: 'ES256', digest: 'SHA-256' }; 18 | AlgFromTags[-35] = { sign: 'ES384', digest: 'SHA-384' }; 19 | AlgFromTags[-36] = { sign: 'ES512', digest: 'SHA-512' }; 20 | AlgFromTags[-37] = { sign: 'PS256', digest: 'SHA-256' }; 21 | AlgFromTags[-38] = { sign: 'PS384', digest: 'SHA-384' }; 22 | AlgFromTags[-39] = { sign: 'PS512', digest: 'SHA-512' }; 23 | AlgFromTags[-257] = { sign: 'RS256', digest: 'SHA-256' }; 24 | AlgFromTags[-258] = { sign: 'RS384', digest: 'SHA-384' }; 25 | AlgFromTags[-259] = { sign: 'RS512', digest: 'SHA-512' }; 26 | 27 | const COSEAlgToNodeAlg = { 28 | ES256: { sign: 'p256', digest: 'sha256' }, 29 | ES384: { sign: 'p384', digest: 'sha384' }, 30 | ES512: { sign: 'p521', digest: 'sha512' }, 31 | RS256: { sign: 'RSA-SHA256' }, 32 | RS384: { sign: 'RSA-SHA384' }, 33 | RS512: { sign: 'RSA-SHA512' }, 34 | PS256: { alg: 'pss-sha256', saltLen: 32 }, 35 | PS384: { alg: 'pss-sha384', saltLen: 48 }, 36 | PS512: { alg: 'pss-sha512', saltLen: 64 } 37 | }; 38 | 39 | function doSign (SigStructure, signer, alg) { 40 | if (!AlgFromTags[alg]) { 41 | throw new Error('Unknown algorithm, ' + alg); 42 | } 43 | if (!COSEAlgToNodeAlg[AlgFromTags[alg].sign]) { 44 | throw new Error('Unsupported algorithm, ' + AlgFromTags[alg].sign); 45 | } 46 | 47 | let ToBeSigned = cbor.encode(SigStructure); 48 | 49 | let sig; 50 | if (AlgFromTags[alg].sign.startsWith('ES')) { 51 | const hash = crypto.createHash(COSEAlgToNodeAlg[AlgFromTags[alg].sign].digest); 52 | hash.update(ToBeSigned); 53 | ToBeSigned = hash.digest(); 54 | const ec = new EC(COSEAlgToNodeAlg[AlgFromTags[alg].sign].sign); 55 | const key = ec.keyFromPrivate(signer.key.d); 56 | const signature = key.sign(ToBeSigned); 57 | const bitLength = Math.ceil(ec.curve._bitLength / 8); 58 | sig = Buffer.concat([signature.r.toArrayLike(Buffer, undefined, bitLength), signature.s.toArrayLike(Buffer, undefined, bitLength)]); 59 | } else if (AlgFromTags[alg].sign.startsWith('PS')) { 60 | signer.key.dmp1 = signer.key.dp; 61 | signer.key.dmq1 = signer.key.dq; 62 | signer.key.coeff = signer.key.qi; 63 | const key = new NodeRSA().importKey(signer.key, 'components-private'); 64 | key.setOptions({ 65 | signingScheme: { 66 | scheme: COSEAlgToNodeAlg[AlgFromTags[alg].sign].alg.split('-')[0], 67 | hash: COSEAlgToNodeAlg[AlgFromTags[alg].sign].alg.split('-')[1], 68 | saltLength: COSEAlgToNodeAlg[AlgFromTags[alg].sign].saltLen 69 | } 70 | }); 71 | sig = key.sign(ToBeSigned); 72 | } else { 73 | const sign = crypto.createSign(COSEAlgToNodeAlg[AlgFromTags[alg].sign].sign); 74 | sign.update(ToBeSigned); 75 | sign.end(); 76 | sig = sign.sign(signer.key); 77 | } 78 | return sig; 79 | } 80 | 81 | exports.create = function (headers, payload, signers, options) { 82 | options = options || {}; 83 | let u = headers.u || {}; 84 | let p = headers.p || {}; 85 | 86 | p = common.TranslateHeaders(p); 87 | u = common.TranslateHeaders(u); 88 | let bodyP = p || {}; 89 | bodyP = (bodyP.size === 0) ? EMPTY_BUFFER : cbor.encode(bodyP); 90 | if (Array.isArray(signers)) { 91 | if (signers.length === 0) { 92 | throw new Error('There has to be at least one signer'); 93 | } 94 | if (signers.length > 1) { 95 | throw new Error('Only one signer is supported'); 96 | } 97 | // TODO handle multiple signers 98 | const signer = signers[0]; 99 | const externalAAD = signer.externalAAD || EMPTY_BUFFER; 100 | let signerP = signer.p || {}; 101 | let signerU = signer.u || {}; 102 | 103 | signerP = common.TranslateHeaders(signerP); 104 | signerU = common.TranslateHeaders(signerU); 105 | const alg = signerP.get(common.HeaderParameters.alg); 106 | signerP = (signerP.size === 0) ? EMPTY_BUFFER : cbor.encode(signerP); 107 | 108 | const SigStructure = [ 109 | 'Signature', 110 | bodyP, 111 | signerP, 112 | externalAAD, 113 | payload 114 | ]; 115 | 116 | const sig = doSign(SigStructure, signer, alg); 117 | if (p.size === 0 && options.encodep === 'empty') { 118 | p = EMPTY_BUFFER; 119 | } else { 120 | p = cbor.encode(p); 121 | } 122 | const signed = [p, u, payload, [[signerP, signerU, sig]]]; 123 | return cbor.encodeAsync(options.excludetag ? signed : new Tagged(SignTag, signed)); 124 | } else { 125 | const signer = signers; 126 | const externalAAD = signer.externalAAD || EMPTY_BUFFER; 127 | const alg = p.get(common.HeaderParameters.alg) || u.get(common.HeaderParameters.alg); 128 | const SigStructure = [ 129 | 'Signature1', 130 | bodyP, 131 | externalAAD, 132 | payload 133 | ]; 134 | const sig = doSign(SigStructure, signer, alg); 135 | if (p.size === 0 && options.encodep === 'empty') { 136 | p = EMPTY_BUFFER; 137 | } else { 138 | p = cbor.encode(p); 139 | } 140 | const signed = [p, u, payload, sig]; 141 | return cbor.encodeAsync(options.excludetag ? signed : new Tagged(Sign1Tag, signed), { canonical: true }); 142 | } 143 | }; 144 | 145 | function doVerify (SigStructure, verifier, alg, sig) { 146 | if (!AlgFromTags[alg]) { 147 | throw new Error('Unknown algorithm, ' + alg); 148 | } 149 | const nodeAlg = COSEAlgToNodeAlg[AlgFromTags[alg].sign]; 150 | if (!nodeAlg) { 151 | throw new Error('Unsupported algorithm, ' + AlgFromTags[alg].sign); 152 | } 153 | const ToBeSigned = cbor.encode(SigStructure); 154 | 155 | if (AlgFromTags[alg].sign.startsWith('ES')) { 156 | const hash = crypto.createHash(nodeAlg.digest); 157 | hash.update(ToBeSigned); 158 | const msgHash = hash.digest(); 159 | 160 | const pub = { x: verifier.key.x, y: verifier.key.y }; 161 | const ec = new EC(nodeAlg.sign); 162 | const key = ec.keyFromPublic(pub); 163 | sig = { r: sig.slice(0, sig.length / 2), s: sig.slice(sig.length / 2) }; 164 | if (!key.verify(msgHash, sig)) { 165 | throw new Error('Signature missmatch'); 166 | } 167 | } else if (AlgFromTags[alg].sign.startsWith('PS')) { 168 | const key = new NodeRSA().importKey(verifier.key, 'components-public'); 169 | key.setOptions({ 170 | signingScheme: { 171 | scheme: COSEAlgToNodeAlg[AlgFromTags[alg].sign].alg.split('-')[0], 172 | hash: COSEAlgToNodeAlg[AlgFromTags[alg].sign].alg.split('-')[1], 173 | saltLength: COSEAlgToNodeAlg[AlgFromTags[alg].sign].saltLen 174 | } 175 | }); 176 | if (!key.verify(ToBeSigned, sig, 'buffer', 'buffer')) { 177 | throw new Error('Signature missmatch'); 178 | } 179 | } else { 180 | const verify = crypto.createVerify(nodeAlg.sign); 181 | verify.update(ToBeSigned); 182 | if (!verify.verify(verifier.key, sig)) { 183 | throw new Error('Signature missmatch'); 184 | } 185 | } 186 | } 187 | 188 | function getSigner (signers, verifier) { 189 | for (let i = 0; i < signers.length; i++) { 190 | const kid = signers[i][1].get(common.HeaderParameters.kid); // TODO create constant for header locations 191 | if (kid.equals(Buffer.from(verifier.key.kid, 'utf8'))) { 192 | return signers[i]; 193 | } 194 | } 195 | } 196 | 197 | function getCommonParameter (first, second, parameter) { 198 | let result; 199 | if (first.get) { 200 | result = first.get(parameter); 201 | } 202 | if (!result && second.get) { 203 | result = second.get(parameter); 204 | } 205 | return result; 206 | } 207 | 208 | exports.verify = async function (payload, verifier, options) { 209 | options = options || {}; 210 | const obj = await cbor.decodeFirst(payload); 211 | return verifyInternal(verifier, options, obj); 212 | }; 213 | 214 | exports.verifySync = function (payload, verifier, options) { 215 | options = options || {}; 216 | const obj = cbor.decodeFirstSync(payload); 217 | return verifyInternal(verifier, options, obj); 218 | }; 219 | 220 | function verifyInternal (verifier, options, obj) { 221 | options = options || {}; 222 | let type = options.defaultType ? options.defaultType : SignTag; 223 | if (obj instanceof Tagged) { 224 | if (obj.tag !== SignTag && obj.tag !== Sign1Tag) { 225 | throw new Error('Unexpected cbor tag, \'' + obj.tag + '\''); 226 | } 227 | type = obj.tag; 228 | obj = obj.value; 229 | } 230 | 231 | if (!Array.isArray(obj)) { 232 | throw new Error('Expecting Array'); 233 | } 234 | 235 | if (obj.length !== 4) { 236 | throw new Error('Expecting Array of lenght 4'); 237 | } 238 | 239 | let [p, u, plaintext, signers] = obj; 240 | 241 | if (type === SignTag && !Array.isArray(signers)) { 242 | throw new Error('Expecting signature Array'); 243 | } 244 | 245 | p = (!p.length) ? EMPTY_BUFFER : cbor.decodeFirstSync(p); 246 | u = (!u.size) ? EMPTY_BUFFER : u; 247 | 248 | const signer = (type === SignTag ? getSigner(signers, verifier) : signers); 249 | 250 | if (!signer) { 251 | throw new Error('Failed to find signer with kid' + verifier.key.kid); 252 | } 253 | 254 | if (type === SignTag) { 255 | const externalAAD = verifier.externalAAD || EMPTY_BUFFER; 256 | let [signerP, , sig] = signer; 257 | signerP = (!signerP.length) ? EMPTY_BUFFER : signerP; 258 | p = (!p.size) ? EMPTY_BUFFER : cbor.encode(p); 259 | const signerPMap = cbor.decode(signerP); 260 | const alg = signerPMap.get(common.HeaderParameters.alg); 261 | const SigStructure = [ 262 | 'Signature', 263 | p, 264 | signerP, 265 | externalAAD, 266 | plaintext 267 | ]; 268 | doVerify(SigStructure, verifier, alg, sig); 269 | return plaintext; 270 | } else { 271 | const externalAAD = verifier.externalAAD || EMPTY_BUFFER; 272 | 273 | const alg = getCommonParameter(p, u, common.HeaderParameters.alg); 274 | p = (!p.size) ? EMPTY_BUFFER : cbor.encode(p); 275 | const SigStructure = [ 276 | 'Signature1', 277 | p, 278 | externalAAD, 279 | plaintext 280 | ]; 281 | doVerify(SigStructure, verifier, alg, signer); 282 | return plaintext; 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cose-js", 3 | "version": "0.8.3", 4 | "description": "JavaScript COSE implementation", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "clean": "rm -rf coverage/ .nyc_output/", 8 | "pretest": "semistandard --fix", 9 | "test": "pwd & ava ./test/*.js", 10 | "coverage": "nyc npm test", 11 | "coveragehtml": "nyc report -r html", 12 | "precoveragehtml": "npm run coverage", 13 | "coveralls": "nyc report --reporter=text-lcov | coveralls", 14 | "live": "live-server -q --port=4003 --ignorePattern='(js|css|png)$' coverage", 15 | "watch": "watch 'npm run coveragehtml' test lib", 16 | "dev": "npm-run-all -p --silent watch live" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/erdtman/COSE-JS.git" 21 | }, 22 | "keywords": [ 23 | "COSE", 24 | "Signing", 25 | "MAC", 26 | "Encrypt", 27 | "IoT" 28 | ], 29 | "author": "Samuel Erdtman", 30 | "contributors": [ 31 | { 32 | "name": "Joe Hildebrand", 33 | "email": "joe-github@cursive.net" 34 | } 35 | ], 36 | "license": "Apache-2.0", 37 | "bugs": { 38 | "url": "https://github.com/erdtman/cose-js/issues" 39 | }, 40 | "homepage": "https://github.com/erdtman/cose-js#readme", 41 | "dependencies": { 42 | "aes-cbc-mac": "^1.0.1", 43 | "any-promise": "^1.3.0", 44 | "cbor": "^8.1.0", 45 | "elliptic": "^6.4.0", 46 | "node-hkdf-sync": "^1.0.0", 47 | "node-rsa": "^1.1.1" 48 | }, 49 | "devDependencies": { 50 | "ava": "^3.15.0", 51 | "base64url": "^3.0.1", 52 | "jsonfile": "^2.4.0", 53 | "jwk-to-pem": "^2.0.5", 54 | "live-server": "*", 55 | "npm-run-all": "*", 56 | "nyc": "^15.1.0", 57 | "semistandard": "^16.0.1", 58 | "watch": "*" 59 | }, 60 | "engines": { 61 | "node": ">=12.0" 62 | }, 63 | "semistandard": { 64 | "ignore": "tmp/**" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/aes-ccm-examples.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | function randomSource (bytes) { 13 | if (bytes === 12) { 14 | return Buffer.from('02D1F7E6F26C43D4868D87CE', 'hex'); 15 | } else if (bytes === 2) { 16 | return Buffer.from('61A7', 'hex'); 17 | } else if (bytes === 13) { 18 | return Buffer.from('89F52F65A1C580933B5261A72F', 'hex'); 19 | } else if (bytes === 7) { 20 | return Buffer.from('89F52F65A1C580', 'hex'); 21 | } 22 | } 23 | 24 | test('create aes-ccm-enc-01', async t => { 25 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-01.json'); 26 | const p = example.input.encrypted.protected; 27 | const u = example.input.encrypted.unprotected; 28 | const plaintext = Buffer.from(example.input.plaintext); 29 | 30 | const recipient = { 31 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 32 | u: example.input.encrypted.recipients[0].unprotected 33 | }; 34 | 35 | const options = { 36 | randomSource: randomSource 37 | }; 38 | const header = { p: p, u: u }; 39 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 40 | t.true(Buffer.isBuffer(buf)); 41 | t.true(buf.length > 0); 42 | const actual = cbor.decodeFirstSync(buf); 43 | const expected = cbor.decodeFirstSync(example.output.cbor); 44 | t.true(deepEqual(actual, expected)); 45 | }); 46 | 47 | test('create aes-ccm-enc-02', async t => { 48 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-02.json'); 49 | const p = example.input.encrypted.protected; 50 | const u = example.input.encrypted.unprotected; 51 | const plaintext = Buffer.from(example.input.plaintext); 52 | 53 | const recipient = { 54 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 55 | u: example.input.encrypted.recipients[0].unprotected 56 | }; 57 | 58 | const options = { 59 | randomSource: randomSource 60 | }; 61 | const header = { p: p, u: u }; 62 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 63 | t.true(Buffer.isBuffer(buf)); 64 | t.true(buf.length > 0); 65 | const actual = cbor.decodeFirstSync(buf); 66 | const expected = cbor.decodeFirstSync(example.output.cbor); 67 | t.true(deepEqual(actual, expected)); 68 | }); 69 | 70 | test('create aes-ccm-enc-03', async t => { 71 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-03.json'); 72 | const p = example.input.encrypted.protected; 73 | const u = example.input.encrypted.unprotected; 74 | const plaintext = Buffer.from(example.input.plaintext); 75 | 76 | const recipient = { 77 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 78 | u: example.input.encrypted.recipients[0].unprotected 79 | }; 80 | 81 | const options = { 82 | randomSource: randomSource 83 | }; 84 | const header = { p: p, u: u }; 85 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 86 | 87 | t.true(Buffer.isBuffer(buf)); 88 | t.true(buf.length > 0); 89 | const actual = cbor.decodeFirstSync(buf); 90 | const expected = cbor.decodeFirstSync(example.output.cbor); 91 | t.true(deepEqual(actual, expected)); 92 | }); 93 | 94 | test('create aes-ccm-enc-04', async t => { 95 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-04.json'); 96 | const p = example.input.encrypted.protected; 97 | const u = example.input.encrypted.unprotected; 98 | const plaintext = Buffer.from(example.input.plaintext); 99 | 100 | const recipient = { 101 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 102 | u: example.input.encrypted.recipients[0].unprotected 103 | }; 104 | 105 | const options = { 106 | randomSource: randomSource 107 | }; 108 | const header = { p: p, u: u }; 109 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 110 | 111 | t.true(Buffer.isBuffer(buf)); 112 | t.true(buf.length > 0); 113 | const actual = cbor.decodeFirstSync(buf); 114 | const expected = cbor.decodeFirstSync(example.output.cbor); 115 | t.true(deepEqual(actual, expected)); 116 | }); 117 | 118 | test('create aes-ccm-enc-05', async t => { 119 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-05.json'); 120 | const p = example.input.encrypted.protected; 121 | const u = example.input.encrypted.unprotected; 122 | const plaintext = Buffer.from(example.input.plaintext); 123 | 124 | const recipient = { 125 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 126 | u: example.input.encrypted.recipients[0].unprotected 127 | }; 128 | 129 | const options = { 130 | randomSource: randomSource 131 | }; 132 | const header = { p: p, u: u }; 133 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 134 | 135 | t.true(Buffer.isBuffer(buf)); 136 | t.true(buf.length > 0); 137 | const actual = cbor.decodeFirstSync(buf); 138 | const expected = cbor.decodeFirstSync(example.output.cbor); 139 | t.true(deepEqual(actual, expected)); 140 | }); 141 | 142 | test('create aes-ccm-enc-06', async t => { 143 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-06.json'); 144 | const p = example.input.encrypted.protected; 145 | const u = example.input.encrypted.unprotected; 146 | const plaintext = Buffer.from(example.input.plaintext); 147 | 148 | const recipient = { 149 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 150 | u: example.input.encrypted.recipients[0].unprotected 151 | }; 152 | 153 | const options = { 154 | randomSource: randomSource 155 | }; 156 | const header = { p: p, u: u }; 157 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 158 | 159 | t.true(Buffer.isBuffer(buf)); 160 | t.true(buf.length > 0); 161 | const actual = cbor.decodeFirstSync(buf); 162 | const expected = cbor.decodeFirstSync(example.output.cbor); 163 | t.true(deepEqual(actual, expected)); 164 | }); 165 | 166 | test('create aes-ccm-enc-07', async t => { 167 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-07.json'); 168 | const p = example.input.encrypted.protected; 169 | const u = example.input.encrypted.unprotected; 170 | const plaintext = Buffer.from(example.input.plaintext); 171 | 172 | const recipient = { 173 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 174 | u: example.input.encrypted.recipients[0].unprotected 175 | }; 176 | 177 | const options = { 178 | randomSource: randomSource 179 | }; 180 | const header = { p: p, u: u }; 181 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 182 | 183 | t.true(Buffer.isBuffer(buf)); 184 | t.true(buf.length > 0); 185 | const actual = cbor.decodeFirstSync(buf); 186 | const expected = cbor.decodeFirstSync(example.output.cbor); 187 | t.true(deepEqual(actual, expected)); 188 | }); 189 | 190 | test('create aes-ccm-enc-08', async t => { 191 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-08.json'); 192 | const p = example.input.encrypted.protected; 193 | const u = example.input.encrypted.unprotected; 194 | const plaintext = Buffer.from(example.input.plaintext); 195 | 196 | const recipient = { 197 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 198 | u: example.input.encrypted.recipients[0].unprotected 199 | }; 200 | 201 | const options = { 202 | randomSource: randomSource 203 | }; 204 | const header = { p: p, u: u }; 205 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 206 | 207 | t.true(Buffer.isBuffer(buf)); 208 | t.true(buf.length > 0); 209 | const actual = cbor.decodeFirstSync(buf); 210 | const expected = cbor.decodeFirstSync(example.output.cbor); 211 | t.true(deepEqual(actual, expected)); 212 | }); 213 | 214 | test('decrypt aes-ccm-enc-01', async t => { 215 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-01.json'); 216 | const plaintext = example.input.plaintext; 217 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 218 | const data = example.output.cbor; 219 | const buf = await cose.encrypt.read(data, key); 220 | t.true(Buffer.isBuffer(buf)); 221 | t.true(buf.length > 0); 222 | t.is(buf.toString('utf8'), plaintext); 223 | }); 224 | 225 | test('decrypt aes-ccm-enc-02', async t => { 226 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-02.json'); 227 | const plaintext = example.input.plaintext; 228 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 229 | const data = example.output.cbor; 230 | const buf = await cose.encrypt.read(data, key); 231 | t.true(Buffer.isBuffer(buf)); 232 | t.true(buf.length > 0); 233 | t.is(buf.toString('utf8'), plaintext); 234 | }); 235 | 236 | test('decrypt aes-ccm-enc-03', async t => { 237 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-03.json'); 238 | const plaintext = example.input.plaintext; 239 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 240 | const data = example.output.cbor; 241 | const buf = await cose.encrypt.read(data, key); 242 | t.true(Buffer.isBuffer(buf)); 243 | t.true(buf.length > 0); 244 | t.is(buf.toString('utf8'), plaintext); 245 | }); 246 | 247 | test('decrypt aes-ccm-enc-04', async t => { 248 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-04.json'); 249 | const plaintext = example.input.plaintext; 250 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 251 | const data = example.output.cbor; 252 | const buf = await cose.encrypt.read(data, key); 253 | t.true(Buffer.isBuffer(buf)); 254 | t.true(buf.length > 0); 255 | t.is(buf.toString('utf8'), plaintext); 256 | }); 257 | 258 | test('decrypt aes-ccm-enc-05', async t => { 259 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-05.json'); 260 | const plaintext = example.input.plaintext; 261 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 262 | const data = example.output.cbor; 263 | const buf = await cose.encrypt.read(data, key); 264 | t.true(Buffer.isBuffer(buf)); 265 | t.true(buf.length > 0); 266 | t.is(buf.toString('utf8'), plaintext); 267 | }); 268 | 269 | test('decrypt aes-ccm-enc-06', async t => { 270 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-06.json'); 271 | const plaintext = example.input.plaintext; 272 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 273 | const data = example.output.cbor; 274 | const buf = await cose.encrypt.read(data, key); 275 | t.true(Buffer.isBuffer(buf)); 276 | t.true(buf.length > 0); 277 | t.is(buf.toString('utf8'), plaintext); 278 | }); 279 | 280 | test('decrypt aes-ccm-enc-07', async t => { 281 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-07.json'); 282 | const plaintext = example.input.plaintext; 283 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 284 | const data = example.output.cbor; 285 | const buf = await cose.encrypt.read(data, key); 286 | t.true(Buffer.isBuffer(buf)); 287 | t.true(buf.length > 0); 288 | t.is(buf.toString('utf8'), plaintext); 289 | }); 290 | 291 | test('decrypt aes-ccm-enc-08', async t => { 292 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-enc-08.json'); 293 | const plaintext = example.input.plaintext; 294 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 295 | const data = example.output.cbor; 296 | const buf = await cose.encrypt.read(data, key); 297 | t.true(Buffer.isBuffer(buf)); 298 | t.true(buf.length > 0); 299 | t.is(buf.toString('utf8'), plaintext); 300 | }); 301 | 302 | test('create aes-ccm-01', async t => { 303 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-01.json'); 304 | const p = example.input.enveloped.protected; 305 | const u = example.input.enveloped.unprotected; 306 | const plaintext = Buffer.from(example.input.plaintext); 307 | 308 | const recipient = [{ 309 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 310 | u: example.input.enveloped.recipients[0].unprotected 311 | }]; 312 | 313 | const options = { 314 | randomSource: randomSource 315 | }; 316 | const header = { p: p, u: u }; 317 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 318 | 319 | t.true(Buffer.isBuffer(buf)); 320 | t.true(buf.length > 0); 321 | t.is(buf.toString('hex'), example.output.cbor.toLowerCase()); 322 | }); 323 | 324 | test('create aes-ccm-02', async t => { 325 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-02.json'); 326 | const p = example.input.enveloped.protected; 327 | const u = example.input.enveloped.unprotected; 328 | const plaintext = Buffer.from(example.input.plaintext); 329 | 330 | const recipient = [{ 331 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 332 | u: example.input.enveloped.recipients[0].unprotected 333 | }]; 334 | 335 | const options = { 336 | randomSource: randomSource 337 | }; 338 | const header = { p: p, u: u }; 339 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 340 | 341 | t.true(Buffer.isBuffer(buf)); 342 | t.true(buf.length > 0); 343 | const actual = cbor.decodeFirstSync(buf); 344 | const expected = cbor.decodeFirstSync(example.output.cbor); 345 | t.true(deepEqual(actual, expected)); 346 | }); 347 | 348 | test('create aes-ccm-03', async t => { 349 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-03.json'); 350 | const p = example.input.enveloped.protected; 351 | const u = example.input.enveloped.unprotected; 352 | const plaintext = Buffer.from(example.input.plaintext); 353 | 354 | const recipient = [{ 355 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 356 | u: example.input.enveloped.recipients[0].unprotected 357 | }]; 358 | 359 | const options = { 360 | randomSource: randomSource 361 | }; 362 | const header = { p: p, u: u }; 363 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 364 | 365 | t.true(Buffer.isBuffer(buf)); 366 | t.true(buf.length > 0); 367 | const actual = cbor.decodeFirstSync(buf); 368 | const expected = cbor.decodeFirstSync(example.output.cbor); 369 | t.true(deepEqual(actual, expected)); 370 | }); 371 | 372 | test('create aes-ccm-04', async t => { 373 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-04.json'); 374 | const p = example.input.enveloped.protected; 375 | const u = example.input.enveloped.unprotected; 376 | const plaintext = Buffer.from(example.input.plaintext); 377 | 378 | const recipient = [{ 379 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 380 | u: example.input.enveloped.recipients[0].unprotected 381 | }]; 382 | 383 | const options = { 384 | randomSource: randomSource 385 | }; 386 | const header = { p: p, u: u }; 387 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 388 | 389 | t.true(Buffer.isBuffer(buf)); 390 | t.true(buf.length > 0); 391 | const actual = cbor.decodeFirstSync(buf); 392 | const expected = cbor.decodeFirstSync(example.output.cbor); 393 | t.true(deepEqual(actual, expected)); 394 | }); 395 | 396 | test('create aes-ccm-05', async t => { 397 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-05.json'); 398 | const p = example.input.enveloped.protected; 399 | const u = example.input.enveloped.unprotected; 400 | const plaintext = Buffer.from(example.input.plaintext); 401 | 402 | const recipient = [{ 403 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 404 | u: example.input.enveloped.recipients[0].unprotected 405 | }]; 406 | 407 | const options = { 408 | randomSource: randomSource 409 | }; 410 | const header = { p: p, u: u }; 411 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 412 | 413 | t.true(Buffer.isBuffer(buf)); 414 | t.true(buf.length > 0); 415 | const actual = cbor.decodeFirstSync(buf); 416 | const expected = cbor.decodeFirstSync(example.output.cbor); 417 | t.true(deepEqual(actual, expected)); 418 | }); 419 | 420 | test('create aes-ccm-06', async t => { 421 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-06.json'); 422 | const p = example.input.enveloped.protected; 423 | const u = example.input.enveloped.unprotected; 424 | const plaintext = Buffer.from(example.input.plaintext); 425 | 426 | const recipient = [{ 427 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 428 | u: example.input.enveloped.recipients[0].unprotected 429 | }]; 430 | 431 | const options = { 432 | randomSource: randomSource 433 | }; 434 | const header = { p: p, u: u }; 435 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 436 | 437 | t.true(Buffer.isBuffer(buf)); 438 | t.true(buf.length > 0); 439 | const actual = cbor.decodeFirstSync(buf); 440 | const expected = cbor.decodeFirstSync(example.output.cbor); 441 | t.true(deepEqual(actual, expected)); 442 | }); 443 | 444 | test('create aes-ccm-07', async t => { 445 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-07.json'); 446 | const p = example.input.enveloped.protected; 447 | const u = example.input.enveloped.unprotected; 448 | const plaintext = Buffer.from(example.input.plaintext); 449 | 450 | const recipient = [{ 451 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 452 | u: example.input.enveloped.recipients[0].unprotected 453 | }]; 454 | 455 | const options = { 456 | randomSource: randomSource 457 | }; 458 | const header = { p: p, u: u }; 459 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 460 | 461 | t.true(Buffer.isBuffer(buf)); 462 | t.true(buf.length > 0); 463 | const actual = cbor.decodeFirstSync(buf); 464 | const expected = cbor.decodeFirstSync(example.output.cbor); 465 | t.true(deepEqual(actual, expected)); 466 | }); 467 | 468 | test('create aes-ccm-08', async t => { 469 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-08.json'); 470 | const p = example.input.enveloped.protected; 471 | const u = example.input.enveloped.unprotected; 472 | const plaintext = Buffer.from(example.input.plaintext); 473 | 474 | const recipient = [{ 475 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 476 | u: example.input.enveloped.recipients[0].unprotected 477 | }]; 478 | 479 | const options = { 480 | randomSource: randomSource 481 | }; 482 | const header = { p: p, u: u }; 483 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 484 | 485 | t.true(Buffer.isBuffer(buf)); 486 | t.true(buf.length > 0); 487 | const actual = cbor.decodeFirstSync(buf); 488 | const expected = cbor.decodeFirstSync(example.output.cbor); 489 | t.true(deepEqual(actual, expected)); 490 | }); 491 | 492 | test('decrypt aes-ccm-01', async t => { 493 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-01.json'); 494 | const plaintext = example.input.plaintext; 495 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 496 | const data = example.output.cbor; 497 | const buf = await cose.encrypt.read(data, key); 498 | t.true(Buffer.isBuffer(buf)); 499 | t.true(buf.length > 0); 500 | t.is(buf.toString('utf8'), plaintext); 501 | }); 502 | 503 | test('decrypt aes-ccm-02', async t => { 504 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-02.json'); 505 | const plaintext = example.input.plaintext; 506 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 507 | const data = example.output.cbor; 508 | const buf = await cose.encrypt.read(data, key); 509 | t.true(Buffer.isBuffer(buf)); 510 | t.true(buf.length > 0); 511 | t.is(buf.toString('utf8'), plaintext); 512 | }); 513 | 514 | test('decrypt aes-ccm-03', async t => { 515 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-03.json'); 516 | const plaintext = example.input.plaintext; 517 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 518 | const data = example.output.cbor; 519 | const buf = await cose.encrypt.read(data, key); 520 | t.true(Buffer.isBuffer(buf)); 521 | t.true(buf.length > 0); 522 | t.is(buf.toString('utf8'), plaintext); 523 | }); 524 | 525 | test('decrypt aes-ccm-04', async t => { 526 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-04.json'); 527 | const plaintext = example.input.plaintext; 528 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 529 | const data = example.output.cbor; 530 | const buf = await cose.encrypt.read(data, key); 531 | t.true(Buffer.isBuffer(buf)); 532 | t.true(buf.length > 0); 533 | t.is(buf.toString('utf8'), plaintext); 534 | }); 535 | 536 | test('decrypt aes-ccm-05', async t => { 537 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-05.json'); 538 | const plaintext = example.input.plaintext; 539 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 540 | const data = example.output.cbor; 541 | const buf = await cose.encrypt.read(data, key); 542 | t.true(Buffer.isBuffer(buf)); 543 | t.true(buf.length > 0); 544 | t.is(buf.toString('utf8'), plaintext); 545 | }); 546 | 547 | test('decrypt aes-ccm-06', async t => { 548 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-06.json'); 549 | const plaintext = example.input.plaintext; 550 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 551 | const data = example.output.cbor; 552 | const buf = await cose.encrypt.read(data, key); 553 | t.true(Buffer.isBuffer(buf)); 554 | t.true(buf.length > 0); 555 | t.is(buf.toString('utf8'), plaintext); 556 | }); 557 | 558 | test('decrypt aes-ccm-07', async t => { 559 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-07.json'); 560 | const plaintext = example.input.plaintext; 561 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 562 | const data = example.output.cbor; 563 | const buf = await cose.encrypt.read(data, key); 564 | t.true(Buffer.isBuffer(buf)); 565 | t.true(buf.length > 0); 566 | t.is(buf.toString('utf8'), plaintext); 567 | }); 568 | 569 | test('decrypt aes-ccm-08', async t => { 570 | const example = jsonfile.readFileSync('test/Examples/aes-ccm-examples/aes-ccm-08.json'); 571 | const plaintext = example.input.plaintext; 572 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 573 | const data = example.output.cbor; 574 | const buf = await cose.encrypt.read(data, key); 575 | t.true(Buffer.isBuffer(buf)); 576 | t.true(buf.length > 0); 577 | t.is(buf.toString('utf8'), plaintext); 578 | }); 579 | -------------------------------------------------------------------------------- /test/aes-gcm-examples.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | function randomSource (bytes) { 13 | if (bytes === 12) { 14 | return Buffer.from('02D1F7E6F26C43D4868D87CE', 'hex'); 15 | } else { 16 | return Buffer.from('61A7', 'hex'); 17 | } 18 | } 19 | 20 | test('create aes-gcm-01', async t => { 21 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-01.json'); 22 | const p = example.input.enveloped.protected; 23 | const u = example.input.enveloped.unprotected; 24 | const plaintext = Buffer.from(example.input.plaintext); 25 | 26 | const recipients = [{ 27 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 28 | u: example.input.enveloped.recipients[0].unprotected 29 | }]; 30 | 31 | const options = { 32 | randomSource: randomSource 33 | }; 34 | const header = { p: p, u: u }; 35 | const buf = await cose.encrypt.create(header, plaintext, recipients, options); 36 | t.true(Buffer.isBuffer(buf)); 37 | t.true(buf.length > 0); 38 | const actual = cbor.decodeFirstSync(buf); 39 | const expected = cbor.decodeFirstSync(example.output.cbor); 40 | t.true(deepEqual(actual, expected)); 41 | }); 42 | 43 | test('create aes-gcm-02', async t => { 44 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-02.json'); 45 | const p = example.input.enveloped.protected; 46 | const u = example.input.enveloped.unprotected; 47 | const plaintext = Buffer.from(example.input.plaintext); 48 | 49 | const recipients = [{ 50 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 51 | u: example.input.enveloped.recipients[0].unprotected 52 | }]; 53 | 54 | const options = { 55 | randomSource: randomSource 56 | }; 57 | const header = { p: p, u: u }; 58 | const buf = await cose.encrypt.create(header, plaintext, recipients, options); 59 | t.true(Buffer.isBuffer(buf)); 60 | t.true(buf.length > 0); 61 | const actual = cbor.decodeFirstSync(buf); 62 | const expected = cbor.decodeFirstSync(example.output.cbor); 63 | t.true(deepEqual(actual, expected)); 64 | }); 65 | 66 | test('create aes-gcm-03', async t => { 67 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-03.json'); 68 | const p = example.input.enveloped.protected; 69 | const u = example.input.enveloped.unprotected; 70 | const plaintext = Buffer.from(example.input.plaintext); 71 | 72 | const recipients = [{ 73 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 74 | u: example.input.enveloped.recipients[0].unprotected 75 | }]; 76 | 77 | const options = { 78 | randomSource: randomSource 79 | }; 80 | const header = { p: p, u: u }; 81 | const buf = await cose.encrypt.create(header, plaintext, recipients, options); 82 | t.true(Buffer.isBuffer(buf)); 83 | t.true(buf.length > 0); 84 | const actual = cbor.decodeFirstSync(buf); 85 | const expected = cbor.decodeFirstSync(example.output.cbor); 86 | t.true(deepEqual(actual, expected)); 87 | }); 88 | 89 | // aes-gcm-04 is an error example and cannot be recreated 90 | 91 | test('create aes-gcm-05', async t => { 92 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-05.json'); 93 | const p = example.input.enveloped.protected; 94 | const u = example.input.enveloped.unprotected; 95 | const plaintext = Buffer.from(example.input.plaintext); 96 | 97 | const recipients = [{ 98 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 99 | u: example.input.enveloped.recipients[0].unprotected 100 | }]; 101 | 102 | example.input.enveloped.unprotected.Partial_IV = Buffer.from(example.input.enveloped.unprotected.partialIV_hex, 'hex'); 103 | delete example.input.enveloped.unprotected.partialIV_hex; 104 | 105 | const contextIv = Buffer.from(example.input.enveloped.unsent.IV_hex, 'hex'); 106 | contextIv[10] = 0; 107 | contextIv[11] = 0; 108 | 109 | const options = { 110 | randomSource: randomSource, 111 | contextIv: contextIv 112 | }; 113 | const header = { p: p, u: u }; 114 | const buf = await cose.encrypt.create(header, plaintext, recipients, options); 115 | t.true(Buffer.isBuffer(buf)); 116 | t.true(buf.length > 0); 117 | const actual = cbor.decodeFirstSync(buf); 118 | const expected = cbor.decodeFirstSync(example.output.cbor); 119 | t.true(deepEqual(actual, expected)); 120 | }); 121 | 122 | test('decrypt aes-gcm-01', async t => { 123 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-01.json'); 124 | const plaintext = example.input.plaintext; 125 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 126 | const data = example.output.cbor; 127 | const buf = await cose.encrypt.read(data, key); 128 | t.true(Buffer.isBuffer(buf)); 129 | t.true(buf.length > 0); 130 | t.is(buf.toString('utf8'), plaintext); 131 | }); 132 | 133 | test('decrypt aes-gcm-02', async t => { 134 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-02.json'); 135 | const plaintext = example.input.plaintext; 136 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 137 | const data = example.output.cbor; 138 | const buf = await cose.encrypt.read(data, key); 139 | t.true(Buffer.isBuffer(buf)); 140 | t.true(buf.length > 0); 141 | t.is(buf.toString('utf8'), plaintext); 142 | }); 143 | 144 | test('decrypt aes-gcm-03', async t => { 145 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-03.json'); 146 | const plaintext = example.input.plaintext; 147 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 148 | const data = example.output.cbor; 149 | const buf = await cose.encrypt.read(data, key); 150 | t.true(Buffer.isBuffer(buf)); 151 | t.true(buf.length > 0); 152 | t.is(buf.toString('utf8'), plaintext); 153 | }); 154 | 155 | test('decrypt aes-gcm-04', async t => { 156 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-04.json'); 157 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 158 | const data = example.output.cbor; 159 | try { 160 | await cose.encrypt.read(data, key); 161 | t.fail('Unsupported state or unable to authenticate data'); 162 | } catch (error) { 163 | t.is(error.message, 'Unsupported state or unable to authenticate data'); 164 | } 165 | }); 166 | 167 | test('decrypt aes-gcm-05', async t => { 168 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-05.json'); 169 | const plaintext = example.input.plaintext; 170 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 171 | const data = example.output.cbor; 172 | const contextIv = Buffer.from(example.input.enveloped.unsent.IV_hex, 'hex'); 173 | contextIv[10] = 0; 174 | contextIv[11] = 0; 175 | const options = { contextIv: contextIv }; 176 | const buf = await cose.encrypt.read(data, key, options); 177 | t.true(Buffer.isBuffer(buf)); 178 | t.true(buf.length > 0); 179 | t.is(buf.toString('utf8'), plaintext); 180 | }); 181 | 182 | test('create aes-gcm-enc-01', async t => { 183 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-enc-01.json'); 184 | const p = example.input.encrypted.protected; 185 | const u = example.input.encrypted.unprotected; 186 | const plaintext = Buffer.from(example.input.plaintext); 187 | 188 | const recipient = { 189 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 190 | u: example.input.encrypted.recipients[0].unprotected 191 | }; 192 | 193 | const options = { 194 | randomSource: randomSource 195 | }; 196 | const header = { p: p, u: u }; 197 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 198 | t.true(Buffer.isBuffer(buf)); 199 | t.true(buf.length > 0); 200 | const actual = cbor.decodeFirstSync(buf); 201 | const expected = cbor.decodeFirstSync(example.output.cbor); 202 | t.true(deepEqual(actual, expected)); 203 | }); 204 | 205 | test('create aes-gcm-enc-02', async t => { 206 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-enc-02.json'); 207 | const p = example.input.encrypted.protected; 208 | const u = example.input.encrypted.unprotected; 209 | const plaintext = Buffer.from(example.input.plaintext); 210 | 211 | const recipient = { 212 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 213 | u: example.input.encrypted.recipients[0].unprotected 214 | }; 215 | 216 | const options = { 217 | randomSource: randomSource 218 | }; 219 | const header = { p: p, u: u }; 220 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 221 | t.true(Buffer.isBuffer(buf)); 222 | t.true(buf.length > 0); 223 | const actual = cbor.decodeFirstSync(buf); 224 | const expected = cbor.decodeFirstSync(example.output.cbor); 225 | t.true(deepEqual(actual, expected)); 226 | }); 227 | 228 | test('create aes-gcm-enc-03', async t => { 229 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-enc-03.json'); 230 | const p = example.input.encrypted.protected; 231 | const u = example.input.encrypted.unprotected; 232 | const plaintext = Buffer.from(example.input.plaintext); 233 | 234 | const recipient = { 235 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 236 | u: example.input.encrypted.recipients[0].unprotected 237 | }; 238 | 239 | const options = { 240 | randomSource: randomSource 241 | }; 242 | const header = { p: p, u: u }; 243 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 244 | t.true(Buffer.isBuffer(buf)); 245 | t.true(buf.length > 0); 246 | const actual = cbor.decodeFirstSync(buf); 247 | const expected = cbor.decodeFirstSync(example.output.cbor); 248 | t.true(deepEqual(actual, expected)); 249 | }); 250 | 251 | // create aes-gcm-enc-04 is an error example and cannot be recreated 252 | 253 | test('decrypt aes-gcm-enc-01', async t => { 254 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-enc-01.json'); 255 | const plaintext = example.input.plaintext; 256 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 257 | const data = example.output.cbor; 258 | const buf = await cose.encrypt.read(data, key); 259 | t.true(Buffer.isBuffer(buf)); 260 | t.true(buf.length > 0); 261 | t.is(buf.toString('utf8'), plaintext); 262 | }); 263 | 264 | test('decrypt aes-gcm-enc-02', async t => { 265 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-enc-02.json'); 266 | const plaintext = example.input.plaintext; 267 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 268 | const data = example.output.cbor; 269 | const buf = await cose.encrypt.read(data, key); 270 | t.true(Buffer.isBuffer(buf)); 271 | t.true(buf.length > 0); 272 | t.is(buf.toString('utf8'), plaintext); 273 | }); 274 | 275 | test('decrypt aes-gcm-enc-03', async t => { 276 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-enc-03.json'); 277 | const plaintext = example.input.plaintext; 278 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 279 | const data = example.output.cbor; 280 | const buf = await cose.encrypt.read(data, key); 281 | t.true(Buffer.isBuffer(buf)); 282 | t.true(buf.length > 0); 283 | t.is(buf.toString('utf8'), plaintext); 284 | }); 285 | 286 | test('decrypt aes-gcm-enc-04', async t => { 287 | const example = jsonfile.readFileSync('test/Examples/aes-gcm-examples/aes-gcm-enc-04.json'); 288 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 289 | const data = example.output.cbor; 290 | try { 291 | await cose.encrypt.read(data, key); 292 | t.fail('Unsupported state or unable to authenticate data'); 293 | } catch (error) { 294 | t.is(error.message, 'Unsupported state or unable to authenticate data'); 295 | } 296 | }); 297 | -------------------------------------------------------------------------------- /test/cbc-mac-examples.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | test('create cbc-mac-01', async t => { 13 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-01.json'); 14 | const p = example.input.mac.protected; 15 | const u = example.input.mac.recipients[0].unprotected; 16 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 17 | const plaintext = Buffer.from(example.input.plaintext); 18 | 19 | const header = { p: p }; 20 | const recipents = [{ key: key, u: u }]; 21 | const buf = await cose.mac.create(header, plaintext, recipents); 22 | 23 | t.true(Buffer.isBuffer(buf)); 24 | t.true(buf.length > 0); 25 | const actual = cbor.decodeFirstSync(buf); 26 | const expected = cbor.decodeFirstSync(example.output.cbor); 27 | t.true(deepEqual(actual, expected)); 28 | }); 29 | 30 | test('create cbc-mac-02', async t => { 31 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-02.json'); 32 | const p = example.input.mac.protected; 33 | const u = example.input.mac.recipients[0].unprotected; 34 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 35 | const plaintext = Buffer.from(example.input.plaintext); 36 | 37 | const header = { p: p }; 38 | const recipents = [{ key: key, u: u }]; 39 | const buf = await cose.mac.create(header, plaintext, recipents); 40 | 41 | t.true(Buffer.isBuffer(buf)); 42 | t.true(buf.length > 0); 43 | const actual = cbor.decodeFirstSync(buf); 44 | const expected = cbor.decodeFirstSync(example.output.cbor); 45 | 46 | t.true(deepEqual(actual, expected)); 47 | }); 48 | 49 | test('create cbc-mac-03', async t => { 50 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-03.json'); 51 | const p = example.input.mac.protected; 52 | const u = example.input.mac.recipients[0].unprotected; 53 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 54 | const plaintext = Buffer.from(example.input.plaintext); 55 | 56 | const header = { p: p }; 57 | const recipents = [{ key: key, u: u }]; 58 | const buf = await cose.mac.create(header, plaintext, recipents); 59 | 60 | t.true(Buffer.isBuffer(buf)); 61 | t.true(buf.length > 0); 62 | const actual = cbor.decodeFirstSync(buf); 63 | const expected = cbor.decodeFirstSync(example.output.cbor); 64 | 65 | t.true(deepEqual(actual, expected)); 66 | }); 67 | 68 | test('create cbc-mac-04', async t => { 69 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-04.json'); 70 | const p = example.input.mac.protected; 71 | const u = example.input.mac.recipients[0].unprotected; 72 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 73 | const plaintext = Buffer.from(example.input.plaintext); 74 | 75 | const header = { p: p }; 76 | const recipents = [{ key: key, u: u }]; 77 | const buf = await cose.mac.create(header, plaintext, recipents); 78 | 79 | t.true(Buffer.isBuffer(buf)); 80 | t.true(buf.length > 0); 81 | const actual = cbor.decodeFirstSync(buf); 82 | const expected = cbor.decodeFirstSync(example.output.cbor); 83 | 84 | t.true(deepEqual(actual, expected)); 85 | }); 86 | 87 | test('create cbc-mac-enc-01', async t => { 88 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-enc-01.json'); 89 | const p = example.input.mac0.protected; 90 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 91 | const plaintext = Buffer.from(example.input.plaintext); 92 | 93 | const header = { p: p }; 94 | const recipents = { key: key }; 95 | const buf = await cose.mac.create(header, plaintext, recipents); 96 | 97 | t.true(Buffer.isBuffer(buf)); 98 | t.true(buf.length > 0); 99 | 100 | const actual = cbor.decodeFirstSync(buf); 101 | const expected = cbor.decodeFirstSync(example.output.cbor); 102 | 103 | t.true(deepEqual(actual, expected)); 104 | }); 105 | 106 | test('create cbc-mac-enc-02', async t => { 107 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-enc-02.json'); 108 | const p = example.input.mac0.protected; 109 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 110 | const plaintext = Buffer.from(example.input.plaintext); 111 | 112 | const header = { p: p }; 113 | const recipent = { key: key }; 114 | const buf = await cose.mac.create(header, plaintext, recipent); 115 | 116 | t.true(Buffer.isBuffer(buf)); 117 | t.true(buf.length > 0); 118 | 119 | const actual = cbor.decodeFirstSync(buf); 120 | const expected = cbor.decodeFirstSync(example.output.cbor); 121 | 122 | t.true(deepEqual(actual, expected)); 123 | }); 124 | 125 | test('create cbc-mac-enc-03', async t => { 126 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-enc-03.json'); 127 | const p = example.input.mac0.protected; 128 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 129 | const plaintext = Buffer.from(example.input.plaintext); 130 | 131 | const header = { p: p }; 132 | const recipent = { key: key }; 133 | const buf = await cose.mac.create(header, plaintext, recipent); 134 | 135 | t.true(Buffer.isBuffer(buf)); 136 | t.true(buf.length > 0); 137 | 138 | const actual = cbor.decodeFirstSync(buf); 139 | const expected = cbor.decodeFirstSync(example.output.cbor); 140 | 141 | t.true(deepEqual(actual, expected)); 142 | }); 143 | 144 | test('create cbc-mac-enc-04', async t => { 145 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-enc-04.json'); 146 | const p = example.input.mac0.protected; 147 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 148 | const plaintext = Buffer.from(example.input.plaintext); 149 | 150 | const header = { p: p }; 151 | const recipent = { key: key }; 152 | const buf = await cose.mac.create(header, plaintext, recipent); 153 | 154 | t.true(Buffer.isBuffer(buf)); 155 | t.true(buf.length > 0); 156 | 157 | const actual = cbor.decodeFirstSync(buf); 158 | const expected = cbor.decodeFirstSync(example.output.cbor); 159 | 160 | t.true(deepEqual(actual, expected)); 161 | }); 162 | 163 | test('verify cbc-mac-01', async t => { 164 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-01.json'); 165 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 166 | 167 | const data = example.output.cbor; 168 | const buf = await cose.mac.read(data, key); 169 | t.true(Buffer.isBuffer(buf)); 170 | t.true(buf.length > 0); 171 | t.is(buf.toString('utf8'), example.input.plaintext); 172 | }); 173 | 174 | test('verify cbc-mac-02', async t => { 175 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-02.json'); 176 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 177 | 178 | const data = example.output.cbor; 179 | const buf = await cose.mac.read(data, key); 180 | t.true(Buffer.isBuffer(buf)); 181 | t.true(buf.length > 0); 182 | t.is(buf.toString('utf8'), example.input.plaintext); 183 | }); 184 | 185 | test('verify cbc-mac-03', async t => { 186 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-03.json'); 187 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 188 | 189 | const data = example.output.cbor; 190 | const buf = await cose.mac.read(data, key); 191 | t.true(Buffer.isBuffer(buf)); 192 | t.true(buf.length > 0); 193 | t.is(buf.toString('utf8'), example.input.plaintext); 194 | }); 195 | 196 | test('verify cbc-mac-04', async t => { 197 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-04.json'); 198 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 199 | 200 | const data = example.output.cbor; 201 | const buf = await cose.mac.read(data, key); 202 | t.true(Buffer.isBuffer(buf)); 203 | t.true(buf.length > 0); 204 | t.is(buf.toString('utf8'), example.input.plaintext); 205 | }); 206 | 207 | test('verify cbc-mac-enc-01', async t => { 208 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-enc-02.json'); 209 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 210 | 211 | const data = example.output.cbor; 212 | const buf = await cose.mac.read(data, key); 213 | t.true(Buffer.isBuffer(buf)); 214 | t.true(buf.length > 0); 215 | t.is(buf.toString('utf8'), example.input.plaintext); 216 | }); 217 | 218 | test('verify cbc-mac-enc-02', async t => { 219 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-enc-02.json'); 220 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 221 | 222 | const data = example.output.cbor; 223 | const buf = await cose.mac.read(data, key); 224 | t.true(Buffer.isBuffer(buf)); 225 | t.true(buf.length > 0); 226 | t.is(buf.toString('utf8'), example.input.plaintext); 227 | }); 228 | 229 | test('verify cbc-mac-enc-03', async t => { 230 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-enc-03.json'); 231 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 232 | 233 | const data = example.output.cbor; 234 | const buf = await cose.mac.read(data, key); 235 | t.true(Buffer.isBuffer(buf)); 236 | t.true(buf.length > 0); 237 | t.is(buf.toString('utf8'), example.input.plaintext); 238 | }); 239 | 240 | test('verify cbc-mac-enc-04', async t => { 241 | const example = jsonfile.readFileSync('test/Examples/cbc-mac-examples/cbc-mac-enc-04.json'); 242 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 243 | 244 | const data = example.output.cbor; 245 | const buf = await cose.mac.read(data, key); 246 | t.true(Buffer.isBuffer(buf)); 247 | t.true(buf.length > 0); 248 | t.is(buf.toString('utf8'), example.input.plaintext); 249 | }); 250 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | const test = require('ava'); 5 | const cose = require('../'); 6 | 7 | test('translate headers', (t) => { 8 | let h = cose.common.TranslateHeaders({}); 9 | t.is(h.constructor.name, 'Map'); 10 | h = cose.common.TranslateHeaders({ alg: 'SHA-256', crit: 2 }); 11 | t.is(h.constructor.name, 'Map'); 12 | t.is(h.size, 2); 13 | t.is(h.get(cose.common.HeaderParameters.alg), 5); 14 | t.is(h.get(cose.common.HeaderParameters.crit), 2); 15 | }); 16 | 17 | /* 18 | test('translate headers', (t) => { 19 | const result = cose.common.TranslateHeaders({ 20 | 'ephemeral_key': Buffer.from('beef', 'hex'), 21 | 'partyUNonce': Buffer.from('dead', 'hex'), 22 | 'kid': Buffer.from('0b0b', 'hex'), 23 | }); 24 | console.log(result); 25 | }); 26 | */ 27 | 28 | test('invalid', (t) => { 29 | t.throws(() => { 30 | cose.common.TranslateHeaders({ 'fizzle stomp': 12 }); 31 | }); 32 | }); 33 | 34 | test('xor1', (t) => { 35 | const a = Buffer.from('00ff0f', 'hex'); 36 | const b = Buffer.from('f0f0', 'hex'); 37 | const actual = cose.common.xor(a, b); 38 | const expected = '000fff'; 39 | t.is(actual.toString('hex'), expected); 40 | }); 41 | 42 | test('xor2', (t) => { 43 | const a = Buffer.from('f0f0', 'hex'); 44 | const b = Buffer.from('00ff0f', 'hex'); 45 | const actual = cose.common.xor(a, b); 46 | const expected = '000fff'; 47 | t.is(actual.toString('hex'), expected); 48 | }); 49 | 50 | test('xor3', (t) => { 51 | const a = Buffer.from('f0f0f0', 'hex'); 52 | const b = Buffer.from('00ff0f', 'hex'); 53 | const actual = cose.common.xor(a, b); 54 | const expected = 'f00fff'; 55 | t.is(actual.toString('hex'), expected); 56 | }); 57 | -------------------------------------------------------------------------------- /test/ecdh-direct-examples.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | function randomSource (bytes) { 13 | if (bytes === 12) { 14 | return Buffer.from('C9CF4DF2FE6C632BF7886413', 'hex'); 15 | } else { 16 | return Buffer.from('02D1F7E6F26C43D4868D87CEB2353161740AACF1F7163647984B522A848DF1C3', 'hex'); 17 | } 18 | } 19 | 20 | test('create p256-hkdf-256-01', async t => { 21 | const example = jsonfile.readFileSync('test/Examples/ecdh-direct-examples/p256-hkdf-256-01.json'); 22 | const p = example.input.enveloped.protected; 23 | const u = example.input.enveloped.unprotected; 24 | const plaintext = Buffer.from(example.input.plaintext); 25 | 26 | const recipient = [{ 27 | key: { 28 | kty: example.input.enveloped.recipients[0].key.kty, 29 | kid: example.input.enveloped.recipients[0].key.kid, 30 | crv: example.input.enveloped.recipients[0].key.crv, 31 | x: base64url.toBuffer(example.input.enveloped.recipients[0].key.x), 32 | y: base64url.toBuffer(example.input.enveloped.recipients[0].key.y), 33 | d: base64url.toBuffer(example.input.enveloped.recipients[0].key.d) 34 | }, 35 | p: example.input.enveloped.recipients[0].protected, 36 | u: example.input.enveloped.recipients[0].unprotected 37 | }]; 38 | 39 | const options = { 40 | randomSource: randomSource 41 | }; 42 | 43 | const header = { p: p, u: u }; 44 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 45 | t.true(Buffer.isBuffer(buf)); 46 | t.true(buf.length > 0); 47 | const actual = cbor.decodeFirstSync(buf); 48 | const expected = cbor.decodeFirstSync(example.output.cbor); 49 | t.true(deepEqual(actual, expected)); 50 | }); 51 | 52 | test('create p256-hkdf-256-02', async t => { 53 | const example = jsonfile.readFileSync('test/Examples/ecdh-direct-examples/p256-hkdf-256-02.json'); 54 | const p = example.input.enveloped.protected; 55 | const u = example.input.enveloped.unprotected; 56 | const plaintext = Buffer.from(example.input.plaintext); 57 | 58 | const recipient = [{ 59 | key: { 60 | kty: example.input.enveloped.recipients[0].key.kty, 61 | kid: example.input.enveloped.recipients[0].key.kid, 62 | crv: example.input.enveloped.recipients[0].key.crv, 63 | x: base64url.toBuffer(example.input.enveloped.recipients[0].key.x), 64 | y: base64url.toBuffer(example.input.enveloped.recipients[0].key.y), 65 | d: base64url.toBuffer(example.input.enveloped.recipients[0].key.d) 66 | }, 67 | p: example.input.enveloped.recipients[0].protected, 68 | u: example.input.enveloped.recipients[0].unprotected 69 | }]; 70 | 71 | const options = { 72 | randomSource: randomSource 73 | }; 74 | 75 | const header = { p: p, u: u }; 76 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 77 | t.true(Buffer.isBuffer(buf)); 78 | t.true(buf.length > 0); 79 | const actual = cbor.decodeFirstSync(buf); 80 | const expected = cbor.decodeFirstSync(example.output.cbor); 81 | t.true(deepEqual(actual, expected)); 82 | }); 83 | 84 | // create p256-hkdf-256-03 85 | 86 | test('create p256-hkdf-512-01', async t => { 87 | const example = jsonfile.readFileSync('test/Examples/ecdh-direct-examples/p256-hkdf-512-01.json'); 88 | const p = example.input.enveloped.protected; 89 | const u = example.input.enveloped.unprotected; 90 | const plaintext = Buffer.from(example.input.plaintext); 91 | 92 | const recipient = [{ 93 | key: { 94 | kty: example.input.enveloped.recipients[0].key.kty, 95 | kid: example.input.enveloped.recipients[0].key.kid, 96 | crv: example.input.enveloped.recipients[0].key.crv, 97 | x: base64url.toBuffer(example.input.enveloped.recipients[0].key.x), 98 | y: base64url.toBuffer(example.input.enveloped.recipients[0].key.y), 99 | d: base64url.toBuffer(example.input.enveloped.recipients[0].key.d) 100 | }, 101 | p: example.input.enveloped.recipients[0].protected, 102 | u: example.input.enveloped.recipients[0].unprotected 103 | }]; 104 | 105 | const options = { 106 | randomSource: randomSource 107 | }; 108 | 109 | const header = { p: p, u: u }; 110 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 111 | t.true(Buffer.isBuffer(buf)); 112 | t.true(buf.length > 0); 113 | const actual = cbor.decodeFirstSync(buf); 114 | const expected = cbor.decodeFirstSync(example.output.cbor); 115 | t.true(deepEqual(actual, expected)); 116 | }); 117 | 118 | test('create p256-hkdf-512-02', async t => { 119 | const example = jsonfile.readFileSync('test/Examples/ecdh-direct-examples/p256-hkdf-512-02.json'); 120 | const p = example.input.enveloped.protected; 121 | const u = example.input.enveloped.unprotected; 122 | const plaintext = Buffer.from(example.input.plaintext); 123 | 124 | const recipient = [{ 125 | key: { 126 | kty: example.input.enveloped.recipients[0].key.kty, 127 | kid: example.input.enveloped.recipients[0].key.kid, 128 | crv: example.input.enveloped.recipients[0].key.crv, 129 | x: base64url.toBuffer(example.input.enveloped.recipients[0].key.x), 130 | y: base64url.toBuffer(example.input.enveloped.recipients[0].key.y), 131 | d: base64url.toBuffer(example.input.enveloped.recipients[0].key.d) 132 | }, 133 | p: example.input.enveloped.recipients[0].protected, 134 | u: example.input.enveloped.recipients[0].unprotected 135 | }]; 136 | 137 | const options = { 138 | randomSource: (bytes) => { 139 | if (bytes === 12) { 140 | return Buffer.from('C9CF4DF2FE6C632BF7886413', 'hex'); 141 | } else { 142 | return Buffer.from('02D1F7E6F26C43D4868D87CEB2353161740AACF1F7163647984B522A848DF1C3', 'hex'); 143 | } 144 | } 145 | }; 146 | 147 | const header = { p: p, u: u }; 148 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 149 | t.true(Buffer.isBuffer(buf)); 150 | t.true(buf.length > 0); 151 | const actual = cbor.decodeFirstSync(buf); 152 | const expected = cbor.decodeFirstSync(example.output.cbor); 153 | t.true(deepEqual(actual, expected)); 154 | }); 155 | 156 | // create p256-hkdf-512-03 157 | 158 | test('create p256-ss-hkdf-256-01', async t => { 159 | const example = jsonfile.readFileSync('test/Examples/ecdh-direct-examples/p256-ss-hkdf-256-01.json'); 160 | const p = example.input.enveloped.protected; 161 | const u = example.input.enveloped.unprotected; 162 | const plaintext = Buffer.from(example.input.plaintext); 163 | 164 | const recipient = [{ 165 | key: { 166 | kty: example.input.enveloped.recipients[0].key.kty, 167 | kid: example.input.enveloped.recipients[0].key.kid, 168 | crv: example.input.enveloped.recipients[0].key.crv, 169 | x: base64url.toBuffer(example.input.enveloped.recipients[0].key.x), 170 | y: base64url.toBuffer(example.input.enveloped.recipients[0].key.y), 171 | d: base64url.toBuffer(example.input.enveloped.recipients[0].key.d) 172 | }, 173 | sender: { 174 | kty: example.input.enveloped.recipients[0].sender_key.kty, 175 | crv: example.input.enveloped.recipients[0].sender_key.crv, 176 | x: base64url.toBuffer(example.input.enveloped.recipients[0].sender_key.x), 177 | y: base64url.toBuffer(example.input.enveloped.recipients[0].sender_key.y), 178 | d: base64url.toBuffer(example.input.enveloped.recipients[0].sender_key.d) 179 | }, 180 | p: example.input.enveloped.recipients[0].protected, 181 | u: example.input.enveloped.recipients[0].unprotected 182 | }]; 183 | 184 | const options = { 185 | randomSource: (bytes) => { 186 | if (bytes === 12) { 187 | return Buffer.from('D7923E677B71A3F40A179643', 'hex'); 188 | } else { 189 | return Buffer.from('02D1F7E6F26C43D4868D87CEB2353161740AACF1F7163647984B522A848DF1C3C9CF4DF2FE6C632BF7886413F76E88523A8260B857D70B350027FD842B5E5947', 'hex'); 190 | } 191 | } 192 | }; 193 | 194 | const header = { p: p, u: u }; 195 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 196 | t.true(Buffer.isBuffer(buf)); 197 | t.true(buf.length > 0); 198 | const actual = cbor.decodeFirstSync(buf); 199 | const expected = cbor.decodeFirstSync(example.output.cbor); 200 | t.true(deepEqual(actual, expected)); 201 | }); 202 | 203 | // create p256-ss-hkdf-256-02 204 | 205 | // create p256-ss-hkdf-256-03 206 | 207 | // create p256-ss-hkdf-512-01 208 | 209 | // create p256-ss-hkdf-512-02 210 | 211 | // create p256-ss-hkdf-512-03 212 | 213 | test('create p521-hkdf-256-01', async t => { 214 | const example = jsonfile.readFileSync('test/Examples/ecdh-direct-examples/p521-hkdf-256-01.json'); 215 | const p = example.input.enveloped.protected; 216 | const u = example.input.enveloped.unprotected; 217 | const plaintext = Buffer.from(example.input.plaintext); 218 | 219 | const recipient = [{ 220 | key: { 221 | kty: example.input.enveloped.recipients[0].key.kty, 222 | kid: example.input.enveloped.recipients[0].key.kid, 223 | crv: example.input.enveloped.recipients[0].key.crv, 224 | x: base64url.toBuffer(example.input.enveloped.recipients[0].key.x), 225 | y: base64url.toBuffer(example.input.enveloped.recipients[0].key.y), 226 | d: base64url.toBuffer(example.input.enveloped.recipients[0].key.d) 227 | }, 228 | p: example.input.enveloped.recipients[0].protected, 229 | u: example.input.enveloped.recipients[0].unprotected 230 | }]; 231 | 232 | const options = { 233 | randomSource: (bytes) => { 234 | if (bytes === 12) { 235 | return Buffer.from('3082660901A9B9CD87AACB71', 'hex'); 236 | } else { 237 | return Buffer.from('02D1F7E6F26C43D4868D87CEB2353161740AACF1F7163647984B522A848DF1C3C9CF4DF2FE6C632BF7886413F76E885252908DF901D1581A045444DD996E1704B9B6', 'hex'); 238 | } 239 | } 240 | }; 241 | 242 | const header = { p: p, u: u }; 243 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 244 | t.true(Buffer.isBuffer(buf)); 245 | t.true(buf.length > 0); 246 | const actual = cbor.decodeFirstSync(buf); 247 | const expected = cbor.decodeFirstSync(example.output.cbor); 248 | t.true(deepEqual(actual, expected)); 249 | }); 250 | 251 | test('create p521-hkdf-256-02', async t => { 252 | const example = jsonfile.readFileSync('test/Examples/ecdh-direct-examples/p521-hkdf-256-02.json'); 253 | const p = example.input.enveloped.protected; 254 | const u = example.input.enveloped.unprotected; 255 | const plaintext = Buffer.from(example.input.plaintext); 256 | 257 | const recipient = [{ 258 | key: { 259 | kty: example.input.enveloped.recipients[0].key.kty, 260 | kid: example.input.enveloped.recipients[0].key.kid, 261 | crv: example.input.enveloped.recipients[0].key.crv, 262 | x: base64url.toBuffer(example.input.enveloped.recipients[0].key.x), 263 | y: base64url.toBuffer(example.input.enveloped.recipients[0].key.y), 264 | d: base64url.toBuffer(example.input.enveloped.recipients[0].key.d) 265 | }, 266 | p: example.input.enveloped.recipients[0].protected, 267 | u: example.input.enveloped.recipients[0].unprotected 268 | }]; 269 | 270 | const options = { 271 | randomSource: (bytes) => { 272 | if (bytes === 12) { 273 | return Buffer.from('512C9CC879517ADCF0FA768E', 'hex'); 274 | } else { 275 | return Buffer.from('02D1F7E6F26C43D4868D87CEB2353161740AACF1F7163647984B522A848DF1C3C9CF4DF2FE6C632BF7886413F76E88527FAC271A4C7EA34B7E28D7BBB54C682BED7A', 'hex'); 276 | } 277 | } 278 | }; 279 | 280 | const header = { p: p, u: u }; 281 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 282 | t.true(Buffer.isBuffer(buf)); 283 | t.true(buf.length > 0); 284 | const actual = cbor.decodeFirstSync(buf); 285 | const expected = cbor.decodeFirstSync(example.output.cbor); 286 | t.true(deepEqual(actual, expected)); 287 | }); 288 | 289 | // create p521-hkdf-256-03 290 | 291 | test('create p521-hkdf-512-01', async t => { 292 | const example = jsonfile.readFileSync('test/Examples/ecdh-direct-examples/p521-hkdf-512-01.json'); 293 | const p = example.input.enveloped.protected; 294 | const u = example.input.enveloped.unprotected; 295 | const plaintext = Buffer.from(example.input.plaintext); 296 | 297 | const recipient = [{ 298 | key: { 299 | kty: example.input.enveloped.recipients[0].key.kty, 300 | kid: example.input.enveloped.recipients[0].key.kid, 301 | crv: example.input.enveloped.recipients[0].key.crv, 302 | x: base64url.toBuffer(example.input.enveloped.recipients[0].key.x), 303 | y: base64url.toBuffer(example.input.enveloped.recipients[0].key.y), 304 | d: base64url.toBuffer(example.input.enveloped.recipients[0].key.d) 305 | }, 306 | p: example.input.enveloped.recipients[0].protected, 307 | u: example.input.enveloped.recipients[0].unprotected 308 | }]; 309 | 310 | const options = { 311 | randomSource: (bytes) => { 312 | if (bytes === 12) { 313 | return Buffer.from('CFDC1CA6D690CF1964458D42', 'hex'); 314 | } else { 315 | return Buffer.from('02D1F7E6F26C43D4868D87CEB2353161740AACF1F7163647984B522A848DF1C3C9CF4DF2FE6C632BF7886413F76E88526B37FD0B58734CA925A389AD361ECEF28358', 'hex'); 316 | } 317 | } 318 | }; 319 | 320 | const header = { p: p, u: u }; 321 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 322 | t.true(Buffer.isBuffer(buf)); 323 | t.true(buf.length > 0); 324 | const actual = cbor.decodeFirstSync(buf); 325 | const expected = cbor.decodeFirstSync(example.output.cbor); 326 | t.true(deepEqual(actual, expected)); 327 | }); 328 | 329 | test('create p521-hkdf-512-02', async t => { 330 | const example = jsonfile.readFileSync('test/Examples/ecdh-direct-examples/p521-hkdf-512-02.json'); 331 | const p = example.input.enveloped.protected; 332 | const u = example.input.enveloped.unprotected; 333 | const plaintext = Buffer.from(example.input.plaintext); 334 | 335 | const recipient = [{ 336 | key: { 337 | kty: example.input.enveloped.recipients[0].key.kty, 338 | kid: example.input.enveloped.recipients[0].key.kid, 339 | crv: example.input.enveloped.recipients[0].key.crv, 340 | x: base64url.toBuffer(example.input.enveloped.recipients[0].key.x), 341 | y: base64url.toBuffer(example.input.enveloped.recipients[0].key.y), 342 | d: base64url.toBuffer(example.input.enveloped.recipients[0].key.d) 343 | }, 344 | p: example.input.enveloped.recipients[0].protected, 345 | u: example.input.enveloped.recipients[0].unprotected 346 | }]; 347 | 348 | const options = { 349 | randomSource: (bytes) => { 350 | if (bytes === 12) { 351 | return Buffer.from('E89FD3534E1ABAF69C65CFE0', 'hex'); 352 | } else { 353 | return Buffer.from('02D1F7E6F26C43D4868D87CEB2353161740AACF1F7163647984B522A848DF1C3C9CF4DF2FE6C632BF7886413F76E885238FB137F6C20764D89E26452937675B5E3B4', 'hex'); 354 | } 355 | } 356 | }; 357 | 358 | const header = { p: p, u: u }; 359 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 360 | t.true(Buffer.isBuffer(buf)); 361 | t.true(buf.length > 0); 362 | const actual = cbor.decodeFirstSync(buf); 363 | const expected = cbor.decodeFirstSync(example.output.cbor); 364 | t.true(deepEqual(actual, expected)); 365 | }); 366 | 367 | // create p521-hkdf-512-03 368 | 369 | // create p521-ss-hkdf-256-01 370 | 371 | // create p521-ss-hkdf-256-02 372 | 373 | // create p521-ss-hkdf-256-03 374 | 375 | // create p521-ss-hkdf-512-01 376 | 377 | // create p521-ss-hkdf-512-02 378 | 379 | // create p521-ss-hkdf-512-03 380 | -------------------------------------------------------------------------------- /test/encrypted-tests.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | function randomSource (bytes) { 13 | if (bytes === 12) { 14 | return Buffer.from('02D1F7E6F26C43D4868D87CE', 'hex'); 15 | } else { 16 | return Buffer.from('61A7', 'hex'); 17 | } 18 | } 19 | 20 | test('create aes-gcm-01', async t => { 21 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/aes-gcm-01.json'); 22 | const p = example.input.encrypted.protected; 23 | const u = example.input.encrypted.unprotected; 24 | const plaintext = Buffer.from(example.input.plaintext); 25 | 26 | const recipient = { 27 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 28 | u: example.input.encrypted.recipients[0].unprotected 29 | }; 30 | 31 | const options = { 32 | randomSource: randomSource 33 | }; 34 | 35 | const header = { p: p, u: u }; 36 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 37 | t.true(Buffer.isBuffer(buf)); 38 | t.true(buf.length > 0); 39 | const actual = cbor.decodeFirstSync(buf); 40 | const expected = cbor.decodeFirstSync(example.output.cbor); 41 | t.true(deepEqual(actual, expected)); 42 | }); 43 | 44 | test('create enc-pass-01', async t => { 45 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-pass-01.json'); 46 | const p = example.input.encrypted.protected; 47 | const u = example.input.encrypted.unprotected; 48 | const plaintext = Buffer.from(example.input.plaintext); 49 | 50 | const recipient = { 51 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 52 | u: example.input.encrypted.recipients[0].unprotected 53 | }; 54 | 55 | const options = { 56 | randomSource: randomSource 57 | }; 58 | const header = { p: p, u: u }; 59 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 60 | t.true(Buffer.isBuffer(buf)); 61 | t.true(buf.length > 0); 62 | const actual = cbor.decodeFirstSync(buf); 63 | const expected = cbor.decodeFirstSync(example.output.cbor); 64 | t.true(deepEqual(actual, expected)); 65 | }); 66 | 67 | test('create enc-pass-02', async t => { 68 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-pass-02.json'); 69 | const p = example.input.encrypted.protected; 70 | const u = example.input.encrypted.unprotected; 71 | const plaintext = Buffer.from(example.input.plaintext); 72 | const external = Buffer.from(example.input.encrypted.external, 'hex'); 73 | 74 | const recipient = { 75 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 76 | u: example.input.encrypted.recipients[0].unprotected 77 | }; 78 | 79 | const options = { 80 | randomSource: randomSource, 81 | externalAAD: external, 82 | encodep: 'empty' 83 | }; 84 | 85 | const header = { p: p, u: u }; 86 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 87 | t.true(Buffer.isBuffer(buf)); 88 | t.true(buf.length > 0); 89 | const actual = cbor.decodeFirstSync(buf); 90 | const expected = cbor.decodeFirstSync(example.output.cbor); 91 | t.true(deepEqual(actual, expected)); 92 | }); 93 | 94 | test('create enc-pass-03', async t => { 95 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-pass-03.json'); 96 | const p = example.input.encrypted.protected; 97 | const u = example.input.encrypted.unprotected; 98 | const plaintext = Buffer.from(example.input.plaintext); 99 | 100 | const recipient = { 101 | key: base64url.toBuffer(example.input.encrypted.recipients[0].key.k), 102 | u: example.input.encrypted.recipients[0].unprotected 103 | }; 104 | 105 | const options = { 106 | randomSource: randomSource, 107 | excludetag: true, 108 | encodep: 'empty' 109 | }; 110 | 111 | const header = { p: p, u: u }; 112 | const buf = await cose.encrypt.create(header, plaintext, recipient, options); 113 | t.true(Buffer.isBuffer(buf)); 114 | t.true(buf.length > 0); 115 | const actual = cbor.decodeFirstSync(buf); 116 | const expected = cbor.decodeFirstSync(example.output.cbor); 117 | t.true(deepEqual(actual, expected)); 118 | }); 119 | 120 | test('decrypt aes-gcm-01', async t => { 121 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/aes-gcm-01.json'); 122 | const plaintext = example.input.plaintext; 123 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 124 | 125 | const data = example.output.cbor; 126 | const buf = await cose.encrypt.read(data, key); 127 | t.true(Buffer.isBuffer(buf)); 128 | t.true(buf.length > 0); 129 | t.is(buf.toString('utf8'), plaintext); 130 | }); 131 | 132 | test('decrypt enc-pass-01', async t => { 133 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-pass-01.json'); 134 | const plaintext = example.input.plaintext; 135 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 136 | 137 | const data = example.output.cbor; 138 | const buf = await cose.encrypt.read(data, key); 139 | t.true(Buffer.isBuffer(buf)); 140 | t.true(buf.length > 0); 141 | t.is(buf.toString('utf8'), plaintext); 142 | }); 143 | 144 | test('decrypt enc-pass-02', async t => { 145 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-pass-02.json'); 146 | const plaintext = example.input.plaintext; 147 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 148 | const options = { 149 | externalAAD: Buffer.from(example.input.encrypted.external, 'hex') 150 | }; 151 | 152 | const data = example.output.cbor; 153 | const buf = await cose.encrypt.read(data, key, options); 154 | t.true(Buffer.isBuffer(buf)); 155 | t.true(buf.length > 0); 156 | t.is(buf.toString('utf8'), plaintext); 157 | }); 158 | 159 | test('decrypt enc-pass-03', async t => { 160 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-pass-03.json'); 161 | const plaintext = example.input.plaintext; 162 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 163 | const options = { 164 | defaultType: cose.encrypt.Encrypt0Tag 165 | }; 166 | const data = example.output.cbor; 167 | const buf = await cose.encrypt.read(data, key, options); 168 | t.true(Buffer.isBuffer(buf)); 169 | t.true(buf.length > 0); 170 | t.is(buf.toString('utf8'), plaintext); 171 | }); 172 | 173 | test('decrypt enc-fail-01', async t => { 174 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-fail-01.json'); 175 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 176 | const data = example.output.cbor; 177 | try { 178 | await cose.encrypt.read(data, key); 179 | t.fail('Unknown tag, 995'); 180 | } catch (error) { 181 | t.is(error.message, 'Unknown tag, 995'); 182 | } 183 | }); 184 | 185 | test('decrypt enc-fail-02', async t => { 186 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-fail-02.json'); 187 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 188 | const data = example.output.cbor; 189 | try { 190 | await cose.encrypt.read(data, key); 191 | t.fail('Unsupported state or unable to authenticate data'); 192 | } catch (error) { 193 | t.is(error.message, 'Unsupported state or unable to authenticate data'); 194 | } 195 | }); 196 | 197 | test('decrypt enc-fail-03', async t => { 198 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-fail-03.json'); 199 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 200 | const data = example.output.cbor; 201 | try { 202 | await cose.encrypt.read(data, key); 203 | t.fail('Unknown or unsupported algorithm -999'); 204 | } catch (error) { 205 | t.is(error.message, 'Unknown or unsupported algorithm -999'); 206 | } 207 | }); 208 | 209 | test('decrypt enc-fail-04', async t => { 210 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-fail-04.json'); 211 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 212 | const data = example.output.cbor; 213 | try { 214 | await cose.encrypt.read(data, key); 215 | t.fail('Unknown or unsupported algorithm Unknown'); 216 | } catch (error) { 217 | t.is(error.message, 'Unknown or unsupported algorithm Unknown'); 218 | } 219 | }); 220 | 221 | test('decrypt enc-fail-06', async t => { 222 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-fail-06.json'); 223 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 224 | const data = example.output.cbor; 225 | try { 226 | await cose.encrypt.read(data, key); 227 | t.fail('Unsupported state or unable to authenticate data'); 228 | } catch (error) { 229 | t.is(error.message, 'Unsupported state or unable to authenticate data'); 230 | } 231 | }); 232 | 233 | test('decrypt enc-fail-07', async t => { 234 | const example = jsonfile.readFileSync('test/Examples/encrypted-tests/enc-fail-07.json'); 235 | const key = base64url.toBuffer(example.input.encrypted.recipients[0].key.k); 236 | const data = example.output.cbor; 237 | try { 238 | await cose.encrypt.read(data, key); 239 | t.fail('Unsupported state or unable to authenticate data'); 240 | } catch (error) { 241 | t.is(error.message, 'Unsupported state or unable to authenticate data'); 242 | } 243 | }); 244 | -------------------------------------------------------------------------------- /test/enveloped-tests.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | function randomSource (bytes) { 13 | if (bytes === 12) { 14 | return Buffer.from('02D1F7E6F26C43D4868D87CE', 'hex'); 15 | } else { 16 | return Buffer.from('61A7', 'hex'); 17 | } 18 | } 19 | 20 | test('create aes-gcm-01', async t => { 21 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/aes-gcm-01.json'); 22 | const p = example.input.enveloped.protected; 23 | const u = example.input.enveloped.unprotected; 24 | const plaintext = Buffer.from(example.input.plaintext); 25 | 26 | const recipients = [{ 27 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 28 | u: example.input.enveloped.recipients[0].unprotected 29 | }]; 30 | 31 | const options = { 32 | randomSource: randomSource 33 | }; 34 | const header = { p: p, u: u }; 35 | const buf = await cose.encrypt.create(header, plaintext, recipients, options); 36 | t.true(Buffer.isBuffer(buf)); 37 | t.true(buf.length > 0); 38 | const actual = cbor.decodeFirstSync(buf); 39 | const expected = cbor.decodeFirstSync(example.output.cbor); 40 | t.true(deepEqual(actual, expected)); 41 | }); 42 | 43 | test('create env-pass-01', async t => { 44 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-pass-01.json'); 45 | const p = example.input.enveloped.protected; 46 | const u = example.input.enveloped.unprotected; 47 | const plaintext = Buffer.from(example.input.plaintext); 48 | 49 | const recipients = [{ 50 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 51 | u: example.input.enveloped.recipients[0].unprotected 52 | }]; 53 | 54 | const options = { 55 | randomSource: randomSource 56 | }; 57 | 58 | const header = { p: p, u: u }; 59 | const buf = await cose.encrypt.create(header, plaintext, recipients, options); 60 | t.true(Buffer.isBuffer(buf)); 61 | t.true(buf.length > 0); 62 | const actual = cbor.decodeFirstSync(buf); 63 | const expected = cbor.decodeFirstSync(example.output.cbor); 64 | t.true(deepEqual(actual, expected)); 65 | }); 66 | 67 | test('create env-pass-02', async t => { 68 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-pass-02.json'); 69 | const p = example.input.enveloped.protected; 70 | const u = example.input.enveloped.unprotected; 71 | const plaintext = Buffer.from(example.input.plaintext); 72 | 73 | const recipients = [{ 74 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 75 | u: example.input.enveloped.recipients[0].unprotected 76 | }]; 77 | 78 | const options = { 79 | randomSource: randomSource, 80 | externalAAD: Buffer.from(example.input.enveloped.external, 'hex') 81 | }; 82 | const header = { p: p, u: u }; 83 | const buf = await cose.encrypt.create(header, plaintext, recipients, options); 84 | t.true(Buffer.isBuffer(buf)); 85 | t.true(buf.length > 0); 86 | const actual = cbor.decodeFirstSync(buf); 87 | const expected = cbor.decodeFirstSync(example.output.cbor); 88 | t.true(deepEqual(actual, expected)); 89 | }); 90 | 91 | test('create env-pass-03', async t => { 92 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-pass-03.json'); 93 | const p = example.input.enveloped.protected; 94 | const u = example.input.enveloped.unprotected; 95 | const plaintext = Buffer.from(example.input.plaintext); 96 | 97 | const recipients = [{ 98 | key: base64url.toBuffer(example.input.enveloped.recipients[0].key.k), 99 | u: example.input.enveloped.recipients[0].unprotected 100 | }]; 101 | 102 | const options = { 103 | randomSource: randomSource, 104 | excludetag: true, 105 | encodep: 'empty' 106 | }; 107 | const header = { p: p, u: u }; 108 | const buf = await cose.encrypt.create(header, plaintext, recipients, options); 109 | t.true(Buffer.isBuffer(buf)); 110 | t.true(buf.length > 0); 111 | const actual = cbor.decodeFirstSync(buf); 112 | const expected = cbor.decodeFirstSync(example.output.cbor); 113 | t.true(deepEqual(actual, expected)); 114 | }); 115 | 116 | test('decrypt aes-gcm-01', async t => { 117 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/aes-gcm-01.json'); 118 | const plaintext = example.input.plaintext; 119 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 120 | 121 | const data = example.output.cbor; 122 | const buf = await cose.encrypt.read(data, key); 123 | t.true(Buffer.isBuffer(buf)); 124 | t.true(buf.length > 0); 125 | t.is(buf.toString('utf8'), plaintext); 126 | }); 127 | 128 | test('decrypt enc-pass-01', async t => { 129 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-pass-01.json'); 130 | const plaintext = example.input.plaintext; 131 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 132 | const data = example.output.cbor; 133 | const buf = await cose.encrypt.read(data, key); 134 | t.true(Buffer.isBuffer(buf)); 135 | t.true(buf.length > 0); 136 | t.is(buf.toString('utf8'), plaintext); 137 | }); 138 | 139 | test('decrypt enc-pass-02', async t => { 140 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-pass-02.json'); 141 | const plaintext = example.input.plaintext; 142 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 143 | const options = { 144 | externalAAD: Buffer.from(example.input.enveloped.external, 'hex') 145 | }; 146 | const data = example.output.cbor; 147 | const buf = await cose.encrypt.read(data, key, options); 148 | t.true(Buffer.isBuffer(buf)); 149 | t.true(buf.length > 0); 150 | t.is(buf.toString('utf8'), plaintext); 151 | }); 152 | 153 | test('decrypt enc-pass-03', async t => { 154 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-pass-03.json'); 155 | const plaintext = example.input.plaintext; 156 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 157 | const data = example.output.cbor; 158 | const buf = await cose.encrypt.read(data, key); 159 | t.true(Buffer.isBuffer(buf)); 160 | t.true(buf.length > 0); 161 | t.is(buf.toString('utf8'), plaintext); 162 | }); 163 | 164 | test('decrypt enc-fail-01', async t => { 165 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-fail-01.json'); 166 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 167 | const data = example.output.cbor; 168 | 169 | try { 170 | await cose.encrypt.read(data, key); 171 | t.fail('Unknown tag, 995'); 172 | } catch (error) { 173 | t.is(error.message, 'Unknown tag, 995'); 174 | } 175 | }); 176 | 177 | test('decrypt enc-fail-02', async t => { 178 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-fail-02.json'); 179 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 180 | const data = example.output.cbor; 181 | 182 | try { 183 | await cose.encrypt.read(data, key); 184 | t.fail('Unsupported state or unable to authenticate data'); 185 | } catch (error) { 186 | t.is(error.message, 'Unsupported state or unable to authenticate data'); 187 | } 188 | }); 189 | 190 | test('decrypt enc-fail-03', async t => { 191 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-fail-03.json'); 192 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 193 | const data = example.output.cbor; 194 | 195 | try { 196 | await cose.encrypt.read(data, key); 197 | t.fail('Unknown or unsupported algorithm -999'); 198 | } catch (error) { 199 | t.is(error.message, 'Unknown or unsupported algorithm -999'); 200 | } 201 | }); 202 | 203 | test('decrypt enc-fail-04', async t => { 204 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-fail-04.json'); 205 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 206 | const data = example.output.cbor; 207 | 208 | try { 209 | await cose.encrypt.read(data, key); 210 | t.fail('Unknown or unsupported algorithm Unknown'); 211 | } catch (error) { 212 | t.is(error.message, 'Unknown or unsupported algorithm Unknown'); 213 | } 214 | }); 215 | 216 | test('decrypt enc-fail-06', async t => { 217 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-fail-06.json'); 218 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 219 | const data = example.output.cbor; 220 | 221 | try { 222 | await cose.encrypt.read(data, key); 223 | t.fail('Unsupported state or unable to authenticate data'); 224 | } catch (error) { 225 | t.is(error.message, 'Unsupported state or unable to authenticate data'); 226 | } 227 | }); 228 | 229 | test('decrypt enc-fail-07', async t => { 230 | const example = jsonfile.readFileSync('test/Examples/enveloped-tests/env-fail-07.json'); 231 | const key = base64url.toBuffer(example.input.enveloped.recipients[0].key.k); 232 | const data = example.output.cbor; 233 | 234 | try { 235 | await cose.encrypt.read(data, key); 236 | t.fail('Unsupported state or unable to authenticate data'); 237 | } catch (error) { 238 | t.is(error.message, 'Unsupported state or unable to authenticate data'); 239 | } 240 | }); 241 | -------------------------------------------------------------------------------- /test/hmac-examples.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | test('create HMac-enc-01', async t => { 13 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-enc-01.json'); 14 | const p = example.input.mac0.protected; 15 | const u = example.input.mac0.unprotected; 16 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 17 | const plaintext = Buffer.from(example.input.plaintext); 18 | const header = { p: p, u: u }; 19 | const recipeient = { key: key }; 20 | const buf = await cose.mac.create(header, plaintext, recipeient); 21 | t.true(Buffer.isBuffer(buf)); 22 | t.true(buf.length > 0); 23 | const actual = cbor.decodeFirstSync(buf); 24 | const expected = cbor.decodeFirstSync(example.output.cbor); 25 | t.true(deepEqual(actual, expected)); 26 | }); 27 | 28 | test('create HMac-enc-02', async t => { 29 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-enc-02.json'); 30 | const p = example.input.mac0.protected; 31 | const u = example.input.mac0.unprotected; 32 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 33 | const plaintext = Buffer.from(example.input.plaintext); 34 | const header = { p: p, u: u }; 35 | const recipient = { key: key }; 36 | const buf = await cose.mac.create(header, plaintext, recipient); 37 | t.true(Buffer.isBuffer(buf)); 38 | t.true(buf.length > 0); 39 | const actual = cbor.decodeFirstSync(buf); 40 | const expected = cbor.decodeFirstSync(example.output.cbor); 41 | t.true(deepEqual(actual, expected)); 42 | }); 43 | 44 | test('create HMac-enc-03', async t => { 45 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-enc-03.json'); 46 | const p = example.input.mac0.protected; 47 | const u = example.input.mac0.unprotected; 48 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 49 | const plaintext = Buffer.from(example.input.plaintext); 50 | const header = { p: p, u: u }; 51 | const recipient = { key: key }; 52 | const buf = await cose.mac.create(header, plaintext, recipient); 53 | t.true(Buffer.isBuffer(buf)); 54 | t.true(buf.length > 0); 55 | const actual = cbor.decodeFirstSync(buf); 56 | const expected = cbor.decodeFirstSync(example.output.cbor); 57 | t.true(deepEqual(actual, expected)); 58 | }); 59 | 60 | // HMac-enc-04 is a negative test and cannot be recreated 61 | 62 | test('create HMac-enc-05', async t => { 63 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-enc-05.json'); 64 | const p = example.input.mac0.protected; 65 | const u = example.input.mac0.unprotected; 66 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 67 | const plaintext = Buffer.from(example.input.plaintext); 68 | const header = { p: p, u: u }; 69 | const recipeint = { key: key }; 70 | const buf = await cose.mac.create(header, plaintext, recipeint); 71 | t.true(Buffer.isBuffer(buf)); 72 | t.true(buf.length > 0); 73 | const actual = cbor.decodeFirstSync(buf); 74 | const expected = cbor.decodeFirstSync(example.output.cbor); 75 | t.true(deepEqual(actual, expected)); 76 | }); 77 | 78 | test('verify HMac-enc-01', async t => { 79 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-enc-01.json'); 80 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 81 | const data = example.output.cbor; 82 | const buf = await cose.mac.read(data, key); 83 | t.true(Buffer.isBuffer(buf)); 84 | t.true(buf.length > 0); 85 | t.is(buf.toString('utf8'), example.input.plaintext); 86 | }); 87 | 88 | test('verify HMac-enc-02', async t => { 89 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-enc-02.json'); 90 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 91 | 92 | const data = example.output.cbor; 93 | const buf = await cose.mac.read(data, key); 94 | t.true(Buffer.isBuffer(buf)); 95 | t.true(buf.length > 0); 96 | t.is(buf.toString('utf8'), example.input.plaintext); 97 | }); 98 | 99 | test('verify HMac-enc-03', async t => { 100 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-enc-03.json'); 101 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 102 | const data = example.output.cbor; 103 | const buf = await cose.mac.read(data, key); 104 | t.true(Buffer.isBuffer(buf)); 105 | t.true(buf.length > 0); 106 | t.is(buf.toString('utf8'), example.input.plaintext); 107 | }); 108 | 109 | test('verify HMac-enc-04', async t => { 110 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-enc-04.json'); 111 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 112 | const data = example.output.cbor; 113 | try { 114 | await cose.mac.read(data, key); 115 | t.fail('Tag mismatch'); 116 | } catch (error) { 117 | t.is(error.message, 'Tag mismatch'); 118 | } 119 | }); 120 | 121 | test('verify HMac-enc-05', async t => { 122 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-enc-05.json'); 123 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 124 | const data = example.output.cbor; 125 | const buf = await cose.mac.read(data, key); 126 | t.true(Buffer.isBuffer(buf)); 127 | t.true(buf.length > 0); 128 | t.is(buf.toString('utf8'), example.input.plaintext); 129 | }); 130 | 131 | test('create HMac-01', async t => { 132 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-01.json'); 133 | const p = example.input.mac.protected; 134 | const u = example.input.mac.recipients[0].unprotected; 135 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 136 | const plaintext = Buffer.from(example.input.plaintext); 137 | const recipents = [{ key: key, u: u }]; 138 | const header = { p: p }; 139 | const buf = await cose.mac.create(header, plaintext, recipents); 140 | t.true(Buffer.isBuffer(buf)); 141 | t.true(buf.length > 0); 142 | const actual = cbor.decodeFirstSync(buf); 143 | const expected = cbor.decodeFirstSync(example.output.cbor); 144 | t.true(deepEqual(actual, expected)); 145 | }); 146 | 147 | test('create HMac-02', async t => { 148 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-02.json'); 149 | const p = example.input.mac.protected; 150 | const u = example.input.mac.recipients[0].unprotected; 151 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 152 | const plaintext = Buffer.from(example.input.plaintext); 153 | const recipents = [{ key: key, u: u }]; 154 | const header = { p: p }; 155 | const buf = await cose.mac.create(header, plaintext, recipents); 156 | t.true(Buffer.isBuffer(buf)); 157 | t.true(buf.length > 0); 158 | const actual = cbor.decodeFirstSync(buf); 159 | const expected = cbor.decodeFirstSync(example.output.cbor); 160 | t.true(deepEqual(actual, expected)); 161 | }); 162 | 163 | test('create HMac-03', async t => { 164 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-03.json'); 165 | const p = example.input.mac.protected; 166 | const u = example.input.mac.recipients[0].unprotected; 167 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 168 | const plaintext = Buffer.from(example.input.plaintext); 169 | const recipents = [{ key: key, u: u }]; 170 | const header = { p: p }; 171 | const buf = await cose.mac.create(header, plaintext, recipents); 172 | t.true(Buffer.isBuffer(buf)); 173 | t.true(buf.length > 0); 174 | const actual = cbor.decodeFirstSync(buf); 175 | const expected = cbor.decodeFirstSync(example.output.cbor); 176 | t.true(deepEqual(actual, expected)); 177 | }); 178 | 179 | // HMac-04 is a negative test and cannot be recreated 180 | 181 | test('create HMac-05', async t => { 182 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-05.json'); 183 | const p = example.input.mac.protected; 184 | const u = example.input.mac.recipients[0].unprotected; 185 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 186 | const plaintext = Buffer.from(example.input.plaintext); 187 | const recipents = [{ key: key, u: u }]; 188 | const header = { p: p }; 189 | const buf = await cose.mac.create(header, plaintext, recipents); 190 | t.true(Buffer.isBuffer(buf)); 191 | t.true(buf.length > 0); 192 | const actual = cbor.decodeFirstSync(buf); 193 | const expected = cbor.decodeFirstSync(example.output.cbor); 194 | t.true(deepEqual(actual, expected)); 195 | }); 196 | 197 | test('verify HMac-01', async t => { 198 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-01.json'); 199 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 200 | const data = example.output.cbor; 201 | const buf = await cose.mac.read(data, key); 202 | t.true(Buffer.isBuffer(buf)); 203 | t.true(buf.length > 0); 204 | t.is(buf.toString('utf8'), example.input.plaintext); 205 | }); 206 | 207 | test('verify HMac-02', async t => { 208 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-02.json'); 209 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 210 | const data = example.output.cbor; 211 | const buf = await cose.mac.read(data, key); 212 | t.true(Buffer.isBuffer(buf)); 213 | t.true(buf.length > 0); 214 | t.is(buf.toString('utf8'), example.input.plaintext); 215 | }); 216 | 217 | test('verify HMac-03', async t => { 218 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-03.json'); 219 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 220 | const data = example.output.cbor; 221 | const buf = await cose.mac.read(data, key); 222 | t.true(Buffer.isBuffer(buf)); 223 | t.true(buf.length > 0); 224 | t.is(buf.toString('utf8'), example.input.plaintext); 225 | }); 226 | 227 | test('verify HMac-04', async t => { 228 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-04.json'); 229 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 230 | const data = example.output.cbor; 231 | try { 232 | await cose.mac.read(data, key); 233 | t.fail('Tag mismatch'); 234 | } catch (error) { 235 | t.is(error.message, 'Tag mismatch'); 236 | } 237 | }); 238 | 239 | test('verify HMac-05', async t => { 240 | const example = jsonfile.readFileSync('test/Examples/hmac-examples/HMac-05.json'); 241 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 242 | const data = example.output.cbor; 243 | const buf = await cose.mac.read(data, key); 244 | t.true(Buffer.isBuffer(buf)); 245 | t.true(buf.length > 0); 246 | t.is(buf.toString('utf8'), example.input.plaintext); 247 | }); 248 | -------------------------------------------------------------------------------- /test/mac-tests.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | test('create HMac-01', async t => { 13 | const example = jsonfile.readFileSync('test/Examples/mac-tests/HMac-01.json'); 14 | const p = example.input.mac.protected; 15 | const u = example.input.mac.recipients[0].unprotected; 16 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 17 | const plaintext = Buffer.from(example.input.plaintext); 18 | 19 | const recipents = [{ key: key, u: u }]; 20 | const header = { p: p }; 21 | const buf = await cose.mac.create(header, plaintext, recipents); 22 | t.true(Buffer.isBuffer(buf)); 23 | t.true(buf.length > 0); 24 | const actual = cbor.decodeFirstSync(buf); 25 | const expected = cbor.decodeFirstSync(example.output.cbor); 26 | t.true(deepEqual(actual, expected)); 27 | }); 28 | 29 | test('verify HMac-01', async t => { 30 | const example = jsonfile.readFileSync('test/Examples/mac-tests/HMac-01.json'); 31 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 32 | 33 | const data = example.output.cbor; 34 | const buf = await cose.mac.read(data, key); 35 | t.true(Buffer.isBuffer(buf)); 36 | t.true(buf.length > 0); 37 | t.is(buf.toString('utf8'), example.input.plaintext); 38 | }); 39 | 40 | test('create mac-pass-01', async t => { 41 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-pass-01.json'); 42 | const p = example.input.mac.protected; 43 | const u = example.input.mac.unprotected; 44 | const ru = example.input.mac.recipients[0].unprotected; 45 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 46 | const plaintext = Buffer.from(example.input.plaintext); 47 | const recipents = [{ key: key, u: ru }]; 48 | const header = { p: p, u: u }; 49 | const buf = await cose.mac.create(header, plaintext, recipents); 50 | t.true(Buffer.isBuffer(buf)); 51 | t.true(buf.length > 0); 52 | t.is(buf.toString('hex'), example.output.cbor.toLowerCase()); 53 | const actual = cbor.decodeFirstSync(buf); 54 | const expected = cbor.decodeFirstSync(example.output.cbor); 55 | t.true(deepEqual(actual, expected)); 56 | }); 57 | 58 | test('create mac-pass-02', async t => { 59 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-pass-02.json'); 60 | const p = example.input.mac.protected; 61 | const u = example.input.mac.unprotected; 62 | const ru = example.input.mac.recipients[0].unprotected; 63 | const external = Buffer.from(example.input.mac.external, 'hex'); 64 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 65 | const plaintext = Buffer.from(example.input.plaintext); 66 | const options = { encodep: 'empty' }; 67 | const header = { p: p, u: u }; 68 | const recipents = [{ key: key, u: ru }]; 69 | const buf = await cose.mac.create(header, plaintext, recipents, external, options); 70 | t.true(Buffer.isBuffer(buf)); 71 | t.true(buf.length > 0); 72 | const actual = cbor.decodeFirstSync(buf); 73 | const expected = cbor.decodeFirstSync(example.output.cbor); 74 | t.true(deepEqual(actual, expected)); 75 | }); 76 | 77 | test('create mac-pass-03', async t => { 78 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-pass-03.json'); 79 | const p = example.input.mac.protected; 80 | const u = example.input.mac.unprotected; 81 | const ru = example.input.mac.recipients[0].unprotected; 82 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 83 | const plaintext = Buffer.from(example.input.plaintext); 84 | const options = { encodep: 'empty', excludetag: true }; 85 | const header = { p: p, u: u }; 86 | const recipents = [{ key: key, u: ru }]; 87 | const external = null; 88 | const buf = await cose.mac.create(header, plaintext, recipents, external, options); 89 | t.true(Buffer.isBuffer(buf)); 90 | t.true(buf.length > 0); 91 | const actual = cbor.decodeFirstSync(buf); 92 | const expected = cbor.decodeFirstSync(example.output.cbor); 93 | t.true(deepEqual(actual, expected)); 94 | }); 95 | 96 | test('verify mac-pass-01', async t => { 97 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-pass-01.json'); 98 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 99 | const data = example.output.cbor; 100 | const buf = await cose.mac.read(data, key); 101 | t.true(Buffer.isBuffer(buf)); 102 | t.true(buf.length > 0); 103 | t.is(buf.toString('utf8'), example.input.plaintext); 104 | }); 105 | 106 | test('verify mac-pass-02', async t => { 107 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-pass-02.json'); 108 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 109 | const external = Buffer.from(example.input.mac.external, 'hex'); 110 | const data = example.output.cbor; 111 | const buf = await cose.mac.read(data, key, external); 112 | t.true(Buffer.isBuffer(buf)); 113 | t.true(buf.length > 0); 114 | t.is(buf.toString('utf8'), example.input.plaintext); 115 | }); 116 | 117 | test('verify mac-pass-03', async t => { 118 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-pass-03.json'); 119 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 120 | const options = { defaultType: cose.mac.MACTag }; 121 | const external = null; 122 | const data = example.output.cbor; 123 | const buf = await cose.mac.read(data, key, external, options); 124 | t.true(Buffer.isBuffer(buf)); 125 | t.true(buf.length > 0); 126 | t.is(buf.toString('utf8'), example.input.plaintext); 127 | }); 128 | 129 | test('verify mac-fail-01', async t => { 130 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-fail-01.json'); 131 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 132 | const data = example.output.cbor; 133 | try { 134 | await cose.mac.read(data, key); 135 | t.fail('Expecting Array of lenght 4'); 136 | } catch (error) { 137 | t.is(error.message, 'Expecting Array of lenght 4'); 138 | } 139 | }); 140 | 141 | test('verify mac-fail-02', async t => { 142 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-fail-02.json'); 143 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 144 | const data = example.output.cbor; 145 | try { 146 | await cose.mac.read(data, key); 147 | t.fail('Tag mismatch'); 148 | } catch (error) { 149 | t.is(error.message, 'Tag mismatch'); 150 | } 151 | }); 152 | 153 | test('verify mac-fail-03', async t => { 154 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-fail-03.json'); 155 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 156 | const data = example.output.cbor; 157 | try { 158 | await cose.mac.read(data, key); 159 | t.fail('Unknown algorithm, -999'); 160 | } catch (error) { 161 | t.is(error.message, 'Unknown algorithm, -999'); 162 | } 163 | }); 164 | 165 | test('verify mac-fail-04', async t => { 166 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-fail-04.json'); 167 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 168 | const data = example.output.cbor; 169 | try { 170 | await cose.mac.read(data, key); 171 | t.fail('Unknown algorithm, Unknown'); 172 | } catch (error) { 173 | t.is(error.message, 'Unknown algorithm, Unknown'); 174 | } 175 | }); 176 | 177 | test('verify mac-fail-06', async t => { 178 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-fail-06.json'); 179 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 180 | const data = example.output.cbor; 181 | try { 182 | await cose.mac.read(data, key); 183 | t.fail('Tag mismatch'); 184 | } catch (error) { 185 | t.is(error.message, 'Tag mismatch'); 186 | } 187 | }); 188 | 189 | test('verify mac-fail-07', async t => { 190 | const example = jsonfile.readFileSync('test/Examples/mac-tests/mac-fail-07.json'); 191 | const key = base64url.toBuffer(example.input.mac.recipients[0].key.k); 192 | const data = example.output.cbor; 193 | try { 194 | await cose.mac.read(data, key); 195 | t.fail('Tag mismatch'); 196 | } catch (error) { 197 | t.is(error.message, 'Tag mismatch'); 198 | } 199 | }); 200 | -------------------------------------------------------------------------------- /test/mac0-tests.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | test('create HMac-01', async t => { 13 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/HMac-01.json'); 14 | const p = example.input.mac0.protected; 15 | const u = example.input.mac0.unprotected; 16 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 17 | const plaintext = Buffer.from(example.input.plaintext); 18 | 19 | const header = { p: p, u: u }; 20 | const recipents = { key: key }; 21 | const buf = await cose.mac.create(header, plaintext, recipents); 22 | t.true(Buffer.isBuffer(buf)); 23 | t.true(buf.length > 0); 24 | const actual = cbor.decodeFirstSync(buf); 25 | const expected = cbor.decodeFirstSync(example.output.cbor); 26 | t.true(deepEqual(actual, expected)); 27 | }); 28 | 29 | test('verify HMac-01', async t => { 30 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/HMac-01.json'); 31 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 32 | 33 | const data = example.output.cbor; 34 | const buf = await cose.mac.read(data, key); 35 | t.true(Buffer.isBuffer(buf)); 36 | t.true(buf.length > 0); 37 | t.is(buf.toString('utf8'), example.input.plaintext); 38 | }); 39 | 40 | test('create mac-pass-01', async t => { 41 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-pass-01.json'); 42 | 43 | const u = example.input.mac0.unprotected; 44 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 45 | const plaintext = Buffer.from(example.input.plaintext); 46 | const header = { u: u }; 47 | const recipents = { key: key }; 48 | const buf = await cose.mac.create(header, plaintext, recipents); 49 | t.true(Buffer.isBuffer(buf)); 50 | t.true(buf.length > 0); 51 | const actual = cbor.decodeFirstSync(buf); 52 | const expected = cbor.decodeFirstSync(example.output.cbor); 53 | t.true(deepEqual(actual, expected)); 54 | }); 55 | 56 | test('create mac-pass-02', async t => { 57 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-pass-02.json'); 58 | const p = undefined; 59 | const u = example.input.mac0.unprotected; 60 | const external = Buffer.from(example.input.mac0.external, 'hex'); 61 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 62 | const plaintext = Buffer.from(example.input.plaintext); 63 | const options = { encodep: 'empty' }; 64 | 65 | const header = { p: p, u: u }; 66 | const recipents = { key: key }; 67 | const buf = await cose.mac.create(header, plaintext, recipents, external, options); 68 | t.true(Buffer.isBuffer(buf)); 69 | t.true(buf.length > 0); 70 | const actual = cbor.decodeFirstSync(buf); 71 | const expected = cbor.decodeFirstSync(example.output.cbor); 72 | t.true(deepEqual(actual, expected)); 73 | }); 74 | 75 | test('create mac-pass-03', async t => { 76 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-pass-03.json'); 77 | const p = undefined; 78 | const u = example.input.mac0.unprotected; 79 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 80 | const plaintext = Buffer.from(example.input.plaintext); 81 | const options = { encodep: 'empty', excludetag: true }; 82 | 83 | const recipents = { key: key }; 84 | const header = { p: p, u: u }; 85 | const external = null; 86 | const buf = await cose.mac.create(header, plaintext, recipents, external, options); 87 | 88 | t.true(Buffer.isBuffer(buf)); 89 | t.true(buf.length > 0); 90 | const actual = cbor.decodeFirstSync(buf); 91 | const expected = cbor.decodeFirstSync(example.output.cbor); 92 | t.true(deepEqual(actual, expected)); 93 | }); 94 | 95 | test('verify mac-pass-01', async t => { 96 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-pass-01.json'); 97 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 98 | 99 | const data = example.output.cbor; 100 | const buf = await cose.mac.read(data, key); 101 | 102 | t.true(Buffer.isBuffer(buf)); 103 | t.true(buf.length > 0); 104 | t.is(buf.toString('utf8'), example.input.plaintext); 105 | }); 106 | 107 | test('verify mac-pass-02', async t => { 108 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-pass-02.json'); 109 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 110 | const external = Buffer.from(example.input.mac0.external, 'hex'); 111 | 112 | const data = example.output.cbor; 113 | const buf = await cose.mac.read(data, key, external); 114 | t.true(Buffer.isBuffer(buf)); 115 | t.true(buf.length > 0); 116 | t.is(buf.toString('utf8'), example.input.plaintext); 117 | }); 118 | 119 | test('verify mac-pass-03', async t => { 120 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-pass-03.json'); 121 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 122 | 123 | const data = example.output.cbor; 124 | const buf = await cose.mac.read(data, key); 125 | 126 | t.true(Buffer.isBuffer(buf)); 127 | t.true(buf.length > 0); 128 | t.is(buf.toString('utf8'), example.input.plaintext); 129 | }); 130 | 131 | test('verify mac-fail-01', async t => { 132 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-fail-01.json'); 133 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 134 | const data = example.output.cbor; 135 | 136 | try { 137 | await cose.mac.read(data, key); 138 | t.fail('Unexpected cbor tag, \'992\''); 139 | } catch (error) { 140 | t.is(error.message, 'Unexpected cbor tag, \'992\''); 141 | } 142 | }); 143 | 144 | test('verify mac-fail-02', async t => { 145 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-fail-02.json'); 146 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 147 | const data = example.output.cbor; 148 | 149 | try { 150 | await cose.mac.read(data, key); 151 | t.fail('Tag mismatch'); 152 | } catch (error) { 153 | t.is(error.message, 'Tag mismatch'); 154 | } 155 | }); 156 | 157 | test('verify mac-fail-03', async t => { 158 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-fail-03.json'); 159 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 160 | const data = example.output.cbor; 161 | 162 | try { 163 | await cose.mac.read(data, key); 164 | t.fail('Unknown algorithm, -999'); 165 | } catch (error) { 166 | t.is(error.message, 'Unknown algorithm, -999'); 167 | } 168 | }); 169 | 170 | test('verify mac-fail-04', async t => { 171 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-fail-04.json'); 172 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 173 | const data = example.output.cbor; 174 | 175 | try { 176 | await cose.mac.read(data, key); 177 | t.fail('Unknown algorithm, Unknown'); 178 | } catch (error) { 179 | t.is(error.message, 'Unknown algorithm, Unknown'); 180 | } 181 | }); 182 | 183 | test('verify mac-fail-06', async t => { 184 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-fail-06.json'); 185 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 186 | const data = example.output.cbor; 187 | 188 | try { 189 | await cose.mac.read(data, key); 190 | t.fail('Tag mismatch'); 191 | } catch (error) { 192 | t.is(error.message, 'Tag mismatch'); 193 | } 194 | }); 195 | 196 | test('verify mac-fail-07', async t => { 197 | const example = jsonfile.readFileSync('test/Examples/mac0-tests/mac-fail-07.json'); 198 | const key = base64url.toBuffer(example.input.mac0.recipients[0].key.k); 199 | const data = example.output.cbor; 200 | 201 | try { 202 | await cose.mac.read(data, key); 203 | t.fail('Tag mismatch'); 204 | } catch (error) { 205 | t.is(error.message, 'Tag mismatch'); 206 | } 207 | }); 208 | -------------------------------------------------------------------------------- /test/rsa-pkcs-examples.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const jwkToPem = require('jwk-to-pem'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | function hexToB64 (hex) { 13 | return Buffer.from(hex, 'hex').toString('base64'); 14 | } 15 | 16 | test('create rsa-pkcs-01', async (t) => { 17 | const example = jsonfile.readFileSync('test/rsa-pkcs-examples/rsa-pkcs-01.json'); 18 | const p = example.input.sign0.protected; 19 | const u = example.input.sign0.unprotected; 20 | const plaintext = Buffer.from(example.input.plaintext); 21 | 22 | const testKey = example.input.sign0.key; 23 | const signer = { 24 | key: jwkToPem({ 25 | kty: testKey.kty, 26 | n: hexToB64(testKey.n_hex), 27 | e: hexToB64(testKey.e_hex), 28 | d: hexToB64(testKey.d_hex), 29 | p: hexToB64(testKey.p_hex), 30 | q: hexToB64(testKey.q_hex), 31 | dp: hexToB64(testKey.dP_hex), 32 | dq: hexToB64(testKey.dQ_hex), 33 | qi: hexToB64(testKey.qi_hex) 34 | }, { private: true }) 35 | }; 36 | const header = { p: p, u: u }; 37 | const options = { excludetag: true }; 38 | const buf = await cose.sign.create(header, plaintext, signer, options); 39 | t.true(Buffer.isBuffer(buf)); 40 | t.true(buf.length > 0); 41 | const actual = cbor.decodeFirstSync(buf); 42 | const expected = cbor.decodeFirstSync(example.output.cbor); 43 | t.true(deepEqual(actual, expected)); 44 | }); 45 | 46 | test('create rsa-pkcs-01 Sync', async (t) => { 47 | const example = jsonfile.readFileSync('test/rsa-pkcs-examples/rsa-pkcs-01.json'); 48 | const p = example.input.sign0.protected; 49 | const u = example.input.sign0.unprotected; 50 | const plaintext = Buffer.from(example.input.plaintext); 51 | 52 | const testKey = example.input.sign0.key; 53 | const signer = { 54 | key: jwkToPem({ 55 | kty: testKey.kty, 56 | n: hexToB64(testKey.n_hex), 57 | e: hexToB64(testKey.e_hex), 58 | d: hexToB64(testKey.d_hex), 59 | p: hexToB64(testKey.p_hex), 60 | q: hexToB64(testKey.q_hex), 61 | dp: hexToB64(testKey.dP_hex), 62 | dq: hexToB64(testKey.dQ_hex), 63 | qi: hexToB64(testKey.qi_hex) 64 | }, { private: true }) 65 | }; 66 | const header = { p: p, u: u }; 67 | const options = { excludetag: true }; 68 | const buf = await cose.sign.create(header, plaintext, signer, options); 69 | t.true(Buffer.isBuffer(buf)); 70 | t.true(buf.length > 0); 71 | const actual = cbor.decodeFirstSync(buf); 72 | const expected = cbor.decodeFirstSync(example.output.cbor); 73 | t.true(deepEqual(actual, expected)); 74 | }); 75 | 76 | test('verify rsa-pkcs-01', async (t) => { 77 | const example = jsonfile.readFileSync('test/rsa-pkcs-examples/rsa-pkcs-01.json'); 78 | 79 | const testKey = example.input.sign0.key; 80 | 81 | const verifier = { 82 | key: jwkToPem({ 83 | kty: testKey.kty, 84 | n: hexToB64(testKey.n_hex), 85 | e: hexToB64(testKey.e_hex) 86 | }) 87 | }; 88 | 89 | const signature = Buffer.from(example.output.cbor, 'hex'); 90 | 91 | const options = { defaultType: cose.sign.Sign1Tag }; 92 | const buf = await cose.sign.verify(signature, verifier, options); 93 | t.true(Buffer.isBuffer(buf)); 94 | t.true(buf.length > 0); 95 | t.is(buf.toString('utf8'), example.input.plaintext); 96 | }); 97 | 98 | test('verify rsa-pkcs-01 Sync', async (t) => { 99 | const example = jsonfile.readFileSync('test/rsa-pkcs-examples/rsa-pkcs-01.json'); 100 | 101 | const testKey = example.input.sign0.key; 102 | 103 | const verifier = { 104 | key: jwkToPem({ 105 | kty: testKey.kty, 106 | n: hexToB64(testKey.n_hex), 107 | e: hexToB64(testKey.e_hex) 108 | }) 109 | }; 110 | 111 | const signature = Buffer.from(example.output.cbor, 'hex'); 112 | const options = { defaultType: cose.sign.Sign1Tag }; 113 | const buf = await cose.sign.verify(signature, verifier, options); 114 | t.true(Buffer.isBuffer(buf)); 115 | t.true(buf.length > 0); 116 | t.is(buf.toString('utf8'), example.input.plaintext); 117 | }); 118 | -------------------------------------------------------------------------------- /test/rsa-pkcs-examples/rsa-pkcs-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "RSA-PKCS1-v1_5 w/ SHA-256", 3 | "input": { 4 | "plaintext": "This is the content.", 5 | "sign0": { 6 | "key": { 7 | "kty": "RSA", 8 | "n_hex": "BC7E29D0DF7E20CC9DC8D509E0F68895922AF0EF452190D402C61B554334A7BF91C9A570240F994FAE1B69035BCFAD4F7E249EB26087C2665E7C958C967B1517413DC3F97A431691A5999B257CC6CD356BAD168D929B8BAE9020750E74CF60F6FD35D6BB3FC93FC28900478694F508B33E7C00E24F90EDF37457FC3E8EFCFD2F42306301A8205AB740515331D5C18F0C64D4A43BE52FC440400F6BFC558A6E32884C2AF56F29E5C52780CEA7285F5C057FC0DFDA232D0ADA681B01495D9D0E32196633588E289E59035FF664F056189F2F10FE05827B796C326E3E748FFA7C589ED273C9C43436CDDB4A6A22523EF8BCB2221615B799966F1ABA5BC84B7A27CF", 9 | "e_hex": "010001", 10 | "d_hex": "0969FF04FCC1E1647C20402CF3F736D4CAE33F264C1C6EE3252CFCC77CDEF533D700570AC09A50D7646EDFB1F86A13BCABCF00BD659F27813D08843597271838BC46ED4743FE741D9BC38E0BF36D406981C7B81FCE54861CEBFB85AD23A8B4833C1BEE18C05E4E436A869636980646EECB839E4DAF434C9C6DFBF3A55CE1DB73E4902F89384BD6F9ECD3399FB1ED4B83F28D356C8E619F1F0DC96BBE8B75C1812CA58F360259EAEB1D17130C3C0A2715A99BE49898E871F6088A29570DC2FFA0CEFFFA27F1F055CBAABFD8894E0CC24F176E34EBAD32278A466F8A34A685ACC8207D9EC1FCBBD094996DC73C6305FCA31668BE57B1699D0BB456CC8871BFFBCD", 11 | "p_hex": "F331593E147FD3A3235675F0D36A06E5426F7C5E78E49B2ACD3E268BA50E48ED2A52F3B4FA492D6BCF70EB3F915A716078A113652E3FA4C6D50AF8606C2D2C28ECAF083B712D6CEE1263C1205DA03BBBFA6F5C2D8B1A96194089CACB306C844A832E2B032B5F96A7EAB6CFE1107299013C8B0E9F089BBABBC504DD8BC138BA4B", 12 | "q_hex": "C66B5DDCAB7017E14083F2854F61997F35636C86F2F92B172D2555588EE1ED899BA6B6ADEC0A02024B2E78A91C891256A8571E0EFB3BAC3F41724DE036EC8FA0F93E2CFBDDA59C6FF1816EB3DC938D4E45912423F3F34B7E96C39E2E4D65A3DCD6DFD2B4EF527841001272F77855B6D75D40D54BB65BD1DF8538E96EC4DAD60D", 13 | "dP_hex": "1F677CFDBE49EF7B7EA1B8A33BB9D260229F20F1562D373864BEA4DD9D97E5A4F2B53991624CB6D7D836DDBA1CBC102E0405D0EA5CF98CFEBC1E298AD20D5749859EE8B23C604053D1FE1DBF5F37C4DEF66D10FB349E5F49AD82DDB435719DF7BD4EE5F107D5D52FA3E8AD9983B538BAE72591E2C98ACAA75ABED1192DFF7457", 14 | "dQ_hex": "2CCC9F13ACCD9146B57755318E3BBE197FA7642090097C162E86485FC75AF173E965D9C7290D1569092A83E9C2DC9BFC5EE3D490935EE4C41F75BC698C5D1B0CC059AE746B95F1DD408CF5BEBC65C038D4F23153C0C7C4DADF1569C890870B5958568ECF755D8C73389DF1C138353A242414F853B0E7C85A0C4D4E3F4949139D", 15 | "qi_hex": "7B6E2406FD03BC75EA22AB94A8D242506A6BBFE36BC8132DBBCE50B8425425062B697AFA180F5685E90E11EB5712D2E6E2B24E2A1E7C75D5940E08301E824470EF38561BE3E9D05F9FCA8E6F69A028A928E85E58212E789BA577B80378D7A995FA6AFEA74BE364661A679F82776C5905F43F7A35692986271E594E1D11F9668D", 16 | "kid": "meriadoc.brandybuck@rsa.example" 17 | }, 18 | "unprotected": { 19 | "kid": "meriadoc.brandybuck@rsa.example" 20 | }, 21 | "protected": { 22 | "alg": "RS256" 23 | } 24 | } 25 | }, 26 | "output": { 27 | "cbor": "8445a101390100a104581f6d65726961646f632e6272616e64796275636b407273612e6578616d706c6554546869732069732074686520636f6e74656e742e590100a533c0490c109e639b3325c457675ba692debf20ef4f56b6cec37e28739b21d2af824eb93975aaac5e3292660ebeefcaa1a8bf0bab8e6611a4c2fcf2d0bba499f84c7eb7079e04eb9c2bb797191ba1ef1b13cf39617fe6fccb1018aec54414c14f93666db86cac66421d053ab2171c225172e5fec4009e7a3c0fa4a05d8c483434f6fb19679f34b7ef773f8aee1d73ae5661905f6c7f1c66b506895c4048fc4d4b851a851e64489e051088bfd0df95a887095acb5037dc4c2db814b77d044ac22aea062694400755dfee253d83714a868d2d7b809b0902aaf18ae6dbc25cab3f04d3f3d1d9e40d1c501da02b3e54144a9392fe92d8cde0050febd1df8a69e3d9" 28 | } 29 | } -------------------------------------------------------------------------------- /test/sign-performance-tests.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const base64url = require('base64url'); 8 | const cbor = require('cbor'); 9 | const jsonfile = require('jsonfile'); 10 | 11 | test('create and verify really huge payload', async (t) => { 12 | // uses the keys from here but it has nothing to do with the test 13 | const example = jsonfile.readFileSync('test/Examples/sign-tests/ecdsa-01.json'); 14 | const BIG_LENGHT = 100 * 1000; 15 | const p = example.input.sign.protected; 16 | const u = example.input.sign.unprotected; 17 | const signers = [{ 18 | key: { 19 | d: base64url.toBuffer(example.input.sign.signers[0].key.d) 20 | }, 21 | u: example.input.sign.signers[0].unprotected, 22 | p: example.input.sign.signers[0].protected 23 | }]; 24 | const plaintext = Buffer.from('a'.repeat(BIG_LENGHT)); 25 | 26 | const verifier = { 27 | key: { 28 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 29 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 30 | kid: example.input.sign.signers[0].key.kid 31 | } 32 | }; 33 | 34 | const header = { p: p, u: u }; 35 | const buf = await cose.sign.create(header, plaintext, signers); 36 | t.true(Buffer.isBuffer(buf)); 37 | t.true(buf.length > 0); 38 | 39 | const actual = await cbor.decodeFirst(buf); 40 | t.is(actual.value[2].length, BIG_LENGHT); 41 | 42 | const verifiedBuf = await cose.sign.verify(buf, verifier); 43 | t.true(Buffer.isBuffer(verifiedBuf)); 44 | t.true(verifiedBuf.length > 0); 45 | t.is(verifiedBuf.toString('utf8'), plaintext.toString('utf8')); 46 | }); 47 | -------------------------------------------------------------------------------- /test/sign-tests.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const deepEqual = require('./util.js').deepEqual; 11 | 12 | test('ecdsa-examples verify ecdsa-01', async (t) => { 13 | const example = jsonfile.readFileSync('test/Examples/ecdsa-examples/ecdsa-01.json'); 14 | 15 | const verifier = { 16 | key: { 17 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 18 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 19 | kid: example.input.sign.signers[0].key.kid 20 | } 21 | }; 22 | 23 | const signature = Buffer.from(example.output.cbor, 'hex'); 24 | 25 | const buf = await cose.sign.verify(signature, verifier); 26 | t.true(Buffer.isBuffer(buf)); 27 | t.true(buf.length > 0); 28 | t.is(buf.toString('utf8'), example.input.plaintext); 29 | }); 30 | 31 | test('ecdsa-examples verify ecdsa-02', async (t) => { 32 | const example = jsonfile.readFileSync('test/Examples/ecdsa-examples/ecdsa-02.json'); 33 | 34 | const verifier = { 35 | key: { 36 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 37 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 38 | kid: example.input.sign.signers[0].key.kid 39 | } 40 | }; 41 | 42 | const signature = Buffer.from(example.output.cbor, 'hex'); 43 | 44 | const buf = await cose.sign.verify(signature, verifier); 45 | t.true(Buffer.isBuffer(buf)); 46 | t.true(buf.length > 0); 47 | t.is(buf.toString('utf8'), example.input.plaintext); 48 | }); 49 | 50 | test('create ecdsa-01', async (t) => { 51 | const example = jsonfile.readFileSync('test/Examples/sign-tests/ecdsa-01.json'); 52 | const p = example.input.sign.protected; 53 | const u = example.input.sign.unprotected; 54 | const plaintext = Buffer.from(example.input.plaintext); 55 | const signers = [{ 56 | key: { 57 | d: base64url.toBuffer(example.input.sign.signers[0].key.d) 58 | }, 59 | u: example.input.sign.signers[0].unprotected, 60 | p: example.input.sign.signers[0].protected 61 | }]; 62 | 63 | const header = { p: p, u: u }; 64 | const buf = await cose.sign.create(header, plaintext, signers); 65 | t.true(Buffer.isBuffer(buf)); 66 | t.true(buf.length > 0); 67 | const actual = cbor.decodeFirstSync(buf); 68 | const expected = cbor.decodeFirstSync(example.output.cbor); 69 | t.true(deepEqual(actual, expected)); 70 | }); 71 | 72 | test('sign+verify rsa-pss-01', async (t) => { 73 | const example = jsonfile.readFileSync('test/Examples/rsa-pss-examples/rsa-pss-01.json'); 74 | const p = example.input.sign.protected; 75 | 76 | const u = example.input.sign.unprotected; 77 | const plaintext = Buffer.from(example.input.plaintext); 78 | const signers = [{ 79 | key: { 80 | n: Buffer.from(example.input.sign.signers[0].key.n_hex, 'hex'), 81 | d: Buffer.from(example.input.sign.signers[0].key.d_hex, 'hex'), 82 | p: Buffer.from(example.input.sign.signers[0].key.p_hex, 'hex'), 83 | q: Buffer.from(example.input.sign.signers[0].key.q_hex, 'hex'), 84 | dp: Buffer.from(example.input.sign.signers[0].key.dP_hex, 'hex'), 85 | dq: Buffer.from(example.input.sign.signers[0].key.dQ_hex, 'hex'), 86 | qi: Buffer.from(example.input.sign.signers[0].key.qi_hex, 'hex'), 87 | e: Buffer.from(example.input.sign.signers[0].key.e_hex, 'hex'), 88 | kid: example.input.sign.signers[0].key.kid 89 | }, 90 | u: example.input.sign.signers[0].unprotected, 91 | p: example.input.sign.signers[0].protected 92 | }]; 93 | signers[0].p.alg = 'PS256'; 94 | 95 | const header = { p: p, u: u }; 96 | const buf = await cose.sign.create(header, plaintext, signers); 97 | t.true(Buffer.isBuffer(buf)); 98 | t.true(buf.length > 0); 99 | 100 | const verifiedBuf = await cose.sign.verify(buf, signers[0]); 101 | t.true(Buffer.isBuffer(verifiedBuf)); 102 | t.true(verifiedBuf.length > 0); 103 | t.is(verifiedBuf.toString('utf8'), example.input.plaintext); 104 | }); 105 | 106 | test('sign+verify rsa-pss-02', async (t) => { 107 | const example = jsonfile.readFileSync('test/Examples/rsa-pss-examples/rsa-pss-02.json'); 108 | const p = example.input.sign.protected; 109 | 110 | const u = example.input.sign.unprotected; 111 | const plaintext = Buffer.from(example.input.plaintext); 112 | const signers = [{ 113 | key: { 114 | n: Buffer.from(example.input.sign.signers[0].key.n_hex, 'hex'), 115 | d: Buffer.from(example.input.sign.signers[0].key.d_hex, 'hex'), 116 | p: Buffer.from(example.input.sign.signers[0].key.p_hex, 'hex'), 117 | q: Buffer.from(example.input.sign.signers[0].key.q_hex, 'hex'), 118 | dp: Buffer.from(example.input.sign.signers[0].key.dP_hex, 'hex'), 119 | dq: Buffer.from(example.input.sign.signers[0].key.dQ_hex, 'hex'), 120 | qi: Buffer.from(example.input.sign.signers[0].key.qi_hex, 'hex'), 121 | e: Buffer.from(example.input.sign.signers[0].key.e_hex, 'hex'), 122 | kid: example.input.sign.signers[0].key.kid 123 | }, 124 | u: example.input.sign.signers[0].unprotected, 125 | p: example.input.sign.signers[0].protected 126 | }]; 127 | signers[0].p.alg = 'PS384'; 128 | 129 | const header = { p: p, u: u }; 130 | const buf = await cose.sign.create(header, plaintext, signers); 131 | t.true(Buffer.isBuffer(buf)); 132 | t.true(buf.length > 0); 133 | 134 | const verifiedBuf = await cose.sign.verify(buf, signers[0]); 135 | t.true(Buffer.isBuffer(buf)); 136 | t.true(verifiedBuf.length > 0); 137 | t.is(verifiedBuf.toString('utf8'), example.input.plaintext); 138 | }); 139 | test('sign+verify rsa-pss-03', async (t) => { 140 | const example = jsonfile.readFileSync('test/Examples/rsa-pss-examples/rsa-pss-03.json'); 141 | const p = example.input.sign.protected; 142 | 143 | const u = example.input.sign.unprotected; 144 | const plaintext = Buffer.from(example.input.plaintext); 145 | const signers = [{ 146 | key: { 147 | n: Buffer.from(example.input.sign.signers[0].key.n_hex, 'hex'), 148 | d: Buffer.from(example.input.sign.signers[0].key.d_hex, 'hex'), 149 | p: Buffer.from(example.input.sign.signers[0].key.p_hex, 'hex'), 150 | q: Buffer.from(example.input.sign.signers[0].key.q_hex, 'hex'), 151 | dp: Buffer.from(example.input.sign.signers[0].key.dP_hex, 'hex'), 152 | dq: Buffer.from(example.input.sign.signers[0].key.dQ_hex, 'hex'), 153 | qi: Buffer.from(example.input.sign.signers[0].key.qi_hex, 'hex'), 154 | e: Buffer.from(example.input.sign.signers[0].key.e_hex, 'hex'), 155 | kid: example.input.sign.signers[0].key.kid 156 | }, 157 | u: example.input.sign.signers[0].unprotected, 158 | p: example.input.sign.signers[0].protected 159 | }]; 160 | signers[0].p.alg = 'PS512'; 161 | 162 | const header = { p: p, u: u }; 163 | const buf = await cose.sign.create(header, plaintext, signers); 164 | t.true(Buffer.isBuffer(buf)); 165 | t.true(buf.length > 0); 166 | 167 | const verifiedBuf = await cose.sign.verify(buf, signers[0]); 168 | t.true(Buffer.isBuffer(verifiedBuf)); 169 | t.true(verifiedBuf.length > 0); 170 | t.is(verifiedBuf.toString('utf8'), example.input.plaintext); 171 | }); 172 | 173 | test('verify ecdsa-01', async (t) => { 174 | const example = jsonfile.readFileSync('test/Examples/sign-tests/ecdsa-01.json'); 175 | 176 | const verifier = { 177 | key: { 178 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 179 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 180 | kid: example.input.sign.signers[0].key.kid 181 | } 182 | }; 183 | 184 | const signature = Buffer.from(example.output.cbor, 'hex'); 185 | 186 | const buf = await cose.sign.verify(signature, verifier); 187 | t.true(Buffer.isBuffer(buf)); 188 | t.true(buf.length > 0); 189 | t.is(buf.toString('utf8'), example.input.plaintext); 190 | }); 191 | 192 | test('create sign-pass-01', async (t) => { 193 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-pass-01.json'); 194 | const p = example.input.sign.protected; 195 | const u = example.input.sign.unprotected; 196 | const plaintext = Buffer.from(example.input.plaintext); 197 | 198 | const signers = [{ 199 | key: { 200 | d: base64url.toBuffer(example.input.sign.signers[0].key.d) 201 | }, 202 | u: example.input.sign.signers[0].unprotected, 203 | p: example.input.sign.signers[0].protected 204 | }]; 205 | 206 | const header = { p: p, u: u }; 207 | const buf = await cose.sign.create(header, plaintext, signers); 208 | t.true(Buffer.isBuffer(buf)); 209 | t.true(buf.length > 0); 210 | const actual = cbor.decodeFirstSync(buf); 211 | const expected = cbor.decodeFirstSync(example.output.cbor); 212 | t.true(deepEqual(actual, expected)); 213 | }); 214 | 215 | test('create sign-pass-01 Sync', async (t) => { 216 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-pass-01.json'); 217 | const p = example.input.sign.protected; 218 | const u = example.input.sign.unprotected; 219 | const plaintext = Buffer.from(example.input.plaintext); 220 | 221 | const signers = [{ 222 | key: { 223 | d: base64url.toBuffer(example.input.sign.signers[0].key.d) 224 | }, 225 | u: example.input.sign.signers[0].unprotected, 226 | p: example.input.sign.signers[0].protected 227 | }]; 228 | 229 | const header = { p: p, u: u }; 230 | const buf = await cose.sign.create(header, plaintext, signers); 231 | t.true(Buffer.isBuffer(buf)); 232 | t.true(buf.length > 0); 233 | const actual = cbor.decodeFirstSync(buf); 234 | const expected = cbor.decodeFirstSync(example.output.cbor); 235 | t.true(deepEqual(actual, expected)); 236 | }); 237 | 238 | test('create sign-pass-02', async (t) => { 239 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-pass-02.json'); 240 | const p = example.input.sign.protected; 241 | const u = example.input.sign.unprotected; 242 | const plaintext = Buffer.from(example.input.plaintext); 243 | 244 | const signers = [{ 245 | key: { d: base64url.toBuffer(example.input.sign.signers[0].key.d) }, 246 | u: example.input.sign.signers[0].unprotected, 247 | p: example.input.sign.signers[0].protected, 248 | externalAAD: Buffer.from(example.input.sign.signers[0].external, 'hex') 249 | }]; 250 | 251 | const header = { p: p, u: u }; 252 | const options = { encodep: 'empty' }; 253 | const buf = await cose.sign.create(header, plaintext, signers, options); 254 | t.true(Buffer.isBuffer(buf)); 255 | t.true(buf.length > 0); 256 | const actual = cbor.decodeFirstSync(buf); 257 | const expected = cbor.decodeFirstSync(example.output.cbor); 258 | t.true(deepEqual(actual, expected)); 259 | }); 260 | 261 | test('create sign-pass-03', async (t) => { 262 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-pass-03.json'); 263 | const p = example.input.sign.protected; 264 | const u = example.input.sign.unprotected; 265 | const plaintext = Buffer.from(example.input.plaintext); 266 | 267 | const signers = [{ 268 | key: { 269 | d: base64url.toBuffer(example.input.sign.signers[0].key.d) 270 | }, 271 | u: example.input.sign.signers[0].unprotected, 272 | p: example.input.sign.signers[0].protected 273 | }]; 274 | 275 | const header = { p: p, u: u }; 276 | const options = { encodep: 'empty', excludetag: true }; 277 | const buf = await cose.sign.create(header, plaintext, signers, options); 278 | t.true(Buffer.isBuffer(buf)); 279 | t.true(buf.length > 0); 280 | const actual = cbor.decodeFirstSync(buf); 281 | const expected = cbor.decodeFirstSync(example.output.cbor); 282 | t.true(deepEqual(actual, expected)); 283 | }); 284 | 285 | test('verify sign-pass-01', async (t) => { 286 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-pass-01.json'); 287 | 288 | const verifier = { 289 | key: { 290 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 291 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 292 | kid: example.input.sign.signers[0].key.kid 293 | } 294 | }; 295 | 296 | const signature = Buffer.from(example.output.cbor, 'hex'); 297 | 298 | const buf = await cose.sign.verify(signature, verifier); 299 | t.true(Buffer.isBuffer(buf)); 300 | t.true(buf.length > 0); 301 | t.is(buf.toString('utf8'), example.input.plaintext); 302 | }); 303 | 304 | test('verify sign-pass-01 Sync', async (t) => { 305 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-pass-01.json'); 306 | 307 | const verifier = { 308 | key: { 309 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 310 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 311 | kid: example.input.sign.signers[0].key.kid 312 | } 313 | }; 314 | 315 | const signature = Buffer.from(example.output.cbor, 'hex'); 316 | 317 | const buf = await cose.sign.verify(signature, verifier); 318 | t.true(Buffer.isBuffer(buf)); 319 | t.true(buf.length > 0); 320 | t.is(buf.toString('utf8'), example.input.plaintext); 321 | }); 322 | 323 | test('verify sign-pass-02', async (t) => { 324 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-pass-02.json'); 325 | 326 | const verifier = { 327 | key: { 328 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 329 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 330 | kid: example.input.sign.signers[0].key.kid 331 | }, 332 | externalAAD: Buffer.from(example.input.sign.signers[0].external, 'hex') 333 | }; 334 | 335 | const signature = Buffer.from(example.output.cbor, 'hex'); 336 | 337 | const buf = await cose.sign.verify(signature, verifier); 338 | t.true(Buffer.isBuffer(buf)); 339 | t.true(buf.length > 0); 340 | t.is(buf.toString('utf8'), example.input.plaintext); 341 | }); 342 | 343 | test('verify sign-pass-03', async (t) => { 344 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-pass-03.json'); 345 | 346 | const verifier = { 347 | key: { 348 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 349 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 350 | kid: example.input.sign.signers[0].key.kid 351 | } 352 | }; 353 | 354 | const signature = Buffer.from(example.output.cbor, 'hex'); 355 | 356 | const buf = await cose.sign.verify(signature, verifier); 357 | t.true(Buffer.isBuffer(buf)); 358 | t.true(buf.length > 0); 359 | t.is(buf.toString('utf8'), example.input.plaintext); 360 | }); 361 | 362 | test('verify sign-fail-01', async (t) => { 363 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-fail-01.json'); 364 | 365 | const verifier = { 366 | key: { 367 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 368 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 369 | kid: example.input.sign.signers[0].key.kid 370 | } 371 | }; 372 | 373 | const signature = Buffer.from(example.output.cbor, 'hex'); 374 | 375 | try { 376 | await cose.sign.verify(signature, verifier); 377 | t.fail('Unexpected cbor tag, \'998\''); 378 | } catch (error) { 379 | t.is(error.message, 'Unexpected cbor tag, \'998\''); 380 | } 381 | }); 382 | 383 | test('verify sign-fail-02', async (t) => { 384 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-fail-02.json'); 385 | 386 | const verifier = { 387 | key: { 388 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 389 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 390 | kid: example.input.sign.signers[0].key.kid 391 | } 392 | }; 393 | 394 | const signature = Buffer.from(example.output.cbor, 'hex'); 395 | 396 | try { 397 | await cose.sign.verify(signature, verifier); 398 | t.fail('Signature missmatch'); 399 | } catch (error) { 400 | t.is(error.message, 'Signature missmatch'); 401 | } 402 | }); 403 | 404 | test('verify sign-fail-03', async (t) => { 405 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-fail-03.json'); 406 | 407 | const verifier = { 408 | key: { 409 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 410 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 411 | kid: example.input.sign.signers[0].key.kid 412 | } 413 | }; 414 | 415 | const signature = Buffer.from(example.output.cbor, 'hex'); 416 | try { 417 | await cose.sign.verify(signature, verifier); 418 | t.fail('Unknown algorithm, -999'); 419 | } catch (error) { 420 | t.is(error.message, 'Unknown algorithm, -999'); 421 | } 422 | }); 423 | 424 | test('verify sign-fail-04', async (t) => { 425 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-fail-04.json'); 426 | 427 | const verifier = { 428 | key: { 429 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 430 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 431 | kid: example.input.sign.signers[0].key.kid 432 | } 433 | }; 434 | 435 | const signature = Buffer.from(example.output.cbor, 'hex'); 436 | try { 437 | await cose.sign.verify(signature, verifier); 438 | t.fail('Unknown algorithm, unknown'); 439 | } catch (error) { 440 | t.is(error.message, 'Unknown algorithm, unknown'); 441 | } 442 | }); 443 | 444 | test('verify sign-fail-06', async (t) => { 445 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-fail-06.json'); 446 | 447 | const verifier = { 448 | key: { 449 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 450 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 451 | kid: example.input.sign.signers[0].key.kid 452 | } 453 | }; 454 | 455 | const signature = Buffer.from(example.output.cbor, 'hex'); 456 | 457 | try { 458 | await cose.sign.verify(signature, verifier); 459 | t.fail('Signature missmatch'); 460 | } catch (error) { 461 | t.is(error.message, 'Signature missmatch'); 462 | } 463 | }); 464 | 465 | test('verify sign-fail-07', async (t) => { 466 | const example = jsonfile.readFileSync('test/Examples/sign-tests/sign-fail-07.json'); 467 | 468 | const verifier = { 469 | key: { 470 | x: base64url.toBuffer(example.input.sign.signers[0].key.x), 471 | y: base64url.toBuffer(example.input.sign.signers[0].key.y), 472 | kid: example.input.sign.signers[0].key.kid 473 | } 474 | }; 475 | 476 | const signature = Buffer.from(example.output.cbor, 'hex'); 477 | 478 | try { 479 | await cose.sign.verify(signature, verifier); 480 | t.fail('Signature missmatch'); 481 | } catch (error) { 482 | t.is(error.message, 'Signature missmatch'); 483 | } 484 | }); 485 | 486 | test('verify rsa-pss-01', async (t) => { 487 | const example = jsonfile.readFileSync('test/Examples/rsa-pss-examples/rsa-pss-01.json'); 488 | 489 | const verifier = { 490 | key: { 491 | n: Buffer.from(example.input.sign.signers[0].key.n_hex, 'hex'), 492 | e: Buffer.from(example.input.sign.signers[0].key.e_hex, 'hex'), 493 | kid: example.input.sign.signers[0].key.kid 494 | } 495 | }; 496 | 497 | const signature = Buffer.from(example.output.cbor, 'hex'); 498 | 499 | const buf = await cose.sign.verify(signature, verifier); 500 | t.true(Buffer.isBuffer(buf)); 501 | t.true(buf.length > 0); 502 | t.is(buf.toString('utf8'), example.input.plaintext); 503 | }); 504 | 505 | test('verify rsa-pss-02', async (t) => { 506 | const example = jsonfile.readFileSync('test/Examples/rsa-pss-examples/rsa-pss-02.json'); 507 | 508 | const verifier = { 509 | key: { 510 | n: Buffer.from(example.input.sign.signers[0].key.n_hex, 'hex'), 511 | e: Buffer.from(example.input.sign.signers[0].key.e_hex, 'hex'), 512 | kid: example.input.sign.signers[0].key.kid 513 | } 514 | }; 515 | 516 | const signature = Buffer.from(example.output.cbor, 'hex'); 517 | 518 | const buf = await cose.sign.verify(signature, verifier); 519 | t.true(Buffer.isBuffer(buf)); 520 | t.true(buf.length > 0); 521 | t.is(buf.toString('utf8'), example.input.plaintext); 522 | }); 523 | 524 | test('verify rsa-pss-03', async (t) => { 525 | const example = jsonfile.readFileSync('test/Examples/rsa-pss-examples/rsa-pss-03.json'); 526 | 527 | const verifier = { 528 | key: { 529 | n: Buffer.from(example.input.sign.signers[0].key.n_hex, 'hex'), 530 | e: Buffer.from(example.input.sign.signers[0].key.e_hex, 'hex'), 531 | kid: example.input.sign.signers[0].key.kid 532 | } 533 | }; 534 | 535 | const signature = Buffer.from(example.output.cbor, 'hex'); 536 | 537 | const buf = await cose.sign.verify(signature, verifier); 538 | t.true(Buffer.isBuffer(buf)); 539 | t.true(buf.length > 0); 540 | t.is(buf.toString('utf8'), example.input.plaintext); 541 | }); 542 | -------------------------------------------------------------------------------- /test/sign1-tests.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const cose = require('../'); 6 | const test = require('ava'); 7 | const jsonfile = require('jsonfile'); 8 | const base64url = require('base64url'); 9 | const cbor = require('cbor'); 10 | const { deepEqual } = require('./util.js'); 11 | 12 | test('create sign-pass-01', async (t) => { 13 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-pass-01.json'); 14 | const u = example.input.sign0.unprotected; 15 | const plaintext = Buffer.from(example.input.plaintext); 16 | 17 | const signer = { 18 | key: { 19 | d: base64url.toBuffer(example.input.sign0.key.d) 20 | } 21 | }; 22 | 23 | const header = { u: u }; 24 | const buf = await cose.sign.create(header, plaintext, signer); 25 | t.true(Buffer.isBuffer(buf)); 26 | t.true(buf.length > 0); 27 | const actual = cbor.decodeFirstSync(buf); 28 | const expected = cbor.decodeFirstSync(example.output.cbor); 29 | t.true(deepEqual(actual, expected)); 30 | }); 31 | 32 | test('create sign-pass-02', async (t) => { 33 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-pass-02.json'); 34 | const p = example.input.sign0.protected; 35 | const u = example.input.sign0.unprotected; 36 | const plaintext = Buffer.from(example.input.plaintext); 37 | 38 | const signer = { 39 | key: { 40 | d: base64url.toBuffer(example.input.sign0.key.d) 41 | }, 42 | externalAAD: Buffer.from(example.input.sign0.external, 'hex') 43 | }; 44 | 45 | const header = { p: p, u: u }; 46 | const buf = await cose.sign.create(header, plaintext, signer); 47 | t.true(Buffer.isBuffer(buf)); 48 | t.true(buf.length > 0); 49 | const actual = cbor.decodeFirstSync(buf); 50 | const expected = cbor.decodeFirstSync(example.output.cbor); 51 | t.true(deepEqual(actual, expected)); 52 | }); 53 | 54 | test('create sign-pass-03', async (t) => { 55 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-pass-03.json'); 56 | const p = example.input.sign0.protected; 57 | const u = example.input.sign0.unprotected; 58 | const plaintext = Buffer.from(example.input.plaintext); 59 | 60 | const signer = { 61 | key: { 62 | d: base64url.toBuffer(example.input.sign0.key.d) 63 | } 64 | }; 65 | 66 | const header = { p: p, u: u }; 67 | const options = { excludetag: true }; 68 | const buf = await cose.sign.create(header, plaintext, signer, options); 69 | t.true(Buffer.isBuffer(buf)); 70 | t.true(buf.length > 0); 71 | const actual = cbor.decodeFirstSync(buf); 72 | const expected = cbor.decodeFirstSync(example.output.cbor); 73 | t.true(deepEqual(actual, expected)); 74 | }); 75 | 76 | test('verify sign-pass-01', async (t) => { 77 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-pass-01.json'); 78 | 79 | const verifier = { 80 | key: { 81 | x: base64url.toBuffer(example.input.sign0.key.x), 82 | y: base64url.toBuffer(example.input.sign0.key.y) 83 | } 84 | }; 85 | 86 | const signature = Buffer.from(example.output.cbor, 'hex'); 87 | 88 | const buf = await cose.sign.verify(signature, verifier); 89 | t.true(Buffer.isBuffer(buf)); 90 | t.true(buf.length > 0); 91 | t.is(buf.toString('utf8'), example.input.plaintext); 92 | }); 93 | 94 | test('verify sign-pass-02', async (t) => { 95 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-pass-02.json'); 96 | 97 | const verifier = { 98 | key: { 99 | x: base64url.toBuffer(example.input.sign0.key.x), 100 | y: base64url.toBuffer(example.input.sign0.key.y) 101 | }, 102 | externalAAD: Buffer.from(example.input.sign0.external, 'hex') 103 | }; 104 | 105 | const signature = Buffer.from(example.output.cbor, 'hex'); 106 | 107 | const buf = await cose.sign.verify(signature, verifier); 108 | t.true(Buffer.isBuffer(buf)); 109 | t.true(buf.length > 0); 110 | t.is(buf.toString('utf8'), example.input.plaintext); 111 | }); 112 | 113 | test('verify sign-pass-03', async (t) => { 114 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-pass-03.json'); 115 | 116 | const verifier = { 117 | key: { 118 | x: base64url.toBuffer(example.input.sign0.key.x), 119 | y: base64url.toBuffer(example.input.sign0.key.y) 120 | } 121 | }; 122 | 123 | const signature = Buffer.from(example.output.cbor, 'hex'); 124 | 125 | const options = { defaultType: cose.sign.Sign1Tag }; 126 | const buf = await cose.sign.verify(signature, verifier, options); 127 | t.true(Buffer.isBuffer(buf)); 128 | t.true(buf.length > 0); 129 | t.is(buf.toString('utf8'), example.input.plaintext); 130 | }); 131 | 132 | test('verify sign-fail-01', async (t) => { 133 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-fail-01.json'); 134 | 135 | const verifier = { 136 | key: { 137 | x: base64url.toBuffer(example.input.sign0.key.x), 138 | y: base64url.toBuffer(example.input.sign0.key.y) 139 | } 140 | }; 141 | 142 | const signature = Buffer.from(example.output.cbor, 'hex'); 143 | try { 144 | await cose.sign.verify(signature, verifier); 145 | t.fail('Unexpected cbor tag, \'998\''); 146 | } catch (error) { 147 | t.is(error.message, 'Unexpected cbor tag, \'998\''); 148 | } 149 | }); 150 | 151 | test('verify sign-fail-02', async (t) => { 152 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-fail-02.json'); 153 | 154 | const verifier = { 155 | key: { 156 | x: base64url.toBuffer(example.input.sign0.key.x), 157 | y: base64url.toBuffer(example.input.sign0.key.y) 158 | } 159 | }; 160 | 161 | const signature = Buffer.from(example.output.cbor, 'hex'); 162 | try { 163 | await cose.sign.verify(signature, verifier); 164 | t.fail('Signature missmatch'); 165 | } catch (error) { 166 | t.is(error.message, 'Signature missmatch'); 167 | } 168 | }); 169 | 170 | test('verify sign-fail-03', async (t) => { 171 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-fail-03.json'); 172 | 173 | const verifier = { 174 | key: { 175 | x: base64url.toBuffer(example.input.sign0.key.x), 176 | y: base64url.toBuffer(example.input.sign0.key.y) 177 | } 178 | }; 179 | 180 | const signature = Buffer.from(example.output.cbor, 'hex'); 181 | try { 182 | await cose.sign.verify(signature, verifier); 183 | t.fail('Unknown algorithm, -999'); 184 | } catch (error) { 185 | t.is(error.message, 'Unknown algorithm, -999'); 186 | } 187 | }); 188 | 189 | test('verify sign-fail-04', async (t) => { 190 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-fail-04.json'); 191 | 192 | const verifier = { 193 | key: { 194 | x: base64url.toBuffer(example.input.sign0.key.x), 195 | y: base64url.toBuffer(example.input.sign0.key.y) 196 | } 197 | }; 198 | 199 | const signature = Buffer.from(example.output.cbor, 'hex'); 200 | try { 201 | await cose.sign.verify(signature, verifier); 202 | t.fail('Unknown algorithm, unknown'); 203 | } catch (error) { 204 | t.is(error.message, 'Unknown algorithm, unknown'); 205 | } 206 | }); 207 | 208 | test('verify sign-fail-06', async (t) => { 209 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-fail-06.json'); 210 | 211 | const verifier = { 212 | key: { 213 | x: base64url.toBuffer(example.input.sign0.key.x), 214 | y: base64url.toBuffer(example.input.sign0.key.y) 215 | } 216 | }; 217 | 218 | const signature = Buffer.from(example.output.cbor, 'hex'); 219 | try { 220 | await cose.sign.verify(signature, verifier); 221 | t.fail('Signature missmatch'); 222 | } catch (error) { 223 | t.is(error.message, 'Signature missmatch'); 224 | } 225 | }); 226 | 227 | test('verify sign-fail-07', async (t) => { 228 | const example = jsonfile.readFileSync('test/Examples/sign1-tests/sign-fail-07.json'); 229 | 230 | const verifier = { 231 | key: { 232 | x: base64url.toBuffer(example.input.sign0.key.x), 233 | y: base64url.toBuffer(example.input.sign0.key.y) 234 | } 235 | }; 236 | 237 | const signature = Buffer.from(example.output.cbor, 'hex'); 238 | try { 239 | await cose.sign.verify(signature, verifier); 240 | t.fail('Signature missmatch'); 241 | } catch (error) { 242 | t.is(error.message, 'Signature missmatch'); 243 | } 244 | }); 245 | -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | /* jslint node: true */ 3 | 'use strict'; 4 | 5 | const test = require('ava'); 6 | 7 | function isObject (item) { 8 | return item && typeof item === 'object' && !Array.isArray(item); 9 | } 10 | 11 | function mapDeepEqual (actual, expected, depth) { 12 | const sortedActualKeys = [...actual.keys()].sort(); 13 | const sortedExpectedKeys = [...expected.keys()].sort(); 14 | if (sortedActualKeys.length !== sortedExpectedKeys.length) { 15 | return false; 16 | } 17 | for (let i = 0; i < sortedActualKeys.length; i++) { 18 | const actualKey = sortedActualKeys[i]; 19 | const expectedKey = sortedExpectedKeys[i]; 20 | if (actualKey !== expectedKey) { 21 | return false; 22 | } 23 | if (!deepEqual(actual.get(actualKey), expected.get(expectedKey), depth + 1)) { 24 | return false; 25 | } 26 | } 27 | return true; 28 | } 29 | 30 | function objectDeepEqual (actual, expected, depth) { 31 | const sortedActualKeys = Object.keys(actual).sort(); 32 | const sortedExpectedKeys = Object.keys(expected).sort(); 33 | if (sortedActualKeys.length !== sortedExpectedKeys.length) { 34 | return false; 35 | } 36 | for (let i = 0; i < sortedActualKeys.length; i++) { 37 | const actualKey = sortedActualKeys[i]; 38 | const expectedKey = sortedExpectedKeys[i]; 39 | if (actualKey !== expectedKey) { 40 | return false; 41 | } 42 | if (!deepEqual(actual[actualKey], expected[expectedKey], depth + 1)) { 43 | return false; 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | function arrayDeepEqual (actual, expected, depth) { 50 | if (actual.length !== expected.length) { 51 | return false; 52 | } 53 | for (let i = 0; i < actual.length; i++) { 54 | if (!deepEqual(actual[i], expected[i], depth + 1)) { 55 | return false; 56 | } 57 | } 58 | return true; 59 | } 60 | 61 | function deepEqual (actual, expected, depth) { 62 | const currentDepth = (depth !== undefined ? depth : 0); 63 | if (currentDepth === 50) { 64 | throw new Error('Structure is to deeply nested.'); 65 | } 66 | 67 | if (actual instanceof Map && expected instanceof Map) { 68 | return mapDeepEqual(actual, expected, currentDepth); 69 | } else if (actual instanceof Set && expected instanceof Set) { 70 | throw new Error('Set is not supported.'); 71 | } else if (isObject(actual) && isObject(expected)) { 72 | return objectDeepEqual(actual, expected, currentDepth); 73 | } else if (Array.isArray(actual) && Array.isArray(expected)) { 74 | return arrayDeepEqual(actual, expected, currentDepth); 75 | } else { 76 | return actual === expected; 77 | } 78 | } 79 | 80 | exports.deepEqual = deepEqual; 81 | 82 | test('deep equal array', (t) => { 83 | const actual = [1, 2, 3, '4', [1, 2, 3], { hello: 'world', world: 'hello' }]; 84 | const expected = [1, 2, 3, '4', [1, 2, 3], { hello: 'world', world: 'hello' }]; 85 | t.true(deepEqual(actual, expected)); 86 | expected.push(4); 87 | t.false(deepEqual(actual, expected)); 88 | }); 89 | 90 | test('deep equal deep array', (t) => { 91 | const actual = [1, [1, [1, [1, [1, [1, [1, [1, [1, [1, [1, [1, [1, 1]]]]]]]]]]]]]; 92 | const expected = [1, [1, [1, [1, [1, [1, [1, [1, [1, [1, [1, [1, [1, 1]]]]]]]]]]]]]; 93 | t.true(deepEqual(actual, expected)); 94 | }); 95 | 96 | test('deep equal objects', (t) => { 97 | const actual = { 98 | world: 'hello', 99 | hello: 'world', 100 | complex: { 101 | world: 'hello', 102 | hello: 'world' 103 | } 104 | }; 105 | const expected = { 106 | hello: 'world', 107 | world: 'hello', 108 | complex: { 109 | hello: 'world', 110 | world: 'hello' 111 | } 112 | }; 113 | t.true(deepEqual(actual, expected)); 114 | expected.test = 'test'; 115 | t.false(deepEqual(actual, expected)); 116 | }); 117 | 118 | test('deep equal Map', (t) => { 119 | const actual = new Map(); 120 | actual.set(1, 1); 121 | actual.set('hello', 'world'); 122 | actual.set('object', { hello: 'world', world: 'hello' }); 123 | const expected = new Map(); 124 | expected.set(1, 1); 125 | expected.set('hello', 'world'); 126 | expected.set('object', { hello: 'world', world: 'hello' }); 127 | t.true(deepEqual(actual, expected)); 128 | expected.set(2, 2); 129 | t.false(deepEqual(actual, expected)); 130 | }); 131 | --------------------------------------------------------------------------------