1 /** 2 * Copyright (c) 2017, 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 android.net.wifi.hotspot2.pps; 18 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 import android.text.TextUtils; 22 import android.util.Base64; 23 import android.util.Log; 24 25 import java.nio.charset.StandardCharsets; 26 import java.util.Arrays; 27 import java.util.Objects; 28 29 /** 30 * Class representing configuration parameters for subscription or policy update in 31 * PerProviderSubscription (PPS) Management Object (MO) tree. This is used by both 32 * PerProviderSubscription/Policy/PolicyUpdate and PerProviderSubscription/SubscriptionUpdate 33 * subtree. 34 * 35 * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 36 * Release 2 Technical Specification. 37 * 38 * @hide 39 */ 40 public final class UpdateParameter implements Parcelable { 41 private static final String TAG = "UpdateParameter"; 42 43 /** 44 * Value indicating policy update is not applicable. Thus, never check with policy server 45 * for updates. 46 */ 47 public static final long UPDATE_CHECK_INTERVAL_NEVER = 0xFFFFFFFFL; 48 49 /** 50 * Valid string for UpdateMethod. 51 */ 52 public static final String UPDATE_METHOD_OMADM = "OMA-DM-ClientInitiated"; 53 public static final String UPDATE_METHOD_SSP = "SSP-ClientInitiated"; 54 55 /** 56 * Valid string for Restriction. 57 */ 58 public static final String UPDATE_RESTRICTION_HOMESP = "HomeSP"; 59 public static final String UPDATE_RESTRICTION_ROAMING_PARTNER = "RoamingPartner"; 60 public static final String UPDATE_RESTRICTION_UNRESTRICTED = "Unrestricted"; 61 62 /** 63 * Maximum bytes for URI string. 64 */ 65 private static final int MAX_URI_BYTES = 1023; 66 67 /** 68 * Maximum bytes for URI string. 69 */ 70 private static final int MAX_URL_BYTES = 1023; 71 72 /** 73 * Maximum bytes for username. 74 */ 75 private static final int MAX_USERNAME_BYTES = 63; 76 77 /** 78 * Maximum bytes for password. 79 */ 80 private static final int MAX_PASSWORD_BYTES = 255; 81 82 /** 83 * Number of bytes for certificate SHA-256 fingerprint byte array. 84 */ 85 private static final int CERTIFICATE_SHA256_BYTES = 32; 86 87 /** 88 * This specifies how often the mobile device shall check with policy server for updates. 89 * 90 * Using Long.MIN_VALUE to indicate unset value. 91 */ 92 private long mUpdateIntervalInMinutes = Long.MIN_VALUE; setUpdateIntervalInMinutes(long updateIntervalInMinutes)93 public void setUpdateIntervalInMinutes(long updateIntervalInMinutes) { 94 mUpdateIntervalInMinutes = updateIntervalInMinutes; 95 } getUpdateIntervalInMinutes()96 public long getUpdateIntervalInMinutes() { 97 return mUpdateIntervalInMinutes; 98 } 99 100 /** 101 * The method used to update the policy. Permitted values are "OMA-DM-ClientInitiated" 102 * and "SPP-ClientInitiated". 103 */ 104 private String mUpdateMethod = null; setUpdateMethod(String updateMethod)105 public void setUpdateMethod(String updateMethod) { 106 mUpdateMethod = updateMethod; 107 } getUpdateMethod()108 public String getUpdateMethod() { 109 return mUpdateMethod; 110 } 111 112 /** 113 * This specifies the hotspots at which the subscription update is permitted. Permitted 114 * values are "HomeSP", "RoamingPartner", or "Unrestricted"; 115 */ 116 private String mRestriction = null; setRestriction(String restriction)117 public void setRestriction(String restriction) { 118 mRestriction = restriction; 119 } getRestriction()120 public String getRestriction() { 121 return mRestriction; 122 } 123 124 /** 125 * The URI of the update server. 126 */ 127 private String mServerUri = null; setServerUri(String serverUri)128 public void setServerUri(String serverUri) { 129 mServerUri = serverUri; 130 } getServerUri()131 public String getServerUri() { 132 return mServerUri; 133 } 134 135 /** 136 * Username used to authenticate with the policy server. 137 */ 138 private String mUsername = null; setUsername(String username)139 public void setUsername(String username) { 140 mUsername = username; 141 } getUsername()142 public String getUsername() { 143 return mUsername; 144 } 145 146 /** 147 * Base64 encoded password used to authenticate with the policy server. 148 */ 149 private String mBase64EncodedPassword = null; setBase64EncodedPassword(String password)150 public void setBase64EncodedPassword(String password) { 151 mBase64EncodedPassword = password; 152 } getBase64EncodedPassword()153 public String getBase64EncodedPassword() { 154 return mBase64EncodedPassword; 155 } 156 157 /** 158 * HTTPS URL for retrieving certificate for trust root. The trust root is used to validate 159 * policy server's identity. 160 */ 161 private String mTrustRootCertUrl = null; setTrustRootCertUrl(String trustRootCertUrl)162 public void setTrustRootCertUrl(String trustRootCertUrl) { 163 mTrustRootCertUrl = trustRootCertUrl; 164 } getTrustRootCertUrl()165 public String getTrustRootCertUrl() { 166 return mTrustRootCertUrl; 167 } 168 169 /** 170 * SHA-256 fingerprint of the certificate located at {@link #trustRootCertUrl} 171 */ 172 private byte[] mTrustRootCertSha256Fingerprint = null; setTrustRootCertSha256Fingerprint(byte[] fingerprint)173 public void setTrustRootCertSha256Fingerprint(byte[] fingerprint) { 174 mTrustRootCertSha256Fingerprint = fingerprint; 175 } getTrustRootCertSha256Fingerprint()176 public byte[] getTrustRootCertSha256Fingerprint() { 177 return mTrustRootCertSha256Fingerprint; 178 } 179 180 /** 181 * Constructor for creating Policy with default values. 182 */ UpdateParameter()183 public UpdateParameter() {} 184 185 /** 186 * Copy constructor. 187 * 188 * @param source The source to copy from 189 */ UpdateParameter(UpdateParameter source)190 public UpdateParameter(UpdateParameter source) { 191 if (source == null) { 192 return; 193 } 194 mUpdateIntervalInMinutes = source.mUpdateIntervalInMinutes; 195 mUpdateMethod = source.mUpdateMethod; 196 mRestriction = source.mRestriction; 197 mServerUri = source.mServerUri; 198 mUsername = source.mUsername; 199 mBase64EncodedPassword = source.mBase64EncodedPassword; 200 mTrustRootCertUrl = source.mTrustRootCertUrl; 201 if (source.mTrustRootCertSha256Fingerprint != null) { 202 mTrustRootCertSha256Fingerprint = Arrays.copyOf(source.mTrustRootCertSha256Fingerprint, 203 source.mTrustRootCertSha256Fingerprint.length); 204 } 205 } 206 207 @Override describeContents()208 public int describeContents() { 209 return 0; 210 } 211 212 @Override writeToParcel(Parcel dest, int flags)213 public void writeToParcel(Parcel dest, int flags) { 214 dest.writeLong(mUpdateIntervalInMinutes); 215 dest.writeString(mUpdateMethod); 216 dest.writeString(mRestriction); 217 dest.writeString(mServerUri); 218 dest.writeString(mUsername); 219 dest.writeString(mBase64EncodedPassword); 220 dest.writeString(mTrustRootCertUrl); 221 dest.writeByteArray(mTrustRootCertSha256Fingerprint); 222 } 223 224 @Override equals(Object thatObject)225 public boolean equals(Object thatObject) { 226 if (this == thatObject) { 227 return true; 228 } 229 if (!(thatObject instanceof UpdateParameter)) { 230 return false; 231 } 232 UpdateParameter that = (UpdateParameter) thatObject; 233 234 return mUpdateIntervalInMinutes == that.mUpdateIntervalInMinutes 235 && TextUtils.equals(mUpdateMethod, that.mUpdateMethod) 236 && TextUtils.equals(mRestriction, that.mRestriction) 237 && TextUtils.equals(mServerUri, that.mServerUri) 238 && TextUtils.equals(mUsername, that.mUsername) 239 && TextUtils.equals(mBase64EncodedPassword, that.mBase64EncodedPassword) 240 && TextUtils.equals(mTrustRootCertUrl, that.mTrustRootCertUrl) 241 && Arrays.equals(mTrustRootCertSha256Fingerprint, 242 that.mTrustRootCertSha256Fingerprint); 243 } 244 245 @Override hashCode()246 public int hashCode() { 247 return Objects.hash(mUpdateIntervalInMinutes, mUpdateMethod, mRestriction, mServerUri, 248 mUsername, mBase64EncodedPassword, mTrustRootCertUrl, 249 mTrustRootCertSha256Fingerprint); 250 } 251 252 @Override toString()253 public String toString() { 254 StringBuilder builder = new StringBuilder(); 255 builder.append("UpdateInterval: ").append(mUpdateIntervalInMinutes).append("\n"); 256 builder.append("UpdateMethod: ").append(mUpdateMethod).append("\n"); 257 builder.append("Restriction: ").append(mRestriction).append("\n"); 258 builder.append("ServerURI: ").append(mServerUri).append("\n"); 259 builder.append("Username: ").append(mUsername).append("\n"); 260 builder.append("TrustRootCertURL: ").append(mTrustRootCertUrl).append("\n"); 261 return builder.toString(); 262 } 263 264 /** 265 * Validate UpdateParameter data. 266 * 267 * @return true on success 268 * @hide 269 */ validate()270 public boolean validate() { 271 if (mUpdateIntervalInMinutes == Long.MIN_VALUE) { 272 Log.d(TAG, "Update interval not specified"); 273 return false; 274 } 275 // Update not applicable. 276 if (mUpdateIntervalInMinutes == UPDATE_CHECK_INTERVAL_NEVER) { 277 return true; 278 } 279 280 if (!TextUtils.equals(mUpdateMethod, UPDATE_METHOD_OMADM) 281 && !TextUtils.equals(mUpdateMethod, UPDATE_METHOD_SSP)) { 282 Log.d(TAG, "Unknown update method: " + mUpdateMethod); 283 return false; 284 } 285 286 if (!TextUtils.equals(mRestriction, UPDATE_RESTRICTION_HOMESP) 287 && !TextUtils.equals(mRestriction, UPDATE_RESTRICTION_ROAMING_PARTNER) 288 && !TextUtils.equals(mRestriction, UPDATE_RESTRICTION_UNRESTRICTED)) { 289 Log.d(TAG, "Unknown restriction: " + mRestriction); 290 return false; 291 } 292 293 if (TextUtils.isEmpty(mServerUri)) { 294 Log.d(TAG, "Missing update server URI"); 295 return false; 296 } 297 if (mServerUri.getBytes(StandardCharsets.UTF_8).length > MAX_URI_BYTES) { 298 Log.d(TAG, "URI bytes exceeded the max: " 299 + mServerUri.getBytes(StandardCharsets.UTF_8).length); 300 return false; 301 } 302 303 if (TextUtils.isEmpty(mUsername)) { 304 Log.d(TAG, "Missing username"); 305 return false; 306 } 307 if (mUsername.getBytes(StandardCharsets.UTF_8).length > MAX_USERNAME_BYTES) { 308 Log.d(TAG, "Username bytes exceeded the max: " 309 + mUsername.getBytes(StandardCharsets.UTF_8).length); 310 return false; 311 } 312 313 if (TextUtils.isEmpty(mBase64EncodedPassword)) { 314 Log.d(TAG, "Missing username"); 315 return false; 316 } 317 if (mBase64EncodedPassword.getBytes(StandardCharsets.UTF_8).length > MAX_PASSWORD_BYTES) { 318 Log.d(TAG, "Password bytes exceeded the max: " 319 + mBase64EncodedPassword.getBytes(StandardCharsets.UTF_8).length); 320 return false; 321 } 322 try { 323 Base64.decode(mBase64EncodedPassword, Base64.DEFAULT); 324 } catch (IllegalArgumentException e) { 325 Log.d(TAG, "Invalid encoding for password: " + mBase64EncodedPassword); 326 return false; 327 } 328 329 if (TextUtils.isEmpty(mTrustRootCertUrl)) { 330 Log.d(TAG, "Missing trust root certificate URL"); 331 return false; 332 } 333 if (mTrustRootCertUrl.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) { 334 Log.d(TAG, "Trust root cert URL bytes exceeded the max: " 335 + mTrustRootCertUrl.getBytes(StandardCharsets.UTF_8).length); 336 return false; 337 } 338 339 if (mTrustRootCertSha256Fingerprint == null) { 340 Log.d(TAG, "Missing trust root certificate SHA-256 fingerprint"); 341 return false; 342 } 343 if (mTrustRootCertSha256Fingerprint.length != CERTIFICATE_SHA256_BYTES) { 344 Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: " 345 + mTrustRootCertSha256Fingerprint.length); 346 return false; 347 } 348 return true; 349 } 350 351 public static final Creator<UpdateParameter> CREATOR = 352 new Creator<UpdateParameter>() { 353 @Override 354 public UpdateParameter createFromParcel(Parcel in) { 355 UpdateParameter updateParam = new UpdateParameter(); 356 updateParam.setUpdateIntervalInMinutes(in.readLong()); 357 updateParam.setUpdateMethod(in.readString()); 358 updateParam.setRestriction(in.readString()); 359 updateParam.setServerUri(in.readString()); 360 updateParam.setUsername(in.readString()); 361 updateParam.setBase64EncodedPassword(in.readString()); 362 updateParam.setTrustRootCertUrl(in.readString()); 363 updateParam.setTrustRootCertSha256Fingerprint(in.createByteArray()); 364 return updateParam; 365 } 366 367 @Override 368 public UpdateParameter[] newArray(int size) { 369 return new UpdateParameter[size]; 370 } 371 }; 372 } 373