upload android base code part6

This commit is contained in:
August 2018-08-08 17:48:24 +08:00
parent 421e214c7d
commit 4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions

View file

@ -0,0 +1,42 @@
The tools in this directory are a scheme for autogenerating crypto doc support.
The central file is data/crypto_support.json, which is a JSON file that contains
the supported API levels for each crypto algorithm that has ever been supported
by Android. The categories are identified as described in the JCA, along with
a couple extra classes.
Each algorithm has up to three properties:
name - The name of the algorithm. These are currently normalized to
all-uppercase versions, but this produces substandard results in some cases
(eg, PBEWITHHMACSHA versus PBEWithHmacSHA), so it could be improved.
supported_api_levels: The set of Android API levels that this algorithm was/is
supported for. It should be a sequence of zero or more "N" values or "N-M"
ranges separated by commas followed by a "N+" value if the algorithm is
currently supported. For example: "17+", "1-8,22+", "1-10,15,22-24".
deprecated: Whether the algorithm is unsupported at the current API level. This
is equivalent to supported_api_levels.endswith('+'), but is included for
clarity's sake.
Updating the documentation is a three-step process: get the set of supported
algorithms, update the data file, then generate HTML based on the data file.
Getting the set of supported algorithms is done using
src/java/libcore/java/security/ListProviders.java. It's important that this is
run using vogar in mode=activity rather than the default of mode=device, as
the Android frameworks (in particular, Android Keystore) can add additional
support.
The data from ListProviders is fed into update_crypto_support.py, which rewrites
the data file. Any newly-added algorithms will be added to the file's data,
and any newly-removed algorithms will have their supported API levels updated.
The preceding two steps can be performed together by running
run_update_crypto_support.sh.
Finally, format_supported_algorithm_table.py reads the data file and outputs
a collection of HTML tables suitable for copying-and-pasting into the docs
to stdout. Add --javadoc to produce output appropriate for inclusion in
Javadoc.

View file

