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

pkcs1.js « crypto « src - github.com/openpgpjs/openpgpjs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 7773b62cfc7b4d84b2200df2603b962a0735937f (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
// 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

/**
 * @fileoverview Provides EME-PKCS1-v1_5 encoding and decoding and EMSA-PKCS1-v1_5 encoding function
 * @see module:crypto/public_key/rsa
 * @see module:crypto/public_key/elliptic/ecdh
 * @see PublicKeyEncryptedSessionKeyPacket
 * @module crypto/pkcs1
 * @private
 */

import { getRandomBytes } from './random';
import hash from './hash';

/**
 * ASN1 object identifiers for hashes
 * @see {@link https://tools.ietf.org/html/rfc4880#section-5.2.2}
 */
const hash_headers = [];
hash_headers[1] = [0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04,
  0x10];
hash_headers[2] = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14];
hash_headers[3] = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14];
hash_headers[8] = [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00,
  0x04, 0x20];
hash_headers[9] = [0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00,
  0x04, 0x30];
hash_headers[10] = [0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
  0x00, 0x04, 0x40];
hash_headers[11] = [0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
  0x00, 0x04, 0x1C];

/**
 * Create padding with secure random data
 * @private
 * @param {Integer} length - Length of the padding in bytes
 * @returns {Promise<Uint8Array>} Random padding.
 * @async
 */
async function getPKCS1Padding(length) {
  const result = new Uint8Array(length);
  let count = 0;
  while (count < length) {
    const randomBytes = await getRandomBytes(length - count);
    for (let i = 0; i < randomBytes.length; i++) {
      if (randomBytes[i] !== 0) {
        result[count++] = randomBytes[i];
      }
    }
  }
  return result;
}

/**
 * Create a EME-PKCS1-v1_5 padded message
 * @see {@link https://tools.ietf.org/html/rfc4880#section-13.1.1|RFC 4880 13.1.1}
 * @param {Uint8Array} message - Message to be encoded
 * @param {Integer} keyLength - The length in octets of the key modulus
 * @returns {Promise<Uint8Array>} EME-PKCS1 padded message.
 * @async
 */
export async function emeEncode(message, keyLength) {
  const mLength = message.length;
  // length checking
  if (mLength > keyLength - 11) {
    throw new Error('Message too long');
  }
  // Generate an octet string PS of length k - mLen - 3 consisting of
  // pseudo-randomly generated nonzero octets
  const PS = await getPKCS1Padding(keyLength - mLength - 3);
  // Concatenate PS, the message M, and other padding to form an
  // encoded message EM of length k octets as EM = 0x00 || 0x02 || PS || 0x00 || M.
  const encoded = new Uint8Array(keyLength);
  // 0x00 byte
  encoded[1] = 2;
  encoded.set(PS, 2);
  // 0x00 bytes
  encoded.set(message, keyLength - mLength);
  return encoded;
}

/**
 * Decode a EME-PKCS1-v1_5 padded message
 * @see {@link https://tools.ietf.org/html/rfc4880#section-13.1.2|RFC 4880 13.1.2}
 * @param {Uint8Array} encoded - Encoded message bytes
 * @returns {Uint8Array} Message.
 */
export function emeDecode(encoded) {
  let i = 2;
  while (encoded[i] !== 0 && i < encoded.length) {
    i++;
  }
  const psLen = i - 2;
  const separator = encoded[i++];
  if (encoded[0] === 0 && encoded[1] === 2 && psLen >= 8 && separator === 0) {
    return encoded.subarray(i);
  }
  throw new Error('Decryption error');
}

/**
 * Create a EMSA-PKCS1-v1_5 padded message
 * @see {@link https://tools.ietf.org/html/rfc4880#section-13.1.3|RFC 4880 13.1.3}
 * @param {Integer} algo - Hash algorithm type used
 * @param {Uint8Array} hashed - Message to be encoded
 * @param {Integer} emLen - Intended length in octets of the encoded message
 * @returns {Uint8Array} Encoded message.
 */
export async function emsaEncode(algo, hashed, emLen) {
  let i;
  if (hashed.length !== hash.getHashByteLength(algo)) {
    throw new Error('Invalid hash length');
  }
  // produce an ASN.1 DER value for the hash function used.
  // Let T be the full hash prefix
  const hashPrefix = new Uint8Array(hash_headers[algo].length);
  for (i = 0; i < hash_headers[algo].length; i++) {
    hashPrefix[i] = hash_headers[algo][i];
  }
  // and let tLen be the length in octets prefix and hashed data
  const tLen = hashPrefix.length + hashed.length;
  if (emLen < tLen + 11) {
    throw new Error('Intended encoded message length too short');
  }
  // an octet string PS consisting of emLen - tLen - 3 octets with hexadecimal value 0xFF
  // The length of PS will be at least 8 octets
  const PS = new Uint8Array(emLen - tLen - 3).fill(0xff);

  // Concatenate PS, the hash prefix, hashed data, and other padding to form the
  // encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || prefix || hashed
  const EM = new Uint8Array(emLen);
  EM[1] = 0x01;
  EM.set(PS, 2);
  EM.set(hashPrefix, emLen - tLen);
  EM.set(hashed, emLen - hashed.length);
  return EM;
}