initial move of files over

This commit is contained in:
brentru 2019-09-17 13:41:42 -04:00
parent 3bbecb9285
commit 7f91693689
21 changed files with 2063 additions and 4 deletions

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
*.mpy
.idea
__pycache__
_build
*.pyc
.env
build*
bundles
*.DS_Store
.eggs
dist
**/*.egg-info
.vscode

433
.pylintrc Normal file
View file

@ -0,0 +1,433 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint.
# jobs=1
jobs=2
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# Specify a configuration file.
#rcfile=
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call
disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=
[REPORTS]
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio).You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
# notes=FIXME,XXX,TODO
notes=FIXME,XXX
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=board
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,_cb
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,future.builtins
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
# expected-line-ending-format=
expected-line-ending-format=LF
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module
max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,dict-separator
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[SIMILARITIES]
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[BASIC]
# Naming hint for argument names
argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct argument names
argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for attribute names
attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct attribute names
attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Naming hint for class attribute names
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Regular expression matching correct class attribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Naming hint for class names
# class-name-hint=[A-Z_][a-zA-Z0-9]+$
class-name-hint=[A-Z_][a-zA-Z0-9_]+$
# Regular expression matching correct class names
# class-rgx=[A-Z_][a-zA-Z0-9]+$
class-rgx=[A-Z_][a-zA-Z0-9_]+$
# Naming hint for constant names
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression matching correct constant names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming hint for function names
function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct function names
function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Good variable names which should always be accepted, separated by a comma
# good-names=i,j,k,ex,Run,_
good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Naming hint for inline iteration names
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
# Regular expression matching correct inline iteration names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Naming hint for method names
method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct method names
method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for module names
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression matching correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
property-classes=abc.abstractproperty
# Naming hint for variable names
variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct variable names
variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
[IMPORTS]
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=optparse,tkinter.tix
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,_fields,_replace,_source,_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Maximum number of attributes for a class (see R0902).
# max-attributes=7
max-attributes=11
# Maximum number of boolean expressions in a if statement
max-bool-expr=5
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of statements in function / method body
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=1
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception

3
.readthedocs.yml Normal file
View file

@ -0,0 +1,3 @@
python:
version: 3
requirements_file: requirements.txt

48
.travis.yml Normal file
View file

@ -0,0 +1,48 @@
# This is a common .travis.yml for generating library release zip files for
# CircuitPython library releases using circuitpython-build-tools.
# See https://github.com/adafruit/circuitpython-build-tools for detailed setup
# instructions.
dist: xenial
language: python
python:
- "3.6"
cache:
pip: true
# TODO: if deployment to PyPi is desired, change 'DEPLOY_PYPI' to "true",
# or remove the env block entirely and remove the condition in the
# deploy block.
env:
- DEPLOY_PYPI="false"
deploy:
- provider: releases
api_key: "$GITHUB_TOKEN"
file_glob: true
file: "$TRAVIS_BUILD_DIR/bundles/*"
skip_cleanup: true
overwrite: true
on:
tags: true
# TODO: Use 'travis encrypt --com -r adafruit/<repo slug>' to generate
# the encrypted password for adafruit-travis. Paste result below.
- provider: pypi
user: adafruit-travis
password:
secure: #-- PASTE ENCRYPTED PASSWORD HERE --#
on:
tags: true
condition: $DEPLOY_PYPI = "true"
install:
- pip install -r requirements.txt
- pip install circuitpython-build-tools Sphinx sphinx-rtd-theme
- pip install --force-reinstall pylint==1.9.2
script:
- pylint adafruit_atecc.py
- ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py)
- circuitpython-build-bundles --filename_prefix adafruit-circuitpython-atecc --library_location .
- cd docs && sphinx-build -E -W -b html . _build/html && cd ..

127
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,127 @@
# Adafruit Community Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and leaders pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level or type of
experience, education, socio-economic status, nationality, personal appearance,
race, religion, or sexual identity and orientation.
## Our Standards
We are committed to providing a friendly, safe and welcoming environment for
all.
Examples of behavior that contributes to creating a positive environment
include:
* Be kind and courteous to others
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Collaborating with other community members
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and sexual attention or advances
* The use of inappropriate images, including in a community member's avatar
* The use of inappropriate language, including in a community member's nickname
* Any spamming, flaming, baiting or other attention-stealing behavior
* Excessive or unwelcome helping; answering outside the scope of the question
asked
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate
The goal of the standards and moderation guidelines outlined here is to build
and maintain a respectful community. We ask that you dont just aim to be
"technically unimpeachable", but rather try to be your best self.
We value many things beyond technical expertise, including collaboration and
supporting others within our community. Providing a positive experience for
other community members can have a much more significant impact than simply
providing the correct answer.
## Our Responsibilities
Project leaders are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project leaders have the right and responsibility to remove, edit, or
reject messages, comments, commits, code, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any community member for other behaviors that they deem
inappropriate, threatening, offensive, or harmful.
## Moderation
Instances of behaviors that violate the Adafruit Community Code of Conduct
may be reported by any member of the community. Community members are
encouraged to report these situations, including situations they witness
involving other community members.
You may report in the following ways:
In any situation, you may send an email to <support@adafruit.com>.
On the Adafruit Discord, you may send an open message from any channel
to all Community Helpers by tagging @community moderators. You may also send an
open message from any channel, or a direct message to @kattni#1507,
@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or
@Andon#8175.
Email and direct message reports will be kept confidential.
In situations on Discord where the issue is particularly egregious, possibly
illegal, requires immediate action, or violates the Discord terms of service,
you should also report the message directly to Discord.
These are the steps for upholding our communitys standards of conduct.
1. Any member of the community may report any situation that violates the
Adafruit Community Code of Conduct. All reports will be reviewed and
investigated.
2. If the behavior is an egregious violation, the community member who
committed the violation may be banned immediately, without warning.
3. Otherwise, moderators will first respond to such behavior with a warning.
4. Moderators follow a soft "three strikes" policy - the community member may
be given another chance, if they are receptive to the warning and change their
behavior.
5. If the community member is unreceptive or unreasonable when warned by a
moderator, or the warning goes unheeded, they may be banned for a first or
second offense. Repeated offenses will result in the community member being
banned.
## Scope
This Code of Conduct and the enforcement policies listed above apply to all
Adafruit Community venues. This includes but is not limited to any community
spaces (both public and private), the entire Adafruit Discord server, and
Adafruit GitHub repositories. Examples of Adafruit Community spaces include
but are not limited to meet-ups, audio chats on the Adafruit Discord, or
interaction at a conference.
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. As a community
member, you are representing our community, and are expected to behave
accordingly.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.4, available at
<https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>,
and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html).
For other projects adopting the Adafruit Community Code of
Conduct, please contact the maintainers of those projects for enforcement.
If you wish to use this code of conduct for your own project, consider
explicitly mentioning your moderation policy or making a copy with your
own moderation policy so as to avoid confusion.

20
LICENSE
View file

@ -1,6 +1,22 @@
MIT License
Copyright (c) 2018 Arduino SA. All rights reserved.
Copyright (c) 2019 Adafruit Industries
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
The MIT License (MIT)
Copyright (c) 2019 Brent Rubell for Adafruit Industries
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,2 +0,0 @@
# Adafruit_CircuitPython_ATECC
Driver for Microchip ATECCx08 cryptographic co-processors with secure hardware-based key storage

101
README.rst Normal file
View file

@ -0,0 +1,101 @@
Introduction
============
.. image:: https://readthedocs.org/projects/adafruit-circuitpython-atecc/badge/?version=latest
:target: https://circuitpython.readthedocs.io/projects/atecc/en/latest/
:alt: Documentation Status
.. image:: https://img.shields.io/discord/327254708534116352.svg
:target: https://discord.gg/nBQh6qu
:alt: Discord
.. image:: https://travis-ci.com/adafruit/Adafruit_CircuitPython_ATECC.svg?branch=master
:target: https://travis-ci.com/adafruit/Adafruit_CircuitPython_ATECC
:alt: Build Status
Driver for `Microchip's ATECCx08 cryptographic co-processors with secure hardware-based key storage <https://www.adafruit.com/product/4314>`_.
Note: This library was developed and tested with an ATECC608A, but should work for ATECC508 modules as well.
Dependencies
=============
This driver depends on:
* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
* `Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_
Please ensure all dependencies are available on the CircuitPython filesystem.
This is easily achieved by downloading
`the Adafruit library and driver bundle <https://github.com/adafruit/Adafruit_CircuitPython_Bundle>`_.
Installing from PyPI
=====================
.. note:: This library is not available on PyPI yet. Install documentation is included
as a standard element. Stay tuned for PyPI availability!
On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
PyPI <https://pypi.org/project/adafruit-circuitpython-atecc/>`_. To install for current user:
.. code-block:: shell
pip3 install adafruit-circuitpython-atecc
To install system-wide (this may be required in some cases):
.. code-block:: shell
sudo pip3 install adafruit-circuitpython-atecc
To install in a virtual environment in your current project:
.. code-block:: shell
mkdir project-name && cd project-name
python3 -m venv .env
source .env/bin/activate
pip3 install adafruit-circuitpython-atecc
Usage Example
=============
.. todo:: Add a quick, simple example. It and other examples should live in the examples folder and be included in docs/examples.rst.
Contributing
============
Contributions are welcome! Please read our `Code of Conduct
<https://github.com/adafruit/Adafruit_CircuitPython_ATECC/blob/master/CODE_OF_CONDUCT.md>`_
before contributing to help this project stay welcoming.
Sphinx documentation
-----------------------
Sphinx is used to build the documentation based on rST files and comments in the code. First,
install dependencies (feel free to reuse the virtual environment from above):
.. code-block:: shell
python3 -m venv .env
source .env/bin/activate
pip install Sphinx sphinx-rtd-theme
Now, once you have the virtual environment activated:
.. code-block:: shell
cd docs
sphinx-build -E -W -b html . _build/html
This will output the documentation to ``docs/_build/html``. Open the index.html in your browser to
view them. It will also (due to -W) error out on any warning like Travis will. This is a good way to
locally verify it will pass.
License
========
This library was written by Arduino SA. We've converted it to work with Adafruit CircuitPython and made
changes for it to work with CircuitPython devices and single-board linux computers running CircuitPython libraries. We've
added examples to demonstrate using the nonce, random, monotonic counter and SHA256 security functions within the library.
This open source code is licensed under the LGPL License (see LICENSE for details).

0
adafruit_atecc/__init__.py Executable file
View file

567
adafruit_atecc/adafruit_atecc.py Executable file
View file

@ -0,0 +1,567 @@
# Copyright (c) 2018 Arduino SA. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# The MIT License (MIT)
#
# Copyright (c) 2019 Brent Rubell for Adafruit Industries
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_atecc`
================================================================================
CircuitPython module for the Microchip ATECCx08A Cryptographic Co-Processor
* Author(s): Brent Rubell
Implementation Notes
--------------------
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
* Adafruit Bus Device library:
https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
* Adafruit binascii library:
https://github.com/adafruit/Adafruit_CircuitPython_binascii
"""
import time
from struct import pack
from micropython import const
from adafruit_bus_device.i2c_device import I2CDevice
from adafruit_binascii import hexlify
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ATECC.git"
# Device Address
_REG_ATECC_ADDR = const(0xC0)
_REG_ATECC_DEVICE_ADDR = _REG_ATECC_ADDR >> 1
# Version Registers
_ATECC_508_VER = const(0x50)
_ATECC_608_VER = const(0x60)
# Clock constants
_WAKE_CLK_FREQ = 100000 # slower clock speed
_TWLO_TIME = 6e-5 # TWlo, in microseconds
# Command Opcodes (9-1-3)
OP_COUNTER = const(0x24)
OP_INFO = const(0x30)
OP_NONCE = const(0x16)
OP_RANDOM = const(0x1B)
OP_SHA = const(0x47)
OP_LOCK = const(0x17)
OP_GEN_KEY = const(0x40)
OP_SIGN = const(0x41)
OP_WRITE = const(0x12)
# Maximum execution times, in milliseconds (9-4)
EXEC_TIME = {OP_COUNTER: const(20),
OP_INFO: const(1),
OP_NONCE: const(7),
OP_RANDOM: const(23),
OP_SHA: const(47),
OP_LOCK: const(32),
OP_GEN_KEY: const(115),
OP_SIGN : const(70),
OP_WRITE : const(26)}
CFG_TLS = b'\x01#\x00\x00\x00\x00P\x00\x00\x00\x00\x00\x00\xc0q\x00 \
\xc0\x00U\x00\x83 \x87 \x87 \x87/\x87/\x8f\x8f\x9f\x8f\xaf \
\x8f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \
\xaf\x8f\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00 \
\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff \
\xff\xff\xff\xff\x00\x00UU\xff\xff\x00\x00\x00\x00\x00\x003 \
\x003\x003\x003\x003\x00\x1c\x00\x1c\x00\x1c\x00<\x00<\x00<\x00< \
\x00<\x00<\x00<\x00\x1c\x00'
class ATECC:
"""
CircuitPython interface for ATECCx08A Crypto Co-Processor Devices.
"""
def __init__(self, i2c_bus, address=_REG_ATECC_DEVICE_ADDR, debug=False):
"""Initializes an ATECC device.
:param busio i2c_bus: I2C Bus object.
:param int address: Device address, defaults to _ATECC_DEVICE_ADDR.
:param bool debug: Library debugging enabled
"""
self._debug = debug
self._i2cbuf = bytearray(12)
self._i2c_bus = i2c_bus
self._i2c_device = None
self.wakeup()
if not self._i2c_device:
self._i2c_device = I2CDevice(self._i2c_bus, address)
self.idle()
if (self.version() >> 8) not in (_ATECC_508_VER, _ATECC_608_VER):
raise RuntimeError("Failed to find 608 or 508 chip. Please check your wiring.")
def wakeup(self):
"""Wakes up THE ATECC608A from sleep or idle modes.
Returns True if device woke up from sleep/idle mode.
"""
while not self._i2c_bus.try_lock():
pass
# check if it exists, first
if 0x60 in self._i2c_bus.scan():
self._i2c_bus.unlock()
return
zero_bits = bytearray(2)
try:
self._i2c_bus.writeto(0x0, zero_bits)
except OSError:
pass # this may fail, that's ok - its just to wake up the chip!
time.sleep(_TWLO_TIME)
data = self._i2c_bus.scan() # check for an i2c device
try:
if data[0] != 96:
raise TypeError('ATECCx08 not found - please check your wiring!')
except IndexError:
raise IndexError("ATECCx08 not found - please check your wiring!")
self._i2c_bus.unlock()
if not self._i2c_device:
self._i2c_device = I2CDevice(self._i2c_bus, _REG_ATECC_DEVICE_ADDR, debug=False)
# check if we are ready to read from
r = bytearray(1)
self._get_response(r)
if r[0] != 0x11:
raise RuntimeError("Failed to wakeup")
def idle(self):
"""Puts the chip into idle mode
until wakeup is called.
"""
self._i2cbuf[0] = 0x2
with self._i2c_device as i2c:
i2c.write(self._i2cbuf, end=1)
time.sleep(0.001)
def sleep(self):
"""Puts the chip into low-power
sleep mode until wakeup is called.
"""
self._i2cbuf[0] = 0x1
with self._i2c_device as i2c:
i2c.write(self._i2cbuf, end=1)
time.sleep(0.001)
@property
def locked(self):
"""Returns if the ATECC is locked."""
config = bytearray(4)
self._read(0x00, 0x15, config)
time.sleep(0.001)
return config[2] == 0x0 and config[3] == 0x00
@property
def serial_number(self):
"""Returns the ATECC serial number."""
serial_num = bytearray(9)
# 4-byte reads only
temp_sn = bytearray(4)
# SN<0:3>
self._read(0, 0x00, temp_sn)
serial_num[0:4] = temp_sn
time.sleep(0.001)
# SN<4:8>
self._read(0, 0x02, temp_sn)
serial_num[4:8] = temp_sn
time.sleep(0.001)
# Append Rev
self._read(0, 0x03, temp_sn)
serial_num[8] = temp_sn[0]
time.sleep(0.001)
# neaten up the serial for printing
serial_num = hexlify(serial_num).decode("utf-8")
serial_num = str(serial_num).upper()
return serial_num
def version(self):
"""Returns the ATECC608As revision number"""
self.wakeup()
self.idle()
vers = bytearray(4)
vers = self.info(0x00)
return (vers[2] << 8) | vers[3]
def lock_all_zones(self):
"""Locks Config, Data and OTP Zones."""
self.lock(0)
self.lock(1)
def lock(self, zone):
"""Locks specific ATECC zones.
:param int zone: ATECC zone to lock.
"""
self.wakeup()
self._send_command(0x17, 0x80 | zone, 0x0000)
time.sleep(EXEC_TIME[OP_LOCK]/1000)
res = bytearray(1)
self._get_response(res)
assert res[0] == 0x00, "Failed locking ATECC!"
self.idle()
def info(self, mode, param=None):
"""Returns device state information
:param int mode: Mode encoding, see Table 9-26.
"""
self.wakeup()
if not param:
self._send_command(OP_INFO, mode)
else:
self._send_command(OP_INFO, mode, param)
time.sleep(EXEC_TIME[OP_INFO]/1000)
info_out = bytearray(4)
self._get_response(info_out)
self.idle()
return info_out
def nonce(self, data, mode=0, zero=0x0000):
"""Generates a nonce by combining internally generated random number
with an input value.
:param bytearray data: Input value from system or external.
:param int mode: Controls the internal RNG and seed mechanism.
:param int zero: Param2, see Table 9-35.
"""
self.wakeup()
if mode in (0x00, 0x01):
if zero == 0x00:
assert len(data) == 20, "Data value must be 20 bytes long."
self._send_command(OP_NONCE, mode, zero, data)
# nonce returns 32 bytes
calculated_nonce = bytearray(32)
elif mode == 0x03:
# Operating in Nonce pass-through mode
assert len(data) == 32, "Data value must be 32 bytes long."
self._send_command(OP_NONCE, mode, zero, data)
# nonce returns 1 byte
calculated_nonce = bytearray(1)
else:
raise RuntimeError("Invalid mode specified!")
time.sleep(EXEC_TIME[OP_NONCE]/1000)
self._get_response(calculated_nonce)
time.sleep(1/1000)
if mode == 0x03:
assert calculated_nonce[0] == 0x00, "Incorrectly calculated nonce in pass-thru mode"
self.idle()
return calculated_nonce
def counter(self, counter=0, increment_counter=True):
"""Reads the binary count value from one of the two monotonic
counters located on the device within the configuration zone.
The maximum value that the counter may have is 2,097,151.
:param int counter: Device's counter to increment.
:param bool increment_counter: Increments the value of the counter specified.
"""
counter = 0x00
self.wakeup()
if counter == 1:
counter = 0x01
if increment_counter:
self._send_command(OP_COUNTER, 0x01, counter)
else:
self._send_command(OP_COUNTER, 0x00, counter)
time.sleep(EXEC_TIME[OP_COUNTER]/1000)
count = bytearray(4)
self._get_response(count)
self.idle()
return count
def random(self, rnd_min=0, rnd_max=0):
"""Generates a random number for use by the system.
:param int rnd_min: Minimum Random value to generate.
:param int rnd_max: Maximum random value to generate.
"""
if rnd_max:
rnd_min = 0
if rnd_min >= rnd_max:
return rnd_min
delta = rnd_max - rnd_min
r = bytes(16)
r = self._random(r)
data = 0
for i in range(len(r)):
data += r[i]
if data < 0:
data = -data
data = data % delta
return data + rnd_min
def _random(self, data):
"""Initializes the random number generator and returns.
:param bytearray data: Response buffer.
"""
self.wakeup()
data_len = len(data)
while data_len:
self._send_command(OP_RANDOM, 0x00, 0x0000)
time.sleep(EXEC_TIME[OP_RANDOM]/1000)
resp = bytearray(32)
self._get_response(resp)
copy_len = min(32, data_len)
data = resp[0:copy_len]
data_len -= copy_len
self.idle()
return data
# SHA-256 Commands
def sha_start(self):
"""Initializes the SHA-256 calculation engine
and the SHA context in memory.
This method MUST be called before sha_update or sha_digest
"""
self.wakeup()
self._send_command(OP_SHA, 0x00)
time.sleep(EXEC_TIME[OP_SHA]/1000)
status = bytearray(1)
self._get_response(status)
assert status[0] == 0x00, "Error during sha_start."
self.idle()
return status
def sha_update(self, message):
"""Appends bytes to the message. Can be repeatedly called.
:param bytes message: Up to 64 bytes of data to be included
into the hash operation.
"""
if not hasattr(message, "append"):
message = pack("B", message)
self.wakeup()
assert len(message) == 64, "Message provided to sha_update must be 64 bytes"
self._send_command(OP_SHA, 0x01, 64, message)
time.sleep(EXEC_TIME[OP_SHA]/1000)
status = bytearray(1)
self._get_response(status)
assert status[0] == 0x00, "Error during SHA Update"
self.idle()
return status
def sha_digest(self, message=None):
"""Returns the digest of the data passed to the
sha_update method so far.
:param bytearray message: Up to 64 bytes of data to be included
into the hash operation.
"""
if not hasattr(message, "append") and message is not None:
message = pack("B", message)
self.wakeup()
# Include optional message
if message:
self._send_command(OP_SHA, 0x02, len(message), message)
else:
self._send_command(OP_SHA, 0x02)
time.sleep(EXEC_TIME[OP_SHA]/1000)
digest = bytearray(32)
self._get_response(digest)
assert len(digest) == 32, "SHA response length does not match expected length."
self.idle()
return digest
def gen_key(self, key, slot_num, private_key=False):
"""Generates a private or public key.
:param int slot_num: ECC slot (from 0 to 4).
:param bool private_key: Generates a private key if true.
"""
assert 0 <= slot_num <= 4, "Provided slot must be between 0 and 4."
self.wakeup()
if private_key:
self._send_command(OP_GEN_KEY, 0x04, slot_num)
else:
self._send_command(OP_GEN_KEY, 0x00, slot_num)
time.sleep(EXEC_TIME[OP_GEN_KEY]/1000)
self._get_response(key)
time.sleep(0.001)
self.idle()
return key
def ecdsa_sign(self, slot, message):
"""Generates and returns a signature using the ECDSA algorithm.
:param int slot: Which ECC slot to use.
:param bytearray message: Message to be signed.
"""
# Load the message digest into TempKey using Nonce (9.1.8)
self.nonce(message, 0x03)
# Generate and return a signature
sig = bytearray(64)
sig = self.sign(slot)
return sig
def sign(self, slot_id):
"""Performs ECDSA signature calculation with key in provided slot.
:param int slot_id: ECC slot containing key for use with signature.
"""
self.wakeup()
self._send_command(0x41, 0x80, slot_id)
time.sleep(EXEC_TIME[OP_SIGN]/1000)
signature = bytearray(64)
self._get_response(signature)
self.idle()
return signature
def write_config(self, data):
"""Writes configuration data to the device's EEPROM.
:param bytearray data: Configuration data to-write
"""
# First 16 bytes of data are skipped, not writable
for i in range(16, 128, 4):
if i == 84:
# can't write
continue
self._write(0, i//4, data[i:i+4])
def _write(self, zone, address, buffer):
self.wakeup()
if len(buffer) not in (4, 32):
raise RuntimeError("Only 4 or 32-byte writes supported.")
if len(buffer) == 32:
zone |= 0x80
self._send_command(0x12, zone, address, buffer)
time.sleep(26/1000)
status = bytearray(1)
self._get_response(status)
self.idle()
def _read(self, zone, address, buffer):
self.wakeup()
if len(buffer) not in (4, 32):
raise RuntimeError("Only 4 and 32 byte reads supported")
if len(buffer) == 32:
zone |= 0x80
self._send_command(2, zone, address)
time.sleep(0.005)
self._get_response(buffer)
time.sleep(0.001)
self.idle()
def _send_command(self, opcode, param_1, param_2=0x00, data=''):
"""Sends a security command packet over i2c.
:param byte opcode: The command Opcode
:param byte param_1: The first parameter
:param byte param_2: The second parameter, can be two bytes.
:param byte param_3 data: Optional remaining input data.
"""
# assembling command packet
command_packet = bytearray(8+len(data))
# word address
command_packet[0] = 0x03
# i/o group: count
command_packet[1] = len(command_packet) - 1 # count
# security command packets
command_packet[2] = opcode
command_packet[3] = param_1
command_packet[4] = param_2 & 0xFF
command_packet[5] = param_2 >> 8
for i, cmd in enumerate(data):
command_packet[6+i] = cmd
if self._debug:
print("Command Packet Sz: ", len(command_packet))
print("\tSending:", [hex(i) for i in command_packet])
# Checksum, CRC16 verification
crc = self._at_crc(command_packet[1:-2])
command_packet[-1] = crc >> 8
command_packet[-2] = crc & 0xFF
self.wakeup()
with self._i2c_device as i2c:
i2c.write(command_packet)
# small sleep
time.sleep(0.001)
def _get_response(self, buf, length=None, retries=20):
self.wakeup()
if length is None:
length = len(buf)
response = bytearray(length+3) # 1 byte header, 2 bytes CRC, len bytes data
with self._i2c_device as i2c:
for _ in range(retries):
try:
i2c.readinto(response)
break
except OSError:
pass
else:
raise RuntimeError("Failed to read data from chip")
if self._debug:
print("\tReceived: ", [hex(i) for i in response])
crc = response[-2] | (response[-1] << 8)
crc2 = self._at_crc(response[0:-2])
if crc != crc2:
raise RuntimeError("CRC Mismatch")
for i in range(length):
buf[i] = response[i+1]
return response[1]
@staticmethod
def _at_crc(data, length=None):
if length is None:
length = len(data)
if not data or not length:
return 0
polynom = 0x8005
crc = 0x0
for b in data:
for shift in range(8):
data_bit = 0
if b & (1<<shift):
data_bit = 1
crc_bit = (crc >> 15) & 0x1
crc <<= 1
crc &= 0xFFFF
if data_bit != crc_bit:
crc ^= polynom
crc &= 0xFFFF
return crc & 0xFFFF

View file

@ -0,0 +1,223 @@
# Copyright (c) 2018 Arduino SA. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# The MIT License (MIT)
#
# Copyright (c) 2019 Brent Rubell for Adafruit Industries
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`atecc_asn1`
================================================================================
ASN.1 Utilities for the Adafruit_ATECC Module.
* Author(s): Brent Rubell
Implementation Notes
--------------------
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
"""
import struct
# pylint: disable=invalid-name
def get_signature(signature, data):
"""Appends signature data to buffer."""
# Signature algorithm
data += b"\x30\x0a\x06\x08"
# ECDSA with SHA256
data += b"\x2a\x86\x48\xce\x3d\x04\x03\x02"
r = signature[0]
s = signature[32]
r_len = 32
s_len = 32
while (r == 0x00 and r_len > 1):
r += 1
r_len -= 1
while (s == 0x00 and s_len > 1):
s += 1
s_len -= 1
if r & 0x80:
r_len += 1
if s & 0x80:
s_len += 1
data += b"\x03" + struct.pack("B", r_len + s_len + 7) + b"\x00"
data += b"\x30" + struct.pack("B", r_len + s_len + 4)
data += b"\x02" + struct.pack("B", r_len)
if r & 0x80:
data += b"\x00"
r_len -= 1
data += signature[0:r_len]
if r & 0x80:
r_len += 1
data += b"\x02" + struct.pack("B", s_len)
if s & 0x80:
data += b"\x00"
s_len -= 1
data += signature[s_len:]
if s & 0x80:
s_len += 1
return 21 + r_len + s_len
# pylint: disable=too-many-arguments
def get_issuer_or_subject(data, country, state_prov, locality,
org, org_unit, common):
"""Appends issuer or subject, if they exist, to data."""
if country:
get_name(country, 0x06, data)
if state_prov:
get_name(state_prov, 0x08, data)
if locality:
get_name(locality, 0x07, data)
if org:
get_name(org, 0x0a, data)
if org_unit:
get_name(org_unit, 0x0b, data)
if common:
get_name(common, 0x03, data)
def get_name(name, obj_type, data):
"""Appends ASN.1 string in form: set -> seq -> objid -> string
:param str name: String to append to buffer.
:param int obj_type: Object identifier type.
:param bytearray data: Buffer to write to.
"""
# ASN.1 SET
data += b"\x31" + struct.pack("B", len(name) + 9)
# ASN.1 SEQUENCE
data += b"\x30" + struct.pack("B", len(name) + 7)
# ASN.1 OBJECT IDENTIFIER
data += b"\x06\x03\x55\x04" + struct.pack("B", obj_type)
# ASN.1 PRINTABLE STRING
data += b"\x13" + struct.pack("B", len(name))
data.extend(name)
return len(name) + 11
def get_version(data):
"""Appends X.509 version to data."""
# If no extensions are present, but a UniqueIdentifier
# is present, the version SHOULD be 2 (value is 1) [4-1-2]
data += b"\x02\x01\x00"
def get_sequence_header(length, data):
"""Appends sequence header to provided data."""
data += b"\x30"
if length > 255:
data += b"\x82"
data.append((length >> 8) & 0xff)
elif length > 127:
data += b"\x81"
length_byte = struct.pack("B", (length) & 0xff)
data += length_byte
def get_public_key(data, public_key):
"""Appends public key subject and object identifiers."""
# Subject: Public Key
data += b"\x30" + struct.pack("B", (0x59) & 0xff) + b"\x30\x13"
# Object identifier: EC Public Key
data += b"\x06\x07\x2a\x86\x48\xce\x3d\x02\x01"
# Object identifier: PRIME 256 v1
data += b"\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04"
# Extend the buffer by the public key
data += public_key
def get_signature_length(signature):
"""Return length of ECDSA signature.
:param bytearray signature: Signed SHA256 hash.
"""
r = signature[0]
s = signature[32]
r_len = 32
s_len = 32
while (r == 0x00 and r_len > 1):
r += 1
r_len -= 1
if r & 0x80:
r_len += 1
while (s == 0x00 and s_len > 1):
s += 1
s_len -= 1
if s & 0x80:
s_len += 1
return 21 + r_len + s_len
def get_sequence_header_length(seq_header_len):
"""Returns length of SEQUENCE header."""
if seq_header_len > 255:
return 4
if seq_header_len > 127:
return 3
return 2
def issuer_or_subject_length(country, state_prov, city, org, org_unit, common):
"""Returns total length of provided certificate information."""
tot_len = 0
if country:
tot_len += 11 + len(country)
if state_prov:
tot_len += 11 + len(state_prov)
if city:
tot_len += 11 + len(city)
if org:
tot_len += 11 + len(org)
if org_unit:
tot_len += 11 + len(org_unit)
if common:
tot_len += 11 + len(common)
else:
raise TypeError("Provided length must be > 0")
return tot_len

View file

@ -0,0 +1,169 @@
# Copyright (c) 2018 Arduino SA. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# The MIT License (MIT)
#
# Copyright (c) 2019 Brent Rubell for Adafruit Industries
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_atecc_cert_util`
================================================================================
Certification Generation and Helper Utilities for the Adafruit_ATECC Module.
* Author(s): Brent Rubell
Implementation Notes
--------------------
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
"""
from adafruit_binascii import b2a_base64
import adafruit_atecc.adafruit_atecc_asn1 as asn1
class CSR:
"""Certificate Signing Request Builder.
:param adafruit_atecc atecc: ATECC module.
:param slot_num: ATECC module slot (from 0 to 4).
:param bool private_key: Generate a new private key in selected slot?
:param str country: 2-letter country code.
:param str state_prov: State or Province name,
:param str city: City name.
:param str org: Organization name.
:param str org_unit: Organizational unit name.
"""
# pylint: disable=too-many-arguments, too-many-instance-attributes
def __init__(self, atecc, slot_num, private_key, country, state_prov,
city, org, org_unit):
self._atecc = atecc
self.private_key = private_key
self._slot = slot_num
self._country = country
self._state_province = state_prov
self._locality = city
self._org = org
self._org_unit = org_unit
self._common = self._atecc.serial_number
self._version_len = 3
self._cert = None
self._key = None
def generate_csr(self):
"""Generates and returns a certificate signing request."""
self._csr_begin()
csr = self._csr_end()
return csr
def _csr_begin(self):
"""Initializes CSR generation. """
assert 0 <= self._slot <= 4, "Provided slot must be between 0 and 4."
# Create a new key
self._key = bytearray(64)
if self.private_key:
self._atecc.gen_key(self._key, self._slot, self.private_key)
return
self._atecc.gen_key(self._key, self._slot, self.private_key)
def _csr_end(self):
"""Generates and returns
a certificate signing request as a base64 string."""
len_issuer_subject = asn1.issuer_or_subject_length(self._country, self._state_province,
self._locality, self._org,
self._org_unit, self._common)
len_sub_header = asn1.get_sequence_header_length(len_issuer_subject)
len_csr_info = self._version_len + len_issuer_subject
len_csr_info += len_sub_header + 91 + 2
len_csr_info_header = asn1.get_sequence_header_length(len_csr_info)
# CSR Info Packet
csr_info = bytearray()
# Append CSR Info --> [0:2]
asn1.get_sequence_header(len_csr_info, csr_info)
# Append Version --> [3:5]
asn1.get_version(csr_info)
# Append Subject --> [6:7]
asn1.get_sequence_header(len_issuer_subject, csr_info)
# Append Issuer or Subject
asn1.get_issuer_or_subject(csr_info, self._country, self._state_province,
self._locality, self._org, self._org_unit, self._common)
# Append Public Key
asn1.get_public_key(csr_info, self._key)
# Terminator
csr_info += b"\xa0\x00"
# Init. SHA-256 Calculation
csr_info_sha_256 = bytearray(64)
self._atecc.sha_start()
for i in range(0, len_csr_info + len_csr_info_header, 64):
chunk_len = (len_csr_info_header + len_csr_info) - i
if chunk_len > 64:
chunk_len = 64
if chunk_len == 64:
self._atecc.sha_update(csr_info[i:i+64])
else:
csr_info_sha_256 = self._atecc.sha_digest(csr_info[i:])
# Sign the SHA256 Digest
signature = bytearray(64)
signature = self._atecc.ecdsa_sign(self._slot, csr_info_sha_256)
# Calculations for signature and csr length
len_signature = asn1.get_signature_length(signature)
len_csr = len_csr_info_header + len_csr_info + len_signature
asn1.get_sequence_header_length(len_csr)
# append signature to csr
csr = bytearray()
asn1.get_sequence_header(len_csr, csr)
# append csr_info
csr += csr_info
asn1.get_signature(signature, csr)
# encode and return
csr = b2a_base64(csr)
return csr

BIN
docs/_static/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

8
docs/api.rst Normal file
View file

@ -0,0 +1,8 @@
.. If you created a package, create one automodule per module in the package.
.. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py)
.. use this format as the module name: "adafruit_foo.foo"
.. automodule:: adafruit_atecc
:members:

160
docs/conf.py Normal file
View file

@ -0,0 +1,160 @@
# -*- coding: utf-8 -*-
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
# -- General configuration ------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.napoleon',
'sphinx.ext.todo',
]
# TODO: Please Read!
# Uncomment the below if you use native CircuitPython modules such as
# digitalio, micropython and busio. List the modules you use. Without it, the
# autodoc module docs will fail to generate with a warning.
# autodoc_mock_imports = ["digitalio", "busio"]
intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'BusDevice': ('https://circuitpython.readthedocs.io/projects/busdevice/en/latest/', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)}
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Adafruit ATECC Library'
copyright = u'2019 Brent Rubell'
author = u'Brent Rubell'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'1.0'
# The full version, including alpha/beta/rc tags.
release = u'1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.env', 'CODE_OF_CONDUCT.md']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#
default_role = "any"
# If true, '()' will be appended to :func: etc. cross-reference text.
#
add_function_parentheses = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# If this is True, todo emits a warning for each TODO entries. The default is False.
todo_emit_warnings = True
napoleon_numpy_docstring = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
if not on_rtd: # only import and set the theme if we're building docs locally
try:
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), '.']
except:
html_theme = 'default'
html_theme_path = ['.']
else:
html_theme_path = ['.']
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# The name of an image file (relative to this directory) to use as a favicon of
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#
html_favicon = '_static/favicon.ico'
# Output file base name for HTML help builder.
htmlhelp_basename = 'AdafruitAteccLibrarydoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'AdafruitATECCLibrary.tex', u'AdafruitATECC Library Documentation',
author, 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'AdafruitATECClibrary', u'Adafruit ATECC Library Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'AdafruitATECCLibrary', u'Adafruit ATECC Library Documentation',
author, 'AdafruitATECCLibrary', 'One line description of project.',
'Miscellaneous'),
]

8
docs/examples.rst Normal file
View file

@ -0,0 +1,8 @@
Simple test
------------
Ensure your device works with this simple test.
.. literalinclude:: ../examples/atecc_simpletest.py
:caption: examples/atecc_simpletest.py
:linenos:

51
docs/index.rst Normal file
View file

@ -0,0 +1,51 @@
.. include:: ../README.rst
Table of Contents
=================
.. toctree::
:maxdepth: 4
:hidden:
self
.. toctree::
:caption: Examples
examples
.. toctree::
:caption: API Reference
:maxdepth: 3
api
.. toctree::
:caption: Tutorials
.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave
the toctree above for use later.
.. toctree::
:caption: Related Products
.. todo:: Add any product links here. If there are none, then simply delete this todo and leave
the toctree above for use later.
.. toctree::
:caption: Other Links
Download <https://github.com/adafruit/Adafruit_CircuitPython_ATECC/releases/latest>
CircuitPython Reference Documentation <https://circuitpython.readthedocs.io>
CircuitPython Support Forum <https://forums.adafruit.com/viewforum.php?f=60>
Discord Chat <https://adafru.it/discord>
Adafruit Learning System <https://learn.adafruit.com>
Adafruit Blog <https://blog.adafruit.com>
Adafruit Store <https://www.adafruit.com>
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

57
examples/atecc_csr.py Executable file
View file

@ -0,0 +1,57 @@
import board
import busio
import time
import adafruit_ssd1306
from adafruit_atecc.adafruit_atecc import ATECC, _WAKE_CLK_FREQ, CFG_TLS
import adafruit_atecc.adafruit_atecc_cert_util as cert_utils
# -- Enter your configuration below -- #
# Lock the ATECC module when the code is run?
LOCK_ATECC = True
# 2-letter country code
MY_COUNTRY = "US"
# State or Province Name
MY_STATE = "New York"
# City Name
MY_CITY = "New York"
# Organization Name
MY_ORG = "Adafruit"
# Organizational Unit Name
MY_SECTION = "Crypto"
# Which ATECC slot (0-4) to use
ATECC_SLOT = 0
# Generate new private key, or use existing key
GENERATE_PRIVATE_KEY = False
# -- END Configuration, code below -- #
# Initialize the i2c bus
i2c = busio.I2C(board.SCL, board.SDA,
frequency=_WAKE_CLK_FREQ)
# Initialize a new atecc object
atecc = ATECC(i2c)
print("ATECC Serial Number: ", atecc.serial_number)
if not atecc.locked:
if not LOCK_ATECC:
raise RuntimeError("The ATECC is not locked, set LOCK_ATECC to True in your code.py to unlock it.")
print("Writing default configuration to the device...")
atecc.write_config(CFG_TLS)
print("Wrote configuration, locking ATECC module...")
# Lock ATECC config, data, and otp zones
atecc.lock_all_zones()
print("ATECC locked!")
print("Generating Certificate Signing Request...")
# Initialize a certificate signing request with provided info
csr = cert_utils.CSR(atecc, ATECC_SLOT, GENERATE_PRIVATE_KEY, MY_COUNTRY, MY_STATE,
MY_CITY, MY_ORG, MY_SECTION)
# Generate CSR
my_csr = csr.generate_csr()
print("-----BEGIN CERTIFICATE REQUEST-----\n")
print(my_csr.decode('utf-8'))
print("-----END CERTIFICATE REQUEST-----")

View file

@ -0,0 +1,10 @@
# testing adafruit atecc module
import board
import adafruit_atecc
import busio
_WAKE_CLK_FREQ = 100000 # slower clock speed
i2c = busio.I2C(board.SCL, board.SDA, frequency=_WAKE_CLK_FREQ)
adafruit_atecc.ATECCx08A(i2c)

2
requirements.txt Normal file
View file

@ -0,0 +1,2 @@
Adafruit-Blinka
adafruit-circuitpython-busdevice

65
setup.py Normal file
View file

@ -0,0 +1,65 @@
"""A setuptools based setup module.
See:
https://packaging.python.org/en/latest/distributing.html
https://github.com/pypa/sampleproject
"""
from setuptools import setup, find_packages
# To use a consistent encoding
from codecs import open
from os import path
here = path.abspath(path.dirname(__file__))
# Get the long description from the README file
with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
long_description = f.read()
setup(
name='adafruit-circuitpython-atecc',
use_scm_version=True,
setup_requires=['setuptools_scm'],
description='Driver for Microchip's ATECCx08 cryptographic co-processors with secure hardware-based key storage',
long_description=long_description,
long_description_content_type='text/x-rst',
# The project's main homepage.
url='https://github.com/adafruit/Adafruit_CircuitPython_ATECC',
# Author details
author='Adafruit Industries',
author_email='circuitpython@adafruit.com',
install_requires=[
'Adafruit-Blinka',
'adafruit-circuitpython-busdevice'
],
# Choose your license
license='MIT',
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'Topic :: Software Development :: Libraries',
'Topic :: System :: Hardware',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
],
# What does your project relate to?
keywords='adafruit blinka circuitpython micropython atecc atecc, microchip, secure, '
'element, key, co-processor',
# You can just specify the packages manually here if your project is
# simple. Or you can use find_packages().
# TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER,
# CHANGE `py_modules=['...']` TO `packages=['...']`
py_modules=['adafruit_atecc'],
)