Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   ***** BEGIN LICENSE BLOCK *****
   * Version: EPL 1.0/GPL 2.0/LGPL 2.1
   *
   * The contents of this file are subject to the Eclipse Public
   * License Version 1.0 (the "License"); you may not use this file
   * except in compliance with the License. You may obtain a copy of
   * the License at http://www.eclipse.org/legal/epl-v10.html
   *
  * Software distributed under the License is distributed on an "AS
  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  * implied. See the License for the specific language governing
  * rights and limitations under the License.
  *
  * Copyright (C) 2006, 2007 Ola Bini <ola@ologix.com>
  * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
  * Copyright (C) 2008 Vladimir Sizikov <vsizikov@gmail.com>
  * Copyright (C) 2009 Joseph LaFata <joe@quibb.org>
  * 
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the EPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the EPL, the GPL or the LGPL.
  ***** END LICENSE BLOCK *****/
 package org.jruby.ext.digest;
 
 import java.util.Map;
 
 import org.jruby.Ruby;
 

Author(s):
Ola Bini
 
 @JRubyModule(name="Digest")
 public class RubyDigest {
     private static Provider provider = null;
     private static final Map<StringMessageDigestCLONEABLE_DIGESTS = new HashMap<StringMessageDigest>();
     static {
         // standard digests from JCA specification; if we can retrieve and clone, save them
         for (String name : new String[] {"MD2""MD5""SHA-1""SHA-256""SHA-384""SHA-512"})
         try {
             MessageDigest digest = MessageDigest.getInstance(name);
             digest.clone();
             .put(namedigest);
         } catch (Exception e) {
             e.printStackTrace();
             // ignore; go to next iteration
         }
     }
 
     public static void createDigest(Ruby runtime) {
         // We're not setting the provider or anything, but it seems that BouncyCastle does some internal things in its
         // provider's constructor which require it to be executed in a secure context.
         // Ideally this hack should be removed. See JRUBY-3919 and this BC bug:
         //   http://www.bouncycastle.org/jira/browse/BJA-227
          = (Provider) AccessController.doPrivileged(new PrivilegedAction() {
             public Object run() {
                 try {
                     return Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider").newInstance();
                 } catch(Throwable t) {
                     // provider is not available
                     return null;
                 }
             }
         });
 
         RubyModule mDigest = runtime.defineModule("Digest");
         mDigest.defineAnnotatedMethods(RubyDigest.class);
         RubyModule mDigestInstance = mDigest.defineModuleUnder("Instance");
         mDigestInstance.defineAnnotatedMethods(DigestInstance.class);
         RubyClass cDigestClass = mDigest.defineClassUnder("Class"runtime.getObject(), .);
         cDigestClass.defineAnnotatedMethods(DigestClass.class);
        cDigestClass.includeModule(mDigestInstance);
        RubyClass cDigestBase = mDigest.defineClassUnder("Base"cDigestClass.);
        cDigestBase.defineAnnotatedMethods(DigestBase.class);
    }
    private static MessageDigest createMessageDigest(Ruby runtimeString providerNamethrows NoSuchAlgorithmException {
        MessageDigest cloneable = .get(providerName);
        if (cloneable != null) {
            try {
                return (MessageDigest)cloneable.clone();
            } catch (CloneNotSupportedException cnse) {
                // should never happen, since we tested it in static init
            }
        }
        // fall back on JCA mechanisms for getting a digest
        if( != null) {
            try {
                return MessageDigest.getInstance(providerName);
            } catch(NoSuchAlgorithmException e) {
                // bouncy castle doesn't support algorithm
            }
        }
        // fall back to default JCA providers
        return MessageDigest.getInstance(providerName);
    }
    private final static byte[] digits = {
        '0''1''2''3''4''5',
        '6''7''8''9''a''b',
        'c''d''e''f''g''h',
        'i''j''k''l''m''n',
        'o''p''q''r''s''t',
        'u''v''w''x''y''z'
    };
    private static ByteList toHex(byte[] val) {
        ByteList byteList = new ByteList(val.length * 2);
        for (int i = 0, j = val.lengthi < ji++) {
            int b = val[i] & 0xFF;
            byteList.append([b >> 4]);
            byteList.append([b & 0xF]);
        }
        return byteList;
    }
    private static IRubyObject toHexString(Ruby runtimebyte[] val) {
        return RubyString.newStringNoCopy(runtime, ByteList.plain(toHex(val)));
    }
    @JRubyMethod(name = "hexencode", required = 1, meta = true)
    public static IRubyObject s_hexencode(IRubyObject recvIRubyObject arg) {
        return toHexString(recv.getRuntime(), arg.convertToString().getBytes());
    }
    private static class Metadata {
        private final String name;
        private final int blockLength;
        Metadata(String nameint blockLength) {
            this. = name;
            this. = blockLength;
        }
        String getName() {
            return ;
        }
        int getBlockLength() {
            return ;
        }
    }
    @JRubyClass(name="Digest::MD5", parent="Digest::Base")
    public static class MD5 {}
    @JRubyClass(name="Digest::RMD160", parent="Digest::Base")
    public static class RMD160 {}
    @JRubyClass(name="Digest::SHA1", parent="Digest::Base")
    public static class SHA1 {}
    @JRubyClass(name="Digest::SHA256", parent="Digest::Base")
    public static class SHA256 {}
    @JRubyClass(name="Digest::SHA384", parent="Digest::Base")
    public static class SHA384 {}
    @JRubyClass(name="Digest::SHA512", parent="Digest::Base")
    public static class SHA512 {}
    public static void createDigestMD5(Ruby runtime) {
        runtime.getLoadService().require("digest");
        RubyModule mDigest = runtime.getModule("Digest");
        RubyClass cDigestBase = mDigest.getClass("Base");
        RubyClass cDigest_MD5 = mDigest.defineClassUnder("MD5",cDigestBase,cDigestBase.getAllocator());
        cDigest_MD5.setInternalVariable("metadata"new Metadata("MD5", 64));
    }
    public static void createDigestRMD160(Ruby runtime) {
        runtime.getLoadService().require("digest");
        if( == null) {
            throw runtime.newLoadError("RMD160 not supported without BouncyCastle");
        }
        RubyModule mDigest = runtime.getModule("Digest");
        RubyClass cDigestBase = mDigest.getClass("Base");
        RubyClass cDigest_RMD160 = mDigest.defineClassUnder("RMD160",cDigestBase,cDigestBase.getAllocator());
        cDigest_RMD160.setInternalVariable("metadata"new Metadata("RIPEMD160", 64));
    }
    public static void createDigestSHA1(Ruby runtime) {
        runtime.getLoadService().require("digest");
        RubyModule mDigest = runtime.getModule("Digest");
        RubyClass cDigestBase = mDigest.getClass("Base");
        RubyClass cDigest_SHA1 = mDigest.defineClassUnder("SHA1",cDigestBase,cDigestBase.getAllocator());
        cDigest_SHA1.setInternalVariable("metadata"new Metadata("SHA1", 64));
    }
    public static void createDigestSHA2(Ruby runtime) {
        runtime.getLoadService().require("digest");
        try {
            createMessageDigest(runtime"SHA-256");
        } catch(NoSuchAlgorithmException e) {
            throw runtime.newLoadError("SHA2 not supported");
        }
        RubyModule mDigest = runtime.getModule("Digest");
        RubyClass cDigestBase = mDigest.getClass("Base");
        RubyClass cDigest_SHA2_256 = mDigest.defineClassUnder("SHA256",cDigestBase,cDigestBase.getAllocator());
        Metadata sha256Metadata = new Metadata("SHA-256", 64);
        cDigest_SHA2_256.setInternalVariable("metadata"sha256Metadata);
        RubyClass cDigest_SHA2_384 = mDigest.defineClassUnder("SHA384",cDigestBase,cDigestBase.getAllocator());
        cDigest_SHA2_384.setInternalVariable("metadata"new Metadata("SHA-384", 128));
        RubyClass cDigest_SHA2_512 = mDigest.defineClassUnder("SHA512",cDigestBase,cDigestBase.getAllocator());
        cDigest_SHA2_512.setInternalVariable("metadata"new Metadata("SHA-512", 128));
    }
    @JRubyModule(name = "Digest::Instance")
    public static class DigestInstance {
        private static IRubyObject throwUnimplError(IRubyObject selfString name) {
            throw self.getRuntime().newRuntimeError(String.format("%s does not implement %s()"self.getMetaClass().getRealClass().getName(), name));
        }
        /* instance methods that should be overridden */
        @JRubyMethod(name = {"update""<<"}, required = 1)
        public static IRubyObject update(ThreadContext ctxIRubyObject selfIRubyObject arg) {
            return throwUnimplError(self"update");
        }
        @JRubyMethod()
        public static IRubyObject finish(ThreadContext ctxIRubyObject self) {
            return throwUnimplError(self"finish");
        }
        @JRubyMethod()
        public static IRubyObject reset(ThreadContext ctxIRubyObject self) {
            return throwUnimplError(self"reset");
        }
        @JRubyMethod()
        public static IRubyObject digest_length(ThreadContext ctxIRubyObject self) {
            return digest(ctxselfnull).convertToString().length();
        }
        @JRubyMethod()
        public static IRubyObject block_length(ThreadContext ctxIRubyObject self) {
            return throwUnimplError(self"block_length");
        }
        /* instance methods that may be overridden */
        @JRubyMethod(name = "==", required = 1)
        public static IRubyObject op_equal(ThreadContext ctxIRubyObject selfIRubyObject oth) {
            RubyString str1str2;
            RubyModule instance = (RubyModule)self.getRuntime().getModule("Digest").getConstantAt("Instance");
            if (oth.getMetaClass().getRealClass().hasModuleInHierarchy(instance)) {
                str1 = digest(ctxselfnull).convertToString();
                str2 = digest(ctxothnull).convertToString();
            } else {
                str1 = to_s(ctxself).convertToString();
                str2 = oth.convertToString();
            }
            boolean ret = str1.length().eql(str2.length()) && (str1.eql(str2));
            return ret ? self.getRuntime().getTrue() : self.getRuntime().getFalse();
        }
        @JRubyMethod()
        public static IRubyObject inspect(ThreadContext ctxIRubyObject self) {
            return RubyString.newStringNoCopy(self.getRuntime(), ByteList.plain("#<" + self.getMetaClass().getRealClass().getName() + ": " + hexdigest(ctxselfnull) + ">"));
        }
        /* instance methods that need not usually be overridden */
        @JRubyMethod(name = "new")
        public static IRubyObject newObject(ThreadContext ctxIRubyObject self) {
            return self.rbClone().callMethod(ctx"reset");
        }
        @JRubyMethod(optional = 1)
        public static IRubyObject digest(ThreadContext ctxIRubyObject selfIRubyObject[] args) {
            IRubyObject value = null;
            if (args != null && args.length > 0) {
                self.callMethod(ctx"reset");
                self.callMethod(ctx"update"args[0]);
                value = self.callMethod(ctx"finish");
                self.callMethod(ctx"reset");
            } else {
                IRubyObject clone = self.rbClone();
                value = clone.callMethod(ctx"finish");
                clone.callMethod(ctx"reset");
            }
            return value;
        }
        @JRubyMethod(name = "digest!")
        public static IRubyObject digest_bang(ThreadContext ctxIRubyObject self) {
            IRubyObject value = self.callMethod(ctx"finish");
            self.callMethod(ctx"reset");
            return value;
        }
        @JRubyMethod(optional = 1)
        public static IRubyObject hexdigest(ThreadContext ctxIRubyObject selfIRubyObject[] args) {
            return toHexString(ctx.runtimedigest(ctxselfargs).convertToString().getBytes());
        }
        @JRubyMethod(name = "hexdigest!")
        public static IRubyObject hexdigest_bang(ThreadContext ctxIRubyObject self) {
            return toHexString(ctx.runtimedigest_bang(ctxself).convertToString().getBytes());
        }
        @JRubyMethod()
        public static IRubyObject to_s(ThreadContext ctxIRubyObject self) {
            return self.callMethod(ctx"hexdigest");
        }
        @JRubyMethod(name = {"length""size"})
        public static IRubyObject length(ThreadContext ctxIRubyObject self) {
            return self.callMethod(ctx"digest_length");
        }
    }
    @JRubyClass(name="Digest::Class")
    public static class DigestClass extends RubyObject {
        protected static final ObjectAllocator DIGEST_CLASS_ALLOCATOR = new ObjectAllocator() {
            public IRubyObject allocate(Ruby runtimeRubyClass klass) {
                return new DigestClass(runtimeklass);
            }
        };
        public DigestClass(Ruby runtimeRubyClass type) {
            super(runtimetype);
        }
        
        @JRubyMethod(name = "digest", required = 1, rest = true, meta = true)
        public static IRubyObject s_digest(ThreadContext ctxIRubyObject recvIRubyObject[] argsBlock unusedBlock) {
            Ruby runtime = recv.getRuntime();
            if (args.length < 1) {
                throw runtime.newArgumentError("no data given");
            }
            RubyString str = args[0].convertToString();
            IRubyObject[] newArgs = new IRubyObject[args.length - 1];
            System.arraycopy(args, 1, newArgs, 0, args.length - 1);
            IRubyObject obj = ((RubyClass)recv).newInstance(ctxnewArgs.);
            return obj.callMethod(ctx"digest"str);
        }
        @JRubyMethod(name = "hexdigest", required = 1, optional = 1, meta = true)
        public static IRubyObject s_hexdigest(ThreadContext ctxIRubyObject recvIRubyObject[] argsBlock unusedBlock) {
            Ruby runtime = recv.getRuntime();
            byte[] digest = recv.callMethod(ctx"digest"args.).convertToString().getBytes();
            return RubyDigest.toHexString(runtimedigest);
        }
    }
    @JRubyClass(name="Digest::Base")
    public static class DigestBase extends RubyObject {
        protected static final ObjectAllocator DIGEST_BASE_ALLOCATOR = new ObjectAllocator() {
            public IRubyObject allocate(Ruby runtimeRubyClass klass) {
                return new DigestBase(runtimeklass);
            }
        };
        private MessageDigest algo;
        private int blockLength = 0;
        public DigestBase(Ruby runtimeRubyClass type) {
            super(runtime,type);
            if(type == runtime.getModule("Digest").getClass("Base")) {
                throw runtime.newNotImplementedError("Digest::Base is an abstract class");
            }
            Metadata metadata = getMetadata(type);
            if(metadata == null) {
                throw runtime.newNotImplementedError("the " + type + "() function is unimplemented on this machine");
            }
            
            try {
                setAlgorithm(metadata);
            } catch(NoSuchAlgorithmException e) {
                throw runtime.newNotImplementedError("the " + type + "() function is unimplemented on this machine");
            }
        }
        // if subclass extends particular digest we need to walk to find it...we should rearchitect digest to avoid walking type systems
        private Metadata getMetadata(RubyModule type) {
            for (RubyModule current = typecurrent != nullcurrent = current.getSuperClass()) {
                Metadata metadata = (Metadatacurrent.getInternalVariable("metadata");
                if (metadata != nullreturn metadata;
            }
            return null;
        }
        @JRubyMethod(required = 1)
        @Override
        public IRubyObject initialize_copy(IRubyObject obj) {
            if(this == obj) {
                return this;
            }
            ((RubyObject)obj).checkFrozen();
            String name = ((DigestBase)obj)..getAlgorithm();
            try {
                 = (MessageDigest)((DigestBase)obj)..clone();
            } catch(CloneNotSupportedException e) {
                throw getRuntime().newTypeError("Could not initialize copy of digest (" + name + ")");
            }
            return this;
        }
        @JRubyMethod(name = {"update""<<"}, required = 1)
        public IRubyObject update(IRubyObject obj) {
            ByteList bytes = obj.convertToString().getByteList();
            .update(bytes.getUnsafeBytes(), bytes.getBegin(), bytes.getRealSize());
            return this;
        }
        @JRubyMethod()
        public IRubyObject finish() {
            IRubyObject digest = RubyString.newStringNoCopy(getRuntime(), .digest());
            .reset();
            return digest;
        }
        
        @JRubyMethod()
        public IRubyObject digest_length() {
            return RubyFixnum.newFixnum(getRuntime(), .getDigestLength());
        }
        @JRubyMethod()
        public IRubyObject block_length() {
            if ( == 0) {
                throw getRuntime().newRuntimeError(
                        this.getMetaClass() + " doesn't implement block_length()");
            }
            return RubyFixnum.newFixnum(getRuntime(), );
        }
        @JRubyMethod()
        public IRubyObject reset() {
            .reset();
            return this;
        }
        private void setAlgorithm(Metadata metadatathrows NoSuchAlgorithmException {
           this. = createMessageDigest(getRuntime(), metadata.getName());
           this. = metadata.getBlockLength();
        }
    }
}// RubyDigest