aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSecurityConstants.java
blob: 1b15f6fdee55d9b12da27ec7aa4aa88066b1a089 (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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
/*
 * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.sufficientlysecure.keychain.pgp;

import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;

import java.util.Arrays;
import java.util.HashSet;

/**
 * NIST requirements for 2011-2030 (http://www.keylength.com/en/4/):
 * - RSA: 2048 bit
 * - ECC: 224 bit
 * - Symmetric: 3TDEA
 * - Digital Signature (hash A): SHA-224 - SHA-512
 *
 * Extreme Decisions for Yahoo's End-to-End:
 * https://github.com/yahoo/end-to-end/issues/31
 * https://gist.github.com/coruus/68a8c65571e2b4225a69
 */
public class PgpSecurityConstants {

    /**
     * Whitelist of accepted symmetric encryption algorithms
     * all other algorithms are rejected with OpenPgpDecryptionResult.RESULT_INSECURE
     */
    private static HashSet<Integer> sSymmetricAlgorithmsWhitelist = new HashSet<>(Arrays.asList(
            // General remarks: We try to keep the whitelist short to reduce attack surface
            // TODO: block IDEA?: Bad key schedule (weak keys), implementation difficulties (easy to make errors)
            SymmetricKeyAlgorithmTags.IDEA,
            SymmetricKeyAlgorithmTags.TRIPLE_DES, // a MUST in RFC
            SymmetricKeyAlgorithmTags.CAST5, // default in many gpg, pgp versions, 128 bit key
            // BLOWFISH: Twofish is the successor
            // SAFER: not used widely
            // DES: < 128 bit security
            SymmetricKeyAlgorithmTags.AES_128,
            SymmetricKeyAlgorithmTags.AES_192,
            SymmetricKeyAlgorithmTags.AES_256,
            SymmetricKeyAlgorithmTags.TWOFISH // 128 bit
            // CAMELLIA_128: not used widely
            // CAMELLIA_192: not used widely
            // CAMELLIA_256: not used widely
    ));

    public static boolean isSecureSymmetricAlgorithm(int id) {
        return sSymmetricAlgorithmsWhitelist.contains(id);
    }

    /**
     * Whitelist of accepted hash algorithms
     * all other algorithms are rejected with OpenPgpSignatureResult.RESULT_INSECURE
     *
     * coorus:
     * Implementations SHOULD use SHA-512 for RSA or DSA signatures. They SHOULD NOT use SHA-384.
     * ((cite to affine padding attacks; unproven status of RSA-PKCSv15))
     *
     * Implementations MUST NOT sign SHA-224 hashes. They SHOULD NOT accept signatures over SHA-224 hashes.
     * ((collision resistance of 112-bits))
     * Implementations SHOULD NOT sign SHA-256 hashes. They MUST NOT default to signing SHA-256 hashes.
     */
    private static HashSet<Integer> sHashAlgorithmsWhitelist = new HashSet<>(Arrays.asList(
            // MD5: broken
            HashAlgorithmTags.SHA1, // TODO: disable when SHA256 is widely deployed
            HashAlgorithmTags.RIPEMD160, // same security properties as SHA1, TODO: disable when SHA256 is widely deployed
            // DOUBLE_SHA: not used widely
            // MD2: not used widely
            // TIGER_192: not used widely
            // HAVAL_5_160: not used widely
            HashAlgorithmTags.SHA256, // compatibility for old Mailvelope versions
            HashAlgorithmTags.SHA384,
            HashAlgorithmTags.SHA512
            // SHA224: Not used widely, Yahoo argues against it
    ));

    public static boolean isSecureHashAlgorithm(int id) {
        return sHashAlgorithmsWhitelist.contains(id);
    }

    /**
     * Whitelist of accepted asymmetric algorithms in switch statement
     * all other algorithms are rejected with OpenPgpSignatureResult.RESULT_INSECURE or
     * OpenPgpDecryptionResult.RESULT_INSECURE
     *
     * coorus:
     * Implementations MUST NOT accept, or treat any signature as valid, by an RSA key with
     * bitlength less than 1023 bits.
     * Implementations MUST NOT accept any RSA keys with bitlength less than 2047 bits after January 1, 2016.
     */
    private static HashSet<String> sCurveWhitelist = new HashSet<>(Arrays.asList(
            NISTNamedCurves.getOID("P-256").getId(),
            NISTNamedCurves.getOID("P-384").getId(),
            NISTNamedCurves.getOID("P-521").getId()
    ));

    public static boolean isSecureKey(CanonicalizedPublicKey key) {
        switch (key.getAlgorithm()) {
            case PublicKeyAlgorithmTags.RSA_GENERAL: {
                return (key.getBitStrength() >= 2048);
            }
            // RSA_ENCRYPT, RSA_SIGN: deprecated in RFC 4880, use RSA_GENERAL with key flags
            case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: {
                return (key.getBitStrength() >= 2048);
            }
            case PublicKeyAlgorithmTags.DSA: {
                return (key.getBitStrength() >= 2048);
            }
            case PublicKeyAlgorithmTags.ECDH:
            case PublicKeyAlgorithmTags.ECDSA: {
                return PgpSecurityConstants.sCurveWhitelist.contains(key.getCurveOid());
            }
            // ELGAMAL_GENERAL: deprecated in RFC 4880, use ELGAMAL_ENCRYPT
            // DIFFIE_HELLMAN: unsure
            default:
                return false;
        }
    }

    /**
     * These array is written as a list of preferred encryption algorithms into keys created by us.
     * Other implementations may choose to honor this selection.
     * (Most preferred is first)
     *
     * REASON: See corresponding whitelist. AES received most cryptanalysis over the years
     * and is still secure!
     */
    public static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
            SymmetricKeyAlgorithmTags.AES_256,
            SymmetricKeyAlgorithmTags.AES_192,
            SymmetricKeyAlgorithmTags.AES_128,
    };

    /**
     * These array is written as a list of preferred hash algorithms into keys created by us.
     * Other implementations may choose to honor this selection.
     * (Most preferred is first)
     *
     * REASON: See corresponding whitelist. If possible use SHA-512, this is state of the art!
     */
    public static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{
            HashAlgorithmTags.SHA512,
    };

    /**
     * These array is written as a list of preferred compression algorithms into keys created by us.
     * Other implementations may choose to honor this selection.
     * (Most preferred is first)
     *
     * REASON: See DEFAULT_COMPRESSION_ALGORITHM
     */
    public static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{
            CompressionAlgorithmTags.ZIP,
    };

    /**
     * Hash algorithm used to certify public keys
     */
    public static final int CERTIFY_HASH_ALGO = HashAlgorithmTags.SHA512;


    /**
     * Always use AES-256! We always ignore the preferred encryption algos of the recipient!
     *
     * coorus:
     * Implementations SHOULD ignore the symmetric algorithm preferences of a recipient's public key;
     * in particular, implementations MUST NOT choose an algorithm forbidden by this
     * document because a recipient prefers it.
     *
     * NEEDCITE downgrade attacks on TLS, other protocols
     */
    public static final int DEFAULT_SYMMETRIC_ALGORITHM = SymmetricKeyAlgorithmTags.AES_256;

    public interface OpenKeychainSymmetricKeyAlgorithmTags extends SymmetricKeyAlgorithmTags {
        int USE_DEFAULT = -1;
    }

    /**
     * Always use SHA-512!  We always ignore the preferred hash algos of the recipient!
     *
     * coorus:
     * Implementations MUST ignore the hash algorithm preferences of a recipient when signing
     * a message to a recipient. The difficulty of forging a signature under a given key,
     * using generic attacks on hash functions, is the difficulty of the weakest hash signed by that key.
     *
     * Implementations MUST default to using SHA-512 for RSA signatures,
     *
     * and either SHA-512 or the matched instance of SHA-2 for ECDSA signatures.
     * TODO: Ed25519
     * CITE: zooko's hash function table CITE: distinguishers on SHA-256
     */
    public static final int DEFAULT_HASH_ALGORITHM = HashAlgorithmTags.SHA512;

    public interface OpenKeychainHashAlgorithmTags extends HashAlgorithmTags {
        int USE_DEFAULT = -1;
    }

    /**
     * Compression is disabled by default.
     *
     * The default compression algorithm is only used if explicitly enabled in the activity's
     * overflow menu or via the OpenPGP API's extra OpenPgpApi.EXTRA_ENABLE_COMPRESSION
     *
     * REASON: Enabling compression can lead to a sidechannel. Consider a voting that is done via
     * OpenPGP. Compression can lead to different ciphertext lengths based on the user's voting.
     * This has happened in a voting done by Wikipedia (Google it).
     *
     * ZLIB: the format provides no benefits over DEFLATE, and is more malleable
     * BZIP2: very slow
     */
    public static final int DEFAULT_COMPRESSION_ALGORITHM = CompressionAlgorithmTags.ZIP;

    public interface OpenKeychainCompressionAlgorithmTags extends CompressionAlgorithmTags {
        int USE_DEFAULT = -1;
    }

    /**
     * Note: s2kcount is a number between 0 and 0xff that controls the
     * number of times to iterate the password hash before use. More
     * iterations are useful against offline attacks, as it takes more
     * time to check each password. The actual number of iterations is
     * rather complex, and also depends on the hash function in use.
     * Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
     * you more iterations.  As a rough rule of thumb, when using
     * SHA256 as the hashing function, 0x10 gives you about 64
     * iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
     * or about 1 million iterations. The maximum you can go to is
     * 0xff, or about 2 million iterations.
     * from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
     *
     * Bouncy Castle default: 0x60
     * kbsriram proposes: 0xc0
     * Yahoo's End-to-End: 96=0x60 (65536 iterations) (https://github.com/yahoo/end-to-end/blob/master/src/javascript/crypto/e2e/openpgp/keyring.js)
     */
    public static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x90;
    public static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA512;
    public static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
    public static final int SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO = HashAlgorithmTags.SHA512;
    // NOTE: only SHA1 is supported for key checksum calculations in OpenPGP,
    // see http://tools.ietf.org/html/rfc488 0#section-5.5.3
    public static final int SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO = HashAlgorithmTags.SHA1;

}