View Javadoc
1   package org.eclipse.aether.util.repository;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   * 
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   * 
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Arrays;
23  import java.util.Map;
24  
25  import org.eclipse.aether.repository.Authentication;
26  import org.eclipse.aether.repository.AuthenticationContext;
27  import org.eclipse.aether.repository.AuthenticationDigest;
28  
29  /**
30   * Authentication block that manages a single authentication key and its secret string value (password, passphrase).
31   * Unlike {@link StringAuthentication}, the string value is kept in an encrypted buffer and only decrypted when needed
32   * to reduce the potential of leaking the secret in a heap dump.
33   */
34  final class SecretAuthentication
35      implements Authentication
36  {
37  
38      private static final Object[] KEYS;
39  
40      static
41      {
42          KEYS = new Object[16];
43          for ( int i = 0; i < KEYS.length; i++ )
44          {
45              KEYS[i] = new Object();
46          }
47      }
48  
49      private final String key;
50  
51      private final char[] value;
52  
53      private final int secretHash;
54  
55      public SecretAuthentication( String key, String value )
56      {
57          this( ( value != null ) ? value.toCharArray() : null, key );
58      }
59  
60      public SecretAuthentication( String key, char[] value )
61      {
62          this( copy( value ), key );
63      }
64  
65      private SecretAuthentication( char[] value, String key )
66      {
67          if ( key == null )
68          {
69              throw new IllegalArgumentException( "authentication key missing" );
70          }
71          this.key = key;
72          this.secretHash = Arrays.hashCode( value ) ^ KEYS[0].hashCode();
73          this.value = xor( value );
74      }
75  
76      private static char[] copy( char[] chars )
77      {
78          return ( chars != null ) ? chars.clone() : null;
79      }
80  
81      private char[] xor( char[] chars )
82      {
83          if ( chars != null )
84          {
85              int mask = System.identityHashCode( this );
86              for ( int i = 0; i < chars.length; i++ )
87              {
88                  int key = KEYS[( i >> 1 ) % KEYS.length].hashCode();
89                  key ^= mask;
90                  chars[i] ^= ( ( i & 1 ) == 0 ) ? ( key & 0xFFFF ) : ( key >>> 16 );
91              }
92          }
93          return chars;
94      }
95  
96      private static void clear( char[] chars )
97      {
98          if ( chars != null )
99          {
100             for ( int i = 0; i < chars.length; i++ )
101             {
102                 chars[i] = '\0';
103             }
104         }
105     }
106 
107     public void fill( AuthenticationContext context, String key, Map<String, String> data )
108     {
109         char[] secret = copy( value );
110         xor( secret );
111         context.put( this.key, secret );
112         // secret will be cleared upon AuthenticationContext.close()
113     }
114 
115     public void digest( AuthenticationDigest digest )
116     {
117         char[] secret = copy( value );
118         try
119         {
120             xor( secret );
121             digest.update( key );
122             digest.update( secret );
123         }
124         finally
125         {
126             clear( secret );
127         }
128     }
129 
130     @Override
131     public boolean equals( Object obj )
132     {
133         if ( this == obj )
134         {
135             return true;
136         }
137         if ( obj == null || !getClass().equals( obj.getClass() ) )
138         {
139             return false;
140         }
141         SecretAuthentication that = (SecretAuthentication) obj;
142         if ( !eq( key, that.key ) || secretHash != that.secretHash )
143         {
144             return false;
145         }
146         char[] secret = copy( value );
147         char[] thatSecret = copy( that.value );
148         try
149         {
150             xor( secret );
151             that.xor( thatSecret );
152             return Arrays.equals( secret, thatSecret );
153         }
154         finally
155         {
156             clear( secret );
157             clear( thatSecret );
158         }
159     }
160 
161     private static <T> boolean eq( T s1, T s2 )
162     {
163         return s1 != null ? s1.equals( s2 ) : s2 == null;
164     }
165 
166     @Override
167     public int hashCode()
168     {
169         int hash = 17;
170         hash = hash * 31 + key.hashCode();
171         hash = hash * 31 + secretHash;
172         return hash;
173     }
174 
175     @Override
176     public String toString()
177     {
178         return key + "=" + ( ( value != null ) ? "***" : "null" );
179     }
180 
181 }