gpg: Avoid error diagnostics with --override-session-key when verifying
[gpgme.git] / lang / js / src / Helpers.js
1 /* gpgme.js - Javascript integration for gpgme
2  * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <https://www.gnu.org/licenses/>.
18  * SPDX-License-Identifier: LGPL-2.1+
19  *
20  * Author(s):
21  *     Maximilian Krambach <mkrambach@intevation.de>
22  */
23
24 import { gpgme_error } from './Errors';
25
26 /**
27  * Helper function that tries to return an array of fingerprints, either from
28  * input fingerprints or from Key objects (openpgp Keys or GPGME_Keys are both
29  * accepted).
30  *
31  * @param {Object | Object[] | String | String[] } input
32  * @returns {String[]} Array of fingerprints, or an empty array
33  */
34 export function toKeyIdArray (input){
35     if (!input){
36         return [];
37     }
38     if (!Array.isArray(input)){
39         input = [input];
40     }
41     let result = [];
42     for (let i=0; i < input.length; i++){
43         if (typeof (input[i]) === 'string'){
44             if (isFingerprint(input[i]) === true){
45                 result.push(input[i]);
46             } else {
47                 // MSG_NOT_A_FPR is just a console warning if warning enabled
48                 // in src/Errors.js
49                 gpgme_error('MSG_NOT_A_FPR');
50             }
51         } else if (typeof (input[i]) === 'object'){
52             let fpr = '';
53             if (input[i].fingerprint !== undefined){
54                 fpr = input[i].fingerprint;
55             } else if (input[i].hasOwnProperty('primaryKey') &&
56                 input[i].primaryKey.hasOwnProperty('getFingerprint')){
57                 fpr = input[i].primaryKey.getFingerprint();
58             }
59             if (isFingerprint(fpr) === true){
60                 result.push(fpr);
61             } else {
62                 gpgme_error('MSG_NOT_A_FPR');
63             }
64         } else {
65             return gpgme_error('PARAM_WRONG');
66         }
67     }
68     if (result.length === 0){
69         return [];
70     } else {
71         return result;
72     }
73 }
74
75 /**
76  * Check if values are valid hexadecimal values of a specified length
77  * @param {String} key input value.
78  * @param {int} len the expected length of the value
79  * @returns {Boolean} true if value passes test
80  * @private
81  */
82 function hextest (key, len){
83     if (!key || typeof (key) !== 'string'){
84         return false;
85     }
86     if (key.length !== len){
87         return false;
88     }
89     let regexp= /^[0-9a-fA-F]*$/i;
90     return regexp.test(key);
91 }
92
93 /**
94  * Checks if the input is a valid Fingerprint
95  *      (Hex string with a length of 40 characters)
96  * @param {String} value to check
97  * @returns {Boolean} true if value passes test
98  */
99 export function isFingerprint (value){
100     return hextest(value, 40);
101 }
102
103 /**
104  * check if the input is a valid gnupg long ID (Hex string with a length of 16
105  * characters)
106  * @param {String} value to check
107  * @returns {Boolean} true if value passes test
108  */
109 export function isLongId (value){
110     return hextest(value, 16);
111 }
112
113 /**
114  * Recursively decodes input (utf8) to output (utf-16; javascript) strings.
115  * @param {Object | Array | String} property
116  * @private
117  */
118 export function decode (property){
119     if (typeof property === 'string'){
120         try {
121             return decodeURIComponent(escape(unescape(property)));
122         }
123         catch (error){
124             if (error instanceof URIError) {
125                 return property;
126             }
127         }
128     } else if (Array.isArray(property)){
129         let res = [];
130         for (let arr=0; arr < property.length; arr++){
131             res.push(decode(property[arr]));
132         }
133         return res;
134     } else if (typeof property === 'object'){
135         const keys = Object.keys(property);
136         if (keys.length){
137             let res = {};
138             for (let k=0; k < keys.length; k++ ){
139                 res[keys[k]] = decode(property[keys[k]]);
140             }
141             return res;
142         }
143         return property;
144     }
145     return property;
146 }
147
148 /**
149  * Turns a base64 encoded string into an uint8 array
150  * adapted from https://gist.github.com/borismus/1032746
151  * @param {String} base64 encoded String
152  * @returns {Uint8Array}
153  * @private
154  */
155 export function atobArray (base64) {
156     if (typeof (base64) !== 'string'){
157         throw gpgme_error('DECODE_FAIL');
158     }
159     const raw = window.atob(base64);
160     const rawLength = raw.length;
161     let array = new Uint8Array(new ArrayBuffer(rawLength));
162     for (let i = 0; i < rawLength; i++) {
163         array[i] = raw.charCodeAt(i);
164     }
165     return array;
166 }
167
168 /**
169  * Turns a Uint8Array into an utf8-String
170  * <pre>
171  * Taken and slightly adapted from
172  *  https://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
173  * (original header:
174  *   utf.js - UTF-8 <=> UTF-16 conversion
175  *
176  *   Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
177  *   Version: 1.0
178  *   LastModified: Dec 25 1999
179  *   This library is free.  You can redistribute it and/or modify it.
180  *  )
181  * </pre>
182  * @param {*} array Uint8Array
183  * @returns {String}
184  * @private
185  */
186 export function Utf8ArrayToStr (array) {
187     let out, i, len, c, char2, char3;
188     out = '';
189     len = array.length;
190     i = 0;
191     if (array instanceof Uint8Array === false){
192         throw gpgme_error('DECODE_FAIL');
193     }
194     while (i < len) {
195         c = array[i++];
196         switch (c >> 4) {
197         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
198             // 0xxxxxxx
199             out += String.fromCharCode(c);
200             break;
201         case 12: case 13:
202             // 110x xxxx   10xx xxxx
203             char2 = array[i++];
204             out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
205             break;
206         case 14:
207             // 1110 xxxx  10xx xxxx  10xx xxxx
208             char2 = array[i++];
209             char3 = array[i++];
210             out += String.fromCharCode(((c & 0x0F) << 12) |
211                             ((char2 & 0x3F) << 6) |
212                             ((char3 & 0x3F) << 0));
213             break;
214         default:
215             break;
216         }
217     }
218     return out;
219 }