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

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

import * as stream from '@openpgp/web-stream-tools';
import crypto from '../crypto';
import enums from '../enums';
import util from '../util';
import defaultConfig from '../config';

import LiteralDataPacket from './literal_data';
import CompressedDataPacket from './compressed_data';
import OnePassSignaturePacket from './one_pass_signature';
import SignaturePacket from './signature';
import PacketList from './packetlist';
import { UnsupportedError } from './packet';

// A SEIP packet can contain the following packet types
const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([
  LiteralDataPacket,
  CompressedDataPacket,
  OnePassSignaturePacket,
  SignaturePacket
]);

const VERSION = 1; // A one-octet version number of the data packet.

/**
 * Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18)
 *
 * {@link https://tools.ietf.org/html/rfc4880#section-5.13|RFC4880 5.13}:
 * The Symmetrically Encrypted Integrity Protected Data packet is
 * a variant of the Symmetrically Encrypted Data packet. It is a new feature
 * created for OpenPGP that addresses the problem of detecting a modification to
 * encrypted data. It is used in combination with a Modification Detection Code
 * packet.
 */
class SymEncryptedIntegrityProtectedDataPacket {
  static get tag() {
    return enums.packet.symEncryptedIntegrityProtectedData;
  }

  constructor() {
    this.version = VERSION;
    this.encrypted = null;
    this.packets = null;
  }

  async read(bytes) {
    await stream.parse(bytes, async reader => {
      const version = await reader.readByte();
      // - A one-octet version number. The only currently defined value is 1.
      if (version !== VERSION) {
        throw new UnsupportedError(`Version ${version} of the SEIP packet is unsupported.`);
      }

      // - Encrypted data, the output of the selected symmetric-key cipher
      //   operating in Cipher Feedback mode with shift amount equal to the
      //   block size of the cipher (CFB-n where n is the block size).
      this.encrypted = reader.remainder();
    });
  }

  write() {
    return util.concat([new Uint8Array([VERSION]), this.encrypted]);
  }

  /**
   * Encrypt the payload in the packet.
   * @param {enums.symmetric} sessionKeyAlgorithm - The symmetric encryption algorithm to use
   * @param {Uint8Array} key - The key of cipher blocksize length to be used
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<Boolean>}
   * @throws {Error} on encryption failure
   * @async
   */
  async encrypt(sessionKeyAlgorithm, key, config = defaultConfig) {
    const { blockSize } = crypto.getCipher(sessionKeyAlgorithm);

    let bytes = this.packets.write();
    if (stream.isArrayStream(bytes)) bytes = await stream.readToEnd(bytes);
    const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm);
    const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet

    const tohash = util.concat([prefix, bytes, mdc]);
    const hash = await crypto.hash.sha1(stream.passiveClone(tohash));
    const plaintext = util.concat([tohash, hash]);

    this.encrypted = await crypto.mode.cfb.encrypt(sessionKeyAlgorithm, key, plaintext, new Uint8Array(blockSize), config);
    return true;
  }

  /**
   * Decrypts the encrypted data contained in the packet.
   * @param {enums.symmetric} sessionKeyAlgorithm - The selected symmetric encryption algorithm to be used
   * @param {Uint8Array} key - The key of cipher blocksize length to be used
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<Boolean>}
   * @throws {Error} on decryption failure
   * @async
   */
  async decrypt(sessionKeyAlgorithm, key, config = defaultConfig) {
    const { blockSize } = crypto.getCipher(sessionKeyAlgorithm);
    let encrypted = stream.clone(this.encrypted);
    if (stream.isArrayStream(encrypted)) encrypted = await stream.readToEnd(encrypted);
    const decrypted = await crypto.mode.cfb.decrypt(sessionKeyAlgorithm, key, encrypted, new Uint8Array(blockSize));

    // there must be a modification detection code packet as the
    // last packet and everything gets hashed except the hash itself
    const realHash = stream.slice(stream.passiveClone(decrypted), -20);
    const tohash = stream.slice(decrypted, 0, -20);
    const verifyHash = Promise.all([
      stream.readToEnd(await crypto.hash.sha1(stream.passiveClone(tohash))),
      stream.readToEnd(realHash)
    ]).then(([hash, mdc]) => {
      if (!util.equalsUint8Array(hash, mdc)) {
        throw new Error('Modification detected.');
      }
      return new Uint8Array();
    });
    const bytes = stream.slice(tohash, blockSize + 2); // Remove random prefix
    let packetbytes = stream.slice(bytes, 0, -2); // Remove MDC packet
    packetbytes = stream.concat([packetbytes, stream.fromAsync(() => verifyHash)]);
    if (!util.isStream(encrypted) || !config.allowUnauthenticatedStream) {
      packetbytes = await stream.readToEnd(packetbytes);
    }
    this.packets = await PacketList.fromBinary(packetbytes, allowedPackets, config);
    return true;
  }
}

export default SymEncryptedIntegrityProtectedDataPacket;