1 /* 2 * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 // common infrastructure for SunPKCS11 tests 26 27 import java.io.*; 28 import java.util.*; 29 import java.lang.reflect.*; 30 31 import java.security.*; 32 import java.security.spec.ECGenParameterSpec; 33 import java.security.spec.ECParameterSpec; 34 35 public abstract class PKCS11Test { 36 37 // directory of the test source 38 static final String BASE = System.getProperty("test.src", "."); 39 40 static final char SEP = File.separatorChar; 41 42 private final static String REL_CLOSED = "../../../../closed/sun/security/pkcs11".replace('/', SEP); 43 44 // directory corresponding to BASE in the /closed hierarchy 45 static final String CLOSED_BASE; 46 47 static { 48 // hack 49 String absBase = new File(BASE).getAbsolutePath(); 50 int k = absBase.indexOf(SEP + "test" + SEP + "sun" + SEP); 51 if (k < 0) k = 0; 52 String p1 = absBase.substring(0, k + 6); 53 String p2 = absBase.substring(k + 5); 54 CLOSED_BASE = p1 + "closed" + p2; 55 } 56 57 static String NSPR_PREFIX = ""; 58 59 // NSS version info 60 public static enum ECCState { None, Basic, Extended }; 61 static double nss_version = -1; 62 static ECCState nss_ecc_status = ECCState.Extended; 63 64 // The NSS library we need to search for in getNSSLibDir() 65 // Default is "libsoftokn3.so", listed as "softokn3" 66 // The other is "libnss3.so", listed as "nss3". 67 static String nss_library = "softokn3"; 68 69 // NSS versions of each library. It is simplier to keep nss_version 70 // for quick checking for generic testing than many if-else statements. 71 static double softoken3_version = -1; 72 static double nss3_version = -1; 73 74 static Provider getSunPKCS11(String config) throws Exception { 75 Class clazz = Class.forName("sun.security.pkcs11.SunPKCS11"); 76 Constructor cons = clazz.getConstructor(new Class[] {String.class}); 77 Object obj = cons.newInstance(new Object[] {config}); 78 return (Provider)obj; 79 } 80 81 public abstract void main(Provider p) throws Exception; 82 83 private void premain(Provider p) throws Exception { 84 long start = System.currentTimeMillis(); 85 System.out.println("Running test with provider " + p.getName() + "..."); 86 main(p); 87 long stop = System.currentTimeMillis(); 88 System.out.println("Completed test with provider " + p.getName() + " (" + (stop - start) + " ms)."); 89 } 90 91 public static void main(PKCS11Test test) throws Exception { 92 Provider[] oldProviders = Security.getProviders(); 93 try { 94 System.out.println("Beginning test run " + test.getClass().getName() + "..."); 95 testDefault(test); 96 testNSS(test); 97 testDeimos(test); 98 } finally { 99 // NOTE: Do not place a 'return' in any finally block 100 // as it will suppress exceptions and hide test failures. 101 Provider[] newProviders = Security.getProviders(); 102 boolean found = true; 103 // Do not restore providers if nothing changed. This is especailly 104 // useful for ./Provider/Login.sh, where a SecurityManager exists. 105 if (oldProviders.length == newProviders.length) { 106 found = false; 107 for (int i = 0; i<oldProviders.length; i++) { 108 if (oldProviders[i] != newProviders[i]) { 109 found = true; 110 break; 111 } 112 } 113 } 114 if (found) { 115 for (Provider p: newProviders) { 116 Security.removeProvider(p.getName()); 117 } 118 for (Provider p: oldProviders) { 119 Security.addProvider(p); 120 } 121 } 122 } 123 } 124 125 public static void testDeimos(PKCS11Test test) throws Exception { 126 if (new File("/opt/SUNWconn/lib/libpkcs11.so").isFile() == false || 127 "true".equals(System.getProperty("NO_DEIMOS"))) { 128 return; 129 } 130 String base = getBase(); 131 String p11config = base + SEP + "nss" + SEP + "p11-deimos.txt"; 132 Provider p = getSunPKCS11(p11config); 133 test.premain(p); 134 } 135 136 public static void testDefault(PKCS11Test test) throws Exception { 137 // run test for default configured PKCS11 providers (if any) 138 139 if ("true".equals(System.getProperty("NO_DEFAULT"))) { 140 return; 141 } 142 143 Provider[] providers = Security.getProviders(); 144 for (int i = 0; i < providers.length; i++) { 145 Provider p = providers[i]; 146 if (p.getName().startsWith("SunPKCS11-")) { 147 test.premain(p); 148 } 149 } 150 } 151 152 private static String PKCS11_BASE; 153 static { 154 try { 155 PKCS11_BASE = getBase(); 156 } catch (Exception e) { 157 // ignore 158 } 159 } 160 161 private final static String PKCS11_REL_PATH = "sun/security/pkcs11"; 162 163 public static String getBase() throws Exception { 164 if (PKCS11_BASE != null) { 165 return PKCS11_BASE; 166 } 167 File cwd = new File(System.getProperty("test.src", ".")).getCanonicalFile(); 168 while (true) { 169 File file = new File(cwd, "TEST.ROOT"); 170 if (file.isFile()) { 171 break; 172 } 173 cwd = cwd.getParentFile(); 174 if (cwd == null) { 175 throw new Exception("Test root directory not found"); 176 } 177 } 178 PKCS11_BASE = new File(cwd, PKCS11_REL_PATH.replace('/', SEP)).getAbsolutePath(); 179 return PKCS11_BASE; 180 } 181 182 public static String getNSSLibDir() throws Exception { 183 return getNSSLibDir(nss_library); 184 } 185 186 static String getNSSLibDir(String library) throws Exception { 187 Properties props = System.getProperties(); 188 String osName = props.getProperty("os.name"); 189 if (osName.startsWith("Win")) { 190 osName = "Windows"; 191 NSPR_PREFIX = "lib"; 192 } else if (osName.equals("Mac OS X")) { 193 osName = "MacOSX"; 194 } 195 String osid = osName + "-" 196 + props.getProperty("os.arch") + "-" + props.getProperty("sun.arch.data.model"); 197 String[] nssLibDirs = osMap.get(osid); 198 if (nssLibDirs == null) { 199 System.out.println("Unsupported OS, skipping: " + osid); 200 return null; 201 } 202 if (nssLibDirs.length == 0) { 203 System.out.println("NSS not supported on this platform, skipping test"); 204 return null; 205 } 206 String nssLibDir = null; 207 for (String dir : nssLibDirs) { 208 if (new File(dir).exists() && 209 new File(dir + System.mapLibraryName(library)).exists()) { 210 nssLibDir = dir; 211 System.setProperty("pkcs11test.nss.libdir", nssLibDir); 212 break; 213 } 214 } 215 return nssLibDir; 216 } 217 218 protected static void safeReload(String lib) throws Exception { 219 try { 220 System.load(lib); 221 } catch (UnsatisfiedLinkError e) { 222 if (e.getMessage().contains("already loaded")) { 223 return; 224 } 225 } 226 } 227 228 static boolean loadNSPR(String libdir) throws Exception { 229 // load NSS softoken dependencies in advance to avoid resolver issues 230 safeReload(libdir + System.mapLibraryName(NSPR_PREFIX + "nspr4")); 231 safeReload(libdir + System.mapLibraryName(NSPR_PREFIX + "plc4")); 232 safeReload(libdir + System.mapLibraryName(NSPR_PREFIX + "plds4")); 233 safeReload(libdir + System.mapLibraryName("sqlite3")); 234 safeReload(libdir + System.mapLibraryName("nssutil3")); 235 return true; 236 } 237 238 // Check the provider being used is NSS 239 public static boolean isNSS(Provider p) { 240 return p.getName().toUpperCase().equals("SUNPKCS11-NSS"); 241 } 242 243 static double getNSSVersion() { 244 if (nss_version == -1) 245 getNSSInfo(); 246 return nss_version; 247 } 248 249 static ECCState getNSSECC() { 250 if (nss_version == -1) 251 getNSSInfo(); 252 return nss_ecc_status; 253 } 254 255 public static double getLibsoftokn3Version() { 256 if (softoken3_version == -1) 257 return getNSSInfo("softokn3"); 258 return softoken3_version; 259 } 260 261 public static double getLibnss3Version() { 262 if (nss3_version == -1) 263 return getNSSInfo("nss3"); 264 return nss3_version; 265 } 266 267 /* Read the library to find out the verison */ 268 static void getNSSInfo() { 269 getNSSInfo(nss_library); 270 } 271 272 static double getNSSInfo(String library) { 273 String nssHeader = "$Header: NSS"; 274 boolean found = false; 275 String s = null; 276 int i = 0; 277 String libfile = ""; 278 279 if (library.compareTo("softokn3") == 0 && softoken3_version > -1) 280 return softoken3_version; 281 if (library.compareTo("nss3") == 0 && nss3_version > -1) 282 return nss3_version; 283 284 try { 285 libfile = getNSSLibDir() + System.mapLibraryName(library); 286 FileInputStream is = new FileInputStream(libfile); 287 byte[] data = new byte[1000]; 288 int read = 0; 289 290 while (is.available() > 0) { 291 if (read == 0) { 292 read = is.read(data, 0, 1000); 293 } else { 294 // Prepend last 100 bytes in case the header was split 295 // between the reads. 296 System.arraycopy(data, 900, data, 0, 100); 297 read = 100 + is.read(data, 100, 900); 298 } 299 300 s = new String(data, 0, read); 301 if ((i = s.indexOf(nssHeader)) > 0) { 302 found = true; 303 // If the nssHeader is before 920 we can break, otherwise 304 // we may not have the whole header so do another read. If 305 // no bytes are in the stream, that is ok, found is true. 306 if (i < 920) { 307 break; 308 } 309 } 310 } 311 312 is.close(); 313 314 } catch (Exception e) { 315 e.printStackTrace(); 316 } 317 318 if (!found) { 319 System.out.println("lib" + library + 320 " version not found, set to 0.0: " + libfile); 321 nss_version = 0.0; 322 return nss_version; 323 } 324 325 // the index after whitespace after nssHeader 326 int afterheader = s.indexOf("NSS", i) + 4; 327 String version = s.substring(afterheader, s.indexOf(' ', afterheader)); 328 329 // If a "dot dot" release, strip the extra dots for double parsing 330 String[] dot = version.split("\\."); 331 if (dot.length > 2) { 332 version = dot[0]+"."+dot[1]; 333 for (int j = 2; dot.length > j; j++) { 334 version += dot[j]; 335 } 336 } 337 338 // Convert to double for easier version value checking 339 try { 340 nss_version = Double.parseDouble(version); 341 } catch (NumberFormatException e) { 342 System.out.println("Failed to parse lib" + library + 343 " version. Set to 0.0"); 344 e.printStackTrace(); 345 } 346 347 System.out.print("lib" + library + " version = "+version+". "); 348 349 // Check for ECC 350 if (s.indexOf("Basic") > 0) { 351 nss_ecc_status = ECCState.Basic; 352 System.out.println("ECC Basic."); 353 } else if (s.indexOf("Extended") > 0) { 354 nss_ecc_status = ECCState.Extended; 355 System.out.println("ECC Extended."); 356 } else { 357 System.out.println("ECC None."); 358 } 359 360 if (library.compareTo("softokn3") == 0) { 361 softoken3_version = nss_version; 362 } else if (library.compareTo("nss3") == 0) { 363 nss3_version = nss_version; 364 } 365 366 return nss_version; 367 } 368 369 // Used to set the nss_library file to search for libsoftokn3.so 370 public static void useNSS() { 371 nss_library = "nss3"; 372 } 373 374 public static void testNSS(PKCS11Test test) throws Exception { 375 String libdir = getNSSLibDir(); 376 if (libdir == null) { 377 return; 378 } 379 String base = getBase(); 380 381 if (loadNSPR(libdir) == false) { 382 return; 383 } 384 385 String libfile = libdir + System.mapLibraryName(nss_library); 386 387 String customDBdir = System.getProperty("CUSTOM_DB_DIR"); 388 String dbdir = (customDBdir != null) ? 389 customDBdir : 390 base + SEP + "nss" + SEP + "db"; 391 // NSS always wants forward slashes for the config path 392 dbdir = dbdir.replace('\\', '/'); 393 394 String customConfig = System.getProperty("CUSTOM_P11_CONFIG"); 395 String customConfigName = System.getProperty("CUSTOM_P11_CONFIG_NAME", "p11-nss.txt"); 396 String p11config = (customConfig != null) ? 397 customConfig : 398 base + SEP + "nss" + SEP + customConfigName; 399 400 System.setProperty("pkcs11test.nss.lib", libfile); 401 System.setProperty("pkcs11test.nss.db", dbdir); 402 Provider p = getSunPKCS11(p11config); 403 test.premain(p); 404 } 405 406 // Generate a vector of supported elliptic curves of a given provider 407 static Vector<ECParameterSpec> getKnownCurves(Provider p) throws Exception { 408 int index; 409 int begin; 410 int end; 411 String curve; 412 KeyPair kp = null; 413 414 Vector<ECParameterSpec> results = new Vector<ECParameterSpec>(); 415 // Get Curves to test from SunEC. 416 String kcProp = Security.getProvider("SunEC"). 417 getProperty("AlgorithmParameters.EC SupportedCurves"); 418 419 if (kcProp == null) { 420 throw new RuntimeException( 421 "\"AlgorithmParameters.EC SupportedCurves property\" not found"); 422 } 423 424 System.out.println("Finding supported curves using list from SunEC\n"); 425 index = 0; 426 for (;;) { 427 // Each set of curve names is enclosed with brackets. 428 begin = kcProp.indexOf('[', index); 429 end = kcProp.indexOf(']', index); 430 if (begin == -1 || end == -1) { 431 break; 432 } 433 434 /* 435 * Each name is separated by a comma. 436 * Just get the first name in the set. 437 */ 438 index = end + 1; 439 begin++; 440 end = kcProp.indexOf(',', begin); 441 if (end == -1) { 442 // Only one name in the set. 443 end = index -1; 444 } 445 446 curve = kcProp.substring(begin, end); 447 ECParameterSpec e = getECParameterSpec(p, curve); 448 System.out.print("\t "+ curve + ": "); 449 try { 450 KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", p); 451 kpg.initialize(e); 452 kp = kpg.generateKeyPair(); 453 results.add(e); 454 System.out.println("Supported"); 455 } catch (ProviderException ex) { 456 System.out.println("Unsupported: PKCS11: " + 457 ex.getCause().getMessage()); 458 } catch (InvalidAlgorithmParameterException ex) { 459 System.out.println("Unsupported: Key Length: " + 460 ex.getMessage()); 461 } 462 } 463 464 if (results.size() == 0) { 465 throw new RuntimeException("No supported EC curves found"); 466 } 467 468 return results; 469 } 470 471 private static ECParameterSpec getECParameterSpec(Provider p, String name) 472 throws Exception { 473 474 AlgorithmParameters parameters = 475 AlgorithmParameters.getInstance("EC", p); 476 477 parameters.init(new ECGenParameterSpec(name)); 478 479 return parameters.getParameterSpec(ECParameterSpec.class); 480 } 481 482 // Check support for a curve with a provided Vector of EC support 483 boolean checkSupport(Vector<ECParameterSpec> supportedEC, 484 ECParameterSpec curve) { 485 boolean found = false; 486 for (ECParameterSpec ec: supportedEC) { 487 if (ec.equals(curve)) { 488 return true; 489 } 490 } 491 return false; 492 } 493 494 private static final Map<String,String[]> osMap; 495 496 // Location of the NSS libraries on each supported platform 497 static { 498 osMap = new HashMap<String,String[]>(); 499 osMap.put("SunOS-sparc-32", new String[]{"/usr/lib/mps/"}); 500 osMap.put("SunOS-sparcv9-64", new String[]{"/usr/lib/mps/64/"}); 501 osMap.put("SunOS-x86-32", new String[]{"/usr/lib/mps/"}); 502 osMap.put("SunOS-amd64-64", new String[]{"/usr/lib/mps/64/"}); 503 osMap.put("Linux-i386-32", new String[]{ 504 "/usr/lib/i386-linux-gnu/", "/usr/lib32/", "/usr/lib/"}); 505 osMap.put("Linux-amd64-64", new String[]{ 506 "/usr/lib/x86_64-linux-gnu/", "/usr/lib/x86_64-linux-gnu/nss/", 507 "/usr/lib64/"}); 508 osMap.put("Windows-x86-32", new String[]{ 509 PKCS11_BASE + "/nss/lib/windows-i586/".replace('/', SEP)}); 510 osMap.put("Windows-amd64-64", new String[]{ 511 PKCS11_BASE + "/nss/lib/windows-amd64/".replace('/', SEP)}); 512 osMap.put("MacOSX-x86_64-64", new String[]{ 513 PKCS11_BASE + "/nss/lib/macosx-x86_64/"}); 514 } 515 516 private final static char[] hexDigits = "0123456789abcdef".toCharArray(); 517 518 public static String toString(byte[] b) { 519 if (b == null) { 520 return "(null)"; 521 } 522 StringBuffer sb = new StringBuffer(b.length * 3); 523 for (int i = 0; i < b.length; i++) { 524 int k = b[i] & 0xff; 525 if (i != 0) { 526 sb.append(':'); 527 } 528 sb.append(hexDigits[k >>> 4]); 529 sb.append(hexDigits[k & 0xf]); 530 } 531 return sb.toString(); 532 } 533 534 public static byte[] parse(String s) { 535 if (s.equals("(null)")) { 536 return null; 537 } 538 try { 539 int n = s.length(); 540 ByteArrayOutputStream out = new ByteArrayOutputStream(n / 3); 541 StringReader r = new StringReader(s); 542 while (true) { 543 int b1 = nextNibble(r); 544 if (b1 < 0) { 545 break; 546 } 547 int b2 = nextNibble(r); 548 if (b2 < 0) { 549 throw new RuntimeException("Invalid string " + s); 550 } 551 int b = (b1 << 4) | b2; 552 out.write(b); 553 } 554 return out.toByteArray(); 555 } catch (IOException e) { 556 throw new RuntimeException(e); 557 } 558 } 559 560 private static int nextNibble(StringReader r) throws IOException { 561 while (true) { 562 int ch = r.read(); 563 if (ch == -1) { 564 return -1; 565 } else if ((ch >= '0') && (ch <= '9')) { 566 return ch - '0'; 567 } else if ((ch >= 'a') && (ch <= 'f')) { 568 return ch - 'a' + 10; 569 } else if ((ch >= 'A') && (ch <= 'F')) { 570 return ch - 'A' + 10; 571 } 572 } 573 } 574 575 <T> T[] concat(T[] a, T[] b) { 576 if ((b == null) || (b.length == 0)) { 577 return a; 578 } 579 T[] r = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), a.length + b.length); 580 System.arraycopy(a, 0, r, 0, a.length); 581 System.arraycopy(b, 0, r, a.length, b.length); 582 return r; 583 } 584 585 /** 586 * Returns supported algorithms of specified type. 587 */ 588 static List<String> getSupportedAlgorithms(String type, String alg, 589 Provider p) { 590 // prepare a list of supported algorithms 591 List<String> algorithms = new ArrayList<>(); 592 Set<Provider.Service> services = p.getServices(); 593 for (Provider.Service service : services) { 594 if (service.getType().equals(type) 595 && service.getAlgorithm().startsWith(alg)) { 596 algorithms.add(service.getAlgorithm()); 597 } 598 } 599 return algorithms; 600 } 601 602 }