001/** 002 * 003 * Copyright 2017 Florian Schmaus. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.ox; 018 019import java.io.IOException; 020import java.nio.charset.Charset; 021 022import org.jivesoftware.smack.util.Objects; 023import org.jivesoftware.smack.xml.XmlPullParserException; 024 025import org.jivesoftware.smackx.ox.element.CryptElement; 026import org.jivesoftware.smackx.ox.element.OpenPgpContentElement; 027import org.jivesoftware.smackx.ox.element.OpenPgpElement; 028import org.jivesoftware.smackx.ox.element.SignElement; 029import org.jivesoftware.smackx.ox.element.SigncryptElement; 030import org.jivesoftware.smackx.ox.provider.OpenPgpContentElementProvider; 031 032import org.pgpainless.decryption_verification.OpenPgpMetadata; 033 034/** 035 * This class embodies a decrypted and/or verified {@link OpenPgpElement}. 036 * <br> 037 * The content can be one of the following three {@link OpenPgpContentElement}s: 038 * <br><br> 039 * {@link SignElement}: The content is expected to be signed with the senders key, but unencrypted.<br> 040 * {@link CryptElement}: The content is expected to be encrypted, but not signed.<br> 041 * {@link SigncryptElement}: The content is expected to be signed with the senders key and encrypted.<br> 042 * <br> 043 * To determine, of which nature the content of the message is, use {@link #getState()}. You should utilize this 044 * information to cast the return value of {@link #getOpenPgpContentElement()} correctly. 045 * <br> 046 * Use {@link #getMetadata()} in order to get information about the messages encryption status, its signatures etc. 047 */ 048public class OpenPgpMessage { 049 050 public enum State { 051 /** 052 * Represents a {@link SigncryptElement}. 053 */ 054 signcrypt, 055 /** 056 * Represents a {@link SignElement}. 057 */ 058 sign, 059 /** 060 * Represents a {@link CryptElement}. 061 */ 062 crypt, 063 } 064 065 private final String element; 066 private final State state; 067 private final OpenPgpMetadata metadata; 068 069 private OpenPgpContentElement openPgpContentElement; 070 071 /** 072 * Constructor. 073 * 074 * @param content XML representation of the decrypted {@link OpenPgpContentElement}. 075 * @param state {@link State} of the {@link OpenPgpContentElement}. 076 * @param metadata Metadata about the encryption. 077 */ 078 public OpenPgpMessage(String content, State state, OpenPgpMetadata metadata) { 079 this.metadata = Objects.requireNonNull(metadata); 080 this.state = Objects.requireNonNull(state); 081 this.element = Objects.requireNonNull(content); 082 } 083 084 /** 085 * Constructor. 086 * 087 * @param bytes bytes of the XML representation of the decrypted {@link OpenPgpContentElement}. 088 * @param state {@link State} of the {@link OpenPgpContentElement}. 089 * @param metadata metadata about the encryption. 090 */ 091 public OpenPgpMessage(byte[] bytes, State state, OpenPgpMetadata metadata) { 092 this(new String(Objects.requireNonNull(bytes), Charset.forName("UTF-8")), state, metadata); 093 } 094 095 /** 096 * Return the decrypted {@link OpenPgpContentElement} of this message. 097 * To determine, whether the element is a {@link SignElement}, {@link CryptElement} or {@link SigncryptElement}, 098 * please consult {@link #getState()}. 099 * 100 * @return {@link OpenPgpContentElement} 101 * @throws XmlPullParserException if the parser encounters an error. 102 * @throws IOException if the parser encounters an error. 103 */ 104 public OpenPgpContentElement getOpenPgpContentElement() throws XmlPullParserException, IOException { 105 ensureOpenPgpContentElementSet(); 106 107 return openPgpContentElement; 108 } 109 110 private void ensureOpenPgpContentElementSet() throws XmlPullParserException, IOException { 111 if (openPgpContentElement != null) 112 return; 113 114 openPgpContentElement = OpenPgpContentElementProvider.parseOpenPgpContentElement(element); 115 if (openPgpContentElement == null) { 116 return; 117 } 118 119 // Determine the state of the content element. 120 if (openPgpContentElement instanceof SigncryptElement) { 121 if (state != State.signcrypt) { 122 throw new IllegalStateException("OpenPgpContentElement was signed and encrypted, but is not a SigncryptElement."); 123 } 124 } else if (openPgpContentElement instanceof SignElement) { 125 if (state != State.sign) { 126 throw new IllegalStateException("OpenPgpContentElement was signed and unencrypted, but is not a SignElement."); 127 } 128 } else if (openPgpContentElement instanceof CryptElement) { 129 if (state != State.crypt) { 130 throw new IllegalStateException("OpenPgpContentElement was unsigned and encrypted, but is not a CryptElement."); 131 } 132 } 133 } 134 135 /** 136 * Return the state of the message. This value determines, whether the message was a {@link SignElement}, 137 * {@link CryptElement} or {@link SigncryptElement}. 138 * 139 * @return state of the content element. 140 * @throws IOException if the parser encounters an error. 141 * @throws XmlPullParserException if the parser encounters and error. 142 */ 143 public State getState() throws IOException, XmlPullParserException { 144 ensureOpenPgpContentElementSet(); 145 return state; 146 } 147 148 /** 149 * Return metadata about the encrypted message. 150 * 151 * @return metadata TODO javadoc me please 152 */ 153 public OpenPgpMetadata getMetadata() { 154 return metadata; 155 } 156}