blob: 0c9c0c1171eaaa422bbd451911259f28be0493cd [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package io.v.v23.security;
import com.google.common.collect.ImmutableList;
import io.v.v23.context.VContext;
import io.v.v23.security.access.Permissions;
import io.v.v23.security.access.PermissionsAuthorizer;
import io.v.v23.verror.VException;
import io.v.v23.vom.VomUtil;
import org.joda.time.DateTime;
import java.lang.reflect.Type;
import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.util.Arrays;
import java.util.List;
/**
* Various functions used for creating and managing Vanadium security primitives.
*/
public class VSecurity {
private static native String[] nativeGetRemoteBlessingNames(VContext context, Call call)
throws VException;
private static native String[] nativeGetLocalBlessingNames(VContext context, Call call)
throws VException;
private static native String[] nativeGetBlessingNames(VPrincipal principal, Blessings blessings)
throws VException;
private static native String[] nativeGetSigningBlessingNames(VContext context,
VPrincipal principal, Blessings blessings) throws VException;
private static native void nativeAddToRoots(VPrincipal principal, Blessings blessings)
throws VException;
/**
* Creates a new instance of {@link VSigner} using the provided public/private key pair.
* <p>
* The returned {@link VSigner} respests the location of the private key: if the key
* is stored in a safe place (e.g., hardware token), it will remain secure.
*
* @param privKey private key
* @param pubKey corresponding public key
* @return new instance of {@link VSigner} that uses the provided key pair to
* do the signing
*/
public static VSigner newSigner(PrivateKey privKey, ECPublicKey pubKey) {
return new ECDSASigner(privKey, pubKey);
}
/**
* Mints a new private key and creates a {@link VSigner} based on this key. The key is stored
* in the clear in memory of the running process.
*/
public static VSigner newInMemorySigner() throws VException {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(256);
KeyPair keyPair = keyGen.generateKeyPair();
PrivateKey privKey = keyPair.getPrivate();
ECPublicKey pubKey = (ECPublicKey) keyPair.getPublic();
return new ECDSASigner(privKey, pubKey);
} catch (NoSuchAlgorithmException e) {
throw new VException("Couldn't mint private key: " + e.getMessage());
}
}
/**
* Mints a new private key and generates a principal based on this key, storing its
* blessing roots and blessing store in memory.
*
* @return in-memory principal using the newly minted private key
* @throws VException if the principal couldn't be created
*/
public static VPrincipal newPrincipal() throws VException {
return VPrincipalImpl.create();
}
/**
* Creates a principal using the provided signer, storing its blessing roots and
* blessing store in memory.
*
* @param signer signer to be used by the new principal
* @return in-memory principal using the provided signer
* @throws VException if the principal couldn't be created
*/
public static VPrincipal newPrincipal(VSigner signer) throws VException {
return VPrincipalImpl.create(signer);
}
/**
* Creates a principal using the provided signer, blessing store, and blessing roots. If the
* provided store is {@code null}, the principal will use a store whose every opration will
* fail. If the provided roots are {@code null}, the principal will not trust any public keys
* and all subsequent {@link VSecurity#addToRoots} operations with that principal will fail.
*
* @param signer signer to be used by the principal
* @param store blessing store to be used by the principal
* @param roots blessing roots to be used by the principal
* @return newly created principal
*/
public static VPrincipal newPrincipal(VSigner signer, BlessingStore store, BlessingRoots roots)
throws VException {
return VPrincipalImpl.create(signer, store, roots);
}
/**
* Reads the entire state for a principal (i.e., private key, blessing roots, blessing store)
* from the provided directory {@code dir} and commits all state changes to the same directory.
* <p>
* If the directory does not contain state, a new private key is minted and all state of the
* principal is committed to {@code dir}. If the directory does not exist, it is created.
*
* @param passphrase passphrase used to encrypt the private key; if empty, no encryption
* is done
* @param dir directory where the state for a principal is to be persisted
* @return principal whose state is persisted in the provided directory
* @throws VException if the principal couldn't be created
*/
public static VPrincipal newPersistentPrincipal(String passphrase, String dir)
throws VException {
return VPrincipalImpl.createPersistent(passphrase, dir);
}
/**
* Creates a new principal using the provided signer and a partial state (i.e., blessing roots,
* blessing store) that is read from the provided directory {@code dir}. Changes to the
* partial state are persisted and commited to the same directory. The provided signer isn't
* persisted: the caller is expected to persist it separately.
* <p>
* If the directory does not contain any partial state, a new partial state instances are
* created and subsequently commited to {@code dir}. If the directory does not exist, it
* is created.
*
* @param signer signer to be used by the new principal
* @param dir directory where the partial state for a principal is to be persisted
* @return principal whose partial state is persisted in the provided directory
* @throws VException if the principal couldn't be created
*/
public static VPrincipal newPersistentPrincipal(VSigner signer, String dir)
throws VException {
return VPrincipalImpl.createPersistent(signer, dir);
}
/**
* Returns a {@link Blessings} object that carries the union of the provided blessings.
* All provided blessings must have the same public key. Returns {@code null} if invoked
* without arguments.
*
* @param blessings blessings that will be merged
* @return the union of the provided blessings
* @throws VException if there was an error creating an union
*/
public static Blessings unionOfBlessings(Blessings... blessings) throws VException {
return Blessings.createUnion(blessings);
}
/**
* Returns a validated set of blessing names presented by the remote end of a call.
* <p>
* These returned blessings (strings) are guaranteed to:
* <p><ol>
* <li>Satisfy all the caveats given the call.</li>
* <li>Be rooted in {@code call.localPrincipal().roots()}.</li>
* </ol>
* <p>
* Caveats are considered satisfied in the given call if the {@link CaveatValidator}
* implementation can be found in the address space of the caller and
* {@link CaveatValidator#validate validate} doesn't throw an exception.
*
* @param context vanadium context
* @param call security-related state associated with the call
* @return validated set of blessing names presented by the remote end of a call
* (possibly empty, but never {@code null})
*/
public static String[] getRemoteBlessingNames(VContext context, Call call) {
try {
return nativeGetRemoteBlessingNames(context, call);
} catch (VException e) {
throw new RuntimeException("Couldn't get blessings for call", e);
}
}
/**
* Returns the set of human-readable blessing names presented by the local end of the call.
* <p>
* This is merely a convenience over
* {@code getBlessingNames(call.localPrincipal(), call.localBlessings())}
*
* @param context vanadium context
* @param call security-related state associated with the call
* @return set of blessing names presented by the local end of the call
* (possibly empty, but never {@code null})
*/
public static String[] getLocalBlessingNames(VContext context, Call call) {
try {
return nativeGetLocalBlessingNames(context, call);
} catch (VException e) {
throw new RuntimeException("Couldn't get blessings for call", e);
}
}
/**
* Returns the set of human-readable blessing names encapsulated in blessings.
* <p>
* The returned names are guaranteed to be rooted in {@link VPrincipal#roots}
* though caveats may not be validated.
* <p>
* The blessings must be bound to the provided principal. There is
* intentionally not API to obtain blessing names bound to other principals
* by ignoring caveats. This is to prevent accidental authorization based
* on potentially invalid blessing names (since caveats are not validated).
*
* @param principal principal to which the provided blessings are bound
* @param blessings the blessings whose names are to be extracted
* @return set of blessing names bound to principal, encapsulated
* in blessings (possibly empty, but never {@code null})
*/
public static String[] getBlessingNames(VPrincipal principal, Blessings blessings) {
try {
return nativeGetBlessingNames(principal, blessings);
} catch (VException e) {
throw new RuntimeException("Couldn't get blessing names", e);
}
}
/**
* Returns the set of validated human-readable signing blessing names encapsulated in the
* provided blessings object, as determined by the provided principal. Only names that are
* available for signing are returned by this method, even though the given blessings object
* might have more blessing names that are valid at the time. (The name on a certificate chain
* is valid if all the caveats on the chain are signing caveats, and the chain's validity can
* be checked by the given principal).
* <p>
* The blessing names are guaranteed to be rooted in {@code call.localPrincipal().roots()}.
* <p>
* This method does not validate caveats on the blessing names.
*
* @param context vanadium context
* @param principal vanadium principal
* @param blessings vanadium blessings
* @return validated set of human-readable blessing names encapsulated in the
* provided signing blessings object, as determined by the provided
* {@link VPrincipal} (possibly empty, but never {@code null})
*/
public static String[] getSigningBlessingNames(VContext context, VPrincipal principal,
Blessings blessings) {
try {
return nativeGetSigningBlessingNames(context, principal, blessings);
} catch (VException e) {
throw new RuntimeException("Couldn't get signing blessing names", e);
}
}
/**
* Returns a caveat that requires validation by the validator corresponding to the
* given descriptor and uses the provided parameter.
*
* @param desc caveat descriptor
* @param param caveat parameter used by the associated validator
* @return caveat that requires validation by the validator corresponding to the
* given descriptor and uses the provided parameter
* @throws VException if the caveat couldn't be created
*/
public static Caveat newCaveat(CaveatDescriptor desc, Object param) throws VException {
byte[] paramVOM = VomUtil.encode(param, desc.getParamType().getTypeObject());
return new Caveat(desc.getId(), paramVOM);
}
/**
* Returns a caveat that validates iff the current time is before the provided time.
*
* @param time time before which the caveat validates
* @return caveat that validates if the current time is before the provided time
* @throws VException if the caveat couldn't be created
*/
public static Caveat newExpiryCaveat(DateTime time) throws VException {
return newCaveat(Constants.EXPIRY_CAVEAT, time);
}
/**
* Returns a caveat that validates iff the method being invoked by the peer is listed in an
* argument to this function.
*
* @param method name of the method for which this caveat should validate
* @param additionalMethods additional method names for which this caveat should validate
* @return caveat that validates iff the method being invoked by the peer is
* one of the provided methods
* @throws VException if the caveat couldn't be created
*/
public static Caveat newMethodCaveat(String method, String... additionalMethods)
throws VException {
List<String> methods = ImmutableList.<String>builder()
.add(method)
.add(additionalMethods)
.build();
return newCaveat(Constants.METHOD_CAVEAT, methods);
}
/**
* Returns a caveat that never fails to validate. This is useful only for providing
* unconstrained blessings to another principal.
*
* @return a caveat that never fails to validate
*/
public static Caveat newUnconstrainedUseCaveat() throws VException {
return newCaveat(Constants.CONST_CAVEAT, true);
}
/**
* Returns a new security call that uses the provided params.
*
* @param params call params
* @return new security call that uses the provided params
*/
public static Call newCall(CallParams params) {
return new CallParamsImpl(params);
}
/**
* Returns an authorizer that subscribes to an authorization policy where access is granted if
* the remote end presents blessings included in the Access Control Lists (ACLs) associated with
* the set of relevant tags.
* <p>
* See {@link io.v.v23.security.access.PermissionsAuthorizer} for a more detailed description
* of this authorizer.
*
* @param acls ACLs containing authorization rules
* @param type type of the method tags this authorizer checks
* @return a newly created authorizer
* @throws VException if the authorizer couldn't be created
*/
public static Authorizer newPermissionsAuthorizer(Permissions acls, Type type)
throws VException {
return PermissionsAuthorizer.create(acls, type);
}
/**
* Returns an authorizer that allows all requests.
*/
public static Authorizer newAllowEveryoneAuthorizer() {
return new Authorizer() {
@Override
public void authorize(VContext ctx, Call call) throws VException {
// do nothing
}
};
}
/**
* Verifies the provides signature of the given message, using the supplied public key.
*
* @param sig signature in the Vanadium format
* @param key public key
* @param message message whose signature is verified
* @throws VException iff the signature doesn't verify
*/
public static void verifySignature(VSignature sig, ECPublicKey key, byte[] message)
throws VException {
String vHashAlgorithm = sig.getHash().getValue();
String verifyAlgorithm = CryptoUtil.javaSigningAlgorithm(vHashAlgorithm);
try {
message = CryptoUtil.messageDigest(vHashAlgorithm, message, sig.getPurpose(), key);
byte[] jSig = CryptoUtil.javaSignature(sig);
java.security.Signature verifier = java.security.Signature.getInstance(verifyAlgorithm);
verifier.initVerify(key);
verifier.update(message);
if (!verifier.verify(jSig)) {
throw new VException("Signature doesn't verify.");
}
} catch (NoSuchAlgorithmException e) {
throw new VException("Verifying algorithm " + verifyAlgorithm +
" not supported by the runtime: " + e.getMessage());
} catch (InvalidKeyException e) {
throw new VException("Invalid private key: " + e.getMessage());
} catch (SignatureException e) {
throw new VException(
"Invalid signing data [ " + Arrays.toString(message) + " ]: " + e.getMessage());
}
}
/**
* Marks the root principals of all blessing chains represented by {@code blessings} as an
* authority on blessing chains beginning at that root.
* <p>
* For example, if {@code blessings} represents the blessing chains
* {@code ["alice/friend/spouse", "charlie/family/daughter"]} then {@code addToRoots(blessing)}
* will mark the root public key of the chain {@code "alice/friend/bob"} as the as authority on
* all blessings that match the pattern {@code "alice/..."}, and root public key of the chain
* {@code "charlie/family/daughter"} as an authority on all blessings that match the pattern
* {@code "charlie/..."}.
*
* @param principal the principal whose {@link BlessingRoots} object should be edited
* @param blessings blessings to be used as authorities on blessing chains beginning at
* those roots
* @throws VException if there was an error assigning the said authorities
*/
public static void addToRoots(VPrincipal principal, Blessings blessings) throws VException {
nativeAddToRoots(principal, blessings);
}
private VSecurity() {}
}