1 /*
   2  * Copyright (c) 2011, 2013, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.oracle.ipack.signature;
  27 
  28 import java.io.DataOutput;
  29 import java.io.IOException;
  30 import java.io.UnsupportedEncodingException;
  31 
  32 public abstract class Requirement {
  33     private static final Requirement APPLE_GENERIC_ANCHOR =
  34             new Anchor(0xf);
  35     private static final Match MATCH_EXISTS =
  36             new Match(0);
  37 
  38     public static Requirement createDefault(
  39             final String appIdent,
  40             final String subjectName) {
  41         final byte[] oid = {
  42             (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7,
  43             (byte) 0x63, (byte) 0x64, (byte) 0x06, (byte) 0x02, (byte) 0x01
  44         };
  45 
  46         return and(ident(appIdent),
  47                    and(appleGenericAnchor(),
  48                        and(certField(0, "subject.CN",
  49                                      matchEqual(subjectName)),
  50                            certGeneric(1, oid, matchExists()))));
  51     }
  52 
  53     public static Requirement and(final Requirement left,
  54                                   final Requirement right) {
  55         return new BinaryOp(6, left, right);
  56     }
  57 
  58     public static Requirement ident(final String value) {
  59         return new Ident(value);
  60     }
  61 
  62     public static Requirement appleGenericAnchor() {
  63         return APPLE_GENERIC_ANCHOR;
  64     }
  65 
  66     public static Requirement certField(final int certIndex,
  67                                         final String fielName,
  68                                         final Match match) {
  69         return new CertField(certIndex, fielName, match);
  70     }
  71 
  72     public static Requirement certGeneric(final int certIndex,
  73                                           final byte[] oid,
  74                                           final Match match) {
  75         return new CertGeneric(certIndex, (byte[]) oid.clone(), match);
  76     }
  77 
  78     public static Match matchExists() {
  79         return MATCH_EXISTS;
  80     }
  81 
  82     public static Match matchEqual(final String value) {
  83         return new MatchEqual(value);
  84     }
  85 
  86     private Requirement() {
  87     }
  88 
  89     public final int getSize() {
  90         return 4 + getPayloadSize();
  91     }
  92 
  93     public final void write(final DataOutput dataOutput) throws IOException {
  94         dataOutput.writeInt(getIdent());
  95         writePayload(dataOutput);
  96     }
  97 
  98     protected abstract int getIdent();
  99 
 100     protected abstract int getPayloadSize();
 101 
 102     protected abstract void writePayload(DataOutput dataOutput)
 103             throws IOException;
 104 
 105     private static final class BinaryOp extends Requirement {
 106         private final int ident;
 107         private final Requirement left;
 108         private final Requirement right;
 109 
 110         public BinaryOp(final int ident,
 111                         final Requirement left,
 112                         final Requirement right) {
 113             this.ident = ident;
 114             this.left = left;
 115             this.right = right;
 116         }
 117 
 118         @Override
 119         protected int getIdent() {
 120             return ident;
 121         }
 122 
 123         @Override
 124         protected int getPayloadSize() {
 125             return left.getSize() + right.getSize();
 126         }
 127 
 128         @Override
 129         protected void writePayload(final DataOutput dataOutput)
 130                 throws IOException {
 131             left.write(dataOutput);
 132             right.write(dataOutput);
 133         }
 134     }
 135 
 136     private static final class Ident extends Requirement {
 137         private final StringData value;
 138 
 139         public Ident(final String value) {
 140             this.value = new StringData(value);
 141         }
 142 
 143         @Override
 144         protected int getIdent() {
 145             return 2;
 146         }
 147 
 148         @Override
 149         protected int getPayloadSize() {
 150             return value.getSize();
 151         }
 152 
 153         @Override
 154         protected void writePayload(final DataOutput dataOutput)
 155                 throws IOException {
 156             value.write(dataOutput);
 157         }
 158     }
 159 
 160     private static final class Anchor extends Requirement {
 161         private final int ident;
 162 
 163         public Anchor(final int ident) {
 164             this.ident = ident;
 165         }
 166 
 167         @Override
 168         protected int getIdent() {
 169             return ident;
 170         }
 171 
 172         @Override
 173         protected int getPayloadSize() {
 174             return 0;
 175         }
 176 
 177         @Override
 178         protected void writePayload(final DataOutput dataOutput)
 179                 throws IOException {
 180         }
 181     }
 182 
 183     private static final class CertField extends Requirement {
 184         private final int certIndex;
 185         private final StringData fieldName;
 186         private final Match match;
 187 
 188         public CertField(final int certIndex,
 189                          final String fieldName,
 190                          final Match match) {
 191             this.certIndex = certIndex;
 192             this.fieldName = new StringData(fieldName);
 193             this.match = match;
 194         }
 195 
 196         @Override
 197         protected int getIdent() {
 198             return 0xb;
 199         }
 200 
 201         @Override
 202         protected int getPayloadSize() {
 203             return 4 + fieldName.getSize() + match.getSize();
 204         }
 205 
 206         @Override
 207         protected void writePayload(final DataOutput dataOutput)
 208                 throws IOException {
 209             dataOutput.writeInt(certIndex);
 210             fieldName.write(dataOutput);
 211             match.write(dataOutput);
 212         }
 213     }
 214 
 215     private static final class CertGeneric extends Requirement {
 216         private final int certIndex;
 217         private final BinaryData oid;
 218         private final Match match;
 219 
 220         public CertGeneric(final int certIndex,
 221                            final byte[] oid,
 222                            final Match match) {
 223             this.certIndex = certIndex;
 224             this.oid = new BinaryData(oid);
 225             this.match = match;
 226         }
 227 
 228         @Override
 229         protected int getIdent() {
 230             return 0xe;
 231         }
 232 
 233         @Override
 234         protected int getPayloadSize() {
 235             return 4 + oid.getSize() + match.getSize();
 236         }
 237 
 238         @Override
 239         protected void writePayload(final DataOutput dataOutput)
 240                 throws IOException {
 241             dataOutput.writeInt(certIndex);
 242             oid.write(dataOutput);
 243             match.write(dataOutput);
 244         }
 245     }
 246 
 247     private static class BinaryData {
 248         private byte[] bytes;
 249 
 250         public BinaryData(final byte[] bytes) {
 251             this.bytes = bytes;
 252         }
 253 
 254         public int getSize() {
 255             final int rawSize = 4 + bytes.length;
 256             return (rawSize + 3) & ~3;
 257         }
 258 
 259         public void write(final DataOutput dataOutput) throws IOException {
 260             dataOutput.writeInt(bytes.length);
 261             dataOutput.write(bytes);
 262             final int padding = getSize() - 4 - bytes.length;
 263             for (int i = 0; i < padding; ++i) {
 264                 dataOutput.write(0);
 265             }
 266         }
 267     }
 268 
 269     private static final class StringData extends BinaryData {
 270         public StringData(final String value) {
 271             super(getBytes(value));
 272         }
 273 
 274         private static byte[] getBytes(final String value) {
 275             try {
 276                 return value.getBytes("UTF-8");
 277             } catch (final UnsupportedEncodingException e) {
 278                 throw new IllegalStateException(e);
 279             }
 280         }
 281     }
 282 
 283     public static class Match {
 284         private final int ident;
 285 
 286         private Match(final int ident) {
 287             this.ident = ident;
 288         }
 289 
 290         public final int getSize() {
 291             return 4 + getArgSize();
 292         }
 293 
 294         public final void write(final DataOutput dataOutput)
 295                 throws IOException {
 296             dataOutput.writeInt(ident);
 297             writeArg(dataOutput);
 298         }
 299 
 300         protected int getArgSize() {
 301             return 0;
 302         }
 303 
 304         protected void writeArg(final DataOutput dataOutput)
 305                 throws IOException {
 306         }
 307     }
 308 
 309     private static final class MatchEqual extends Match {
 310         private final StringData arg;
 311 
 312         public MatchEqual(final String arg) {
 313             super(1);
 314             this.arg = new StringData(arg);
 315         }
 316 
 317         @Override
 318         protected int getArgSize() {
 319             return arg.getSize();
 320         }
 321 
 322         @Override
 323         protected void writeArg(final DataOutput dataOutput)
 324                 throws IOException {
 325             arg.write(dataOutput);
 326         }
 327     }
 328 }