001/* 002 * Syncany, www.syncany.org 003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com> 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package org.syncany.crypto; 019 020import java.io.InputStream; 021import java.io.OutputStream; 022import java.util.regex.Pattern; 023 024/** 025 * A cipher spec represents the definition of a cipher/encryption algorithm and the 026 * corresponding settings required to instantiate a new cipher object. 027 * 028 * <p>Cipher specs are identified by an identifier (<i>id</i>), which will (when the 029 * cipher spec is used by the {@link MultiCipherOutputStream}) be written to the output 030 * file format. When the file is read by {@link MultiCipherInputStream}, the identifier 031 * is looked up using the {@link CipherSpecs} class. 032 * 033 * <p>While it would be technically possible to define any kind of cipher using this class, 034 * this class restricts the allowed algorithms to a few ones that are considered secure. 035 * 036 * <p>Instantiating a cipher spec that does pass the sanity checks will result in a 037 * RuntimeException. 038 * 039 * @author Philipp C. Heckel (philipp.heckel@gmail.com) 040 */ 041public abstract class CipherSpec { 042 public static final Pattern ALLOWED_CIPHER_ALGORITHMS = Pattern.compile("^HmacSHA256$|(^(AES|Twofish)/(GCM|EAX)/.+)"); 043 044 private int id; 045 private String algorithm; 046 private int keySize; // in bits 047 private int ivSize; // in bits 048 private boolean needsUnlimitedStrength; 049 050 public CipherSpec(int id, String algorithm, int keySize, int ivSize, boolean needsUnlimitedStrength) { 051 this.id = id; 052 this.algorithm = algorithm; 053 this.keySize = keySize; 054 this.ivSize = ivSize; 055 this.needsUnlimitedStrength = needsUnlimitedStrength; 056 057 doSanityChecks(); 058 } 059 060 public int getId() { 061 return id; 062 } 063 064 public boolean needsUnlimitedStrength() { 065 return needsUnlimitedStrength; 066 } 067 068 public String getAlgorithm() { 069 return algorithm; 070 } 071 072 public int getKeySize() { 073 return keySize; 074 } 075 076 public int getIvSize() { 077 return ivSize; 078 } 079 080 public abstract OutputStream newCipherOutputStream(OutputStream underlyingOutputStream, byte[] secretKey, byte[] iv) throws CipherException; 081 082 public abstract InputStream newCipherInputStream(InputStream underlyingInputStream, byte[] secretKey, byte[] iv) throws CipherException; 083 084 @Override 085 public String toString() { 086 return algorithm + ", " + keySize + " bit"; 087 } 088 089 private void doSanityChecks() { 090 if (!ALLOWED_CIPHER_ALGORITHMS.matcher(algorithm).matches()) { 091 throw new RuntimeException("Cipher algorithm or mode not allowed: " + algorithm + ". This mode is not considered secure."); 092 } 093 } 094 095 @Override 096 public int hashCode() { 097 final int prime = 31; 098 int result = 1; 099 result = prime * result + ((algorithm == null) ? 0 : algorithm.hashCode()); 100 result = prime * result + id; 101 result = prime * result + ivSize; 102 result = prime * result + keySize; 103 result = prime * result + (needsUnlimitedStrength ? 1231 : 1237); 104 return result; 105 } 106 107 @Override 108 public boolean equals(Object obj) { 109 if (this == obj) { 110 return true; 111 } 112 if (obj == null) { 113 return false; 114 } 115 if (!(obj instanceof CipherSpec)) { 116 return false; 117 } 118 CipherSpec other = (CipherSpec) obj; 119 if (algorithm == null) { 120 if (other.algorithm != null) { 121 return false; 122 } 123 } 124 else if (!algorithm.equals(other.algorithm)) { 125 return false; 126 } 127 if (id != other.id) { 128 return false; 129 } 130 if (ivSize != other.ivSize) { 131 return false; 132 } 133 if (keySize != other.keySize) { 134 return false; 135 } 136 if (needsUnlimitedStrength != other.needsUnlimitedStrength) { 137 return false; 138 } 139 return true; 140 } 141}