001/* 002 * Copyright 2018 Paul Schaub. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.pgpainless.util; 017 018import javax.annotation.Nullable; 019import java.util.Arrays; 020 021public class Passphrase { 022 023 private final Object lock = new Object(); 024 025 private final char[] chars; 026 private boolean valid = true; 027 028 /** 029 * Passphrase for keys etc. 030 * 031 * @param chars may be null for empty passwords. 032 */ 033 public Passphrase(@Nullable char[] chars) { 034 this.chars = chars; 035 } 036 037 /** 038 * Overwrite the char array with spaces and mark the {@link Passphrase} as invalidated. 039 */ 040 public void clear() { 041 synchronized (lock) { 042 if (chars != null) { 043 Arrays.fill(chars, ' '); 044 } 045 valid = false; 046 } 047 } 048 049 /** 050 * Call {@link #clear()} to make sure the memory is overwritten. 051 * 052 * @throws Throwable bad things might happen in {@link Object#finalize()}. 053 */ 054 @Override 055 protected void finalize() throws Throwable { 056 clear(); 057 super.finalize(); 058 } 059 060 /** 061 * Return a copy of the underlying char array. 062 * A return value of {@code null} represents no password. 063 * 064 * @return passphrase chars. 065 * 066 * @throws IllegalStateException in case the password has been cleared at this point. 067 */ 068 public @Nullable char[] getChars() { 069 synchronized (lock) { 070 if (!valid) { 071 throw new IllegalStateException("Passphrase has been cleared."); 072 } 073 074 if (chars == null) { 075 return null; 076 } 077 078 char[] copy = new char[chars.length]; 079 System.arraycopy(chars, 0, copy, 0, chars.length); 080 return copy; 081 } 082 } 083 084 /** 085 * Return true if the passphrase has not yet been cleared. 086 * 087 * @return valid 088 */ 089 public boolean isValid() { 090 synchronized (lock) { 091 return valid; 092 } 093 } 094 095 /** 096 * Represents a {@link Passphrase} instance that represents no password. 097 * 098 * @return empty passphrase 099 */ 100 public static Passphrase emptyPassphrase() { 101 return new Passphrase(null); 102 } 103}