Welcome to mirror list, hosted at ThFree Co, Russian Federation.

s2k.js « type « src - github.com/openpgpjs/openpgpjs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 611740791fd7da53ba6274e4067678b66573a76c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3.0 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

/**
 * Implementation of the String-to-key specifier
 *
 * {@link https://tools.ietf.org/html/rfc4880#section-3.7|RFC4880 3.7}:
 * String-to-key (S2K) specifiers are used to convert passphrase strings
 * into symmetric-key encryption/decryption keys.  They are used in two
 * places, currently: to encrypt the secret part of private keys in the
 * private keyring, and to convert passphrases to encryption keys for
 * symmetrically encrypted messages.
 * @module type/s2k
 * @private
 */

import defaultConfig from '../config';
import crypto from '../crypto';
import enums from '../enums';
import util from '../util';

class S2K {
  /**
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   */
  constructor(config = defaultConfig) {
    /**
     * Hash function identifier, or 0 for gnu-dummy keys
     * @type {module:enums.hash | 0}
     */
    this.algorithm = enums.hash.sha256;
    /**
     * enums.s2k identifier or 'gnu-dummy'
     * @type {String}
     */
    this.type = 'iterated';
    /** @type {Integer} */
    this.c = config.s2kIterationCountByte;
    /** Eight bytes of salt in a binary string.
     * @type {Uint8Array}
     */
    this.salt = null;
  }

  getCount() {
    // Exponent bias, defined in RFC4880
    const expbias = 6;

    return (16 + (this.c & 15)) << ((this.c >> 4) + expbias);
  }

  /**
   * Parsing function for a string-to-key specifier ({@link https://tools.ietf.org/html/rfc4880#section-3.7|RFC 4880 3.7}).
   * @param {Uint8Array} bytes - Payload of string-to-key specifier
   * @returns {Integer} Actual length of the object.
   */
  read(bytes) {
    let i = 0;
    this.type = enums.read(enums.s2k, bytes[i++]);
    this.algorithm = bytes[i++];

    switch (this.type) {
      case 'simple':
        break;

      case 'salted':
        this.salt = bytes.subarray(i, i + 8);
        i += 8;
        break;

      case 'iterated':
        this.salt = bytes.subarray(i, i + 8);
        i += 8;

        // Octet 10: count, a one-octet, coded value
        this.c = bytes[i++];
        break;

      case 'gnu':
        if (util.uint8ArrayToString(bytes.subarray(i, i + 3)) === 'GNU') {
          i += 3; // GNU
          const gnuExtType = 1000 + bytes[i++];
          if (gnuExtType === 1001) {
            this.type = 'gnu-dummy';
            // GnuPG extension mode 1001 -- don't write secret key at all
          } else {
            throw new Error('Unknown s2k gnu protection mode.');
          }
        } else {
          throw new Error('Unknown s2k type.');
        }
        break;

      default:
        throw new Error('Unknown s2k type.');
    }

    return i;
  }

  /**
   * Serializes s2k information
   * @returns {Uint8Array} Binary representation of s2k.
   */
  write() {
    if (this.type === 'gnu-dummy') {
      return new Uint8Array([101, 0, ...util.stringToUint8Array('GNU'), 1]);
    }
    const arr = [new Uint8Array([enums.write(enums.s2k, this.type), this.algorithm])];

    switch (this.type) {
      case 'simple':
        break;
      case 'salted':
        arr.push(this.salt);
        break;
      case 'iterated':
        arr.push(this.salt);
        arr.push(new Uint8Array([this.c]));
        break;
      case 'gnu':
        throw new Error('GNU s2k type not supported.');
      default:
        throw new Error('Unknown s2k type.');
    }

    return util.concatUint8Array(arr);
  }

  /**
   * Produces a key using the specified passphrase and the defined
   * hashAlgorithm
   * @param {String} passphrase - Passphrase containing user input
   * @returns {Promise<Uint8Array>} Produced key with a length corresponding to.
   * hashAlgorithm hash length
   * @async
   */
  async produceKey(passphrase, numBytes) {
    passphrase = util.encodeUTF8(passphrase);

    const arr = [];
    let rlength = 0;

    let prefixlen = 0;
    while (rlength < numBytes) {
      let toHash;
      switch (this.type) {
        case 'simple':
          toHash = util.concatUint8Array([new Uint8Array(prefixlen), passphrase]);
          break;
        case 'salted':
          toHash = util.concatUint8Array([new Uint8Array(prefixlen), this.salt, passphrase]);
          break;
        case 'iterated': {
          const data = util.concatUint8Array([this.salt, passphrase]);
          let datalen = data.length;
          const count = Math.max(this.getCount(), datalen);
          toHash = new Uint8Array(prefixlen + count);
          toHash.set(data, prefixlen);
          for (let pos = prefixlen + datalen; pos < count; pos += datalen, datalen *= 2) {
            toHash.copyWithin(pos, prefixlen, pos);
          }
          break;
        }
        case 'gnu':
          throw new Error('GNU s2k type not supported.');
        default:
          throw new Error('Unknown s2k type.');
      }
      const result = await crypto.hash.digest(this.algorithm, toHash);
      arr.push(result);
      rlength += result.length;
      prefixlen++;
    }

    return util.concatUint8Array(arr).subarray(0, numBytes);
  }
}

export default S2K;