@ -0,0 +1,39 @@
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
'''Utility functions for crypto doc updating tools.'''
import json
def load_json(filename):
'''Returns an object containing the JSON data from the provided file.'''
f = open(filename)
# JSON doesn't allow comments, but we have some header docs in our file,
# so strip comments out before parsing
stripped_contents = ''
for line in f:
if not line.strip().startswith('#'):
stripped_contents += line
data = json.loads(stripped_contents)
f.close()
return data
def find_by_name(seq, name):
"""Returns the first element in seq with the given name."""
for item in seq:
if item['name'] == name:
return item
return None

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,226 @@
#!/usr/bin/env python
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Outputs HTML based on an input JSON file.
Outputs HTML tables suitable for inclusion in the Android documentation that
reflect the crypto algorithm support shown in the provided data file.
"""
import argparse
import operator
import crypto_docs
find_by_name = crypto_docs.find_by_name
def sort_by_name(seq):
return sorted(seq, key=lambda x: x['name'])
def main():
parser = argparse.ArgumentParser(description='Output algorithm support HTML tables')
parser.add_argument('--for_javadoc',
action='store_true',
help='If specified, format for inclusion in class documentation')
parser.add_argument('file',
help='The JSON file to use for data')
args = parser.parse_args()
output = []
data = crypto_docs.load_json(args.file)
categories = sort_by_name(data['categories'])
output.append('<h2 id="SupportedAlgorithms">Supported Algorithms</h2>')
output.append('')
output.append('<ul>')
for category in categories:
if not category['name'].endswith('.Enabled'):
output.append(' <li><a href="#Supported{name}">'
'<code>{name}</code></a></li>'.format(**category))
output.append('</ul>')
for category in categories:
if category['name'].endswith('.Enabled'):
# These are handled in the "Supported" section below
continue
if category['name'] == 'Cipher':
# We display ciphers in a four-column table to conserve space and
# so that it's more comprehensible. To do this, we have to
# collapse all our ciphers into "equivalence classes" of a sort.
# First, collect the relevant data for each algorithm into a tuple.
# The mode and padding are in lists because we are going to collapse
# multiple tuples with those in later steps.
algorithms = sort_by_name(category['algorithms'])
tuples = []
for algorithm in algorithms:
name, mode, padding = algorithm['name'].split('/')
tuples.append((
name,
[mode],
[padding],
algorithm['supported_api_levels'],
'deprecated' in algorithm and algorithm['deprecated']))
# Sort the tuples by all items except padding, then collapse
# items with all non-padding values the same (which will always be
# neighboring items) into a single item.
tuples.sort(key=operator.itemgetter(0, 1, 3, 4))
i = 0
while i < len(tuples) - 1:
if (tuples[i][0] == tuples[i+1][0]
and tuples[i][1] == tuples[i+1][1]
and tuples[i][3] == tuples[i+1][3]
and tuples[i][4] == tuples[i+1][4]):
tuples[i][2].extend(tuples[i+1][2])
del tuples[i+1]
else:
i += 1
# Do the same thing as above, but with modes.
tuples.sort(key=operator.itemgetter(0, 2, 3, 4))
i = 0
while i < len(tuples) - 1:
if (tuples[i][0] == tuples[i+1][0]
and tuples[i][2] == tuples[i+1][2]
and tuples[i][3] == tuples[i+1][3]
and tuples[i][4] == tuples[i+1][4]):
tuples[i][1].extend(tuples[i+1][1])
del tuples[i+1]
else:
i += 1
# Display the table with rowspans for those entries where all the
# items have the same algorithm, mode, etc
output.append('<h3 id="Supported{name}">{name}</h3>'.format(**category))
output.append('<table>')
output.append(' <thead>')
output.append(' <tr>')
output.append(' <th>Algorithm</th>')
output.append(' <th>Modes</th>')
output.append(' <th>Paddings</th>')
output.append(' <th>Supported API Levels</th>')
output.append(' </tr>')
output.append(' </thead>')
output.append(' <tbody>')
tuples.sort(key=operator.itemgetter(0, 4, 1, 2, 3))
i = 0
cur_deprecated = None
cur_algorithm = None
cur_mode = None
while i < len(tuples):
row = tuples[i]
if row[4] != cur_deprecated:
cur_deprecated = row[4]
cur_algorithm = None
cur_mode = None
if cur_deprecated:
output.append(' <tr class="deprecated">')
else:
output.append(' <tr>')
if row[0] != cur_algorithm:
cur_algorithm = row[0]
cur_mode = None
j = i + 1
while (j < len(tuples)
and tuples[j][4] == cur_deprecated
and tuples[j][0] == cur_algorithm):
j += 1
rowspan = j - i
if rowspan > 1:
output.append(' <td rowspan="%d">%s</td>' % (rowspan, cur_algorithm))
else:
output.append(' <td>%s</td>' % cur_algorithm)
if row[1] != cur_mode:
cur_mode = row[1]
j = i + 1
while (j < len(tuples)
and tuples[j][4] == cur_deprecated
and tuples[j][0] == cur_algorithm
and tuples[j][1] == cur_mode):
j += 1
rowspan = j - i
modestring = '<br>'.join(cur_mode)
if rowspan > 1:
output.append(' <td rowspan="%d">%s</td>' % (rowspan, modestring))
else:
output.append(' <td>%s</td>' % modestring)
output.append(' <td>%s</td>' % '<br>'.join(row[2]))
output.append(' <td>%s</td>' % row[3])
output.append(' </tr>')
i += 1
output.append(' </tbody>')
output.append('</table>')
elif category['name'].endswith('.Supported'):
# Some categories come with a "Supported" and "Enabled" list, and we
# group those together in one table for display. Every entry that's enabled
# must be supported, so we can just look up the enabled version for each
# supported item
basename = category['name'][:-len('.Supported')]
supported = sort_by_name(category['algorithms'])
enabled = sort_by_name(find_by_name(categories, basename + '.Enabled')['algorithms'])
output.append('<h3 id="Supported{0}">{0}</h3>'.format(basename))
output.append('<table>')
output.append(' <thead>')
output.append(' <tr>')
output.append(' <th>Algorithm</th>')
output.append(' <th>Supported API Levels</th>')
output.append(' <th>Enabled By Default</th>')
output.append(' </tr>')
output.append(' </thead>')
output.append(' <tbody>')
for algorithm in supported:
if 'deprecated' in algorithm and algorithm['deprecated']:
output.append(' <tr class="deprecated">')
else:
output.append(' <tr>')
output.append(' <td>{name}</td>'.format(**algorithm))
output.append(' <td>{supported_api_levels}</td>'.format(**algorithm))
enabled_alg = find_by_name(enabled, algorithm['name'])
if enabled_alg is None:
output.append(' <td></td>')
else:
output.append(' <td>{supported_api_levels}</td>'.format(**enabled_alg))
output.append(' </tr>')
output.append(' </tbody>')
output.append('</table>')
else:
output.append('<h3 id="Supported{name}">{name}</h3>'.format(**category))
output.append('<table>')
output.append(' <thead>')
output.append(' <tr>')
output.append(' <th>Algorithm</th>')
output.append(' <th>Supported API Levels</th>')
output.append(' </tr>')
output.append(' </thead>')
output.append(' <tbody>')
algorithms = sort_by_name(category['algorithms'])
for algorithm in algorithms:
if 'deprecated' in algorithm and algorithm['deprecated']:
output.append(' <tr class="deprecated">')
else:
output.append(' <tr>')
output.append(' <td>{name}</td>'.format(**algorithm))
output.append(' <td>{supported_api_levels}</td>'.format(**algorithm))
output.append(' </tr>')
output.append(' </tbody>')
output.append('</table>')
if args.for_javadoc:
for i in range(len(output)):
output[i] = ' * ' + output[i]
print '\n'.join(output)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,17 @@
#!/usr/bin/env bash
#
# Updates the crypto support JSON data file by running the appropriate tools.
# Ensure that the files we need are in the place we expect
if [ ! -f libcore/tools/docs/crypto/src/java/libcore/java/security/ListProviders.java -o ! -f libcore/tools/docs/crypto/data/crypto_support.json ]; then
echo "This command must be run from the repo root."
exit 1
fi
if [ -z "$1" ]; then
echo "The current API level must be specified as an argument."
exit 1
fi
make -j48 vogar dx
vogar --mode=activity --toolchain=jdk --multidex=false libcore/tools/docs/crypto/src/java/libcore/java/security/ListProviders.java | libcore/tools/docs/crypto/update_crypto_support.py --api_level=$1 --rewrite_file libcore/tools/docs/crypto/data/crypto_support.json

View file

@ -0,0 +1,160 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package libcore.java.security;
import android.net.PskKeyManager;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
/**
* Prints a list of all algorithms provided by security providers. Intended to be run
* via vogar as part of the algorithm documentation update process.
* <p>
* {@code vogar libcore/tools/src/java/libcore/java/security/ListProviders.java}
*/
public class ListProviders {
private static final boolean SHOW_PROVIDER = false;
// These algorithms were previously provided, but now are aliases for a different
// algorithm. For documentation purposes, we want to continue having them show up
// as supported.
private static final Set<String> KNOWN_ALIASES = new TreeSet<>(Arrays.asList(new String[]{
"Alg.Alias.Signature.DSA",
"Alg.Alias.Signature.DSAwithSHA1",
"Alg.Alias.Signature.ECDSA",
"Alg.Alias.Signature.ECDSAwithSHA1",
}));
// Ciphers come in algorithm/mode/padding combinations, and not all combinations are explicitly
// registered by the providers (sometimes only the base algorithm is registered). While there
// is a mechanism for providers to specify which modes and/or paddings are supported for a
// given algorithm, none of our providers use it. Thus, when a base algorithm is seen, all
// combinations of modes and paddings will be tried to see which ones are supported.
private static final Set<String> CIPHER_MODES = new TreeSet<>(Arrays.asList(new String[]{
"CBC",
"CFB",
"CTR",
"CTS",
"ECB",
"GCM",
"OFB",
"NONE",
}));
private static final Set<String> CIPHER_PADDINGS = new TreeSet<>(Arrays.asList(new String[]{
"NoPadding",
"OAEPPadding",
"OAEPwithSHA-1andMGF1Padding",
"OAEPwithSHA-224andMGF1Padding",
"OAEPwithSHA-256andMGF1Padding",
"OAEPwithSHA-384andMGF1Padding",
"OAEPwithSHA-512andMGF1Padding",
"PKCS1Padding",
"PKCS5Padding",
"ISO10126Padding",
}));
private static void print(Provider p, String type, String algorithm) {
System.out.println((SHOW_PROVIDER ? p.getName() + ": " : "") + type + " " + algorithm);
}
public static void main(String[] argv) throws Exception {
System.out.println("BEGIN ALGORITHM LIST");
for (Provider p : Security.getProviders()) {
Set<Provider.Service> services = new TreeSet<Provider.Service>(
new Comparator<Provider.Service>() {
public int compare(Provider.Service a, Provider.Service b) {
int typeCompare = a.getType().compareTo(b.getType());
if (typeCompare != 0) {
return typeCompare;
}
return a.getAlgorithm().compareTo(b.getAlgorithm());
}
});
services.addAll(p.getServices());
for (Provider.Service s : services) {
if (s.getType().equals("Cipher") && s.getAlgorithm().startsWith("PBE")) {
// PBE ciphers are a mess and generally don't do anything but delegate
// to the underlying cipher. We don't want to document them.
continue;
}
if (s.getType().equals("Cipher") && s.getAlgorithm().indexOf('/') == -1) {
for (String mode : CIPHER_MODES) {
for (String padding : CIPHER_PADDINGS) {
try {
String name = s.getAlgorithm() + "/" + mode + "/" + padding;
Cipher.getInstance(name, p);
print(p, s.getType(), name);
} catch (NoSuchAlgorithmException
|NoSuchPaddingException
|IllegalArgumentException e) {
// This combination doesn't work
}
}
}
} else {
print(p, s.getType(), s.getAlgorithm());
}
}
for (String alias : KNOWN_ALIASES) {
if (p.containsKey(alias)) {
String[] elements = alias.split("\\."); // Split takes a regex
print(p, elements[2], elements[3]);
}
}
}
// SSLEngine and SSLSocket algorithms are handled outside the default provider system
SSLContext defaultContext = SSLContext.getDefault();
// PSK cipher suites are only enabled when a PskKeyManager is available, but some other
// suites are disabled in that case, so check for both
SSLContext pskContext = SSLContext.getInstance("TLS");
pskContext.init(
new KeyManager[] {new PskKeyManager(){}},
new TrustManager[0],
null);
for (SSLContext sslContext : new SSLContext[] {defaultContext, pskContext}) {
SSLEngine engine = sslContext.createSSLEngine();
for (String suite : engine.getSupportedCipherSuites()) {
print(sslContext.getProvider(), "SSLEngine.Supported", suite);
}
for (String suite : engine.getEnabledCipherSuites()) {
print(sslContext.getProvider(), "SSLEngine.Enabled", suite);
}
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
for (String suite : socketFactory.getSupportedCipherSuites()) {
print(sslContext.getProvider(), "SSLSocket.Supported", suite);
}
for (String suite : socketFactory.getDefaultCipherSuites()) {
print(sslContext.getProvider(), "SSLSocket.Enabled", suite);
}
}
System.out.println("END ALGORITHM LIST");
}
}

View file

@ -0,0 +1,279 @@
#!/usr/bin/env python
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Updates a JSON data file of supported algorithms.
Takes input on stdin a list of provided algorithms as produced by
ListProviders.java along with a JSON file of the previous set of algorithm
support and what the current API level is, and produces an updated JSON
record of algorithm support.
"""
import argparse
import collections
import datetime
import json
import re
import sys
import crypto_docs
SUPPORTED_CATEGORIES = [
'AlgorithmParameterGenerator',
'AlgorithmParameters',
'CertificateFactory',
'CertPathBuilder',
'CertPathValidator',
'CertStore',
'Cipher',
'KeyAgreement',
'KeyFactory',
'KeyGenerator',
'KeyManagerFactory',
'KeyPairGenerator',
'KeyStore',
'Mac',
'MessageDigest',
'SecretKeyFactory',
'SecureRandom',
'Signature',
'SSLContext',
'SSLEngine.Enabled',
'SSLEngine.Supported',
'SSLSocket.Enabled',
'SSLSocket.Supported',
'TrustManagerFactory',
]
# For these categories, we really want to maintain the casing that was in the
# original data, so avoid changing it.
CASE_SENSITIVE_CATEGORIES = [
'SSLEngine.Enabled',
'SSLEngine.Supported',
'SSLSocket.Enabled',
'SSLSocket.Supported',
]
find_by_name = crypto_docs.find_by_name
def find_by_normalized_name(seq, name):
"""Returns the first element in seq with the given normalized name."""
for item in seq:
if normalize_name(item['name']) == name:
return item
return None
def sort_by_name(seq):
"""Returns a copy of the input sequence sorted by name."""
return sorted(seq, key=lambda x: x['name'])
def normalize_name(name):
"""Returns a normalized version of the given algorithm name."""
name = name.upper()
# BouncyCastle uses X.509 with an alias of X509, Conscrypt does the
# reverse. X.509 is the official name of the standard, so use that.
if name == "X509":
name = "X.509"
# PKCS5PADDING and PKCS7PADDING are the same thing (more accurately, PKCS#5
# is a special case of PKCS#7), but providers are inconsistent in their
# naming. Use PKCS5PADDING because that's what our docs have used
# historically.
if name.endswith("/PKCS7PADDING"):
name = name[:-1 * len("/PKCS7PADDING")] + "/PKCS5PADDING"
return name
def fix_name_caps_for_output(name):
"""Returns a version of the given algorithm name with capitalization fixed."""
# It's important that this must only change the capitalization of the
# name, not any of its text, otherwise future runs won't be able to
# match this name with the name coming from the device.
# We current make the following capitalization fixes
# DESede (not DESEDE)
# FOOwithBAR (not FOOWITHBAR or FOOWithBAR)
# Hmac (not HMAC)
name = re.sub('WITH', 'with', name, flags=re.I)
name = re.sub('DESEDE', 'DESede', name, flags=re.I)
name = re.sub('HMAC', 'Hmac', name, flags=re.I)
return name
def get_current_data(f):
"""Returns a map of the algorithms in the given input.
The input file-like object must supply a "BEGIN ALGORITHM LIST" line
followed by any number of lines of an algorithm category and algorithm name
separated by whitespace followed by a "END ALGORITHM LIST" line. The
input can supply arbitrary values outside of the BEGIN and END lines, it
will be ignored.
The returned algorithms will have their names normalized.
Returns:
A dict of categories to lists of normalized algorithm names and a
dict of normalized algorithm names to original algorithm names.
Raises:
EOFError: If either the BEGIN or END sentinel lines are not present.
ValueError: If a line between the BEGIN and END sentinel lines is not
made up of two identifiers separated by whitespace.
"""
current_data = collections.defaultdict(list)
name_dict = {}
saw_begin = False
saw_end = False
for line in f.readlines():
line = line.strip()
if not saw_begin:
if line.strip() == 'BEGIN ALGORITHM LIST':
saw_begin = True
continue
if line == 'END ALGORITHM LIST':
saw_end = True
break
category, algorithm = line.split()
if category not in SUPPORTED_CATEGORIES:
continue
normalized_name = normalize_name(algorithm)
current_data[category].append(normalized_name)
name_dict[normalized_name] = algorithm
if not saw_begin:
raise EOFError(
'Reached the end of input without encountering the begin sentinel')
if not saw_end:
raise EOFError(
'Reached the end of input without encountering the end sentinel')
return dict(current_data), name_dict
def update_data(prev_data, current_data, name_dict, api_level, date):
"""Returns a copy of prev_data, modified to take into account current_data.
Updates the algorithm support metadata structure by starting with the
information in prev_data and updating it to take into account the algorithms
listed in current_data. Algorithms not present in current_data will still
be present in the return value, but their supported_api_levels may be
modified to indicate that they are no longer supported.
Args:
prev_data: The data on algorithm support from the previous API level.
current_data: The algorithms supported in the current API level, as a map
from algorithm category to list of algorithm names.
api_level: An integer representing the current API level.
date: A datetime object containing the time of update.
"""
new_data = {'categories': []}
for category in SUPPORTED_CATEGORIES:
prev_category = find_by_name(prev_data['categories'], category)
if prev_category is None:
prev_category = {'name': category, 'algorithms': []}
current_category = (
current_data[category] if category in current_data else [])
new_category = {'name': category, 'algorithms': []}
prev_algorithms = [normalize_name(x['name']) for x in prev_category['algorithms']]
alg_union = set(prev_algorithms) | set(current_category)
for alg in alg_union:
prev_alg = find_by_normalized_name(prev_category['algorithms'], alg)
if alg in name_dict:
new_algorithm = {'name': name_dict[alg]}
elif prev_alg is not None:
new_algorithm = {'name': prev_alg['name']}
else:
new_algorithm = {'name': alg}
if category not in CASE_SENSITIVE_CATEGORIES:
new_algorithm['name'] = fix_name_caps_for_output(new_algorithm['name'])
new_level = None
if alg in current_category and alg in prev_algorithms:
# Both old and new have it, just ensure the API level is right
if prev_alg['supported_api_levels'].endswith('+'):
new_level = prev_alg['supported_api_levels']
else:
new_level = (prev_alg['supported_api_levels']
+ ',%d+' % api_level)
elif alg in prev_algorithms:
# Only in the old set, so ensure the API level is marked
# as ending
if prev_alg['supported_api_levels'].endswith('+'):
# The algorithm is newly missing, so modify the support
# to end at the previous level
new_level = prev_alg['supported_api_levels'][:-1]
if not new_level.endswith(str(api_level - 1)):
new_level += '-%d' % (api_level - 1)
else:
new_level = prev_alg['supported_api_levels']
new_algorithm['deprecated'] = 'true'
else:
# Only in the new set, so add it
new_level = '%d+' % api_level
new_algorithm['supported_api_levels'] = new_level
new_category['algorithms'].append(new_algorithm)
if new_category['algorithms']:
new_category['algorithms'] = sort_by_name(
new_category['algorithms'])
new_data['categories'].append(new_category)
new_data['categories'] = sort_by_name(new_data['categories'])
new_data['api_level'] = str(api_level)
new_data['last_updated'] = date.strftime('%Y-%m-%d %H:%M:%S UTC')
return new_data
def main():
parser = argparse.ArgumentParser(description='Update JSON support file')
parser.add_argument('--api_level',
required=True,
type=int,
help='The current API level')
parser.add_argument('--rewrite_file',
action='store_true',
help='If specified, rewrite the'
' input file with the result')
parser.add_argument('file',
help='The JSON file to update')
args = parser.parse_args()
prev_data = crypto_docs.load_json(args.file)
current_data, name_dict = get_current_data(sys.stdin)
new_data = update_data(prev_data,
current_data,
name_dict,
args.api_level,
datetime.datetime.utcnow())
if args.rewrite_file:
f = open(args.file, 'w')
f.write('# This file is autogenerated.'
' See libcore/tools/docs/crypto/README for details.\n')
json.dump(
new_data, f, indent=2, sort_keys=True, separators=(',', ': '))
f.close()
else:
print json.dumps(
new_data, indent=2, sort_keys=True, separators=(',', ': '))
if __name__ == '__main__':
main()

