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