001// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org> 002// 003// SPDX-License-Identifier: Apache-2.0 004 005package org.pgpainless.signature.consumer; 006 007import java.io.IOException; 008import java.io.InputStream; 009import java.util.Date; 010 011import org.bouncycastle.openpgp.PGPException; 012import org.bouncycastle.openpgp.PGPPublicKey; 013import org.bouncycastle.openpgp.PGPSignature; 014import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector; 015import org.pgpainless.algorithm.SignatureType; 016import org.pgpainless.exception.SignatureValidationException; 017import org.pgpainless.implementation.ImplementationFactory; 018import org.pgpainless.policy.Policy; 019 020/** 021 * Collection of static methods for signature verification. 022 * Signature verification entails validation of certain criteria (see {@link SignatureValidator}), as well as 023 * cryptographic verification of signature correctness. 024 */ 025public final class SignatureVerifier { 026 027 private SignatureVerifier() { 028 029 } 030 031 /** 032 * Verify a signature (certification or revocation) over a user-id. 033 * 034 * @param userId user-id 035 * @param signature certification signature 036 * @param signingKey key that created the certification 037 * @param keyWithUserId key carrying the user-id 038 * @param policy policy 039 * @param validationDate reference date for signature verification 040 * @return true if signature verification is successful 041 * 042 * @throws SignatureValidationException if signature verification fails for some reason 043 */ 044 public static boolean verifySignatureOverUserId(String userId, PGPSignature signature, PGPPublicKey signingKey, PGPPublicKey keyWithUserId, Policy policy, Date validationDate) 045 throws SignatureValidationException { 046 SignatureType type = SignatureType.valueOf(signature.getSignatureType()); 047 switch (type) { 048 case GENERIC_CERTIFICATION: 049 case NO_CERTIFICATION: 050 case CASUAL_CERTIFICATION: 051 case POSITIVE_CERTIFICATION: 052 return verifyUserIdCertification(userId, signature, signingKey, keyWithUserId, policy, validationDate); 053 case CERTIFICATION_REVOCATION: 054 return verifyUserIdRevocation(userId, signature, signingKey, keyWithUserId, policy, validationDate); 055 default: 056 throw new SignatureValidationException("Signature is not a valid user-id certification/revocation signature: " + type); 057 } 058 } 059 060 /** 061 * Verify a certification self-signature over a user-id. 062 * 063 * @param userId user-id 064 * @param signature certification signature 065 * @param primaryKey primary key 066 * @param policy policy 067 * @param validationDate reference date for signature verification 068 * @return true if the self-signature is verified successfully 069 * 070 * @throws SignatureValidationException if signature verification fails for some reason 071 */ 072 public static boolean verifyUserIdCertification(String userId, PGPSignature signature, PGPPublicKey primaryKey, Policy policy, Date validationDate) 073 throws SignatureValidationException { 074 return verifyUserIdCertification(userId, signature, primaryKey, primaryKey, policy, validationDate); 075 } 076 077 /** 078 * Verify a user-id certification. 079 * 080 * @param userId user-id 081 * @param signature certification signature 082 * @param signingKey key that created the certification 083 * @param keyWithUserId primary key that carries the user-id 084 * @param policy policy 085 * @param validationDate reference date for signature verification 086 * @return true if signature verification is successful 087 * 088 * @throws SignatureValidationException if signature verification fails for some reason 089 */ 090 public static boolean verifyUserIdCertification(String userId, PGPSignature signature, PGPPublicKey signingKey, PGPPublicKey keyWithUserId, Policy policy, Date validationDate) 091 throws SignatureValidationException { 092 SignatureValidator.signatureIsCertification().verify(signature); 093 SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature); 094 SignatureValidator.signatureIsEffective(validationDate).verify(signature); 095 SignatureValidator.correctSignatureOverUserId(userId, keyWithUserId, signingKey).verify(signature); 096 097 return true; 098 } 099 100 /** 101 * Verify a user-id revocation self-signature. 102 * 103 * @param userId user-id 104 * @param signature user-id revocation signature 105 * @param primaryKey primary key 106 * @param policy policy 107 * @param validationDate reference date for signature verification 108 * @return true if the user-id revocation signature is successfully verified 109 * 110 * @throws SignatureValidationException if signature verification fails for some reason 111 */ 112 public static boolean verifyUserIdRevocation(String userId, PGPSignature signature, PGPPublicKey primaryKey, Policy policy, Date validationDate) 113 throws SignatureValidationException { 114 return verifyUserIdRevocation(userId, signature, primaryKey, primaryKey, policy, validationDate); 115 } 116 117 /** 118 * Verify a user-id revocation signature. 119 * 120 * @param userId user-id 121 * @param signature revocation signature 122 * @param signingKey key that created the revocation signature 123 * @param keyWithUserId primary key carrying the user-id 124 * @param policy policy 125 * @param validationDate reference date for signature verification 126 * @return true if the user-id revocation signature is successfully verified 127 * 128 * @throws SignatureValidationException if signature verification fails for some reason 129 */ 130 public static boolean verifyUserIdRevocation(String userId, PGPSignature signature, PGPPublicKey signingKey, PGPPublicKey keyWithUserId, Policy policy, Date validationDate) 131 throws SignatureValidationException { 132 SignatureValidator.signatureIsOfType(SignatureType.CERTIFICATION_REVOCATION).verify(signature); 133 SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature); 134 SignatureValidator.signatureIsEffective(validationDate).verify(signature); 135 SignatureValidator.correctSignatureOverUserId(userId, keyWithUserId, signingKey).verify(signature); 136 137 return true; 138 } 139 140 /** 141 * Verify a certification self-signature over a user-attributes packet. 142 * 143 * @param userAttributes user attributes 144 * @param signature certification self-signature 145 * @param primaryKey primary key that carries the user-attributes 146 * @param policy policy 147 * @param validationDate reference date for signature verification 148 * @return true if the signature can be verified successfully 149 * 150 * @throws SignatureValidationException if signature verification fails for some reason 151 */ 152 public static boolean verifyUserAttributesCertification(PGPUserAttributeSubpacketVector userAttributes, 153 PGPSignature signature, PGPPublicKey primaryKey, 154 Policy policy, Date validationDate) 155 throws SignatureValidationException { 156 return verifyUserAttributesCertification(userAttributes, signature, primaryKey, primaryKey, policy, validationDate); 157 } 158 159 /** 160 * Verify a certification signature over a user-attributes packet. 161 * 162 * @param userAttributes user attributes 163 * @param signature certification signature 164 * @param signingKey key that created the user-attributes certification 165 * @param keyWithUserAttributes key that carries the user-attributes certification 166 * @param policy policy 167 * @param validationDate reference date for signature verification 168 * @return true if the signature can be verified successfully 169 * 170 * @throws SignatureValidationException if signature verification fails for some reason 171 */ 172 public static boolean verifyUserAttributesCertification(PGPUserAttributeSubpacketVector userAttributes, 173 PGPSignature signature, PGPPublicKey signingKey, 174 PGPPublicKey keyWithUserAttributes, Policy policy, 175 Date validationDate) 176 throws SignatureValidationException { 177 SignatureValidator.signatureIsCertification().verify(signature); 178 SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature); 179 SignatureValidator.signatureIsEffective(validationDate).verify(signature); 180 SignatureValidator.correctSignatureOverUserAttributes(userAttributes, keyWithUserAttributes, signingKey).verify(signature); 181 182 return true; 183 } 184 185 /** 186 * Verify a user-attributes revocation self-signature. 187 * 188 * @param userAttributes user-attributes 189 * @param signature user-attributes revocation signature 190 * @param primaryKey primary key that carries the user-attributes 191 * @param policy policy 192 * @param validationDate reference date for signature verification 193 * @return true if the revocation signature can be verified successfully 194 * 195 * @throws SignatureValidationException if signature verification fails for some reason 196 */ 197 public static boolean verifyUserAttributesRevocation(PGPUserAttributeSubpacketVector userAttributes, 198 PGPSignature signature, PGPPublicKey primaryKey, 199 Policy policy, Date validationDate) 200 throws SignatureValidationException { 201 return verifyUserAttributesRevocation(userAttributes, signature, primaryKey, primaryKey, policy, validationDate); 202 } 203 204 /** 205 * Verify a user-attributes revocation signature. 206 * 207 * @param userAttributes user-attributes 208 * @param signature revocation signature 209 * @param signingKey revocation key 210 * @param keyWithUserAttributes key that carries the user-attributes 211 * @param policy policy 212 * @param validationDate reference date for signature verification 213 * @return true if the revocation signature can be verified successfully 214 * 215 * @throws SignatureValidationException if signature verification fails for some reason 216 */ 217 public static boolean verifyUserAttributesRevocation(PGPUserAttributeSubpacketVector userAttributes, 218 PGPSignature signature, PGPPublicKey signingKey, 219 PGPPublicKey keyWithUserAttributes, Policy policy, 220 Date validationDate) 221 throws SignatureValidationException { 222 SignatureValidator.signatureIsOfType(SignatureType.CERTIFICATION_REVOCATION).verify(signature); 223 SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature); 224 SignatureValidator.signatureIsEffective(validationDate).verify(signature); 225 SignatureValidator.correctSignatureOverUserAttributes(userAttributes, keyWithUserAttributes, signingKey).verify(signature); 226 227 return true; 228 } 229 230 /** 231 * Verify a subkey binding signature. 232 * 233 * @param signature binding signature 234 * @param primaryKey primary key 235 * @param subkey subkey 236 * @param policy policy 237 * @param validationDate reference date for signature verification 238 * @return true if the binding signature can be verified successfully 239 * 240 * @throws SignatureValidationException if signature verification fails for some reason 241 */ 242 public static boolean verifySubkeyBindingSignature(PGPSignature signature, PGPPublicKey primaryKey, PGPPublicKey subkey, Policy policy, Date validationDate) 243 throws SignatureValidationException { 244 SignatureValidator.signatureIsOfType(SignatureType.SUBKEY_BINDING).verify(signature); 245 SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature); 246 SignatureValidator.signatureIsEffective(validationDate).verify(signature); 247 SignatureValidator.hasValidPrimaryKeyBindingSignatureIfRequired(primaryKey, subkey, policy, validationDate).verify(signature); 248 SignatureValidator.correctSubkeyBindingSignature(primaryKey, subkey).verify(signature); 249 250 return true; 251 } 252 253 /** 254 * Verify a subkey revocation signature. 255 * 256 * @param signature subkey revocation signature 257 * @param primaryKey primary key 258 * @param subkey subkey 259 * @param policy policy 260 * @param validationDate reference date for signature verification 261 * @return true if the subkey revocation signature can be verified successfully 262 * 263 * @throws SignatureValidationException if signature verification fails for some reason 264 */ 265 public static boolean verifySubkeyBindingRevocation(PGPSignature signature, PGPPublicKey primaryKey, PGPPublicKey subkey, Policy policy, Date validationDate) throws SignatureValidationException { 266 SignatureValidator.signatureIsOfType(SignatureType.SUBKEY_REVOCATION).verify(signature); 267 SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature); 268 SignatureValidator.signatureIsEffective(validationDate).verify(signature); 269 SignatureValidator.correctSignatureOverKey(primaryKey, subkey).verify(signature); 270 271 return true; 272 } 273 274 /** 275 * Verify a direct-key self-signature. 276 * 277 * @param signature signature 278 * @param primaryKey primary key 279 * @param policy policy 280 * @param validationDate reference date for signature verification 281 * @return true if the signature can be verified successfully 282 * 283 * @throws SignatureValidationException if signature verification fails for some reason 284 */ 285 public static boolean verifyDirectKeySignature(PGPSignature signature, PGPPublicKey primaryKey, Policy policy, Date validationDate) 286 throws SignatureValidationException { 287 return verifyDirectKeySignature(signature, primaryKey, primaryKey, policy, validationDate); 288 } 289 290 /** 291 * Verify a direct-key signature. 292 * 293 * @param signature signature 294 * @param signingKey signing key 295 * @param signedKey signed key 296 * @param policy policy 297 * @param validationDate reference date for signature verification 298 * @return true if signature verification is successful 299 * 300 * @throws SignatureValidationException if signature verification fails for some reason 301 */ 302 public static boolean verifyDirectKeySignature(PGPSignature signature, PGPPublicKey signingKey, PGPPublicKey signedKey, Policy policy, Date validationDate) 303 throws SignatureValidationException { 304 SignatureValidator.signatureIsOfType(SignatureType.DIRECT_KEY).verify(signature); 305 SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature); 306 SignatureValidator.signatureIsEffective(validationDate).verify(signature); 307 SignatureValidator.correctSignatureOverKey(signingKey, signedKey).verify(signature); 308 309 return true; 310 } 311 312 /** 313 * Verify a key revocation signature. 314 * 315 * @param signature signature 316 * @param primaryKey primary key 317 * @param policy policy 318 * @param validationDate reference date for signature verification 319 * @return true if signature verification is successful 320 * 321 * @throws SignatureValidationException if signature verification fails for some reason 322 */ 323 public static boolean verifyKeyRevocationSignature(PGPSignature signature, PGPPublicKey primaryKey, Policy policy, Date validationDate) 324 throws SignatureValidationException { 325 SignatureValidator.signatureIsOfType(SignatureType.KEY_REVOCATION).verify(signature); 326 SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature); 327 SignatureValidator.signatureIsEffective(validationDate).verify(signature); 328 SignatureValidator.correctSignatureOverKey(primaryKey, primaryKey).verify(signature); 329 330 return true; 331 } 332 333 /** 334 * Initialize a signature and verify it afterwards by updating it with the signed data. 335 * 336 * @param signature OpenPGP signature 337 * @param signedData input stream containing the signed data 338 * @param signingKey the key that created the signature 339 * @param policy policy 340 * @param validationDate reference date of signature verification 341 * @return true if the signature is successfully verified 342 * 343 * @throws SignatureValidationException if the signature verification fails for some reason 344 */ 345 public static boolean verifyUninitializedSignature(PGPSignature signature, InputStream signedData, PGPPublicKey signingKey, Policy policy, Date validationDate) throws SignatureValidationException { 346 initializeSignatureAndUpdateWithSignedData(signature, signedData, signingKey); 347 return verifyInitializedSignature(signature, signingKey, policy, validationDate); 348 } 349 350 /** 351 * Initialize a signature and then update it with the signed data from the given {@link InputStream}. 352 * 353 * @param signature OpenPGP signature 354 * @param signedData input stream containing signed data 355 * @param signingKey key that created the signature 356 * 357 * @throws SignatureValidationException in case the signature cannot be verified for some reason 358 */ 359 public static void initializeSignatureAndUpdateWithSignedData(PGPSignature signature, InputStream signedData, PGPPublicKey signingKey) 360 throws SignatureValidationException { 361 try { 362 signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey); 363 int read; 364 byte[] buf = new byte[8192]; 365 byte lastByte = -1; 366 while ((read = signedData.read(buf)) != -1) { 367 // If we previously omitted a newline, but the stream is not yet empty, add it now 368 if (lastByte == (byte) '\n') { 369 signature.update(lastByte); 370 } 371 lastByte = buf[read - 1]; 372 373 if (lastByte == (byte) '\n') { 374 // if last byte in buffer is newline, omit it for now 375 signature.update(buf, 0, read - 1); 376 } else { 377 // otherwise, write buffer as usual 378 signature.update(buf, 0, read); 379 } 380 } 381 } catch (PGPException e) { 382 throw new SignatureValidationException("Cannot init signature.", e); 383 } catch (IOException e) { 384 throw new SignatureValidationException("Cannot update signature.", e); 385 } 386 } 387 388 /** 389 * Verify an initialized signature. 390 * An initialized signature was already updated with the signed data. 391 * 392 * @param signature OpenPGP signature 393 * @param signingKey key that created the signature 394 * @param policy policy 395 * @param validationDate reference date for signature verification 396 * @return true if signature is verified successfully 397 * 398 * @throws SignatureValidationException if signature verification fails for some reason 399 */ 400 public static boolean verifyInitializedSignature(PGPSignature signature, PGPPublicKey signingKey, Policy policy, Date validationDate) 401 throws SignatureValidationException { 402 SignatureValidator.wasPossiblyMadeByKey(signingKey).verify(signature); 403 SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature); 404 SignatureValidator.signatureIsEffective(validationDate).verify(signature); 405 406 try { 407 if (!signature.verify()) { 408 throw new SignatureValidationException("Signature is not correct."); 409 } 410 return true; 411 } catch (PGPException e) { 412 throw new SignatureValidationException("Could not verify signature correctness.", e); 413 } 414 } 415 416 public static boolean verifyOnePassSignature(PGPSignature signature, PGPPublicKey signingKey, OnePassSignatureCheck onePassSignature, Policy policy) 417 throws SignatureValidationException { 418 try { 419 SignatureValidator.wasPossiblyMadeByKey(signingKey).verify(signature); 420 SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature); 421 SignatureValidator.signatureIsEffective().verify(signature); 422 } catch (SignatureValidationException e) { 423 throw new SignatureValidationException("Signature is not valid: " + e.getMessage(), e); 424 } 425 426 try { 427 if (onePassSignature.getSignature() == null) { 428 throw new IllegalStateException("No comparison signature provided."); 429 } 430 if (!onePassSignature.getOnePassSignature().verify(signature)) { 431 throw new SignatureValidationException("Bad signature of key " + Long.toHexString(signingKey.getKeyID())); 432 } 433 } catch (PGPException e) { 434 throw new SignatureValidationException("Could not verify correctness of One-Pass-Signature: " + e.getMessage(), e); 435 } 436 437 return true; 438 } 439 440 /** 441 * Verify a signature (certification or revocation) over a user-id. 442 * 443 * @param userId user-id 444 * @param signature self-signature 445 * @param primaryKey primary key that created the signature 446 * @param policy policy 447 * @param validationDate reference date for signature verification 448 * @return true if the signature is successfully verified 449 * 450 * @throws SignatureValidationException if signature verification fails for some reason 451 */ 452 public static boolean verifySignatureOverUserId(String userId, PGPSignature signature, PGPPublicKey primaryKey, Policy policy, Date validationDate) 453 throws SignatureValidationException { 454 return verifySignatureOverUserId(userId, signature, primaryKey, primaryKey, policy, validationDate); 455 } 456}