View file

@ -0,0 +1,417 @@
#!/usr/bin/env python
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import datetime
import StringIO
import unittest
import update_crypto_support
def do_update_data(prev_data, current_data, name_dict={}):
return update_crypto_support.update_data(
prev_data,
current_data,
name_dict,
72,
datetime.datetime.utcfromtimestamp(1234567890))
# The timestamp 1234567890 in our text format
LAST_UPDATED_TEXT = '2009-02-13 23:31:30 UTC'
class TestUpdateData(unittest.TestCase):
def test_find_by_name(self):
self.assertIsNone(update_crypto_support.find_by_name([], 'foo'))
self.assertIsNone(
update_crypto_support.find_by_name([{'name': 'foo'}], 'bar'))
self.assertEqual(
update_crypto_support.find_by_name(
[{'name': 'foo', 'value': 'foo_value'}],
'foo'),
{'name': 'foo', 'value': 'foo_value'})
self.assertEqual(
update_crypto_support.find_by_name(
[{'name': 'foo', 'value': 'foo_value'},
{'name': 'bar', 'value': 'bar_value'}],
'bar'),
{'name': 'bar', 'value': 'bar_value'})
self.assertEqual(
update_crypto_support.find_by_name(
[{'name': 'foo', 'value': 'foo_value'},
{'name': 'bar', 'value': 'bar_value'},
{'name': 'foo', 'value': 'foo2_value'}],
'foo'),
{'name': 'foo', 'value': 'foo_value'})
self.assertEqual(
update_crypto_support.find_by_name(
[{'name': 'foot', 'value': 'foot_value'},
{'name': 'bar', 'value': 'bar_value'},
{'name': 'foo', 'value': 'foo_value'}],
'foo'),
{'name': 'foo', 'value': 'foo_value'})
def test_sort_by_name(self):
self.assertEqual(update_crypto_support.sort_by_name([]), [])
self.assertEqual(
update_crypto_support.sort_by_name(
[{'name': 'foot', 'value': 'foot_value'},
{'name': 'bar', 'value': 'bar_value'},
{'name': 'foo', 'value': 'foo_value'}]),
[{'name': 'bar', 'value': 'bar_value'},
{'name': 'foo', 'value': 'foo_value'},
{'name': 'foot', 'value': 'foot_value'}])
with self.assertRaises(KeyError):
update_crypto_support.sort_by_name([{'not_name': 'foo'}])
def test_get_current_data(self):
self.assertEqual(update_crypto_support.get_current_data(
StringIO.StringIO(
'''
BEGIN ALGORITHM LIST
Mac Bob
Mac Jones
MessageDigest Jim
Mac Amy
OtherThing Mary
END ALGORITHM LIST
''')),
({'Mac': ['BOB', 'JONES', 'AMY'],
'MessageDigest': ['JIM']},
{'AMY': 'Amy',
'BOB': 'Bob',
'JONES': 'Jones',
'JIM': 'Jim'}))
self.assertEqual(update_crypto_support.get_current_data(
StringIO.StringIO(
'''
BEGIN ALGORITHM LIST
Mac Dupe
Mac Jones
MessageDigest Jim
Mac Amy
Mac Dupe
OtherThing Mary
END ALGORITHM LIST
''')),
({'Mac': ['DUPE', 'JONES', 'AMY', 'DUPE'],
'MessageDigest': ['JIM']},
{'AMY': 'Amy',
'DUPE': 'Dupe',
'JONES': 'Jones',
'JIM': 'Jim'}))
self.assertEqual(update_crypto_support.get_current_data(
StringIO.StringIO(
'''
Mac NotAValue
BEGIN ALGORITHM LIST
Mac Bob
Mac Jones
MessageDigest Jim
Mac Amy
OtherThing Mary
END ALGORITHM LIST
Mac AlsoNotAValue
''')),
({'Mac': ['BOB', 'JONES', 'AMY'],
'MessageDigest': ['JIM']},
{'AMY': 'Amy',
'BOB': 'Bob',
'JONES': 'Jones',
'JIM': 'Jim'}))
self.assertEqual(update_crypto_support.get_current_data(
StringIO.StringIO(
'''
BEGIN ALGORITHM LIST OF LISTS
Mac NotAValue
BEGIN ALGORITHM LIST
Mac Bob
Mac Jones
MessageDigest Jim
Mac Amy
OtherThing Mary
END ALGORITHM LIST
''')),
({'Mac': ['BOB', 'JONES', 'AMY'],
'MessageDigest': ['JIM']},
{'AMY': 'Amy',
'BOB': 'Bob',
'JONES': 'Jones',
'JIM': 'Jim'}))
with self.assertRaises(EOFError):
update_crypto_support.get_current_data(StringIO.StringIO(
'''
NOTBEGIN ALGORITHM LIST
Mac Bob
Mac Jones
MessageDigest Jim
Mac Amy
OtherThing Mary
END ALGORITHM LIST
'''))
with self.assertRaises(EOFError):
update_crypto_support.get_current_data(StringIO.StringIO(
'''
BEGIN ALGORITHM LIST
Mac Bob
Mac Jones
MessageDigest Jim
Mac Amy
OtherThing Mary'''))
with self.assertRaises(ValueError):
update_crypto_support.get_current_data(StringIO.StringIO(
'''
BEGIN ALGORITHM LIST
Mac Bob
Mac Jones
MessageDigest Jim OneTooManyItems
Mac Amy
OtherThing Mary
END ALGORITHM LIST
'''))
with self.assertRaises(ValueError):
update_crypto_support.get_current_data(StringIO.StringIO(
'''
BEGIN ALGORITHM LIST
Mac Bob
Mac Jones
TooFewItems
MessageDigest Jim
Mac Amy
OtherThing Mary
END ALGORITHM LIST
'''))
def test_update_data_no_data(self):
self.assertEqual(
do_update_data(
{'categories': []},
{}),
{'categories': [],
'api_level': '72',
'last_updated': LAST_UPDATED_TEXT})
self.assertEqual(
do_update_data(
{'categories': [
{'name': 'MessageDigest',
'algorithms': []}]},
{}),
{'categories': [],
'api_level': '72',
'last_updated': LAST_UPDATED_TEXT})
self.assertEqual(
do_update_data(
{'categories': []},
{'MessageDigest': []}),
{'categories': [],
'api_level': '72',
'last_updated': LAST_UPDATED_TEXT})
def test_update_data_no_updates(self):
self.assertEqual(
do_update_data(
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'SHA-1',
'supported_api_levels': '1+'},
{'name': 'SHA-2',
'supported_api_levels': '1-22',
'deprecated': 'true'}]}]},
{'MessageDigest': ['SHA-1']}),
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'SHA-1',
'supported_api_levels': '1+'},
{'name': 'SHA-2',
'supported_api_levels': '1-22',
'deprecated': 'true'}]}],
'api_level': '72',
'last_updated': LAST_UPDATED_TEXT})
def test_update_data_new_item(self):
self.assertEqual(
do_update_data(
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'SHA-1',
'supported_api_levels': '1+'},
{'name': 'SHA-2',
'supported_api_levels': '1-22',
'deprecated': 'true'},
{'name': 'SHA-384',
'supported_api_levels': '17-32'}]}]},
{'MessageDigest': ['SHA-1', 'SHA-256', 'SHA-384']}),
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'SHA-1',
'supported_api_levels': '1+'},
{'name': 'SHA-2',
'supported_api_levels': '1-22',
'deprecated': 'true'},
{'name': 'SHA-256',
'supported_api_levels': '72+'},
{'name': 'SHA-384',
'supported_api_levels': '17-32,72+'}]}],
'api_level': '72',
'last_updated': LAST_UPDATED_TEXT})
def test_update_data_removed_item(self):
self.assertEqual(
do_update_data(
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'SHA-1',
'supported_api_levels': '1+'},
{'name': 'SHA-2',
'supported_api_levels': '1-22',
'deprecated': 'true'},
{'name': 'SHA-256',
'supported_api_levels': '70+'},
{'name': 'SHA-384',
'supported_api_levels': '71+'},
{'name': 'SHA-512',
'supported_api_levels': '1-3,17-32,47+'}]}]},
{'MessageDigest': ['SHA-1']}),
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'SHA-1',
'supported_api_levels': '1+'},
{'name': 'SHA-2',
'supported_api_levels': '1-22',
'deprecated': 'true'},
{'name': 'SHA-256',
'supported_api_levels': '70-71',
'deprecated': 'true'},
{'name': 'SHA-384',
'supported_api_levels': '71',
'deprecated': 'true'},
{'name': 'SHA-512',
'supported_api_levels': '1-3,17-32,47-71',
'deprecated': 'true'}]}],
'api_level': '72',
'last_updated': LAST_UPDATED_TEXT})
def test_update_data_duplicates(self):
self.assertEqual(
do_update_data(
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'SHA-1',
'supported_api_levels': '1+'},
{'name': 'SHA-2',
'supported_api_levels': '1-22',
'deprecated': 'true'},
{'name': 'SHA-1',
'supported_api_levels': '7+'}]}]},
{'MessageDigest': ['SHA-1']}),
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'SHA-1',
'supported_api_levels': '1+'},
{'name': 'SHA-2',
'supported_api_levels': '1-22',
'deprecated': 'true'}]}],
'api_level': '72',
'last_updated': LAST_UPDATED_TEXT})
self.assertEqual(
do_update_data(
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'SHA-1',
'supported_api_levels': '1+'},
{'name': 'SHA-2',
'supported_api_levels': '1-22',
'deprecated': 'true'}]}]},
{'MessageDigest': ['SHA-1', 'SHA-1']}),
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'SHA-1',
'supported_api_levels': '1+'},
{'name': 'SHA-2',
'supported_api_levels': '1-22',
'deprecated': 'true'}]}],
'api_level': '72',
'last_updated': LAST_UPDATED_TEXT})
def test_update_name_matching(self):
self.assertEqual(
do_update_data(
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'sha-1',
'supported_api_levels': '1+'},
{'name': 'Sha-2',
'supported_api_levels': '1-22',
'deprecated': 'true'},
{'name': 'SHA-3',
'supported_api_levels': '7+'}]}]},
{'MessageDigest': ['SHA-1', 'SHA-2', 'SHA-3']},
{'SHA-1': 'Sha-1', 'SHA-2': 'Sha-2', 'SHA-3': 'Sha-3'}),
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'Sha-1',
'supported_api_levels': '1+'},
{'name': 'Sha-2',
'supported_api_levels': '1-22,72+'},
{'name': 'Sha-3',
'supported_api_levels': '7+'}]}],
'api_level': '72',
'last_updated': LAST_UPDATED_TEXT})
self.assertEqual(
do_update_data(
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'sha-1',
'supported_api_levels': '1+'},
{'name': 'Sha-2',
'supported_api_levels': '1-22',
'deprecated': 'true'},
{'name': 'SHA-3',
'supported_api_levels': '7+'}]}]},
{'MessageDigest': ['SHA-1', 'SHA-3']},
{'SHA-1': 'Sha-1', 'SHA-3': 'Sha-3'}),
{'categories': [
{'name': 'MessageDigest',
'algorithms': [
{'name': 'Sha-1',
'supported_api_levels': '1+'},
{'name': 'Sha-2',
'supported_api_levels': '1-22',
'deprecated': 'true'},
{'name': 'Sha-3',
'supported_api_levels': '7+'}]}],
'api_level': '72',
'last_updated': LAST_UPDATED_TEXT})
if __name__ == '__main__':
unittest.main()