Package com.aelitis.azureus.core.subs.impl

Source Code of com.aelitis.azureus.core.subs.impl.SubscriptionBodyImpl

/*
* Created on Jul 15, 2008
* Created by Paul Gardner
*
* Copyright 2008 Vuze, Inc.  All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/


package com.aelitis.azureus.core.subs.impl;

import java.io.File;
import java.io.IOException;
import java.security.Signature;
import java.util.*;

import org.gudy.azureus2.core3.util.BEncoder;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.SHA1Simple;

import com.aelitis.azureus.core.security.CryptoECCUtils;
import com.aelitis.azureus.core.subs.Subscription;
import com.aelitis.azureus.core.subs.SubscriptionException;
import com.aelitis.azureus.core.vuzefile.VuzeFile;
import com.aelitis.azureus.core.vuzefile.VuzeFileComponent;
import com.aelitis.azureus.core.vuzefile.VuzeFileHandler;

public class
SubscriptionBodyImpl
{
  private static final int SIMPLE_ID_LENGTH        = 10;

  protected static byte[]
  encode(
    byte[]    hash,
    int      version,
    int      size )
  {
    int hash_len = hash.length;
   
    byte[]  result = new byte[ hash_len + 4 + 4 ];
   
    System.arraycopy( hash, 0, result, 0, hash_len );
    System.arraycopy( SubscriptionImpl.intToBytes(version), 0, result, hash_len, 4 );
    System.arraycopy( SubscriptionImpl.intToBytes(size), 0, result, hash_len+4, 4 );

    return( result );
  }
 
  protected static byte[]
  sign(
    byte[]    private_key,
    byte[]    hash,
    int      version,
    int      size )
 
    throws Exception
  {
    Signature signature = CryptoECCUtils.getSignature( CryptoECCUtils.rawdataToPrivkey( private_key ));
   
    // key for signature is hash + version + size so we have some
    // control over auto-update process and prevent people from injecting
    // potentially huge bogus updates
 
    signature.update( encode( hash, version, size ));

    return( signature.sign());
  }
 
  protected static boolean
  verify(
    byte[]    public_key,
    byte[]    hash,
    int      version,
    int      size,
    byte[]    sig )
  {
    try{
      Signature signature = CryptoECCUtils.getSignature( CryptoECCUtils.rawdataToPubkey( public_key ));
 
      signature.update( encode( hash, version, size ));
 
      return( signature.verify( sig ));
     
    }catch( Throwable e ){
     
      Debug.out( e );
     
      return( false );
    }
  }
 
  protected static byte[]
  deriveShortID(
    byte[]    public_key,
    Map      singleton_details )
  {
    if ( singleton_details != null ){
     
      return( deriveSingletonShortID( singleton_details ));
             
    }else{
   
      byte[]  hash = new SHA1Simple().calculateHash( public_key );

      byte[]  short_id = new byte[SIMPLE_ID_LENGTH];

      System.arraycopy( hash, 0, short_id, 0, SIMPLE_ID_LENGTH );
   
      return( short_id );
    }
  }
 
  protected static byte[]
    deriveSingletonShortID(
      Map      singleton_details )
    {
      byte[]  short_id = new byte[SIMPLE_ID_LENGTH];

      byte[]   explicit_sid = new SHA1Simple().calculateHash((byte[])singleton_details.get( "key" ));
       
      System.arraycopy( explicit_sid, 0, short_id, 0, SIMPLE_ID_LENGTH );
     
      return( short_id );
    }
 
  private SubscriptionManagerImpl    manager;
 
  private String  name;
  private boolean  is_public;
  private byte[]  public_key;
  private int    version;
  private int    az_version;
  private String  json;
  private Map    singleton_details;
 
 
 
  private byte[]  hash;
  private byte[]  sig;
  private int    sig_data_size;
 
  private Map    map;

    // load constructor
 
  protected
  SubscriptionBodyImpl(
    SubscriptionManagerImpl  _manager,
    SubscriptionImpl    _subs )
     
    throws SubscriptionException
  {
    manager  = _manager;

    try{
      File vuze_file = manager.getVuzeFile( _subs );
 
      VuzeFile  vf = VuzeFileHandler.getSingleton().loadVuzeFile( vuze_file.getAbsolutePath());
 
      if ( vf == null ){
       
        throw( new IOException( "Failed to load vuze file '" + vuze_file + "'" ));
      }
         
      loadvf.getComponents()[0].getContent(), false );
     
    }catch( Throwable e ){
     
      rethrow( e );
    }
  }
 
    // import constructor
 
  protected
  SubscriptionBodyImpl(
    SubscriptionManagerImpl  _manager,
    Map            _map )
 
    throws IOException
  {
    manager  = _manager;

    load( _map, true );
  }
 
  protected void
  load(
    Map      _map,
    boolean    _verify )
 
    throws IOException
  {
    map    = _map;
   
    hash   = (byte[])map.get( "hash" );
    sig     = (byte[])map.get( "sig" );
    Long  l_size  = (Long)map.get( "size" );
   
    Map  details = (Map)map.get( "details" );
   
    if ( details == null || hash == null || sig == null || l_size == null ){
     
      throw( new IOException( "Invalid subscription - details missing" ));
    }
   
    sig_data_size  = l_size.intValue();
   
    name    = new String((byte[])details.get( "name" ), "UTF-8" );
    public_key  = (byte[])details.get( "public_key" );
    version    = ((Long)details.get( "version" )).intValue();
    is_public  = ((Long)details.get( "is_public" )).intValue()==1;
    json    = new String((byte[])details.get( "json"), "UTF-8" );
   
    singleton_details = (Map)details.get( "sin_details" );
   
    Long l_az_version  = (Long)details.get( "az_version" );
   
    az_version = l_az_version==null?Subscription.AZ_VERSION:l_az_version.intValue();
   
    if ( _verify ){
     
        // verify
     
      byte[] contents = BEncoder.encode( details );
     
      byte[] actual_hash = new SHA1Simple().calculateHash( contents );
 
      if ( !Arrays.equals( actual_hash, hash )){
       
          // backwards compat for pre-az_version
             
        Map details_copy = new HashMap( details );
       
        details_copy.remove( "az_version" );
       
        contents = BEncoder.encode( details_copy );
       
        actual_hash = new SHA1Simple().calculateHash( contents );
      }
     
      if ( !Arrays.equals( actual_hash, hash )){

        throw( new IOException( "Hash mismatch" ));
      }
     
      if ( sig_data_size != contents.length ){
       
        throw( new IOException( "Signature data length mismatch" ));
      }
     
      if ( !verify( public_key, hash, version, sig_data_size, sig )){
         
        throw( new IOException( "Signature verification failed" ));
      }
    }
  }
 
    // create constructor
 
  protected
  SubscriptionBodyImpl(
    SubscriptionManagerImpl  _manager,
    String          _name,
    boolean          _is_public,
    String          _json_content,
    byte[]          _public_key,
    int            _version,
    int            _az_version,
    Map            _singleton_details )
 
    throws IOException
  {
    manager    = _manager;
   
    name    = _name;
    is_public  = _is_public;
    public_key  = _public_key;
    version    = _version;
    az_version  = _az_version;
    json    = _json_content;
   
    singleton_details  = _singleton_details;
   
    map      = new HashMap();
   
    Map details = new HashMap();
     
    map.put( "details", details );
   
    details.put( "name", name.getBytes( "UTF-8" ));
    details.put( "is_public", new Long( is_public?1:0 ));
    details.put( "public_key", public_key );
    details.put( "version", new Long( version ));
    details.put( "az_version", new Long( az_version ));
    details.put( "json", _json_content.getBytes( "UTF-8" ));
   
    if ( singleton_details != null ){
     
      details.put( "sin_details", singleton_details );
    }
  }
 
  protected void
  updateDetails(
    SubscriptionImpl    subs,
    Map            details )
 
    throws IOException
  {
    is_public  = subs.isPublic();
    version    = subs.getVersion();
    az_version  = subs.getAZVersion();
    name    = subs.getName();
   
    details.put( "name",name.getBytes( "UTF-8" ));
    details.put( "is_public", new Long( is_public?1:0 ));
    details.put( "version", new Long( version ));
    details.put( "az_version", new Long( az_version ));
   
    if ( json != null ){
   
      details.put( "json", json.getBytes( "UTF-8" ));
    }
   
    if ( singleton_details != null ){
     
      details.put( "sin_details", singleton_details );
    }
  }
 
  protected String
  getName()
  {
    return( name );
  }
 
  protected byte[]
  getPublicKey()
  {
    return( public_key );
  }
 
  public byte[]
  getShortID()
  {
    return( deriveShortID( public_key, singleton_details ));
  }
 
  protected boolean
  isPublic()
  {
    return( is_public );
  }
 
  protected String
  getJSON()
  {
    return( json );
  }
 
  protected Map
  getSingletonDetails()
  {
    return( singleton_details );
  }
 
  protected void
  setJSON(
    String    _json )
  {
    json  = _json;
  }
 
  protected int
  getVersion()
  {
    return( version );
  }
 
  protected int
  getAZVersion()
  {
    return( az_version );
  }
 
    // derived data
 
  protected byte[]
  getHash()
  {
    return( hash );
  }

  protected byte[]
  getSig()
  {
    return( sig );
  }
 
  protected int
  getSigDataSize()
  {
    return( sig_data_size );
  }
 
  protected void
  writeVuzeFile(
    SubscriptionImpl    subs )
 
    throws SubscriptionException
  {
    try{
      File file = manager.getVuzeFile( subs );
             
      Map  details = (Map)map.get( "details" );
     
      updateDetails( subs, details );
     
      byte[] contents = BEncoder.encode( details );
         
      byte[] new_hash = new SHA1Simple().calculateHash( contents );
     
      byte[] old_hash  = (byte[])map.get( "hash" );
     
        // backward compat from before az_version was introduced
     
      if ( old_hash != null && !Arrays.equals( old_hash, new_hash )){
         
        Map details_copy = new HashMap( details );
       
        details_copy.remove( "az_version" );
       
        contents = BEncoder.encode( details_copy );
       
        new_hash = new SHA1Simple().calculateHash( contents );
      }
     
      if ( old_hash == null || !Arrays.equals( old_hash, new_hash )){
       
        byte[]  private_key = subs.getPrivateKey();
       
        if ( private_key == null ){
         
          throw( new SubscriptionException( "Only the originator of a subscription can modify it" ));
        }
             
        map.put( "size", new Long( contents.length ));
       
        try{       
          map.put( "hash", new_hash );
          map.put( "sig", sign( private_key, new_hash, version, contents.length ));
         
        }catch( Throwable e ){
         
          throw( new SubscriptionException( "Crypto failed: " + Debug.getNestedExceptionMessage(e)));
        }
      }
     
      File  backup_file  = null;
     
      if ( file.exists()){
       
        backup_file = new File( file.getParent(), file.getName() + ".bak" );
       
        backup_file.delete();
       
        if ( !file.renameTo( backup_file )){
         
          throw( new SubscriptionException( "Backup failed" ));
        }
      }
     
      try{
        VuzeFile  vf = VuzeFileHandler.getSingleton().create();
       
        vf.addComponent( VuzeFileComponent.COMP_TYPE_SUBSCRIPTION, map );
       
        vf.write( file );
     
        hash      = new_hash;
        sig        = (byte[])map.get( "sig" );
        sig_data_size  = contents.length;
       
      }catch( Throwable e ){
       
        if ( backup_file != null ){
         
          backup_file.renameTo( file );
        }
       
        throw( new SubscriptionException( "File write failed: " + Debug.getNestedExceptionMessage(e)));
      }
    }catch( Throwable e ){
     
      rethrow( e );
    }
  }
 
  protected void
  rethrow(
    Throwable e )
 
    throws SubscriptionException
  {
    if ( e instanceof SubscriptionException ){
     
      throw((SubscriptionException)e);
    }
   
    throw( new SubscriptionException( "Operation failed", e ));
  }
}
TOP

Related Classes of com.aelitis.azureus.core.subs.impl.SubscriptionBodyImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.