1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt.ct; 18 19 import java.security.InvalidKeyException; 20 import java.security.MessageDigest; 21 import java.security.NoSuchAlgorithmException; 22 import java.security.PublicKey; 23 import java.security.Signature; 24 import java.security.SignatureException; 25 import java.util.Arrays; 26 import org.conscrypt.Internal; 27 28 /** 29 * Properties about a Certificate Transparency Log. 30 * This object stores information about a CT log, its public key, description and URL. 31 * It allows verification of SCTs against the log's public key. 32 * 33 * @hide 34 */ 35 @Internal 36 public class CTLogInfo { 37 private final byte[] logId; 38 private final PublicKey publicKey; 39 private final String description; 40 private final String url; 41 42 public CTLogInfo(PublicKey publicKey, String description, String url) { 43 try { 44 this.logId = MessageDigest.getInstance("SHA-256") 45 .digest(publicKey.getEncoded()); 46 } catch (NoSuchAlgorithmException e) { 47 // SHA-256 is guaranteed to be available 48 throw new RuntimeException(e); 49 } 50 51 this.publicKey = publicKey; 52 this.description = description; 53 this.url = url; 54 } 55 56 /** 57 * Get the log's ID, that is the SHA-256 hash of it's public key 58 */ 59 public byte[] getID() { 60 return logId; 61 } 62 63 public PublicKey getPublicKey() { 64 return publicKey; 65 } 66 67 public String getDescription() { 68 return description; 69 } 70 71 public String getUrl() { 72 return url; 73 } 74 75 @Override 76 public boolean equals(Object other) { 77 if (this == other) { 78 return true; 79 } 80 if (!(other instanceof CTLogInfo)) { 81 return false; 82 } 83 84 CTLogInfo that = (CTLogInfo)other; 85 return 86 this.publicKey.equals(that.publicKey) && 87 this.description.equals(that.description) && 88 this.url.equals(that.url); 89 } 90 91 @Override 92 public int hashCode() { 93 int hash = 1; 94 hash = hash * 31 + publicKey.hashCode(); 95 hash = hash * 31 + description.hashCode(); 96 hash = hash * 31 + url.hashCode(); 97 98 return hash; 99 } 100 101 /** 102 * Verify the signature of a signed certificate timestamp for the given certificate entry 103 * against the log's public key. 104 * 105 * @return the result of the verification 106 */ 107 public VerifiedSCT.Status verifySingleSCT(SignedCertificateTimestamp sct, 108 CertificateEntry entry) { 109 if (!Arrays.equals(sct.getLogID(), getID())) { 110 return VerifiedSCT.Status.UNKNOWN_LOG; 111 } 112 113 byte[] toVerify; 114 try { 115 toVerify = sct.encodeTBS(entry); 116 } catch (SerializationException e) { 117 return VerifiedSCT.Status.INVALID_SCT; 118 } 119 120 Signature signature; 121 try { 122 String algorithm = sct.getSignature().getAlgorithm(); 123 signature = Signature.getInstance(algorithm); 124 } catch (NoSuchAlgorithmException e) { 125 return VerifiedSCT.Status.INVALID_SCT; 126 } 127 128 try { 129 signature.initVerify(publicKey); 130 } catch (InvalidKeyException e) { 131 return VerifiedSCT.Status.INVALID_SCT; 132 } 133 134 try { 135 signature.update(toVerify); 136 if (!signature.verify(sct.getSignature().getSignature())) { 137 return VerifiedSCT.Status.INVALID_SIGNATURE; 138 } 139 return VerifiedSCT.Status.VALID; 140 } catch (SignatureException e) { 141 // This only happens if the signature is not initialized, 142 // but we call initVerify just before, so it should never do 143 throw new RuntimeException(e); 144 } 145 } 146 } 147 148