Add GoogleKeymaster implementation.
Squashed commit of the following:
commit 83804621f399f4bcab9281c0eecfcbefe56b054e
Author: Shawn Willden <swillden@google.com>
Date: Thu Aug 28 13:47:40 2014 -0600
Add more logging methods, to distinguish message priorities.
Change-Id: I2308af04eb699fb896d3e701a659945451304ddf
commit 2241bf042c54aa5099bbb99a76e9de0162b92d07
Author: Shawn Willden <swillden@google.com>
Date: Thu Aug 28 09:59:53 2014 -0600
Make keymaster_key_blob_t key_material const, to help prevent
accidental modification.
Change-Id: I505779ed677730d72c310fca1626b1093c71e90d
commit c0c85cf5104f698054d59b28c68f0461ebec2233
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 27 19:56:43 2014 -0600
Make GoogleKeymaster's logger public.
Change-Id: Idbe17e6b6ae7ab403d199323b8e20979a2e56c97
commit e46a43f403ba4fa66c505684ac173c1fa7c35584
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 27 10:35:36 2014 -0600
Add some test TODOs
Change-Id: Id209182f0d153d67dca09846be4df5ef02b74cf2
commit 81effc68a04810b76f0b10594d92df4ffbf35c6c
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 27 10:08:46 2014 -0600
Finish key import implementation.
This is the last bit of GoogleKeymaster that remained incomplete (for
the v0.3 functionality).
Change-Id: I27be52ae032883c004b2df21f0c7b229af512922
commit 368bc7749eaa2e1321d552e45a96d83b5500ba47
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 27 06:45:34 2014 -0600
Move key_blob.h in to include/keymaster, to export it.
Change-Id: If28db94840557e6ca3019b7bcf7b5f29f0ff6cf7
commit b3407024ccfec72831a76b9772a496ab81fc33ce
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 27 06:30:52 2014 -0600
Fix minor bug in operations.
Note that this bug isn't exposed by the v0.3 API, since it
doesn't allow multi-step signing/verification operations.
Change-Id: I18554e7e1017ed83d3708c134f72cf1d34857437
commit 960dd0749380857988c07d40feae7f252bb2209a
Author: Shawn Willden <swillden@google.com>
Date: Tue Aug 26 17:24:28 2014 -0600
Define remaining commands.
Change-Id: Ic68f172efa2b401bee1dcf14cbb94f72b86b31ae
commit 98d9b92547a9a7553b99e3e941a4175926f95b62
Author: Shawn Willden <swillden@google.com>
Date: Tue Aug 26 08:14:10 2014 -0600
Reorganize system/keymaster.
This CL moves the includes that should be exported to include/ and
removes the trusty-specific code (some of which is moving to
hardware/google and some of which is moving to the trusty tree.)
Change-Id: Ie4fabf6b5c5f36b50c2f5ff356548ca2e9140fcb
commit 407d41282d6b0a7f2d6e2826d44a58b016a5d844
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 25 16:49:13 2014 -0600
Implement TrustyKeymaster key generation, plus tests.
Change-Id: I085be101c735d136e7d5b2915a9510102722e695
commit 2f3be368e5ad911cc0b014421dd3682130260ffc
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 25 11:31:39 2014 -0600
Add the beginnings of logging infrastructure.
Change-Id: Ic36134402bfbb098d2242c463a3b4265d1d65209
commit f2282b3c6690ccfaa7878886f01693ef4f0b3bed
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 25 06:49:54 2014 -0600
Add some "fuzzing" tests for deserialization, and fixes for all of the
problems discovered.
Change-Id: I050344f6c6d0a19b7f3304d23729b4ca71c05042
commit b663b61f00b1a51a2535520aa726f788fffdf34b
Author: Shawn Willden <swillden@google.com>
Date: Thu Aug 21 18:54:45 2014 -0600
Fix OTE bug for keymaster.
Change-Id: I71d222ad9ed54098492dcc7b7f16d7c72d42923d
commit 2a4a48d51a057b33f83f09efae09bb354ec6a801
Author: Shawn Willden <swillden@google.com>
Date: Thu Aug 21 16:04:53 2014 -0600
Remove unused variable.
Change-Id: I6327f2092c23bd7aaae8aeda48915c3ac9259080
commit 5acebf56729f1307c4971a601ab38f6a320d0562
Author: Shawn Willden <swillden@google.com>
Date: Thu Aug 21 15:46:35 2014 -0600
Allow GoogleKeymaster caller to specify creation time, since Trusty can't.
Change-Id: Ia843704da726521f36ff4b954dcc6c1b6286f7be
commit 81d3b4fe9a86bae8bac2fb98877af04f39d11250
Author: Shawn Willden <swillden@google.com>
Date: Thu Aug 21 12:16:26 2014 -0600
Modify to be keymaster v0.3 compatible and add some debugging output.
Change-Id: Idc3e15b1af57fa9ddbdfc3a46f32f100b146fd83
commit 235cd7e70389c42ce26b832ad8ddcfefbc812fb7
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 20 17:37:08 2014 -0600
Remove Android.mk to unbreak AOSP build.
Change-Id: I9f78fee36874ff3681b3cc55c2081c1cae8cb343
commit 62de26672193373972f2ce968b51cf8335f118f9
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 20 14:14:49 2014 -0600
Trusty test app.
Note that this code is in the wrong place. The right place is still
begin created so I'm putting them here for now. We'll move them when
it's ready.
Change-Id: Iab7384a531fd4a935dbeef0aebf2652eb06f6e03
commit 437fbd195e7de57b7dc0c449c04458bd90ef50de
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 20 11:59:49 2014 -0600
Add key importing, RSA only.
Change-Id: I639e797939a28b2b2a815541c9926dc194657c54
commit 060e9b04445f91db31b2b412f944aa402b9e1a8d
Author: Shawn Willden <swillden@google.com>
Date: Tue Aug 19 20:27:45 2014 -0600
Make keymaster_defs.h compatible with v0.3 keymaster.h.
Change-Id: I53ae63c9fec3cc7131a1f1373e8bf4448252cc79
commit 3d3e1d388480a2c242a39f4bc5adf000728c8da5
Author: Shawn Willden <swillden@google.com>
Date: Tue Aug 19 16:28:25 2014 -0600
Add *.massif to .gitignore.
Change-Id: I77be33411f2cef6e0a2046489a8c153985b8040f
commit f268d742dbefe0e84b4046db7669c4ffbc110f7d
Author: Shawn Willden <swillden@google.com>
Date: Tue Aug 19 15:36:26 2014 -0600
Refactor export to use new key infrastructure, and work with all key types.
Change-Id: Ie1f621f9db855665d57cde93c24881415de33ca2
commit d67afae61f822463120c36fea846362450dd7d71
Author: Shawn Willden <swillden@google.com>
Date: Tue Aug 19 12:36:27 2014 -0600
Refactor key and operation details.
Change-Id: I80267e6184955ecd98b08ceab91f4afd50c67614
commit 370121346777e13437c275fbe7a975d899cc325c
Author: Shawn Willden <swillden@google.com>
Date: Tue Aug 19 08:15:57 2014 -0600
Added AuthorizationSet push_back method that takes a set.
This is needed for some key refactoring work. Also did some
AuthorizationSet refactoring here.
Change-Id: I681a2793838c1d68b22dc2a39258c30d7ab117bc
commit ffd790c9846b93d0af7b28b1998a9f8f8aa076a4
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 18 21:20:06 2014 -0600
Add key export, RSA only. (rileyspahn@google.com implemented).
Change-Id: I55c3497a1dc5360bfc8518a388b73776388a47e4
commit 5ac2f8ff7c82d2b5c2dd17273ce58c7806df0ec2
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 18 15:33:10 2014 -0600
Add ECDSA signing and verification.
Change-Id: Ic5345ebe6e79e3ee764c3a729dc551c61b87c79b
commit c3864dde9ffa9a52bb60802664e1cab1de5c0287
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 18 15:20:01 2014 -0600
Add ECDSA key generation.
Change-Id: I68a1d46e617124a8ccb7a4b2c09baae89603a5e0
commit 5b41ca2d7f106cc49315a8ecbac2f51fb445fb57
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 18 14:29:14 2014 -0600
Implement DSA signing and verification.
Change-Id: I22a1c4518bcd393d1183e10a906600488ec8e9c8
commit 61644f3d8a7f2374fd579cdeb76e841d4bc0efe0
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 18 13:43:14 2014 -0600
Small refactor of signing/verification tests, to facilitate DSA testing.
Change-Id: I68a8f83d85993f320a0e05e39cefc56bb2823b7d
commit 28e41475a2559824a0f3f2c850ed92a65c586f95
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 18 13:35:22 2014 -0600
Add DSA key generation.
Also refactor RSA key generation a bit.
Change-Id: I838ff58210f0a3be41f04c7e945e998751fca9f5
commit 802bb29cc190fb610367fdb7236ef9c2e93826f2
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 18 10:46:29 2014 -0600
Refactor GoogleKeymaster to move openssl RSA key-generation operations
to RsaOperation.
Change-Id: Id6c66bd431cf3f8895113108027920ffafef578b
commit da8485ea42e53839579575ec9fc2b49f7cf1a1f9
Author: Shawn Willden <swillden@google.com>
Date: Sun Aug 17 08:00:01 2014 -0600
Flesh out all remaining message structures, with serialization.
Still didn't implement recsoping messages, since they're not relevant
for 0.3.
Change-Id: Ia05a04349ff0329557b01d14f6c501540cc74439
commit 172f8c9be706e27f43022063bbc7f4b0177583ac
Author: Shawn Willden <swillden@google.com>
Date: Sun Aug 17 07:50:34 2014 -0600
Housekeeping CL.
Make variable names and formatting more consistent. Also, add doxygen comments to Serializable.
Change-Id: I24ff138611111acf96112be74a04cc35f04908e0
commit 43e999eed16a78cb6d48f1dfd11b33dee4d80a1a
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 13 13:29:50 2014 -0600
Add RSA verification.
Change-Id: Ie9ac37dba7ead62b0ca17054bbf6d2744cea5946
commit 1615f2ecf2537db7b302eb9b5be4394f711fd815
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 13 10:37:40 2014 -0600
Add RSA signing support.
Change-Id: Icdcbd978d58c8764618b995571d1e8b649959ef0
commit 60ebf8e49977683bc8cabe4609ce8b0405db7711
Author: Shawn Willden <swillden@google.com>
Date: Tue Aug 12 11:43:10 2014 -0600
Change to enable KEYMASTER_NAME_TAGS globally.
Selectively changing the size of the TypedTag structure causes subtle
problems when inlining is disabled (e.g. -O0).
Change-Id: I7f87a5a34eb574b0adaa8492f51fbcf2b172b4ca
commit ebf627f0b50c0979e6cf53668464297703371eba
Author: Shawn Willden <swillden@google.com>
Date: Tue Aug 12 11:15:29 2014 -0600
Allow building tests with Clang, and fix some bugs Clang diagnosed.
Change-Id: Ie213deadabdb9c84d4ea1d2f69b1beaa87165717
commit 7b83f18c17b5820f8fcc177fc58eb34cf7ef6d05
Author: Shawn Willden <swillden@google.com>
Date: Tue Aug 12 07:35:37 2014 -0600
Add .gitignore.
Change-Id: I08e9599c699debaddf815e9f65a781920c241e47
commit 7636471bd1c553ac179f0dddc17133491d0e1faf
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 11 17:48:04 2014 -0600
Implement GetKeyCharacteristics.
Still need to add serialization to the messages.
Change-Id: I572c48474bf4d4f553d53cad475b57fa8937a02a
commit 74aff357261879dfa8366528a42c59b042c7bd05
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 11 14:08:31 2014 -0600
Implement and use secure memset to clear sensitive buffers.
Ordinary memset can be optimized away, leaking sensitive data to other
processes.
Change-Id: If4b51e342ef1f21d7e5fa8907bb0534b17bf295b
commit 39b970bea81461af88f83e1c2329eb1b0f4d2e73
Author: Shawn Willden <swillden@google.com>
Date: Mon Aug 11 09:11:21 2014 -0600
Handle "hidden" authorization tags correctly.
Change-Id: I9fa18f8ab465a2faa0f358e12f72daf18ca02fe7
commit 834e80747cbb960f8a4028c5c8604bf5218ecdb9
Author: Shawn Willden <swillden@google.com>
Date: Sat Aug 9 16:38:53 2014 -0600
Improve authorization_set test coverage.
Change-Id: I8dd1830db8c19be07cef768c63c9ecfa3e16ae21
commit 8d336ae10df66da4c0433f17c2d42e85baea32c5
Author: Shawn Willden <swillden@google.com>
Date: Sat Aug 9 15:47:05 2014 -0600
Change authorization set serialization approach to ensure that 32 vs 64
bit size and alignment differences don't cause problems.
Change-Id: I4a308cfac782161db2f1456adb2d6a56537e61f1
commit 4db3fbdda292c0c3120dfe160c1b49670aa18600
Author: Shawn Willden <swillden@google.com>
Date: Fri Aug 8 22:13:44 2014 -0600
Refactor and expand KeyBlob capabilities.
KeyBlob's responsibilities have grown, it makes sense to make it a
first-class class, and to use the Serializable infrastructure.
Change-Id: I76a8dac5b4b4fe47d6677c27ab9eba2755f02dfe
commit 58e1a5486219a1be9264d4e863a9dd3e393906c3
Author: Shawn Willden <swillden@google.com>
Date: Fri Aug 8 21:58:29 2014 -0600
Eliminate in-place serialization.
Not doing in-place serialization will result in greater heap
consumption, but eliminates many alignment-related issues. Given more
time, I'd prefer to solve the alignment issues by computing and
inserting appropriate padding, but we don't have the time.
Change-Id: I86e4bdf57263db26c73372ae2963f21c5f5f00aa
commit 301646f55214ed693e79c7869d54033a74641907
Author: Shawn Willden <swillden@google.com>
Date: Fri Aug 8 21:44:10 2014 -0600
Correct the rest of the #include guard defines.
Change-Id: I8f2bf58f2bebb3f06ae4cd0f90f79d85acd42155
commit b10f3b26af1e3b382d9ef361b3eb5279d16a9c05
Author: Shawn Willden <swillden@google.com>
Date: Thu Aug 7 08:11:51 2014 -0600
Correct #include guard defines.
Change-Id: Ie05c78490f6f3fe8c194cc00c0c87e117508054d
commit 3879f8641d044cf53f4163dc5c46a1399006eb03
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 6 14:40:48 2014 -0600
Fix inclusion error.
Change-Id: I8f49b7e1547575e0bc4616836ed00d6e02c22879
commit f5bebad1ce284d8df37d3469f6b93ecc1522741c
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 6 14:17:53 2014 -0600
Put keymaster_defs.h in system/keymaster.
This file will eventually live in hardware/libhardware/include/hardware,
but for now it's convenient to have it here.
Change-Id: Ia25b59f905db5a54c2e69b5fb745dbd08d0fe303
commit 128ffe07c723d8ffe2d5ea528ba5f64436c8a55a
Author: Shawn Willden <swillden@google.com>
Date: Wed Aug 6 12:31:33 2014 -0600
Add GoogleKeymaster. Very incomplete.
Change-Id: I53542c7132bd1a04afee93f3247b88ed7ed0bedc
commit 5ada7b6c525d2bfd5b556a698ccb11db23e052bb
Author: Shawn Willden <swillden@google.com>
Date: Tue Jul 29 09:44:17 2014 -0600
Add AuthorizationSet class and some supporting utils and a Makefile for
running tests on the dev machine.
Change-Id: I608e660854ace71409dd8bb5395d83dcfbf803c0
commit 7a70abbf29293b30bb1e7ed3a58deb40f8774a53
Author: Bill Yi <byi@google.com>
Date: Mon Jul 28 21:38:52 2014 +0000
Initial empty repository
Change-Id: I199c7a0ca076cfdaba1fecf6109d573f3dca5801
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..5747e19
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+IndentCaseLabels: false
+ColumnLimit: 100
+PointerBindsToType: true
+SpacesBeforeTrailingComments: 2
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fa29ce2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+*.d
+*.gcda
+*.o
+*.gcno
+*.run
+*.memcheck
+*.massif
+/authorization_set_test
+/coverage.info
+/coverage/
+/google_keymaster_messages_test
+/google_keymaster_test
+/key_blob_test
+/trusty_keymaster_device_test
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..48a80b1
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2014 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libkeymaster
+LOCAL_SRC_FILES:= \
+ authorization_set.cpp \
+ dsa_operation.cpp \
+ ecdsa_operation.cpp \
+ google_keymaster \
+ google_keymaster_messages.cpp \
+ google_keymaster_utils.cpp \
+ key_blob.cpp \
+ ocb.c \
+ rsa_operation.cpp \
+ serializable.cpp
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ external/openssl/include
+LOCAL_SHARED_LIBRARIES := libcrypto
+LOCAL_CFLAGS = -Wall -Werror
+LOCAL_MODULE_TAGS := optional
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_STATIC_LIBRARY)
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a533adc
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,149 @@
+BASE=../..
+SUBS=system/core \
+ hardware/libhardware \
+ external/gtest
+GTEST=$(BASE)/external/gtest
+
+INCLUDES=$(foreach dir,$(SUBS),-I $(BASE)/$(dir)/include) \
+ -I $(BASE)/libnativehelper/include/nativehelper \
+ -I $(GTEST) -Iinclude
+
+ifdef USE_CLANG
+CC=/usr/bin/clang
+CXX=/usr/bin/clang
+CLANG_TEST_DEFINE=-DKEYMASTER_CLANG_TEST_BUILD
+COMPILER_SPECIFIC_ARGS=-std=c++11 $(CLANG_TEST_DEFINE)
+else
+COMPILER_SPECIFIC_ARGS=-std=c++0x -fprofile-arcs
+endif
+
+CPPFLAGS=$(INCLUDES) -g -O0 -MD
+CXXFLAGS=-Wall -Werror -Wno-unused -Winit-self -Wpointer-arith -Wunused-parameter \
+ -Wmissing-declarations -ftest-coverage \
+ -Wno-deprecated-declarations -fno-exceptions -DKEYMASTER_NAME_TAGS \
+ $(COMPILER_SPECIFIC_ARGS)
+LDLIBS=-lcrypto -lpthread -lstdc++
+
+CPPSRCS=\
+ asymmetric_key.cpp \
+ authorization_set.cpp \
+ authorization_set_test.cpp \
+ dsa_operation.cpp \
+ ecdsa_operation.cpp \
+ google_keymaster.cpp \
+ google_keymaster_messages.cpp \
+ google_keymaster_messages_test.cpp \
+ google_keymaster_test.cpp \
+ google_keymaster_test_utils.cpp \
+ google_keymaster_utils.cpp \
+ key.cpp \
+ key_blob.cpp \
+ key_blob_test.cpp \
+ rsa_operation.cpp \
+ serializable.cpp
+CCSRCS=$(GTEST)/src/gtest-all.cc
+CSRCS=ocb.c
+
+OBJS=$(CPPSRCS:.cpp=.o) $(CCSRCS:.cc=.o) $(CSRCS:.c=.o)
+DEPS=$(CPPSRCS:.cpp=.d) $(CCSRCS:.cc=.d) $(CSRCS:.c=.d)
+
+LINK.o=$(LINK.cc)
+
+BINARIES=authorization_set_test \
+ google_keymaster_test \
+ google_keymaster_messages_test \
+ key_blob_test
+
+.PHONY: coverage memcheck massif clean run
+
+%.run: %
+ ./$<
+ touch $@
+
+run: $(BINARIES:=.run)
+
+coverage: coverage.info
+ genhtml coverage.info --output-directory coverage
+
+coverage.info: run
+ lcov --capture --directory=. --output-file coverage.info
+
+%.coverage : %
+ $(MAKE) clean && $(MAKE) $<
+ ./$<
+ lcov --capture --directory=. --output-file coverage.info
+ genhtml coverage.info --output-directory coverage
+
+#UNINIT_OPTS=--track-origins=yes
+UNINIT_OPTS=--undef-value-errors=no
+
+MEMCHECK_OPTS=--leak-check=full \
+ --show-reachable=yes \
+ --vgdb=full \
+ $(UNINIT_OPTS) \
+ --error-exitcode=1
+
+MASSIF_OPTS=--tool=massif \
+ --stacks=yes
+
+%.memcheck : %
+ valgrind $(MEMCHECK_OPTS) ./$< && \
+ touch $@
+
+%.massif : %
+ valgrind $(MASSIF_OPTS) --massif-out-file=$@ ./$<
+
+memcheck: $(BINARIES:=.memcheck)
+
+massif: $(BINARIES:=.massif)
+
+authorization_set_test: authorization_set_test.o \
+ authorization_set.o \
+ google_keymaster_test_utils.o \
+ serializable.o \
+ $(GTEST)/src/gtest-all.o
+
+key_blob_test: key_blob_test.o \
+ authorization_set.o \
+ google_keymaster_test_utils.o \
+ key_blob.o \
+ ocb.o \
+ serializable.o \
+ $(GTEST)/src/gtest-all.o
+
+google_keymaster_messages_test: google_keymaster_messages_test.o \
+ authorization_set.o \
+ google_keymaster_messages.o \
+ google_keymaster_test_utils.o \
+ google_keymaster_utils.o \
+ serializable.o \
+ $(GTEST)/src/gtest-all.o
+
+google_keymaster_test: google_keymaster_test.o \
+ asymmetric_key.o \
+ authorization_set.o \
+ dsa_operation.o \
+ ecdsa_operation.o \
+ google_keymaster.o \
+ google_keymaster_messages.o \
+ google_keymaster_test_utils.o \
+ google_keymaster_utils.o \
+ key.o \
+ key_blob.o \
+ ocb.o \
+ rsa_operation.o \
+ serializable.o \
+ $(GTEST)/src/gtest-all.o
+
+$(GTEST)/src/gtest-all.o: CXXFLAGS:=$(subst -Wmissing-declarations,,$(CXXFLAGS))
+ocb.o: CFLAGS=$(CLANG_TEST_DEFINE)
+
+clean:
+ rm -f $(OBJS) $(DEPS) $(BINARIES) \
+ $(BINARIES:=.run) $(BINARIES:=.memcheck) $(BINARIES:=.massif) \
+ *gcno *gcda coverage.info
+ rm -rf coverage
+
+-include $(CPPSRCS:.cpp=.d)
+-include $(CCSRCS:.cc=.d)
+
diff --git a/ae.h b/ae.h
new file mode 100644
index 0000000..864d349
--- /dev/null
+++ b/ae.h
@@ -0,0 +1,164 @@
+/* ---------------------------------------------------------------------------
+ *
+ * AEAD API 0.12 - 23-MAY-2012
+ *
+ * This file gives an interface appropriate for many authenticated
+ * encryption with associated data (AEAD) implementations. It does not try
+ * to accommodate all possible options or limitations that an implementation
+ * might have -- you should consult the documentation of your chosen
+ * implementation to find things like RFC 5116 constants, alignment
+ * requirements, whether the incremental interface is supported, etc.
+ *
+ * This file is in the public domain. It is provided "as is", without
+ * warranty of any kind. Use at your own risk.
+ *
+ * Comments are welcome: Ted Krovetz <ted@krovetz>.
+ *
+ * ------------------------------------------------------------------------ */
+
+#ifndef _AE_H_
+#define _AE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* --------------------------------------------------------------------------
+ *
+ * Constants
+ *
+ * ----------------------------------------------------------------------- */
+
+/* Return status codes: Negative return values indicate an error occurred.
+ * For full explanations of error values, consult the implementation's
+ * documentation. */
+#define AE_SUCCESS (0) /* Indicates successful completion of call */
+#define AE_INVALID (-1) /* Indicates bad tag during decryption */
+#define AE_NOT_SUPPORTED (-2) /* Indicates unsupported option requested */
+
+/* Flags: When data can be processed "incrementally", these flags are used
+ * to indicate whether the submitted data is the last or not. */
+#define AE_FINALIZE (1) /* This is the last of data */
+#define AE_PENDING (0) /* More data of is coming */
+
+/* --------------------------------------------------------------------------
+ *
+ * AEAD opaque structure definition
+ *
+ * ----------------------------------------------------------------------- */
+
+typedef struct _ae_ctx ae_ctx;
+
+/* --------------------------------------------------------------------------
+ *
+ * Data Structure Routines
+ *
+ * ----------------------------------------------------------------------- */
+
+ae_ctx* ae_allocate(void* misc); /* Allocate ae_ctx, set optional ptr */
+void ae_free(ae_ctx* ctx); /* Deallocate ae_ctx struct */
+int ae_clear(ae_ctx* ctx); /* Undo initialization */
+int ae_ctx_sizeof(void); /* Return sizeof(ae_ctx) */
+/* ae_allocate() allocates an ae_ctx structure, but does not initialize it.
+ * ae_free() deallocates an ae_ctx structure, but does not zero it.
+ * ae_clear() zeroes sensitive values associated with an ae_ctx structure
+ * and deallocates any auxiliary structures allocated during ae_init().
+ * ae_ctx_sizeof() returns sizeof(ae_ctx), to aid in any static allocations.
+ */
+
+/* --------------------------------------------------------------------------
+ *
+ * AEAD Routines
+ *
+ * ----------------------------------------------------------------------- */
+
+int ae_init(ae_ctx* ctx, const void* key, int key_len, int nonce_len, int tag_len);
+/* --------------------------------------------------------------------------
+ *
+ * Initialize an ae_ctx context structure.
+ *
+ * Parameters:
+ * ctx - Pointer to an ae_ctx structure to be initialized
+ * key - Pointer to user-supplied key
+ * key_len - Length of key supplied, in bytes
+ * nonce_len - Length of nonces to be used for this key, in bytes
+ * tag_len - Length of tags to be produced for this key, in bytes
+ *
+ * Returns:
+ * AE_SUCCESS - Success. Ctx ready for use.
+ * AE_NOT_SUPPORTED - An unsupported length was supplied. Ctx is untouched.
+ * Otherwise - Error. Check implementation documentation for codes.
+ *
+ * ----------------------------------------------------------------------- */
+
+int ae_encrypt(ae_ctx* ctx, const void* nonce, const void* pt, int pt_len, const void* ad,
+ int ad_len, void* ct, void* tag, int final);
+/* --------------------------------------------------------------------------
+ *
+ * Encrypt plaintext; provide for authentication of ciphertext/associated data.
+ *
+ * Parameters:
+ * ctx - Pointer to an ae_ctx structure initialized by ae_init.
+ * nonce - Pointer to a nonce_len (defined in ae_init) byte nonce.
+ * pt - Pointer to plaintext bytes to be encrypted.
+ * pt_len - number of bytes pointed to by pt.
+ * ad - Pointer to associated data.
+ * ad_len - number of bytes pointed to by ad.
+ * ct - Pointer to buffer to receive ciphertext encryption.
+ * tag - Pointer to receive authentication tag; or NULL
+ * if tag is to be bundled into the ciphertext.
+ * final - Non-zero if this call completes the plaintext being encrypted.
+ *
+ * If nonce!=NULL then a message is being initiated. If final!=0
+ * then a message is being finalized. If final==0 or nonce==NULL
+ * then the incremental interface is being used. If nonce!=NULL and
+ * ad_len<0, then use same ad as last message.
+ *
+ * Returns:
+ * non-negative - Number of bytes written to ct.
+ * AE_NOT_SUPPORTED - Usage mode unsupported (eg, incremental and/or sticky).
+ * Otherwise - Error. Check implementation documentation for codes.
+ *
+ * ----------------------------------------------------------------------- */
+
+int ae_decrypt(ae_ctx* ctx, const void* nonce, const void* ct, int ct_len, const void* ad,
+ int ad_len, void* pt, const void* tag, int final);
+/* --------------------------------------------------------------------------
+ *
+ * Decrypt ciphertext; provide authenticity of plaintext and associated data.
+ *
+ * Parameters:
+ * ctx - Pointer to an ae_ctx structure initialized by ae_init.
+ * nonce - Pointer to a nonce_len (defined in ae_init) byte nonce.
+ * ct - Pointer to ciphertext bytes to be decrypted.
+ * ct_len - number of bytes pointed to by ct.
+ * ad - Pointer to associated data.
+ * ad_len - number of bytes pointed to by ad.
+ * pt - Pointer to buffer to receive plaintext decryption.
+ * tag - Pointer to tag_len (defined in ae_init) bytes; or NULL
+ * if tag is bundled into the ciphertext. Non-NULL tag is only
+ * read when final is non-zero.
+ * final - Non-zero if this call completes the ciphertext being decrypted.
+ *
+ * If nonce!=NULL then "ct" points to the start of a ciphertext. If final!=0
+ * then "in" points to the final piece of ciphertext. If final==0 or nonce==
+ * NULL then the incremental interface is being used. If nonce!=NULL and
+ * ad_len<0, then use same ad as last message.
+ *
+ * Returns:
+ * non-negative - Number of bytes written to pt.
+ * AE_INVALID - Authentication failure.
+ * AE_NOT_SUPPORTED - Usage mode unsupported (eg, incremental and/or sticky).
+ * Otherwise - Error. Check implementation documentation for codes.
+ *
+ * NOTE !!! NOTE !!! -- The ciphertext should be assumed possibly inauthentic
+ * until it has been completely written and it is
+ * verified that this routine did not return AE_INVALID.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+} /* closing brace for extern "C" */
+#endif
+
+#endif /* _AE_H_ */
diff --git a/asymmetric_key.cpp b/asymmetric_key.cpp
new file mode 100644
index 0000000..a06515b
--- /dev/null
+++ b/asymmetric_key.cpp
@@ -0,0 +1,626 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <keymaster/key_blob.h>
+#include <keymaster/keymaster_defs.h>
+
+#include "asymmetric_key.h"
+#include "dsa_operation.h"
+#include "ecdsa_operation.h"
+#include "openssl_utils.h"
+#include "rsa_operation.h"
+
+namespace keymaster {
+
+const uint32_t RSA_DEFAULT_KEY_SIZE = 2048;
+const uint64_t RSA_DEFAULT_EXPONENT = 65537;
+
+const uint32_t DSA_DEFAULT_KEY_SIZE = 2048;
+
+const uint32_t ECDSA_DEFAULT_KEY_SIZE = 192;
+
+keymaster_error_t AsymmetricKey::LoadKey(const KeyBlob& blob) {
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> evp_key(EVP_PKEY_new());
+ if (evp_key.get() == NULL)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ EVP_PKEY* tmp_pkey = evp_key.get();
+ const uint8_t* key_material = blob.key_material();
+ if (d2i_PrivateKey(evp_key_type(), &tmp_pkey, &key_material, blob.key_material_length()) ==
+ NULL) {
+ return KM_ERROR_INVALID_KEY_BLOB;
+ }
+ if (!EvpToInternal(evp_key.get()))
+ return KM_ERROR_UNKNOWN_ERROR;
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AsymmetricKey::key_material(UniquePtr<uint8_t[]>* material, size_t* size) const {
+ if (material == NULL || size == NULL)
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new());
+ if (pkey.get() == NULL)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ if (!InternalToEvp(pkey.get()))
+ return KM_ERROR_UNKNOWN_ERROR;
+
+ *size = i2d_PrivateKey(pkey.get(), NULL /* key_data*/);
+ if (*size <= 0)
+ return KM_ERROR_UNKNOWN_ERROR;
+
+ material->reset(new uint8_t[*size]);
+ uint8_t* tmp = material->get();
+ i2d_PrivateKey(pkey.get(), &tmp);
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AsymmetricKey::formatted_key_material(keymaster_key_format_t format,
+ UniquePtr<uint8_t[]>* material,
+ size_t* size) const {
+ if (format != KM_KEY_FORMAT_X509)
+ return KM_ERROR_UNSUPPORTED_KEY_FORMAT;
+
+ if (material == NULL || size == NULL)
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new());
+ if (!InternalToEvp(pkey.get()))
+ return KM_ERROR_UNKNOWN_ERROR;
+
+ int key_data_length = i2d_PUBKEY(pkey.get(), NULL);
+ if (key_data_length <= 0)
+ return KM_ERROR_UNKNOWN_ERROR;
+
+ material->reset(new uint8_t[key_data_length]);
+ if (material->get() == NULL)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ uint8_t* tmp = material->get();
+ if (i2d_PUBKEY(pkey.get(), &tmp) != key_data_length) {
+ material->reset();
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+
+ *size = key_data_length;
+ return KM_ERROR_OK;
+}
+
+Operation* AsymmetricKey::CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error) {
+ keymaster_digest_t digest;
+ if (!authorizations().GetTagValue(TAG_DIGEST, &digest) || digest != KM_DIGEST_NONE) {
+ *error = KM_ERROR_UNSUPPORTED_DIGEST;
+ return NULL;
+ }
+
+ keymaster_padding_t padding;
+ if (!authorizations().GetTagValue(TAG_PADDING, &padding) || padding != KM_PAD_NONE) {
+ *error = KM_ERROR_UNSUPPORTED_PADDING_MODE;
+ return NULL;
+ }
+
+ return CreateOperation(purpose, digest, padding, error);
+}
+
+/* static */
+RsaKey* RsaKey::GenerateKey(const AuthorizationSet& key_description, const Logger& logger,
+ keymaster_error_t* error) {
+ if (!error)
+ return NULL;
+
+ AuthorizationSet authorizations(key_description);
+
+ uint64_t public_exponent = RSA_DEFAULT_EXPONENT;
+ if (!authorizations.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &public_exponent))
+ authorizations.push_back(Authorization(TAG_RSA_PUBLIC_EXPONENT, public_exponent));
+
+ uint32_t key_size = RSA_DEFAULT_KEY_SIZE;
+ if (!authorizations.GetTagValue(TAG_KEY_SIZE, &key_size))
+ authorizations.push_back(Authorization(TAG_KEY_SIZE, key_size));
+
+ UniquePtr<BIGNUM, BIGNUM_Delete> exponent(BN_new());
+ UniquePtr<RSA, RSA_Delete> rsa_key(RSA_new());
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new());
+ if (rsa_key.get() == NULL || pkey.get() == NULL) {
+ *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return NULL;
+ }
+
+ if (!BN_set_word(exponent.get(), public_exponent) ||
+ !RSA_generate_key_ex(rsa_key.get(), key_size, exponent.get(), NULL /* callback */)) {
+ *error = KM_ERROR_UNKNOWN_ERROR;
+ return NULL;
+ }
+
+ RsaKey* new_key = new RsaKey(rsa_key.release(), authorizations, logger);
+ *error = new_key ? KM_ERROR_OK : KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return new_key;
+}
+
+/* static */
+RsaKey* RsaKey::ImportKey(const AuthorizationSet& key_description, EVP_PKEY* pkey,
+ const Logger& logger, keymaster_error_t* error) {
+ if (!error)
+ return NULL;
+ *error = KM_ERROR_UNKNOWN_ERROR;
+
+ UniquePtr<RSA, RSA_Delete> rsa_key(EVP_PKEY_get1_RSA(pkey));
+ if (!rsa_key.get())
+ return NULL;
+
+ AuthorizationSet authorizations(key_description);
+
+ uint64_t public_exponent;
+ if (authorizations.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &public_exponent)) {
+ // public_exponent specified, make sure it matches the key
+ UniquePtr<BIGNUM, BIGNUM_Delete> public_exponent_bn(BN_new());
+ if (!BN_set_word(public_exponent_bn.get(), public_exponent))
+ return NULL;
+ if (BN_cmp(public_exponent_bn.get(), rsa_key->e) != 0) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ } else {
+ // public_exponent not specified, use the one from the key.
+ public_exponent = BN_get_word(rsa_key->e);
+ if (public_exponent == 0xffffffffL) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ authorizations.push_back(TAG_RSA_PUBLIC_EXPONENT, public_exponent);
+ }
+
+ uint32_t key_size;
+ if (authorizations.GetTagValue(TAG_KEY_SIZE, &key_size)) {
+ // key_size specified, make sure it matches the key.
+ if (RSA_size(rsa_key.get()) != (int)key_size) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ } else {
+ key_size = RSA_size(rsa_key.get()) * 8;
+ authorizations.push_back(TAG_KEY_SIZE, key_size);
+ }
+
+ keymaster_algorithm_t algorithm;
+ if (authorizations.GetTagValue(TAG_ALGORITHM, &algorithm)) {
+ if (algorithm != KM_ALGORITHM_RSA) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ } else {
+ authorizations.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
+ }
+
+ // Don't bother with the other parameters. If the necessary padding, digest, purpose, etc. are
+ // missing, the error will be diagnosed when the key is used (when auth checking is
+ // implemented).
+ *error = KM_ERROR_OK;
+ return new RsaKey(rsa_key.release(), authorizations, logger);
+}
+
+RsaKey::RsaKey(const KeyBlob& blob, const Logger& logger, keymaster_error_t* error)
+ : AsymmetricKey(blob, logger) {
+ if (error)
+ *error = LoadKey(blob);
+}
+
+Operation* RsaKey::CreateOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, keymaster_error_t* error) {
+ Operation* op;
+ switch (purpose) {
+ case KM_PURPOSE_SIGN:
+ op = new RsaSignOperation(purpose, digest, padding, rsa_key_.release());
+ break;
+ case KM_PURPOSE_VERIFY:
+ op = new RsaVerifyOperation(purpose, digest, padding, rsa_key_.release());
+ break;
+ default:
+ *error = KM_ERROR_UNIMPLEMENTED;
+ return NULL;
+ }
+ *error = op ? KM_ERROR_OK : KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return op;
+}
+
+bool RsaKey::EvpToInternal(const EVP_PKEY* pkey) {
+ rsa_key_.reset(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(pkey)));
+ return rsa_key_.get() != NULL;
+}
+
+bool RsaKey::InternalToEvp(EVP_PKEY* pkey) const {
+ return EVP_PKEY_set1_RSA(pkey, rsa_key_.get()) == 1;
+}
+
+template <keymaster_tag_t Tag>
+static void GetDsaParamData(const AuthorizationSet& auths, TypedTag<KM_BIGNUM, Tag> tag,
+ keymaster_blob_t* blob) {
+ if (!auths.GetTagValue(tag, blob))
+ blob->data = NULL;
+}
+
+// Store the specified DSA param in auths
+template <keymaster_tag_t Tag>
+static void SetDsaParamData(AuthorizationSet* auths, TypedTag<KM_BIGNUM, Tag> tag, BIGNUM* number) {
+ keymaster_blob_t blob;
+ convert_bn_to_blob(number, &blob);
+ auths->push_back(Authorization(tag, blob));
+ delete[] blob.data;
+}
+
+DsaKey* DsaKey::GenerateKey(const AuthorizationSet& key_description, const Logger& logger,
+ keymaster_error_t* error) {
+ if (!error)
+ return NULL;
+
+ AuthorizationSet authorizations(key_description);
+
+ keymaster_blob_t g_blob;
+ GetDsaParamData(authorizations, TAG_DSA_GENERATOR, &g_blob);
+
+ keymaster_blob_t p_blob;
+ GetDsaParamData(authorizations, TAG_DSA_P, &p_blob);
+
+ keymaster_blob_t q_blob;
+ GetDsaParamData(authorizations, TAG_DSA_Q, &q_blob);
+
+ uint32_t key_size = DSA_DEFAULT_KEY_SIZE;
+ if (!authorizations.GetTagValue(TAG_KEY_SIZE, &key_size))
+ authorizations.push_back(Authorization(TAG_KEY_SIZE, key_size));
+
+ UniquePtr<DSA, DSA_Delete> dsa_key(DSA_new());
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new());
+ if (dsa_key.get() == NULL || pkey.get() == NULL) {
+ *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return NULL;
+ }
+
+ // If anything goes wrong in the next section, it's a param problem.
+ *error = KM_ERROR_INVALID_DSA_PARAMS;
+
+ if (g_blob.data == NULL && p_blob.data == NULL && q_blob.data == NULL) {
+ logger.log("DSA parameters unspecified, generating them for key size %d\n", key_size);
+ if (!DSA_generate_parameters_ex(dsa_key.get(), key_size, NULL /* seed */, 0 /* seed_len */,
+ NULL /* counter_ret */, NULL /* h_ret */,
+ NULL /* callback */)) {
+ logger.log("DSA parameter generation failed.\n");
+ return NULL;
+ }
+
+ SetDsaParamData(&authorizations, TAG_DSA_GENERATOR, dsa_key->g);
+ SetDsaParamData(&authorizations, TAG_DSA_P, dsa_key->p);
+ SetDsaParamData(&authorizations, TAG_DSA_Q, dsa_key->q);
+ } else if (g_blob.data == NULL || p_blob.data == NULL || q_blob.data == NULL) {
+ logger.log("Some DSA parameters provided. Provide all or none\n");
+ return NULL;
+ } else {
+ // All params provided. Use them.
+ dsa_key->g = BN_bin2bn(g_blob.data, g_blob.data_length, NULL);
+ dsa_key->p = BN_bin2bn(p_blob.data, p_blob.data_length, NULL);
+ dsa_key->q = BN_bin2bn(q_blob.data, q_blob.data_length, NULL);
+
+ if (dsa_key->g == NULL || dsa_key->p == NULL || dsa_key->q == NULL) {
+ return NULL;
+ }
+ }
+
+ if (!DSA_generate_key(dsa_key.get())) {
+ *error = KM_ERROR_UNKNOWN_ERROR;
+ return NULL;
+ }
+
+ DsaKey* new_key = new DsaKey(dsa_key.release(), authorizations, logger);
+ *error = new_key ? KM_ERROR_OK : KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return new_key;
+}
+
+template <keymaster_tag_t T>
+keymaster_error_t GetOrCheckDsaParam(TypedTag<KM_BIGNUM, T> tag, BIGNUM* bn,
+ AuthorizationSet* auths) {
+ keymaster_blob_t blob;
+ if (auths->GetTagValue(tag, &blob)) {
+ // value specified, make sure it matches
+ UniquePtr<BIGNUM, BIGNUM_Delete> extracted_bn(BN_bin2bn(blob.data, blob.data_length, NULL));
+ if (extracted_bn.get() == NULL)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ if (BN_cmp(extracted_bn.get(), bn) != 0)
+ return KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ } else {
+ // value not specified, add it
+ UniquePtr<uint8_t[]> data(new uint8_t[BN_num_bytes(bn)]);
+ BN_bn2bin(bn, data.get());
+ auths->push_back(tag, data.get(), BN_num_bytes(bn));
+ }
+ return KM_ERROR_OK;
+}
+
+/* static */
+size_t DsaKey::key_size_bits(DSA* dsa_key) {
+ // Openssl provides no convenient way to get a DSA key size, but dsa_key->p is L bits long.
+ // There may be some leading zeros that mess up this calculation, but DSA key sizes are also
+ // constrained to be multiples of 64 bits. So the key size is the bit length of p rounded up to
+ // the nearest 64.
+ return ((BN_num_bytes(dsa_key->p) * 8) + 63) / 64 * 64;
+}
+
+/* static */
+DsaKey* DsaKey::ImportKey(const AuthorizationSet& key_description, EVP_PKEY* pkey,
+ const Logger& logger, keymaster_error_t* error) {
+ if (!error)
+ return NULL;
+ *error = KM_ERROR_UNKNOWN_ERROR;
+
+ UniquePtr<DSA, DSA_Delete> dsa_key(EVP_PKEY_get1_DSA(pkey));
+ if (!dsa_key.get())
+ return NULL;
+
+ AuthorizationSet authorizations(key_description);
+
+ *error = GetOrCheckDsaParam(TAG_DSA_GENERATOR, dsa_key->g, &authorizations);
+ if (*error != KM_ERROR_OK)
+ return NULL;
+
+ *error = GetOrCheckDsaParam(TAG_DSA_P, dsa_key->p, &authorizations);
+ if (*error != KM_ERROR_OK)
+ return NULL;
+
+ *error = GetOrCheckDsaParam(TAG_DSA_Q, dsa_key->q, &authorizations);
+ if (*error != KM_ERROR_OK)
+ return NULL;
+
+ // There's no convenient way to get a DSA key size, but dsa_key->p is L bits long. There may be
+ // some leading zeros that mess up this calculation, but DSA key sizes are also constrained to
+ // be multiples of 64 bits. So the bit length of p, rounded up to the nearest 64 bits, is the
+ // key size.
+ uint32_t extracted_key_size_bits = ((BN_num_bytes(dsa_key->p) * 8) + 63) / 64 * 64;
+
+ uint32_t key_size_bits;
+ if (authorizations.GetTagValue(TAG_KEY_SIZE, &key_size_bits)) {
+ // key_size_bits specified, make sure it matches the key.
+ if (key_size_bits != extracted_key_size_bits) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ } else {
+ // key_size_bits not specified, add it.
+ authorizations.push_back(TAG_KEY_SIZE, extracted_key_size_bits);
+ }
+
+ keymaster_algorithm_t algorithm;
+ if (authorizations.GetTagValue(TAG_ALGORITHM, &algorithm)) {
+ if (algorithm != KM_ALGORITHM_DSA) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ } else {
+ authorizations.push_back(TAG_ALGORITHM, KM_ALGORITHM_DSA);
+ }
+
+ // Don't bother with the other parameters. If the necessary padding, digest, purpose, etc. are
+ // missing, the error will be diagnosed when the key is used (when auth checking is
+ // implemented).
+ *error = KM_ERROR_OK;
+ return new DsaKey(dsa_key.release(), authorizations, logger);
+}
+
+DsaKey::DsaKey(const KeyBlob& blob, const Logger& logger, keymaster_error_t* error)
+ : AsymmetricKey(blob, logger) {
+ if (error)
+ *error = LoadKey(blob);
+}
+
+Operation* DsaKey::CreateOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, keymaster_error_t* error) {
+ Operation* op;
+ switch (purpose) {
+ case KM_PURPOSE_SIGN:
+ op = new DsaSignOperation(purpose, digest, padding, dsa_key_.release());
+ break;
+ case KM_PURPOSE_VERIFY:
+ op = new DsaVerifyOperation(purpose, digest, padding, dsa_key_.release());
+ break;
+ default:
+ *error = KM_ERROR_UNIMPLEMENTED;
+ return NULL;
+ }
+ *error = op ? KM_ERROR_OK : KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return op;
+}
+
+bool DsaKey::EvpToInternal(const EVP_PKEY* pkey) {
+ dsa_key_.reset(EVP_PKEY_get1_DSA(const_cast<EVP_PKEY*>(pkey)));
+ return dsa_key_.get() != NULL;
+}
+
+bool DsaKey::InternalToEvp(EVP_PKEY* pkey) const {
+ return EVP_PKEY_set1_DSA(pkey, dsa_key_.get()) == 1;
+}
+
+/* static */
+EcdsaKey* EcdsaKey::GenerateKey(const AuthorizationSet& key_description, const Logger& logger,
+ keymaster_error_t* error) {
+ if (!error)
+ return NULL;
+
+ AuthorizationSet authorizations(key_description);
+
+ uint32_t key_size = ECDSA_DEFAULT_KEY_SIZE;
+ if (!authorizations.GetTagValue(TAG_KEY_SIZE, &key_size))
+ authorizations.push_back(Authorization(TAG_KEY_SIZE, key_size));
+
+ UniquePtr<EC_KEY, ECDSA_Delete> ecdsa_key(EC_KEY_new());
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new());
+ if (ecdsa_key.get() == NULL || pkey.get() == NULL) {
+ *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return NULL;
+ }
+
+ UniquePtr<EC_GROUP, EC_GROUP_Delete> group(choose_group(key_size));
+ if (group.get() == NULL) {
+ // Technically, could also have been a memory allocation problem.
+ *error = KM_ERROR_UNSUPPORTED_KEY_SIZE;
+ return NULL;
+ }
+
+ EC_GROUP_set_point_conversion_form(group.get(), POINT_CONVERSION_UNCOMPRESSED);
+ EC_GROUP_set_asn1_flag(group.get(), OPENSSL_EC_NAMED_CURVE);
+
+ if (EC_KEY_set_group(ecdsa_key.get(), group.get()) != 1 ||
+ EC_KEY_generate_key(ecdsa_key.get()) != 1 || EC_KEY_check_key(ecdsa_key.get()) < 0) {
+ *error = KM_ERROR_UNKNOWN_ERROR;
+ return NULL;
+ }
+
+ EcdsaKey* new_key = new EcdsaKey(ecdsa_key.release(), authorizations, logger);
+ *error = new_key ? KM_ERROR_OK : KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return new_key;
+}
+
+/* static */
+EcdsaKey* EcdsaKey::ImportKey(const AuthorizationSet& key_description, EVP_PKEY* pkey,
+ const Logger& logger, keymaster_error_t* error) {
+ if (!error)
+ return NULL;
+ *error = KM_ERROR_UNKNOWN_ERROR;
+
+ UniquePtr<EC_KEY, ECDSA_Delete> ecdsa_key(EVP_PKEY_get1_EC_KEY(pkey));
+ if (!ecdsa_key.get())
+ return NULL;
+
+ AuthorizationSet authorizations(key_description);
+
+ size_t extracted_key_size_bits;
+ *error = get_group_size(*EC_KEY_get0_group(ecdsa_key.get()), &extracted_key_size_bits);
+ if (*error != KM_ERROR_OK)
+ return NULL;
+
+ uint32_t key_size_bits;
+ if (authorizations.GetTagValue(TAG_KEY_SIZE, &key_size_bits)) {
+ // key_size_bits specified, make sure it matches the key.
+ if (key_size_bits != extracted_key_size_bits) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ } else {
+ // key_size_bits not specified, add it.
+ authorizations.push_back(TAG_KEY_SIZE, extracted_key_size_bits);
+ }
+
+ keymaster_algorithm_t algorithm;
+ if (authorizations.GetTagValue(TAG_ALGORITHM, &algorithm)) {
+ if (algorithm != KM_ALGORITHM_ECDSA) {
+ *error = KM_ERROR_IMPORT_PARAMETER_MISMATCH;
+ return NULL;
+ }
+ } else {
+ authorizations.push_back(TAG_ALGORITHM, KM_ALGORITHM_ECDSA);
+ }
+
+ // Don't bother with the other parameters. If the necessary padding, digest, purpose, etc. are
+ // missing, the error will be diagnosed when the key is used (when auth checking is
+ // implemented).
+ *error = KM_ERROR_OK;
+ return new EcdsaKey(ecdsa_key.release(), authorizations, logger);
+}
+
+/* static */
+EC_GROUP* EcdsaKey::choose_group(size_t key_size_bits) {
+ switch (key_size_bits) {
+ case 192:
+ return EC_GROUP_new_by_curve_name(NID_X9_62_prime192v1);
+ break;
+ case 224:
+ return EC_GROUP_new_by_curve_name(NID_secp224r1);
+ break;
+ case 256:
+ return EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+ break;
+ case 384:
+ return EC_GROUP_new_by_curve_name(NID_secp384r1);
+ break;
+ case 521:
+ return EC_GROUP_new_by_curve_name(NID_secp521r1);
+ break;
+ default:
+ return NULL;
+ break;
+ }
+}
+
+/* static */
+keymaster_error_t EcdsaKey::get_group_size(const EC_GROUP& group, size_t* key_size_bits) {
+ switch (EC_GROUP_get_curve_name(&group)) {
+ case NID_X9_62_prime192v1:
+ *key_size_bits = 192;
+ break;
+ case NID_secp224r1:
+ *key_size_bits = 224;
+ break;
+ case NID_X9_62_prime256v1:
+ *key_size_bits = 256;
+ break;
+ case NID_secp384r1:
+ *key_size_bits = 384;
+ break;
+ case NID_secp521r1:
+ *key_size_bits = 521;
+ break;
+ default:
+ return KM_ERROR_UNSUPPORTED_EC_FIELD;
+ }
+ return KM_ERROR_OK;
+}
+
+EcdsaKey::EcdsaKey(const KeyBlob& blob, const Logger& logger, keymaster_error_t* error)
+ : AsymmetricKey(blob, logger) {
+ if (error)
+ *error = LoadKey(blob);
+}
+
+Operation* EcdsaKey::CreateOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, keymaster_error_t* error) {
+ Operation* op;
+ switch (purpose) {
+ case KM_PURPOSE_SIGN:
+ op = new EcdsaSignOperation(purpose, digest, padding, ecdsa_key_.release());
+ break;
+ case KM_PURPOSE_VERIFY:
+ op = new EcdsaVerifyOperation(purpose, digest, padding, ecdsa_key_.release());
+ break;
+ default:
+ *error = KM_ERROR_UNIMPLEMENTED;
+ return NULL;
+ }
+ *error = op ? KM_ERROR_OK : KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return op;
+}
+
+bool EcdsaKey::EvpToInternal(const EVP_PKEY* pkey) {
+ ecdsa_key_.reset(EVP_PKEY_get1_EC_KEY(const_cast<EVP_PKEY*>(pkey)));
+ return ecdsa_key_.get() != NULL;
+}
+
+bool EcdsaKey::InternalToEvp(EVP_PKEY* pkey) const {
+ return EVP_PKEY_set1_EC_KEY(pkey, ecdsa_key_.get()) == 1;
+}
+
+} // namespace keymaster
diff --git a/asymmetric_key.h b/asymmetric_key.h
new file mode 100644
index 0000000..6279ff4
--- /dev/null
+++ b/asymmetric_key.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_ASYMMETRIC_KEY_H
+#define SYSTEM_KEYMASTER_ASYMMETRIC_KEY_H
+
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/ecdsa.h>
+#include <openssl/evp.h>
+
+#include "key.h"
+
+namespace keymaster {
+
+class AsymmetricKey : public Key {
+ public:
+ protected:
+ AsymmetricKey(const KeyBlob& blob, const Logger& logger) : Key(blob, logger) {}
+ keymaster_error_t LoadKey(const KeyBlob& blob);
+
+ /**
+ * Return a copy of raw key material, in the key's preferred binary format.
+ */
+ virtual keymaster_error_t key_material(UniquePtr<uint8_t[]>* material, size_t* size) const;
+
+ /**
+ * Return a copy of raw key material, in the specified format.
+ */
+ virtual keymaster_error_t formatted_key_material(keymaster_key_format_t format,
+ UniquePtr<uint8_t[]>* material,
+ size_t* size) const;
+
+ virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error);
+
+ protected:
+ AsymmetricKey(const AuthorizationSet& auths, const Logger& logger) : Key(auths, logger) {}
+
+ private:
+ virtual int evp_key_type() = 0;
+ virtual bool InternalToEvp(EVP_PKEY* pkey) const = 0;
+ virtual bool EvpToInternal(const EVP_PKEY* pkey) = 0;
+ virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, keymaster_error_t* error) = 0;
+};
+
+class RsaKey : public AsymmetricKey {
+ public:
+ static RsaKey* GenerateKey(const AuthorizationSet& key_description, const Logger& logger,
+ keymaster_error_t* error);
+ static RsaKey* ImportKey(const AuthorizationSet& key_description, EVP_PKEY* pkey,
+ const Logger& logger, keymaster_error_t* error);
+ RsaKey(const KeyBlob& blob, const Logger& logger, keymaster_error_t* error);
+
+ virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, keymaster_error_t* error);
+
+ private:
+ RsaKey(RSA* rsa_key, const AuthorizationSet& auths, const Logger& logger)
+ : AsymmetricKey(auths, logger), rsa_key_(rsa_key) {}
+
+ virtual int evp_key_type() { return EVP_PKEY_RSA; }
+ virtual bool InternalToEvp(EVP_PKEY* pkey) const;
+ virtual bool EvpToInternal(const EVP_PKEY* pkey);
+
+ struct RSA_Delete {
+ void operator()(RSA* p) { RSA_free(p); }
+ };
+
+ UniquePtr<RSA, RSA_Delete> rsa_key_;
+};
+
+class DsaKey : public AsymmetricKey {
+ public:
+ static DsaKey* GenerateKey(const AuthorizationSet& key_description, const Logger& logger,
+ keymaster_error_t* error);
+ static DsaKey* ImportKey(const AuthorizationSet& key_description, EVP_PKEY* pkey,
+ const Logger& logger, keymaster_error_t* error);
+ DsaKey(const KeyBlob& blob, const Logger& logger, keymaster_error_t* error);
+
+ virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, keymaster_error_t* error);
+ static size_t key_size_bits(DSA* dsa_key);
+
+ private:
+
+ DsaKey(DSA* dsa_key, const AuthorizationSet auths, const Logger& logger)
+ : AsymmetricKey(auths, logger), dsa_key_(dsa_key) {}
+
+ virtual int evp_key_type() { return EVP_PKEY_DSA; }
+ virtual bool InternalToEvp(EVP_PKEY* pkey) const;
+ virtual bool EvpToInternal(const EVP_PKEY* pkey);
+
+ struct DSA_Delete {
+ void operator()(DSA* p) { DSA_free(p); }
+ };
+
+ UniquePtr<DSA, DSA_Delete> dsa_key_;
+};
+
+class EcdsaKey : public AsymmetricKey {
+ public:
+ static EcdsaKey* GenerateKey(const AuthorizationSet& key_description, const Logger& logger,
+ keymaster_error_t* error);
+ static EcdsaKey* ImportKey(const AuthorizationSet& key_description, EVP_PKEY* pkey,
+ const Logger& logger, keymaster_error_t* error);
+ EcdsaKey(const KeyBlob& blob, const Logger& logger, keymaster_error_t* error);
+
+ virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, keymaster_error_t* error);
+
+ private:
+ EcdsaKey(EC_KEY* ecdsa_key, const AuthorizationSet auths, const Logger& logger)
+ : AsymmetricKey(auths, logger), ecdsa_key_(ecdsa_key) {}
+
+ static EC_GROUP* choose_group(size_t key_size_bits);
+ static keymaster_error_t get_group_size(const EC_GROUP& group, size_t* key_size_bits);
+
+ virtual int evp_key_type() { return EVP_PKEY_EC; }
+ virtual bool InternalToEvp(EVP_PKEY* pkey) const;
+ virtual bool EvpToInternal(const EVP_PKEY* pkey);
+
+ struct ECDSA_Delete {
+ void operator()(EC_KEY* p) { EC_KEY_free(p); }
+ };
+
+ struct EC_GROUP_Delete {
+ void operator()(EC_GROUP* p) { EC_GROUP_free(p); }
+ };
+
+ UniquePtr<EC_KEY, ECDSA_Delete> ecdsa_key_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_ASYMMETRIC_KEY_H
diff --git a/authorization_set.cpp b/authorization_set.cpp
new file mode 100644
index 0000000..88c1a5e
--- /dev/null
+++ b/authorization_set.cpp
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+
+#include <assert.h>
+
+#include <keymaster/authorization_set.h>
+#include <keymaster/google_keymaster_utils.h>
+
+namespace keymaster {
+
+static inline bool is_blob_tag(keymaster_tag_t tag) {
+ return (keymaster_tag_get_type(tag) == KM_BYTES || keymaster_tag_get_type(tag) == KM_BIGNUM);
+}
+
+const size_t STARTING_ELEMS_CAPACITY = 8;
+
+AuthorizationSet::AuthorizationSet(const AuthorizationSet& set)
+ : Serializable(), elems_(NULL), indirect_data_(NULL) {
+ Reinitialize(set.elems_, set.elems_size_);
+}
+
+AuthorizationSet::~AuthorizationSet() {
+ FreeData();
+}
+
+bool AuthorizationSet::reserve_elems(size_t count) {
+ if (is_valid() != OK)
+ return false;
+
+ if (count >= elems_capacity_) {
+ keymaster_key_param_t* new_elems = new keymaster_key_param_t[count];
+ if (new_elems == NULL) {
+ set_invalid(ALLOCATION_FAILURE);
+ return false;
+ }
+ memcpy(new_elems, elems_, sizeof(*elems_) * elems_size_);
+ delete[] elems_;
+ elems_ = new_elems;
+ elems_capacity_ = count;
+ }
+ return true;
+}
+
+bool AuthorizationSet::reserve_indirect(size_t length) {
+ if (is_valid() != OK)
+ return false;
+
+ if (length > indirect_data_capacity_) {
+ uint8_t* new_data = new uint8_t[length];
+ if (new_data == NULL) {
+ set_invalid(ALLOCATION_FAILURE);
+ return false;
+ }
+ memcpy(new_data, indirect_data_, indirect_data_size_);
+
+ // Fix up the data pointers to point into the new region.
+ for (size_t i = 0; i < elems_size_; ++i) {
+ if (is_blob_tag(elems_[i].tag))
+ elems_[i].blob.data = new_data + (elems_[i].blob.data - indirect_data_);
+ }
+ delete[] indirect_data_;
+ indirect_data_ = new_data;
+ indirect_data_capacity_ = length;
+ }
+ return true;
+}
+
+bool AuthorizationSet::Reinitialize(const keymaster_key_param_t* elems, const size_t count) {
+ FreeData();
+
+ if (!reserve_elems(count))
+ return false;
+
+ if (!reserve_indirect(ComputeIndirectDataSize(elems, count)))
+ return false;
+
+ memcpy(elems_, elems, sizeof(keymaster_key_param_t) * count);
+ elems_size_ = count;
+ CopyIndirectData();
+ error_ = OK;
+ return true;
+}
+
+void AuthorizationSet::set_invalid(Error error) {
+ FreeData();
+ error_ = error;
+}
+
+int AuthorizationSet::find(keymaster_tag_t tag, int begin) const {
+ if (is_valid() != OK)
+ return -1;
+
+ int i = ++begin;
+ while (i < (int)elems_size_ && elems_[i].tag != tag)
+ ++i;
+ if (i == (int)elems_size_)
+ return -1;
+ else
+ return i;
+}
+
+keymaster_key_param_t empty;
+keymaster_key_param_t AuthorizationSet::operator[](int at) const {
+ if (is_valid() == OK && at < (int)elems_size_) {
+ return elems_[at];
+ }
+ memset(&empty, 0, sizeof(empty));
+ return empty;
+}
+
+template <typename T> int comparator(const T& a, const T& b) {
+ if (a < b)
+ return -1;
+ else if (a > b)
+ return 1;
+ else
+ return 0;
+}
+
+static int param_comparator(const void* a, const void* b) {
+ const keymaster_key_param_t* lhs = static_cast<const keymaster_key_param_t*>(a);
+ const keymaster_key_param_t* rhs = static_cast<const keymaster_key_param_t*>(b);
+
+ if (lhs->tag < rhs->tag)
+ return -1;
+ else if (lhs->tag > rhs->tag)
+ return 1;
+ else
+ switch (keymaster_tag_get_type(lhs->tag)) {
+ default:
+ case KM_INVALID:
+ return 0;
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ return comparator(lhs->enumerated, rhs->enumerated);
+ case KM_INT:
+ case KM_INT_REP:
+ return comparator(lhs->integer, rhs->integer);
+ case KM_LONG:
+ return comparator(lhs->long_integer, rhs->long_integer);
+ case KM_DATE:
+ return comparator(lhs->date_time, rhs->date_time);
+ case KM_BOOL:
+ return comparator(lhs->boolean, rhs->boolean);
+ case KM_BIGNUM:
+ case KM_BYTES: {
+ size_t min_len = lhs->blob.data_length;
+ if (rhs->blob.data_length < min_len)
+ min_len = rhs->blob.data_length;
+
+ if (lhs->blob.data_length == rhs->blob.data_length && min_len > 0)
+ return memcmp(lhs->blob.data, rhs->blob.data, min_len);
+ int cmp_result = memcmp(lhs->blob.data, rhs->blob.data, min_len);
+ if (cmp_result == 0) {
+ // The blobs are equal up to the length of the shortest (which may have length 0),
+ // so the shorter is less, the longer is greater and if they have the same length
+ // they're identical.
+ return comparator(lhs->blob.data_length, rhs->blob.data_length);
+ }
+ return cmp_result;
+ } break;
+ }
+}
+
+bool AuthorizationSet::push_back(const AuthorizationSet& set) {
+ if (is_valid() != OK)
+ return false;
+
+ if (!reserve_elems(elems_size_ + set.elems_size_))
+ return false;
+
+ if (!reserve_indirect(indirect_data_size_ + set.indirect_data_size_))
+ return false;
+
+ for (size_t i = 0; i < set.size(); ++i)
+ if (!push_back(set[i]))
+ return false;
+
+ return true;
+}
+
+bool AuthorizationSet::push_back(keymaster_key_param_t elem) {
+ if (is_valid() != OK)
+ return false;
+
+ if (elems_size_ >= elems_capacity_)
+ if (!reserve_elems(elems_capacity_ ? elems_capacity_ * 2 : STARTING_ELEMS_CAPACITY))
+ return false;
+
+ if (is_blob_tag(elem.tag)) {
+ if (indirect_data_capacity_ - indirect_data_size_ < elem.blob.data_length)
+ if (!reserve_indirect(2 * (indirect_data_capacity_ + elem.blob.data_length)))
+ return false;
+
+ memcpy(indirect_data_ + indirect_data_size_, elem.blob.data, elem.blob.data_length);
+ elem.blob.data = indirect_data_ + indirect_data_size_;
+ indirect_data_size_ += elem.blob.data_length;
+ }
+
+ elems_[elems_size_++] = elem;
+ return true;
+}
+
+static size_t serialized_size(const keymaster_key_param_t& param) {
+ switch (keymaster_tag_get_type(param.tag)) {
+ case KM_INVALID:
+ default:
+ return sizeof(uint32_t);
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ case KM_INT:
+ case KM_INT_REP:
+ return sizeof(uint32_t) * 2;
+ case KM_LONG:
+ case KM_DATE:
+ return sizeof(uint32_t) + sizeof(uint64_t);
+ case KM_BOOL:
+ return sizeof(uint32_t) + 1;
+ break;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ return sizeof(uint32_t) * 3;
+ }
+}
+
+static uint8_t* serialize(const keymaster_key_param_t& param, uint8_t* buf, const uint8_t* end,
+ const uint8_t* indirect_base) {
+ buf = append_uint32_to_buf(buf, end, param.tag);
+ switch (keymaster_tag_get_type(param.tag)) {
+ case KM_INVALID:
+ break;
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ buf = append_uint32_to_buf(buf, end, param.enumerated);
+ break;
+ case KM_INT:
+ case KM_INT_REP:
+ buf = append_uint32_to_buf(buf, end, param.integer);
+ break;
+ case KM_LONG:
+ buf = append_uint64_to_buf(buf, end, param.long_integer);
+ break;
+ case KM_DATE:
+ buf = append_uint64_to_buf(buf, end, param.date_time);
+ break;
+ case KM_BOOL:
+ if (buf < end)
+ *buf = static_cast<uint8_t>(param.boolean);
+ buf++;
+ break;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ buf = append_uint32_to_buf(buf, end, param.blob.data_length);
+ buf = append_uint32_to_buf(buf, end, param.blob.data - indirect_base);
+ break;
+ }
+ return buf;
+}
+
+static bool deserialize(keymaster_key_param_t* param, const uint8_t** buf_ptr, const uint8_t* end,
+ const uint8_t* indirect_base, const uint8_t* indirect_end) {
+ if (!copy_uint32_from_buf(buf_ptr, end, ¶m->tag))
+ return false;
+
+ switch (keymaster_tag_get_type(param->tag)) {
+ default:
+ case KM_INVALID:
+ return false;
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ return copy_uint32_from_buf(buf_ptr, end, ¶m->enumerated);
+ case KM_INT:
+ case KM_INT_REP:
+ return copy_uint32_from_buf(buf_ptr, end, ¶m->integer);
+ case KM_LONG:
+ return copy_uint64_from_buf(buf_ptr, end, ¶m->long_integer);
+ case KM_DATE:
+ return copy_uint64_from_buf(buf_ptr, end, ¶m->date_time);
+ break;
+ case KM_BOOL:
+ if (*buf_ptr < end) {
+ param->boolean = static_cast<bool>(**buf_ptr);
+ (*buf_ptr)++;
+ return true;
+ }
+ return false;
+
+ case KM_BIGNUM:
+ case KM_BYTES: {
+ uint32_t offset;
+ if (!copy_uint32_from_buf(buf_ptr, end, ¶m->blob.data_length) ||
+ !copy_uint32_from_buf(buf_ptr, end, &offset))
+ return false;
+ if (static_cast<ptrdiff_t>(offset) > indirect_end - indirect_base ||
+ static_cast<ptrdiff_t>(offset + param->blob.data_length) > indirect_end - indirect_base)
+ return false;
+ param->blob.data = indirect_base + offset;
+ return true;
+ }
+ }
+}
+
+size_t AuthorizationSet::SerializedSizeOfElements() const {
+ size_t size = 0;
+ for (size_t i = 0; i < elems_size_; ++i) {
+ size += serialized_size(elems_[i]);
+ }
+ return size;
+}
+
+size_t AuthorizationSet::SerializedSize() const {
+ return sizeof(uint32_t) + // Size of indirect_data_
+ indirect_data_size_ + // indirect_data_
+ sizeof(uint32_t) + // Number of elems_
+ sizeof(uint32_t) + // Size of elems_
+ SerializedSizeOfElements(); // elems_
+}
+
+uint8_t* AuthorizationSet::Serialize(uint8_t* buf, const uint8_t* end) const {
+ buf = append_size_and_data_to_buf(buf, end, indirect_data_, indirect_data_size_);
+ buf = append_uint32_to_buf(buf, end, elems_size_);
+ buf = append_uint32_to_buf(buf, end, SerializedSizeOfElements());
+ for (size_t i = 0; i < elems_size_; ++i) {
+ buf = serialize(elems_[i], buf, end, indirect_data_);
+ }
+ return buf;
+}
+
+bool AuthorizationSet::DeserializeIndirectData(const uint8_t** buf_ptr, const uint8_t* end) {
+ UniquePtr<uint8_t[]> indirect_buf;
+ if (!copy_size_and_data_from_buf(buf_ptr, end, &indirect_data_size_, &indirect_buf)) {
+ set_invalid(MALFORMED_DATA);
+ return false;
+ }
+ indirect_data_ = indirect_buf.release();
+ return true;
+}
+
+bool AuthorizationSet::DeserializeElementsData(const uint8_t** buf_ptr, const uint8_t* end) {
+ uint32_t elements_count;
+ uint32_t elements_size;
+ if (!copy_uint32_from_buf(buf_ptr, end, &elements_count) ||
+ !copy_uint32_from_buf(buf_ptr, end, &elements_size)) {
+ set_invalid(MALFORMED_DATA);
+ return false;
+ }
+
+ // Note that the following validation of elements_count is weak, but it prevents allocation of
+ // elems_ arrays which are clearly too large to be reasonable.
+ if (static_cast<ptrdiff_t>(elements_size) > end - *buf_ptr ||
+ elements_count * sizeof(uint32_t) > elements_size) {
+ set_invalid(MALFORMED_DATA);
+ return false;
+ }
+
+ if (!reserve_elems(elements_count))
+ return false;
+
+ uint8_t* indirect_end = indirect_data_ + indirect_data_size_;
+ const uint8_t* elements_end = *buf_ptr + elements_size;
+ for (size_t i = 0; i < elements_count; ++i) {
+ if (!deserialize(elems_ + i, buf_ptr, elements_end, indirect_data_, indirect_end)) {
+ set_invalid(MALFORMED_DATA);
+ return false;
+ }
+ }
+ elems_size_ = elements_count;
+ return true;
+}
+
+bool AuthorizationSet::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ FreeData();
+
+ if (!DeserializeIndirectData(buf_ptr, end) || !DeserializeElementsData(buf_ptr, end))
+ return false;
+
+ if (indirect_data_size_ != ComputeIndirectDataSize(elems_, elems_size_)) {
+ set_invalid(MALFORMED_DATA);
+ return false;
+ }
+ return true;
+}
+
+void AuthorizationSet::FreeData() {
+ if (elems_ != NULL)
+ memset_s(elems_, 0, elems_size_ * sizeof(keymaster_key_param_t));
+ if (indirect_data_ != NULL)
+ memset_s(indirect_data_, 0, indirect_data_size_);
+
+ delete[] elems_;
+ delete[] indirect_data_;
+
+ elems_ = NULL;
+ indirect_data_ = NULL;
+ elems_size_ = 0;
+ elems_capacity_ = 0;
+ indirect_data_size_ = 0;
+ indirect_data_capacity_ = 0;
+ error_ = OK;
+}
+
+/* static */
+size_t AuthorizationSet::ComputeIndirectDataSize(const keymaster_key_param_t* elems, size_t count) {
+ size_t size = 0;
+ for (size_t i = 0; i < count; ++i) {
+ if (is_blob_tag(elems[i].tag)) {
+ size += elems[i].blob.data_length;
+ }
+ }
+ return size;
+}
+
+void AuthorizationSet::CopyIndirectData() {
+ memset_s(indirect_data_, 0, indirect_data_capacity_);
+
+ uint8_t* indirect_data_pos = indirect_data_;
+ for (size_t i = 0; i < elems_size_; ++i) {
+ assert(indirect_data_pos <= indirect_data_ + indirect_data_capacity_);
+ if (is_blob_tag(elems_[i].tag)) {
+ memcpy(indirect_data_pos, elems_[i].blob.data, elems_[i].blob.data_length);
+ elems_[i].blob.data = indirect_data_pos;
+ indirect_data_pos += elems_[i].blob.data_length;
+ }
+ }
+ assert(indirect_data_pos == indirect_data_ + indirect_data_capacity_);
+ indirect_data_size_ = indirect_data_pos - indirect_data_;
+}
+
+bool AuthorizationSet::GetTagValueEnum(keymaster_tag_t tag, uint32_t* val) const {
+ int pos = find(tag);
+ if (pos == -1) {
+ return false;
+ }
+ *val = elems_[pos].enumerated;
+ return true;
+}
+
+bool AuthorizationSet::GetTagValueEnumRep(keymaster_tag_t tag, size_t instance,
+ uint32_t* val) const {
+ size_t count = 0;
+ int pos = -1;
+ while (count <= instance) {
+ pos = find(tag, pos);
+ if (pos == -1) {
+ return false;
+ }
+ ++count;
+ }
+ *val = elems_[pos].enumerated;
+ return true;
+}
+
+bool AuthorizationSet::GetTagValueInt(keymaster_tag_t tag, uint32_t* val) const {
+ int pos = find(tag);
+ if (pos == -1) {
+ return false;
+ }
+ *val = elems_[pos].integer;
+ return true;
+}
+
+bool AuthorizationSet::GetTagValueIntRep(keymaster_tag_t tag, size_t instance,
+ uint32_t* val) const {
+ size_t count = 0;
+ int pos = -1;
+ while (count <= instance) {
+ pos = find(tag, pos);
+ if (pos == -1) {
+ return false;
+ }
+ ++count;
+ }
+ *val = elems_[pos].integer;
+ return true;
+}
+
+bool AuthorizationSet::GetTagValueLong(keymaster_tag_t tag, uint64_t* val) const {
+ int pos = find(tag);
+ if (pos == -1) {
+ return false;
+ }
+ *val = elems_[pos].long_integer;
+ return true;
+}
+
+bool AuthorizationSet::GetTagValueDate(keymaster_tag_t tag, uint64_t* val) const {
+ int pos = find(tag);
+ if (pos == -1) {
+ return false;
+ }
+ *val = elems_[pos].date_time;
+ return true;
+}
+
+bool AuthorizationSet::GetTagValueBlob(keymaster_tag_t tag, keymaster_blob_t* val) const {
+ int pos = find(tag);
+ if (pos == -1) {
+ return false;
+ }
+ *val = elems_[pos].blob;
+ return true;
+}
+
+} // namespace keymaster
diff --git a/authorization_set_test.cpp b/authorization_set_test.cpp
new file mode 100644
index 0000000..f59e6d9
--- /dev/null
+++ b/authorization_set_test.cpp
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <keymaster/authorization_set.h>
+#include <keymaster/google_keymaster_utils.h>
+
+#include "google_keymaster_test_utils.h"
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ return result;
+}
+
+namespace keymaster {
+
+namespace test {
+
+TEST(Construction, ListProvided) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+ EXPECT_EQ(8U, set.size());
+}
+
+TEST(Construction, Copy) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+ AuthorizationSet set2(set);
+ EXPECT_EQ(set, set2);
+}
+
+TEST(Lookup, NonRepeated) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+ EXPECT_EQ(8U, set.size());
+
+ int pos = set.find(TAG_ALGORITHM);
+ ASSERT_NE(-1, pos);
+ EXPECT_EQ(KM_TAG_ALGORITHM, set[pos].tag);
+ EXPECT_EQ(KM_ALGORITHM_RSA, set[pos].enumerated);
+
+ pos = set.find(TAG_MAC_LENGTH);
+ EXPECT_EQ(-1, pos);
+
+ uint32_t int_val = 0;
+ EXPECT_TRUE(set.GetTagValue(TAG_USER_ID, &int_val));
+ EXPECT_EQ(7U, int_val);
+
+ keymaster_blob_t blob_val;
+ EXPECT_TRUE(set.GetTagValue(TAG_APPLICATION_ID, &blob_val));
+ EXPECT_EQ(6U, blob_val.data_length);
+ EXPECT_EQ(0, memcmp(blob_val.data, "my_app", 6));
+}
+
+TEST(Lookup, Repeated) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+ EXPECT_EQ(8U, set.size());
+
+ int pos = set.find(TAG_PURPOSE);
+ ASSERT_FALSE(pos == -1);
+ EXPECT_EQ(KM_TAG_PURPOSE, set[pos].tag);
+ EXPECT_EQ(KM_PURPOSE_SIGN, set[pos].enumerated);
+
+ pos = set.find(TAG_PURPOSE, pos);
+ EXPECT_EQ(KM_TAG_PURPOSE, set[pos].tag);
+ EXPECT_EQ(KM_PURPOSE_VERIFY, set[pos].enumerated);
+
+ EXPECT_EQ(-1, set.find(TAG_PURPOSE, pos));
+}
+
+TEST(Lookup, Indexed) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+ EXPECT_EQ(8U, set.size());
+
+ EXPECT_EQ(KM_TAG_PURPOSE, set[0].tag);
+ EXPECT_EQ(KM_PURPOSE_SIGN, set[0].enumerated);
+
+ // Lookup beyond end doesn't work, just returns zeros, but doens't blow up either (verify by
+ // running under valgrind).
+ EXPECT_EQ(KM_TAG_INVALID, set[10].tag);
+}
+
+TEST(Serialization, RoundTrip) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ Authorization(TAG_ALL_USERS),
+ Authorization(TAG_RSA_PUBLIC_EXPONENT, 3),
+ Authorization(TAG_ACTIVE_DATETIME, 10),
+ };
+ AuthorizationSet set(params, array_length(params));
+
+ size_t size = set.SerializedSize();
+ EXPECT_TRUE(size > 0);
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size));
+ AuthorizationSet deserialized(buf.get(), size);
+
+ EXPECT_EQ(AuthorizationSet::OK, deserialized.is_valid());
+ EXPECT_EQ(set, deserialized);
+
+ int pos = deserialized.find(TAG_APPLICATION_ID);
+ ASSERT_NE(-1, pos);
+ EXPECT_EQ(KM_TAG_APPLICATION_ID, deserialized[pos].tag);
+ EXPECT_EQ(6U, deserialized[pos].blob.data_length);
+ EXPECT_EQ(0, memcmp(deserialized[pos].blob.data, "my_app", 6));
+}
+
+TEST(Deserialization, Deserialize) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+
+ size_t size = set.SerializedSize();
+ EXPECT_TRUE(size > 0);
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size));
+ AuthorizationSet deserialized;
+ const uint8_t* p = buf.get();
+ EXPECT_TRUE(deserialized.Deserialize(&p, p + size));
+ EXPECT_EQ(p, buf.get() + size);
+
+ EXPECT_EQ(AuthorizationSet::OK, deserialized.is_valid());
+
+ EXPECT_EQ(set.size(), deserialized.size());
+ for (size_t i = 0; i < set.size(); ++i) {
+ EXPECT_EQ(set[i].tag, deserialized[i].tag);
+ }
+
+ int pos = deserialized.find(TAG_APPLICATION_ID);
+ ASSERT_NE(-1, pos);
+ EXPECT_EQ(KM_TAG_APPLICATION_ID, deserialized[pos].tag);
+ EXPECT_EQ(6U, deserialized[pos].blob.data_length);
+ EXPECT_EQ(0, memcmp(deserialized[pos].blob.data, "my_app", 6));
+}
+
+TEST(Deserialization, TooShortBuffer) {
+ uint8_t buf[] = {0, 0, 0};
+ AuthorizationSet deserialized(buf, array_length(buf));
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid());
+
+ const uint8_t* p = buf;
+ EXPECT_FALSE(deserialized.Deserialize(&p, p + array_length(buf)));
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid());
+}
+
+TEST(Deserialization, InvalidLengthField) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+
+ size_t size = set.SerializedSize();
+ EXPECT_TRUE(size > 0);
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size));
+ *reinterpret_cast<uint32_t*>(buf.get()) = 9;
+
+ AuthorizationSet deserialized(buf.get(), size);
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid());
+
+ const uint8_t* p = buf.get();
+ EXPECT_FALSE(deserialized.Deserialize(&p, p + size));
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid());
+}
+
+static uint32_t read_uint32(const uint8_t* buf) {
+ uint32_t val;
+ memcpy(&val, buf, sizeof(val));
+ return val;
+}
+
+static void add_to_uint32(uint8_t* buf, int delta) {
+ uint32_t val;
+ memcpy(&val, buf, sizeof(val));
+ val += delta;
+ memcpy(buf, &val, sizeof(val));
+}
+
+TEST(Deserialization, MalformedIndirectData) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_APPLICATION_DATA, "foo", 3),
+ };
+ AuthorizationSet set(params, array_length(params));
+ size_t size = set.SerializedSize();
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size));
+
+ // This sucks. This test, as written, requires intimate knowledge of the serialized layout of
+ // this particular set, which means it's brittle. But it's important to test that we handle
+ // broken serialized data and I can't think of a better way to write this.
+ //
+ // The contents of buf are:
+ //
+ // Bytes: Content:
+ // 0-3 Length of string data, which is 9.
+ // 4-9 "my_app"
+ // 10-12 "foo"
+ // 13-16 Number of elements, which is 2.
+ // 17-20 Length of elements, which is 24.
+ // 21-24 First tag, TAG_APPLICATION_ID
+ // 25-28 Length of string "my_app", 6
+ // 29-32 Offset of string "my_app", 0
+ // 33-36 Second tag, TAG_APPLICATION_DATA
+ // 37-40 Length of string "foo", 3
+ // 41-44 Offset of string "foo", 6
+
+ // Check that stuff is where we think.
+ EXPECT_EQ('m', buf[4]);
+ EXPECT_EQ('f', buf[10]);
+ // Length of "my_app"
+ EXPECT_EQ(6U, read_uint32(buf.get() + 25));
+ // Offset of "my_app"
+ EXPECT_EQ(0U, read_uint32(buf.get() + 29));
+ // Length of "foo"
+ EXPECT_EQ(3U, read_uint32(buf.get() + 37));
+ // Offset of "foo"
+ EXPECT_EQ(6U, read_uint32(buf.get() + 41));
+
+ // Check that deserialization works.
+ AuthorizationSet deserialized1(buf.get(), size);
+ EXPECT_EQ(AuthorizationSet::OK, deserialized1.is_valid());
+
+ const uint8_t* p = buf.get();
+ EXPECT_TRUE(deserialized1.Deserialize(&p, p + size));
+ EXPECT_EQ(AuthorizationSet::OK, deserialized1.is_valid());
+
+ //
+ // Now mess them up in various ways:
+ //
+
+ // Move "foo" offset so offset + length goes off the end
+ add_to_uint32(buf.get() + 41, 1);
+ AuthorizationSet deserialized2(buf.get(), size);
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized2.is_valid());
+ add_to_uint32(buf.get() + 41, -1);
+
+ // Shorten the "my_app" length to make a gap between the blobs.
+ add_to_uint32(buf.get() + 25, -1);
+ AuthorizationSet deserialized3(buf.get(), size);
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized3.is_valid());
+ add_to_uint32(buf.get() + 25, 1);
+
+ // Extend the "my_app" length to make them overlap, and decrease the "foo" length to keep the
+ // total length the same. We don't detect this but should.
+ // TODO(swillden): Detect overlaps and holes that leave total size correct.
+ add_to_uint32(buf.get() + 25, 1);
+ add_to_uint32(buf.get() + 37, -1);
+ AuthorizationSet deserialized4(buf.get(), size);
+ EXPECT_EQ(AuthorizationSet::OK, deserialized4.is_valid());
+}
+
+TEST(Growable, SuccessfulRoundTrip) {
+ keymaster_key_param_t elems_buf[20];
+ uint8_t data_buf[200];
+
+ AuthorizationSet growable;
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)));
+ EXPECT_EQ(1U, growable.size());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)));
+ EXPECT_EQ(2U, growable.size());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)));
+ EXPECT_EQ(3U, growable.size());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_ID, "data", 4)));
+ EXPECT_EQ(4U, growable.size());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_DATA, "some more data", 14)));
+ EXPECT_EQ(5U, growable.size());
+
+ size_t serialize_size = growable.SerializedSize();
+ UniquePtr<uint8_t[]> serialized(new uint8_t[serialize_size]);
+ EXPECT_EQ(serialized.get() + serialize_size,
+ growable.Serialize(serialized.get(), serialized.get() + serialize_size));
+
+ AuthorizationSet deserialized(serialized.get(), serialize_size);
+ EXPECT_EQ(growable, deserialized);
+}
+
+TEST(Growable, InsufficientElemBuf) {
+ keymaster_key_param_t elems_buf[1];
+ uint8_t data_buf[200];
+
+ AuthorizationSet growable;
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ // First insertion fits.
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)));
+ EXPECT_EQ(1U, growable.size());
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ // Second does too.
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_RSA_PUBLIC_EXPONENT, 3)));
+ EXPECT_EQ(2U, growable.size());
+}
+
+TEST(Growable, InsufficientIndirectBuf) {
+ keymaster_key_param_t elems_buf[3];
+ uint8_t data_buf[10];
+
+ AuthorizationSet growable;
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)));
+ EXPECT_EQ(1U, growable.size());
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_ID, "1234567890", 10)));
+ EXPECT_EQ(2U, growable.size());
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_DATA, "1", 1)));
+ EXPECT_EQ(3U, growable.size());
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ // Can still add another entry without indirect data. Now it's full.
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)));
+ EXPECT_EQ(4U, growable.size());
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+}
+
+TEST(Growable, PushBackSets) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set1(params, array_length(params));
+ AuthorizationSet set2(params, array_length(params));
+
+ AuthorizationSet combined;
+ EXPECT_TRUE(combined.push_back(set1));
+ EXPECT_TRUE(combined.push_back(set2));
+ EXPECT_EQ(array_length(params) * 2, combined.size());
+ EXPECT_EQ(12U, combined.indirect_size());
+}
+
+TEST(GetValue, GetInt) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+
+ uint32_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_USER_ID, &val));
+ EXPECT_EQ(7U, val);
+
+ // Find one that isn't there
+ EXPECT_FALSE(set.GetTagValue(TAG_KEY_SIZE, &val));
+}
+
+TEST(GetValue, GetIntRep) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+
+ uint32_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_USER_AUTH_ID, 0, &val));
+ EXPECT_EQ(8U, val);
+
+ // Find one that isn't there
+ EXPECT_FALSE(set.GetTagValue(TAG_USER_AUTH_ID, 1, &val));
+}
+
+TEST(GetValue, GetLong) {
+ keymaster_key_param_t params1[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ keymaster_param_long(TAG_RSA_PUBLIC_EXPONENT, 3),
+ };
+ AuthorizationSet set1(params1, array_length(params1));
+
+ keymaster_key_param_t params2[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ };
+ AuthorizationSet set2(params2, array_length(params2));
+
+ uint64_t val;
+ EXPECT_TRUE(set1.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &val));
+ EXPECT_EQ(3U, val);
+
+ // Find one that isn't there
+ EXPECT_FALSE(set2.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &val));
+}
+
+TEST(GetValue, GetEnum) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+
+ keymaster_algorithm_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_ALGORITHM, &val));
+ EXPECT_EQ(KM_ALGORITHM_RSA, val);
+
+ // Find one that isn't there
+ keymaster_padding_t val2;
+ EXPECT_FALSE(set.GetTagValue(TAG_PADDING, &val2));
+}
+
+TEST(GetValue, GetEnumRep) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+
+ keymaster_purpose_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_PURPOSE, 0, &val));
+ EXPECT_EQ(KM_PURPOSE_SIGN, val);
+ EXPECT_TRUE(set.GetTagValue(TAG_PURPOSE, 1, &val));
+ EXPECT_EQ(KM_PURPOSE_VERIFY, val);
+
+ // Find one that isn't there
+ EXPECT_FALSE(set.GetTagValue(TAG_PURPOSE, 2, &val));
+}
+
+TEST(GetValue, GetDate) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_ACTIVE_DATETIME, 10),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+
+ uint64_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_ACTIVE_DATETIME, &val));
+ EXPECT_EQ(10U, val);
+
+ // Find one that isn't there
+ EXPECT_FALSE(set.GetTagValue(TAG_USAGE_EXPIRE_DATETIME, &val));
+}
+
+TEST(GetValue, GetBlob) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_ACTIVE_DATETIME, 10),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+
+ keymaster_blob_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_APPLICATION_ID, &val));
+ EXPECT_EQ(6U, val.data_length);
+ EXPECT_EQ(0, memcmp(val.data, "my_app", 6));
+
+ // Find one that isn't there
+ EXPECT_FALSE(set.GetTagValue(TAG_APPLICATION_DATA, &val));
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/dsa_operation.cpp b/dsa_operation.cpp
new file mode 100644
index 0000000..c2e0fb1
--- /dev/null
+++ b/dsa_operation.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <openssl/bn.h>
+
+#include "dsa_operation.h"
+#include "openssl_utils.h"
+
+namespace keymaster {
+
+DsaOperation::~DsaOperation() {
+ if (dsa_key_ != NULL)
+ DSA_free(dsa_key_);
+}
+
+keymaster_error_t DsaOperation::Update(const Buffer& input, Buffer* /* output */) {
+ switch (purpose()) {
+ default:
+ return KM_ERROR_UNIMPLEMENTED;
+ case KM_PURPOSE_SIGN:
+ case KM_PURPOSE_VERIFY:
+ return StoreData(input);
+ }
+}
+
+keymaster_error_t DsaOperation::StoreData(const Buffer& input) {
+ if (!data_.reserve(data_.available_read() + input.available_read()) ||
+ !data_.write(input.peek_read(), input.available_read()))
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t DsaSignOperation::Finish(const Buffer& /* signature */, Buffer* output) {
+ output->Reinitialize(DSA_size(dsa_key_));
+ if (data_.available_read() != output->buffer_size())
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+
+ unsigned int siglen;
+ if (!DSA_sign(0 /* type -- ignored */, data_.peek_read(), data_.available_read(),
+ output->peek_write(), &siglen, dsa_key_))
+ return KM_ERROR_UNKNOWN_ERROR;
+ output->advance_write(siglen);
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t DsaVerifyOperation::Finish(const Buffer& signature, Buffer* /* output */) {
+ if ((int)data_.available_read() != DSA_size(dsa_key_))
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+
+ int result = DSA_verify(0 /* type -- ignored */, data_.peek_read(), data_.available_read(),
+ signature.peek_read(), signature.available_read(), dsa_key_);
+ if (result < 0)
+ return KM_ERROR_UNKNOWN_ERROR;
+ else if (result == 0)
+ return KM_ERROR_VERIFICATION_FAILED;
+ else
+ return KM_ERROR_OK;
+}
+
+} // namespace keymaster
diff --git a/dsa_operation.h b/dsa_operation.h
new file mode 100644
index 0000000..685ec55
--- /dev/null
+++ b/dsa_operation.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_DSA_OPERATION_H_
+#define SYSTEM_KEYMASTER_DSA_OPERATION_H_
+
+#include <openssl/dsa.h>
+
+#include <UniquePtr.h>
+
+#include <keymaster/key_blob.h>
+
+#include "operation.h"
+
+namespace keymaster {
+
+class DsaOperation : public Operation {
+ public:
+ DsaOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, DSA* key)
+ : Operation(purpose), dsa_key_(key), digest_(digest), padding_(padding) {}
+ ~DsaOperation();
+
+ virtual keymaster_error_t Begin() { return KM_ERROR_OK; }
+ virtual keymaster_error_t Update(const Buffer& input, Buffer* output);
+ virtual keymaster_error_t Abort() { return KM_ERROR_OK; }
+
+ protected:
+ keymaster_error_t StoreData(const Buffer& input);
+
+ DSA* dsa_key_;
+ keymaster_digest_t digest_;
+ keymaster_padding_t padding_;
+ Buffer data_;
+};
+
+class DsaSignOperation : public DsaOperation {
+ public:
+ DsaSignOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, DSA* key)
+ : DsaOperation(purpose, digest, padding, key) {}
+ virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
+};
+
+class DsaVerifyOperation : public DsaOperation {
+ public:
+ DsaVerifyOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, DSA* key)
+ : DsaOperation(purpose, digest, padding, key) {}
+ virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_DSA_OPERATION_H_
diff --git a/dsa_privkey_pk8.der b/dsa_privkey_pk8.der
new file mode 100644
index 0000000..2786bc7
--- /dev/null
+++ b/dsa_privkey_pk8.der
Binary files differ
diff --git a/ec_privkey_pk8.der b/ec_privkey_pk8.der
new file mode 100644
index 0000000..a4af673
--- /dev/null
+++ b/ec_privkey_pk8.der
Binary files differ
diff --git a/ecdsa_operation.cpp b/ecdsa_operation.cpp
new file mode 100644
index 0000000..3987394
--- /dev/null
+++ b/ecdsa_operation.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <openssl/ecdsa.h>
+
+#include "ecdsa_operation.h"
+#include "openssl_utils.h"
+
+namespace keymaster {
+
+EcdsaOperation::~EcdsaOperation() {
+ if (ecdsa_key_ != NULL)
+ EC_KEY_free(ecdsa_key_);
+}
+
+keymaster_error_t EcdsaOperation::Update(const Buffer& input, Buffer* /* output */) {
+ switch (purpose()) {
+ default:
+ return KM_ERROR_UNIMPLEMENTED;
+ case KM_PURPOSE_SIGN:
+ case KM_PURPOSE_VERIFY:
+ return StoreData(input);
+ }
+}
+
+keymaster_error_t EcdsaOperation::StoreData(const Buffer& input) {
+ if (!data_.reserve(data_.available_read() + input.available_read()) ||
+ !data_.write(input.peek_read(), input.available_read()))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t EcdsaSignOperation::Finish(const Buffer& /* signature */, Buffer* output) {
+ output->Reinitialize(ECDSA_size(ecdsa_key_));
+ unsigned int siglen;
+ if (!ECDSA_sign(0 /* type -- ignored */, data_.peek_read(), data_.available_read(),
+ output->peek_write(), &siglen, ecdsa_key_))
+ return KM_ERROR_UNKNOWN_ERROR;
+ output->advance_write(siglen);
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t EcdsaVerifyOperation::Finish(const Buffer& signature, Buffer* /* output */) {
+ int result = ECDSA_verify(0 /* type -- ignored */, data_.peek_read(), data_.available_read(),
+ signature.peek_read(), signature.available_read(), ecdsa_key_);
+ if (result < 0)
+ return KM_ERROR_UNKNOWN_ERROR;
+ else if (result == 0)
+ return KM_ERROR_VERIFICATION_FAILED;
+ else
+ return KM_ERROR_OK;
+}
+
+} // namespace keymaster
diff --git a/ecdsa_operation.h b/ecdsa_operation.h
new file mode 100644
index 0000000..473f87f
--- /dev/null
+++ b/ecdsa_operation.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_ECDSA_OPERATION_H_
+#define SYSTEM_KEYMASTER_ECDSA_OPERATION_H_
+
+#include <openssl/ec.h>
+
+#include <UniquePtr.h>
+
+#include <keymaster/key_blob.h>
+
+#include "operation.h"
+
+namespace keymaster {
+
+class EcdsaOperation : public Operation {
+ public:
+ EcdsaOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, EC_KEY* key)
+ : Operation(purpose), ecdsa_key_(key), digest_(digest), padding_(padding) {}
+ ~EcdsaOperation();
+
+ virtual keymaster_error_t Begin() { return KM_ERROR_OK; }
+ virtual keymaster_error_t Update(const Buffer& input, Buffer* output);
+ virtual keymaster_error_t Abort() { return KM_ERROR_OK; }
+
+ protected:
+ keymaster_error_t StoreData(const Buffer& input);
+
+ EC_KEY* ecdsa_key_;
+ keymaster_digest_t digest_;
+ keymaster_padding_t padding_;
+ Buffer data_;
+};
+
+class EcdsaSignOperation : public EcdsaOperation {
+ public:
+ EcdsaSignOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, EC_KEY* key)
+ : EcdsaOperation(purpose, digest, padding, key) {}
+ virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
+};
+
+class EcdsaVerifyOperation : public EcdsaOperation {
+ public:
+ EcdsaVerifyOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, EC_KEY* key)
+ : EcdsaOperation(purpose, digest, padding, key) {}
+ virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_ECDSA_OPERATION_H_
diff --git a/google_keymaster.cpp b/google_keymaster.cpp
new file mode 100644
index 0000000..29a8129
--- /dev/null
+++ b/google_keymaster.cpp
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <cstddef>
+
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+#include <UniquePtr.h>
+
+#include <keymaster/google_keymaster.h>
+#include <keymaster/google_keymaster_utils.h>
+#include <keymaster/key_blob.h>
+
+#include "ae.h"
+#include "key.h"
+#include "operation.h"
+
+namespace keymaster {
+
+GoogleKeymaster::GoogleKeymaster(size_t operation_table_size, Logger* logger)
+ : operation_table_(new OpTableEntry[operation_table_size]),
+ operation_table_size_(operation_table_size), logger_(logger) {
+ if (operation_table_.get() == NULL)
+ operation_table_size_ = 0;
+}
+GoogleKeymaster::~GoogleKeymaster() {
+ for (size_t i = 0; i < operation_table_size_; ++i)
+ if (operation_table_[i].operation != NULL)
+ delete operation_table_[i].operation;
+}
+
+struct AE_CTX_Delete {
+ void operator()(ae_ctx* ctx) const { ae_free(ctx); }
+};
+typedef UniquePtr<ae_ctx, AE_CTX_Delete> Unique_ae_ctx;
+
+keymaster_algorithm_t supported_algorithms[] = {
+ KM_ALGORITHM_RSA, KM_ALGORITHM_DSA, KM_ALGORITHM_ECDSA,
+};
+
+template <typename T>
+bool check_supported(keymaster_algorithm_t algorithm, SupportedResponse<T>* response) {
+ if (!array_contains(supported_algorithms, algorithm)) {
+ response->error = KM_ERROR_UNSUPPORTED_ALGORITHM;
+ return false;
+ }
+ return true;
+}
+
+void
+GoogleKeymaster::SupportedAlgorithms(SupportedResponse<keymaster_algorithm_t>* response) const {
+ if (response == NULL)
+ return;
+ response->SetResults(supported_algorithms);
+}
+
+void
+GoogleKeymaster::SupportedBlockModes(keymaster_algorithm_t algorithm,
+ SupportedResponse<keymaster_block_mode_t>* response) const {
+ if (response == NULL || !check_supported(algorithm, response))
+ return;
+ response->error = KM_ERROR_OK;
+}
+
+keymaster_padding_t supported_padding[] = {KM_PAD_NONE};
+void
+GoogleKeymaster::SupportedPaddingModes(keymaster_algorithm_t algorithm,
+ SupportedResponse<keymaster_padding_t>* response) const {
+ if (response == NULL || !check_supported(algorithm, response))
+ return;
+
+ response->error = KM_ERROR_OK;
+ switch (algorithm) {
+ case KM_ALGORITHM_RSA:
+ case KM_ALGORITHM_DSA:
+ case KM_ALGORITHM_ECDSA:
+ response->SetResults(supported_padding);
+ break;
+ default:
+ response->results_length = 0;
+ break;
+ }
+}
+
+keymaster_digest_t supported_digests[] = {KM_DIGEST_NONE};
+void GoogleKeymaster::SupportedDigests(keymaster_algorithm_t algorithm,
+ SupportedResponse<keymaster_digest_t>* response) const {
+ if (response == NULL || !check_supported(algorithm, response))
+ return;
+
+ response->error = KM_ERROR_OK;
+ switch (algorithm) {
+ case KM_ALGORITHM_RSA:
+ case KM_ALGORITHM_DSA:
+ case KM_ALGORITHM_ECDSA:
+ response->SetResults(supported_digests);
+ break;
+ default:
+ response->results_length = 0;
+ break;
+ }
+}
+
+keymaster_key_format_t supported_import_formats[] = {KM_KEY_FORMAT_PKCS8};
+void
+GoogleKeymaster::SupportedImportFormats(keymaster_algorithm_t algorithm,
+ SupportedResponse<keymaster_key_format_t>* response) const {
+ if (response == NULL || !check_supported(algorithm, response))
+ return;
+
+ response->error = KM_ERROR_OK;
+ switch (algorithm) {
+ case KM_ALGORITHM_RSA:
+ case KM_ALGORITHM_DSA:
+ case KM_ALGORITHM_ECDSA:
+ response->SetResults(supported_import_formats);
+ break;
+ default:
+ response->results_length = 0;
+ break;
+ }
+}
+
+keymaster_key_format_t supported_export_formats[] = {KM_KEY_FORMAT_X509};
+void
+GoogleKeymaster::SupportedExportFormats(keymaster_algorithm_t algorithm,
+ SupportedResponse<keymaster_key_format_t>* response) const {
+ if (response == NULL || !check_supported(algorithm, response))
+ return;
+
+ response->error = KM_ERROR_OK;
+ switch (algorithm) {
+ case KM_ALGORITHM_RSA:
+ case KM_ALGORITHM_DSA:
+ case KM_ALGORITHM_ECDSA:
+ response->SetResults(supported_export_formats);
+ break;
+ default:
+ response->results_length = 0;
+ break;
+ }
+}
+
+void GoogleKeymaster::GenerateKey(const GenerateKeyRequest& request,
+ GenerateKeyResponse* response) {
+ if (response == NULL)
+ return;
+
+ UniquePtr<Key> key(Key::GenerateKey(request.key_description, logger(), &response->error));
+ if (response->error != KM_ERROR_OK)
+ return;
+
+ response->error = SerializeKey(key.get(), origin(), &response->key_blob, &response->enforced,
+ &response->unenforced);
+}
+
+void GoogleKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
+ GetKeyCharacteristicsResponse* response) {
+ if (response == NULL)
+ return;
+ response->error = KM_ERROR_UNKNOWN_ERROR;
+
+ UniquePtr<KeyBlob> blob(
+ LoadKeyBlob(request.key_blob, request.additional_params, &(response->error)));
+ if (blob.get() == NULL)
+ return;
+
+ response->enforced.Reinitialize(blob->enforced());
+ response->unenforced.Reinitialize(blob->unenforced());
+ response->error = KM_ERROR_OK;
+}
+
+void GoogleKeymaster::BeginOperation(const BeginOperationRequest& request,
+ BeginOperationResponse* response) {
+ if (response == NULL)
+ return;
+ response->op_handle = 0;
+
+ UniquePtr<Key> key(LoadKey(request.key_blob, request.additional_params, &response->error));
+ if (key.get() == NULL)
+ return;
+
+ UniquePtr<Operation> operation(key->CreateOperation(request.purpose, &response->error));
+ if (operation.get() == NULL)
+ return;
+
+ response->error = operation->Begin();
+ if (response->error != KM_ERROR_OK)
+ return;
+
+ response->error = AddOperation(operation.release(), &response->op_handle);
+}
+
+void GoogleKeymaster::UpdateOperation(const UpdateOperationRequest& request,
+ UpdateOperationResponse* response) {
+ OpTableEntry* entry = FindOperation(request.op_handle);
+ if (entry == NULL) {
+ response->error = KM_ERROR_INVALID_OPERATION_HANDLE;
+ return;
+ }
+
+ response->error = entry->operation->Update(request.input, &response->output);
+ if (response->error != KM_ERROR_OK) {
+ // Any error invalidates the operation.
+ DeleteOperation(entry);
+ }
+}
+
+void GoogleKeymaster::FinishOperation(const FinishOperationRequest& request,
+ FinishOperationResponse* response) {
+ OpTableEntry* entry = FindOperation(request.op_handle);
+ if (entry == NULL) {
+ response->error = KM_ERROR_INVALID_OPERATION_HANDLE;
+ return;
+ }
+
+ response->error = entry->operation->Finish(request.signature, &response->output);
+ DeleteOperation(entry);
+}
+
+keymaster_error_t GoogleKeymaster::AbortOperation(const keymaster_operation_handle_t op_handle) {
+ OpTableEntry* entry = FindOperation(op_handle);
+ if (entry == NULL)
+ return KM_ERROR_INVALID_OPERATION_HANDLE;
+ DeleteOperation(entry);
+ return KM_ERROR_OK;
+}
+
+bool GoogleKeymaster::is_supported_export_format(keymaster_key_format_t test_format) {
+ unsigned int index;
+ for (index = 0; index < array_length(supported_export_formats); index++) {
+ if (test_format == supported_export_formats[index]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool GoogleKeymaster::is_supported_import_format(keymaster_key_format_t test_format) {
+ unsigned int index;
+ for (index = 0; index < array_length(supported_import_formats); index++) {
+ if (test_format == supported_import_formats[index]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void GoogleKeymaster::ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response) {
+ if (response == NULL)
+ return;
+
+ UniquePtr<Key> to_export(
+ LoadKey(request.key_blob, request.additional_params, &response->error));
+ if (to_export.get() == NULL)
+ return;
+
+ UniquePtr<uint8_t[]> out_key;
+ size_t size;
+ response->error = to_export->formatted_key_material(request.key_format, &out_key, &size);
+ if (response->error == KM_ERROR_OK) {
+ response->key_data = out_key.release();
+ response->key_data_length = size;
+ }
+}
+
+void GoogleKeymaster::ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response) {
+ if (response == NULL)
+ return;
+
+ UniquePtr<Key> key(Key::ImportKey(request.key_description, request.key_format, request.key_data,
+ request.key_data_length, logger(), &response->error));
+ if (response->error != KM_ERROR_OK)
+ return;
+
+ response->error = SerializeKey(key.get(), KM_ORIGIN_IMPORTED, &response->key_blob,
+ &response->enforced, &response->unenforced);
+}
+
+keymaster_error_t GoogleKeymaster::SerializeKey(const Key* key, keymaster_key_origin_t origin,
+ keymaster_key_blob_t* keymaster_blob,
+ AuthorizationSet* enforced,
+ AuthorizationSet* unenforced) {
+ keymaster_error_t error;
+
+ error = SetAuthorizations(key->authorizations(), origin, enforced, unenforced);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ AuthorizationSet hidden_auths;
+ error = BuildHiddenAuthorizations(key->authorizations(), &hidden_auths);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ UniquePtr<uint8_t[]> key_material;
+ size_t key_material_size;
+ error = key->key_material(&key_material, &key_material_size);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ uint8_t nonce[KeyBlob::NONCE_LENGTH];
+ GenerateNonce(nonce, array_size(nonce));
+
+ keymaster_key_blob_t key_data = {key_material.get(), key_material_size};
+ UniquePtr<KeyBlob> blob(
+ new KeyBlob(*enforced, *unenforced, hidden_auths, key_data, MasterKey(), nonce));
+ if (blob.get() == NULL)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ if (blob->error() != KM_ERROR_OK)
+ return blob->error();
+
+ size_t size = blob->SerializedSize();
+ UniquePtr<uint8_t[]> blob_bytes(new uint8_t[size]);
+ if (blob_bytes.get() == NULL)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ blob->Serialize(blob_bytes.get(), blob_bytes.get() + size);
+ keymaster_blob->key_material_size = size;
+ keymaster_blob->key_material = blob_bytes.release();
+
+ return KM_ERROR_OK;
+}
+
+Key* GoogleKeymaster::LoadKey(const keymaster_key_blob_t& key,
+ const AuthorizationSet& client_params, keymaster_error_t* error) {
+ UniquePtr<KeyBlob> blob(LoadKeyBlob(key, client_params, error));
+ if (*error != KM_ERROR_OK)
+ return NULL;
+ return Key::CreateKey(*blob, logger(), error);
+}
+
+KeyBlob* GoogleKeymaster::LoadKeyBlob(const keymaster_key_blob_t& key,
+ const AuthorizationSet& client_params,
+ keymaster_error_t* error) {
+ AuthorizationSet hidden;
+ BuildHiddenAuthorizations(client_params, &hidden);
+ UniquePtr<KeyBlob> blob(new KeyBlob(key, hidden, MasterKey()));
+ if (blob.get() == NULL) {
+ *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return NULL;
+ } else if (blob->error() != KM_ERROR_OK) {
+ *error = blob->error();
+ return NULL;
+ }
+ *error = KM_ERROR_OK;
+ return blob.release();
+}
+
+static keymaster_error_t TranslateAuthorizationSetError(AuthorizationSet::Error err) {
+ switch (err) {
+ case AuthorizationSet::OK:
+ return KM_ERROR_OK;
+ case AuthorizationSet::ALLOCATION_FAILURE:
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ case AuthorizationSet::MALFORMED_DATA:
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t GoogleKeymaster::SetAuthorizations(const AuthorizationSet& key_description,
+ keymaster_key_origin_t origin,
+ AuthorizationSet* enforced,
+ AuthorizationSet* unenforced) {
+ for (size_t i = 0; i < key_description.size(); ++i) {
+ switch (key_description[i].tag) {
+ // These cannot be specified by the client.
+ case KM_TAG_ROOT_OF_TRUST:
+ case KM_TAG_ORIGIN:
+ return KM_ERROR_INVALID_TAG;
+
+ // These don't work.
+ case KM_TAG_ROLLBACK_RESISTANT:
+ return KM_ERROR_UNSUPPORTED_TAG;
+
+ // These are hidden.
+ case KM_TAG_APPLICATION_ID:
+ case KM_TAG_APPLICATION_DATA:
+ break;
+
+ // Everything else we just copy into the appropriate set.
+ default:
+ AddAuthorization(key_description[i], enforced, unenforced);
+ break;
+ }
+ }
+
+ AddAuthorization(Authorization(TAG_CREATION_DATETIME, java_time(time(NULL))), enforced,
+ unenforced);
+ AddAuthorization(Authorization(TAG_ORIGIN, origin), enforced, unenforced);
+
+ if (enforced->is_valid() != AuthorizationSet::OK)
+ return TranslateAuthorizationSetError(enforced->is_valid());
+
+ return TranslateAuthorizationSetError(unenforced->is_valid());
+}
+
+keymaster_error_t GoogleKeymaster::BuildHiddenAuthorizations(const AuthorizationSet& input_set,
+ AuthorizationSet* hidden) {
+ keymaster_blob_t entry;
+ if (input_set.GetTagValue(TAG_APPLICATION_ID, &entry))
+ hidden->push_back(TAG_APPLICATION_ID, entry.data, entry.data_length);
+ if (input_set.GetTagValue(TAG_APPLICATION_DATA, &entry))
+ hidden->push_back(TAG_APPLICATION_DATA, entry.data, entry.data_length);
+ hidden->push_back(RootOfTrustTag());
+
+ return TranslateAuthorizationSetError(hidden->is_valid());
+}
+
+void GoogleKeymaster::AddAuthorization(const keymaster_key_param_t& auth,
+ AuthorizationSet* enforced, AuthorizationSet* unenforced) {
+ if (is_enforced(auth.tag))
+ enforced->push_back(auth);
+ else
+ unenforced->push_back(auth);
+}
+
+keymaster_error_t GoogleKeymaster::AddOperation(Operation* operation,
+ keymaster_operation_handle_t* op_handle) {
+ UniquePtr<Operation> op(operation);
+ if (RAND_bytes(reinterpret_cast<uint8_t*>(op_handle), sizeof(*op_handle)) == 0)
+ return KM_ERROR_UNKNOWN_ERROR;
+ if (*op_handle == 0) {
+ // Statistically this is vanishingly unlikely, which means if it ever happens in practice,
+ // it indicates a broken RNG.
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ for (size_t i = 0; i < operation_table_size_; ++i) {
+ if (operation_table_[i].operation == NULL) {
+ operation_table_[i].operation = op.release();
+ operation_table_[i].handle = *op_handle;
+ return KM_ERROR_OK;
+ }
+ }
+ return KM_ERROR_TOO_MANY_OPERATIONS;
+}
+
+GoogleKeymaster::OpTableEntry*
+GoogleKeymaster::FindOperation(keymaster_operation_handle_t op_handle) {
+ if (op_handle == 0)
+ return NULL;
+
+ for (size_t i = 0; i < operation_table_size_; ++i) {
+ if (operation_table_[i].handle == op_handle)
+ return operation_table_.get() + i;
+ }
+ return NULL;
+}
+
+void GoogleKeymaster::DeleteOperation(OpTableEntry* entry) {
+ delete entry->operation;
+ entry->operation = NULL;
+ entry->handle = 0;
+}
+
+} // namespace keymaster
diff --git a/google_keymaster_messages.cpp b/google_keymaster_messages.cpp
new file mode 100644
index 0000000..90c22f4
--- /dev/null
+++ b/google_keymaster_messages.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <keymaster/google_keymaster_messages.h>
+#include <keymaster/google_keymaster_utils.h>
+
+namespace keymaster {
+
+size_t KeymasterResponse::SerializedSize() const {
+ if (error != KM_ERROR_OK)
+ return sizeof(int32_t);
+ else
+ return sizeof(int32_t) + NonErrorSerializedSize();
+}
+
+uint8_t* KeymasterResponse::Serialize(uint8_t* buf, const uint8_t* end) const {
+ buf = append_uint32_to_buf(buf, end, static_cast<uint32_t>(error));
+ if (error == KM_ERROR_OK)
+ buf = NonErrorSerialize(buf, end);
+ return buf;
+}
+
+bool KeymasterResponse::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ if (!copy_uint32_from_buf(buf_ptr, end, &error))
+ return false;
+ if (error != KM_ERROR_OK)
+ return true;
+ return NonErrorDeserialize(buf_ptr, end);
+}
+
+size_t SupportedAlgorithmsResponse::NonErrorSerializedSize() const {
+ return sizeof(uint32_t) + sizeof(uint32_t) * algorithms_length;
+}
+
+uint8_t* SupportedAlgorithmsResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const {
+ return append_uint32_array_to_buf(buf, end, algorithms, algorithms_length);
+}
+
+bool SupportedAlgorithmsResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ delete[] algorithms;
+ algorithms = NULL;
+ UniquePtr<keymaster_algorithm_t[]> deserialized_algorithms;
+ if (!copy_uint32_array_from_buf(buf_ptr, end, &deserialized_algorithms, &algorithms_length))
+ return false;
+ algorithms = deserialized_algorithms.release();
+ return true;
+}
+
+GenerateKeyResponse::~GenerateKeyResponse() {
+ delete[] key_blob.key_material;
+}
+
+size_t GenerateKeyResponse::NonErrorSerializedSize() const {
+ return sizeof(uint32_t) /* key size */ + key_blob.key_material_size +
+ enforced.SerializedSize() + unenforced.SerializedSize();
+}
+
+uint8_t* GenerateKeyResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const {
+ buf = append_size_and_data_to_buf(buf, end, key_blob.key_material, key_blob.key_material_size);
+ buf = enforced.Serialize(buf, end);
+ return unenforced.Serialize(buf, end);
+}
+
+bool GenerateKeyResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ delete[] key_blob.key_material;
+ key_blob.key_material = NULL;
+ UniquePtr<uint8_t[]> deserialized_key_material;
+ if (!copy_size_and_data_from_buf(buf_ptr, end, &key_blob.key_material_size,
+ &deserialized_key_material) ||
+ !enforced.Deserialize(buf_ptr, end) || !unenforced.Deserialize(buf_ptr, end))
+ return false;
+ key_blob.key_material = deserialized_key_material.release();
+ return true;
+}
+
+GetKeyCharacteristicsRequest::~GetKeyCharacteristicsRequest() {
+ delete[] key_blob.key_material;
+}
+
+void GetKeyCharacteristicsRequest::SetKeyMaterial(const void* key_material, size_t length) {
+ delete[] key_blob.key_material;
+ key_blob.key_material = dup_buffer(key_material, length);
+ key_blob.key_material_size = length;
+}
+
+size_t GetKeyCharacteristicsRequest::SerializedSize() const {
+ return sizeof(uint32_t) /* key blob size */ + key_blob.key_material_size +
+ additional_params.SerializedSize();
+}
+
+uint8_t* GetKeyCharacteristicsRequest::Serialize(uint8_t* buf, const uint8_t* end) const {
+ buf = append_size_and_data_to_buf(buf, end, key_blob.key_material, key_blob.key_material_size);
+ return additional_params.Serialize(buf, end);
+}
+
+bool GetKeyCharacteristicsRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ delete[] key_blob.key_material;
+ key_blob.key_material = NULL;
+ UniquePtr<uint8_t[]> deserialized_key_material;
+ if (!copy_size_and_data_from_buf(buf_ptr, end, &key_blob.key_material_size,
+ &deserialized_key_material) ||
+ !additional_params.Deserialize(buf_ptr, end))
+ return false;
+ key_blob.key_material = deserialized_key_material.release();
+ return true;
+}
+
+size_t GetKeyCharacteristicsResponse::NonErrorSerializedSize() const {
+ return enforced.SerializedSize() + unenforced.SerializedSize();
+}
+
+uint8_t* GetKeyCharacteristicsResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const {
+ buf = enforced.Serialize(buf, end);
+ return unenforced.Serialize(buf, end);
+}
+
+bool GetKeyCharacteristicsResponse::NonErrorDeserialize(const uint8_t** buf_ptr,
+ const uint8_t* end) {
+ return enforced.Deserialize(buf_ptr, end) && unenforced.Deserialize(buf_ptr, end);
+}
+
+void BeginOperationRequest::SetKeyMaterial(const void* key_material, size_t length) {
+ delete[] key_blob.key_material;
+ key_blob.key_material = dup_buffer(key_material, length);
+ key_blob.key_material_size = length;
+}
+
+size_t BeginOperationRequest::SerializedSize() const {
+ return sizeof(uint32_t) /* purpose */ + sizeof(uint32_t) /* key length */ +
+ key_blob.key_material_size + additional_params.SerializedSize();
+}
+
+uint8_t* BeginOperationRequest::Serialize(uint8_t* buf, const uint8_t* end) const {
+ buf = append_uint32_to_buf(buf, end, purpose);
+ buf = append_size_and_data_to_buf(buf, end, key_blob.key_material, key_blob.key_material_size);
+ return additional_params.Serialize(buf, end);
+}
+
+bool BeginOperationRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ delete[] key_blob.key_material;
+ key_blob.key_material = 0;
+ UniquePtr<uint8_t[]> deserialized_key_material;
+ if (!copy_uint32_from_buf(buf_ptr, end, &purpose) ||
+ !copy_size_and_data_from_buf(buf_ptr, end, &key_blob.key_material_size,
+ &deserialized_key_material) ||
+ !additional_params.Deserialize(buf_ptr, end))
+ return false;
+ key_blob.key_material = deserialized_key_material.release();
+ return true;
+}
+
+size_t BeginOperationResponse::NonErrorSerializedSize() const {
+ return sizeof(op_handle);
+}
+
+uint8_t* BeginOperationResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const {
+ return append_uint64_to_buf(buf, end, op_handle);
+}
+
+bool BeginOperationResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ return copy_uint64_from_buf(buf_ptr, end, &op_handle);
+}
+
+size_t UpdateOperationRequest::SerializedSize() const {
+ return sizeof(op_handle) + input.SerializedSize();
+}
+
+uint8_t* UpdateOperationRequest::Serialize(uint8_t* buf, const uint8_t* end) const {
+ buf = append_uint64_to_buf(buf, end, op_handle);
+ return input.Serialize(buf, end);
+}
+
+bool UpdateOperationRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ return copy_uint64_from_buf(buf_ptr, end, &op_handle) && input.Deserialize(buf_ptr, end);
+}
+
+size_t UpdateOperationResponse::NonErrorSerializedSize() const {
+ return output.SerializedSize();
+}
+
+uint8_t* UpdateOperationResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const {
+ return output.Serialize(buf, end);
+}
+
+bool UpdateOperationResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ return output.Deserialize(buf_ptr, end);
+}
+
+size_t FinishOperationRequest::SerializedSize() const {
+ return sizeof(op_handle) + signature.SerializedSize();
+}
+
+uint8_t* FinishOperationRequest::Serialize(uint8_t* buf, const uint8_t* end) const {
+ buf = append_uint64_to_buf(buf, end, op_handle);
+ return signature.Serialize(buf, end);
+}
+
+bool FinishOperationRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ return copy_uint64_from_buf(buf_ptr, end, &op_handle) && signature.Deserialize(buf_ptr, end);
+}
+
+size_t FinishOperationResponse::NonErrorSerializedSize() const {
+ return output.SerializedSize();
+}
+
+uint8_t* FinishOperationResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const {
+ return output.Serialize(buf, end);
+}
+
+bool FinishOperationResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ return output.Deserialize(buf_ptr, end);
+}
+
+void ImportKeyRequest::SetKeyMaterial(const void* key_material, size_t length) {
+ delete[] key_data;
+ key_data = dup_buffer(key_material, length);
+ key_data_length = length;
+}
+
+size_t ImportKeyRequest::SerializedSize() const {
+ return key_description.SerializedSize() + sizeof(uint32_t) /* key_format */ +
+ sizeof(uint32_t) /* key_data_length */ + key_data_length;
+}
+
+uint8_t* ImportKeyRequest::Serialize(uint8_t* buf, const uint8_t* end) const {
+ buf = key_description.Serialize(buf, end);
+ buf = append_uint32_to_buf(buf, end, key_format);
+ return append_size_and_data_to_buf(buf, end, key_data, key_data_length);
+}
+
+bool ImportKeyRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ delete[] key_data;
+ key_data = NULL;
+ UniquePtr<uint8_t[]> deserialized_key_material;
+ if (!key_description.Deserialize(buf_ptr, end) ||
+ !copy_uint32_from_buf(buf_ptr, end, &key_format) ||
+ !copy_size_and_data_from_buf(buf_ptr, end, &key_data_length, &deserialized_key_material))
+ return false;
+ key_data = deserialized_key_material.release();
+ return true;
+}
+
+void ImportKeyResponse::SetKeyMaterial(const void* key_material, size_t length) {
+ delete[] key_blob.key_material;
+ key_blob.key_material = dup_buffer(key_material, length);
+ key_blob.key_material_size = length;
+}
+
+size_t ImportKeyResponse::NonErrorSerializedSize() const {
+ return sizeof(uint32_t) /* key_material length */ + key_blob.key_material_size +
+ enforced.SerializedSize() + unenforced.SerializedSize();
+}
+
+uint8_t* ImportKeyResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const {
+ buf = append_size_and_data_to_buf(buf, end, key_blob.key_material, key_blob.key_material_size);
+ buf = enforced.Serialize(buf, end);
+ return unenforced.Serialize(buf, end);
+}
+
+bool ImportKeyResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ delete[] key_blob.key_material;
+ key_blob.key_material = NULL;
+ UniquePtr<uint8_t[]> deserialized_key_material;
+ if (!copy_size_and_data_from_buf(buf_ptr, end, &key_blob.key_material_size,
+ &deserialized_key_material) ||
+ !enforced.Deserialize(buf_ptr, end) || !unenforced.Deserialize(buf_ptr, end))
+ return false;
+ key_blob.key_material = deserialized_key_material.release();
+ return true;
+}
+
+void ExportKeyRequest::SetKeyMaterial(const void* key_material, size_t length) {
+ delete[] key_blob.key_material;
+ key_blob.key_material = dup_buffer(key_material, length);
+ key_blob.key_material_size = length;
+}
+
+size_t ExportKeyRequest::SerializedSize() const {
+ return additional_params.SerializedSize() + sizeof(uint32_t) /* key_format */ +
+ sizeof(uint32_t) /* key_material_size */ + key_blob.key_material_size;
+}
+
+uint8_t* ExportKeyRequest::Serialize(uint8_t* buf, const uint8_t* end) const {
+ buf = additional_params.Serialize(buf, end);
+ buf = append_uint32_to_buf(buf, end, key_format);
+ return append_size_and_data_to_buf(buf, end, key_blob.key_material, key_blob.key_material_size);
+}
+
+bool ExportKeyRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ delete[] key_blob.key_material;
+ key_blob.key_material = NULL;
+ UniquePtr<uint8_t[]> deserialized_key_material;
+ if (!additional_params.Deserialize(buf_ptr, end) ||
+ !copy_uint32_from_buf(buf_ptr, end, &key_format) ||
+ !copy_size_and_data_from_buf(buf_ptr, end, &key_blob.key_material_size,
+ &deserialized_key_material))
+ return false;
+ key_blob.key_material = deserialized_key_material.release();
+ return true;
+}
+
+void ExportKeyResponse::SetKeyMaterial(const void* key_material, size_t length) {
+ delete[] key_data;
+ key_data = dup_buffer(key_material, length);
+ key_data_length = length;
+}
+
+size_t ExportKeyResponse::NonErrorSerializedSize() const {
+ return sizeof(uint32_t) /* key_data_length */ + key_data_length;
+}
+
+uint8_t* ExportKeyResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const {
+ return append_size_and_data_to_buf(buf, end, key_data, key_data_length);
+}
+
+bool ExportKeyResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ delete[] key_data;
+ key_data = NULL;
+ UniquePtr<uint8_t[]> deserialized_key_material;
+ if (!copy_size_and_data_from_buf(buf_ptr, end, &key_data_length, &deserialized_key_material))
+ return false;
+ key_data = deserialized_key_material.release();
+ return true;
+}
+
+} // namespace keymaster
diff --git a/google_keymaster_messages_test.cpp b/google_keymaster_messages_test.cpp
new file mode 100644
index 0000000..055cfa3
--- /dev/null
+++ b/google_keymaster_messages_test.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <UniquePtr.h>
+
+#include <gtest/gtest.h>
+
+#include <keymaster/keymaster_tags.h>
+#include <keymaster/google_keymaster_utils.h>
+
+#include "google_keymaster_test_utils.h"
+#include "google_softkeymaster.h"
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ return result;
+}
+
+namespace keymaster {
+namespace test {
+
+/**
+ * Serialize and deserialize a message.
+ */
+template <typename Message> Message* round_trip(const Message& message, size_t expected_size) {
+ size_t size = message.SerializedSize();
+ EXPECT_EQ(expected_size, size);
+ if (size == 0)
+ return NULL;
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, message.Serialize(buf.get(), buf.get() + size));
+
+ Message* deserialized = new Message;
+ const uint8_t* p = buf.get();
+ EXPECT_TRUE(deserialized->Deserialize(&p, p + size));
+ EXPECT_EQ((ptrdiff_t)size, p - buf.get());
+ return deserialized;
+}
+
+class EmptyKeymasterResponse : public KeymasterResponse {
+ size_t NonErrorSerializedSize() const { return 1; }
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* /* end */) const {
+ *buf++ = 0;
+ return buf;
+ }
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ if (*buf_ptr >= end)
+ return false;
+ EXPECT_EQ(0, **buf_ptr);
+ (*buf_ptr)++;
+ return true;
+ }
+};
+
+TEST(RoundTrip, EmptyKeymasterResponse) {
+ EmptyKeymasterResponse msg;
+ msg.error = KM_ERROR_OK;
+
+ UniquePtr<EmptyKeymasterResponse> deserialized(round_trip(msg, 5));
+}
+
+TEST(RoundTrip, EmptyKeymasterResponseError) {
+ EmptyKeymasterResponse msg;
+ msg.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ UniquePtr<EmptyKeymasterResponse> deserialized(round_trip(msg, 4));
+}
+
+TEST(RoundTrip, SupportedAlgorithmsResponse) {
+ SupportedAlgorithmsResponse rsp;
+ keymaster_algorithm_t algorithms[] = {KM_ALGORITHM_RSA, KM_ALGORITHM_DSA, KM_ALGORITHM_ECDSA};
+ rsp.error = KM_ERROR_OK;
+ rsp.algorithms = dup_array(algorithms);
+ rsp.algorithms_length = array_length(algorithms);
+
+ UniquePtr<SupportedAlgorithmsResponse> deserialized(round_trip(rsp, 20));
+ EXPECT_EQ(array_length(algorithms), deserialized->algorithms_length);
+ EXPECT_EQ(0, memcmp(deserialized->algorithms, algorithms, array_size(algorithms)));
+}
+
+TEST(RoundTrip, SupportedResponse) {
+ SupportedResponse<keymaster_digest_t> rsp;
+ keymaster_digest_t digests[] = {KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1};
+ rsp.error = KM_ERROR_OK;
+ rsp.SetResults(digests);
+
+ UniquePtr<SupportedResponse<keymaster_digest_t>> deserialized(round_trip(rsp, 20));
+ EXPECT_EQ(array_length(digests), deserialized->results_length);
+ EXPECT_EQ(0, memcmp(deserialized->results, digests, array_size(digests)));
+}
+
+static keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8), Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+};
+uint8_t TEST_DATA[] = "a key blob";
+
+TEST(RoundTrip, GenerateKeyRequest) {
+ GenerateKeyRequest req;
+ req.key_description.Reinitialize(params, array_length(params));
+ UniquePtr<GenerateKeyRequest> deserialized(round_trip(req, 78));
+ EXPECT_EQ(deserialized->key_description, req.key_description);
+}
+
+TEST(RoundTrip, GenerateKeyResponse) {
+ GenerateKeyResponse rsp;
+ rsp.error = KM_ERROR_OK;
+ rsp.key_blob.key_material = dup_array(TEST_DATA);
+ rsp.key_blob.key_material_size = array_length(TEST_DATA);
+ rsp.enforced.Reinitialize(params, array_length(params));
+
+ UniquePtr<GenerateKeyResponse> deserialized(round_trip(rsp, 109));
+ EXPECT_EQ(KM_ERROR_OK, deserialized->error);
+ EXPECT_EQ(deserialized->enforced, rsp.enforced);
+ EXPECT_EQ(deserialized->unenforced, rsp.unenforced);
+}
+
+TEST(RoundTrip, GenerateKeyResponseTestError) {
+ GenerateKeyResponse rsp;
+ rsp.error = KM_ERROR_UNSUPPORTED_ALGORITHM;
+ rsp.key_blob.key_material = dup_array(TEST_DATA);
+ rsp.key_blob.key_material_size = array_length(TEST_DATA);
+ rsp.enforced.Reinitialize(params, array_length(params));
+
+ UniquePtr<GenerateKeyResponse> deserialized(round_trip(rsp, 4));
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM, deserialized->error);
+ EXPECT_EQ(0U, deserialized->enforced.size());
+ EXPECT_EQ(0U, deserialized->unenforced.size());
+ EXPECT_EQ(0U, deserialized->key_blob.key_material_size);
+}
+
+TEST(RoundTrip, GetKeyCharacteristicsRequest) {
+ GetKeyCharacteristicsRequest req;
+ req.additional_params.Reinitialize(params, array_length(params));
+ req.SetKeyMaterial("foo", 3);
+
+ UniquePtr<GetKeyCharacteristicsRequest> deserialized(round_trip(req, 85));
+ EXPECT_EQ(7U, deserialized->additional_params.size());
+ EXPECT_EQ(3U, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp(deserialized->key_blob.key_material, "foo", 3));
+}
+
+TEST(RoundTrip, GetKeyCharacteristicsResponse) {
+ GetKeyCharacteristicsResponse msg;
+ msg.error = KM_ERROR_OK;
+ msg.enforced.Reinitialize(params, array_length(params));
+ msg.unenforced.Reinitialize(params, array_length(params));
+
+ UniquePtr<GetKeyCharacteristicsResponse> deserialized(round_trip(msg, 160));
+ EXPECT_EQ(msg.enforced, deserialized->enforced);
+ EXPECT_EQ(msg.unenforced, deserialized->unenforced);
+}
+
+TEST(RoundTrip, BeginOperationRequest) {
+ BeginOperationRequest msg;
+ msg.purpose = KM_PURPOSE_SIGN;
+ msg.SetKeyMaterial("foo", 3);
+ msg.additional_params.Reinitialize(params, array_length(params));
+
+ UniquePtr<BeginOperationRequest> deserialized(round_trip(msg, 89));
+ EXPECT_EQ(KM_PURPOSE_SIGN, deserialized->purpose);
+ EXPECT_EQ(3U, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp(deserialized->key_blob.key_material, "foo", 3));
+ EXPECT_EQ(msg.additional_params, deserialized->additional_params);
+}
+
+TEST(RoundTrip, BeginOperationResponse) {
+ BeginOperationResponse msg;
+ msg.error = KM_ERROR_OK;
+ msg.op_handle = 0xDEADBEEF;
+
+ UniquePtr<BeginOperationResponse> deserialized(round_trip(msg, 12));
+ EXPECT_EQ(KM_ERROR_OK, deserialized->error);
+ EXPECT_EQ(0xDEADBEEF, deserialized->op_handle);
+}
+
+TEST(RoundTrip, BeginOperationResponseError) {
+ BeginOperationResponse msg;
+ msg.error = KM_ERROR_INVALID_OPERATION_HANDLE;
+ msg.op_handle = 0xDEADBEEF;
+
+ UniquePtr<BeginOperationResponse> deserialized(round_trip(msg, 4));
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, deserialized->error);
+}
+
+TEST(RoundTrip, UpdateOperationRequest) {
+ UpdateOperationRequest msg;
+ msg.op_handle = 0xDEADBEEF;
+ msg.input.Reinitialize("foo", 3);
+
+ UniquePtr<UpdateOperationRequest> deserialized(round_trip(msg, 15));
+ EXPECT_EQ(3U, deserialized->input.available_read());
+ EXPECT_EQ(0, memcmp(deserialized->input.peek_read(), "foo", 3));
+}
+
+TEST(RoundTrip, UpdateOperationResponse) {
+ UpdateOperationResponse msg;
+ msg.error = KM_ERROR_OK;
+ msg.output.Reinitialize("foo", 3);
+
+ UniquePtr<UpdateOperationResponse> deserialized(round_trip(msg, 11));
+ EXPECT_EQ(KM_ERROR_OK, deserialized->error);
+ EXPECT_EQ(3U, deserialized->output.available_read());
+ EXPECT_EQ(0, memcmp(deserialized->output.peek_read(), "foo", 3));
+}
+
+TEST(RoundTrip, FinishOperationRequest) {
+ FinishOperationRequest msg;
+ msg.op_handle = 0xDEADBEEF;
+ msg.signature.Reinitialize("bar", 3);
+
+ UniquePtr<FinishOperationRequest> deserialized(round_trip(msg, 15));
+ EXPECT_EQ(0xDEADBEEF, deserialized->op_handle);
+ EXPECT_EQ(3U, deserialized->signature.available_read());
+ EXPECT_EQ(0, memcmp(deserialized->signature.peek_read(), "bar", 3));
+}
+
+TEST(Round_Trip, FinishOperationResponse) {
+ FinishOperationResponse msg;
+ msg.error = KM_ERROR_OK;
+ msg.output.Reinitialize("foo", 3);
+
+ UniquePtr<FinishOperationResponse> deserialized(round_trip(msg, 11));
+ EXPECT_EQ(msg.error, deserialized->error);
+ EXPECT_EQ(msg.output.available_read(), deserialized->output.available_read());
+ EXPECT_EQ(0, memcmp(msg.output.peek_read(), deserialized->output.peek_read(),
+ msg.output.available_read()));
+}
+
+TEST(RoundTrip, ImportKeyRequest) {
+ ImportKeyRequest msg;
+ msg.key_description.Reinitialize(params, array_length(params));
+ msg.key_format = KM_KEY_FORMAT_X509;
+ msg.SetKeyMaterial("foo", 3);
+
+ UniquePtr<ImportKeyRequest> deserialized(round_trip(msg, 89));
+ EXPECT_EQ(msg.key_description, deserialized->key_description);
+ EXPECT_EQ(msg.key_format, deserialized->key_format);
+ EXPECT_EQ(msg.key_data_length, deserialized->key_data_length);
+ EXPECT_EQ(0, memcmp(msg.key_data, deserialized->key_data, msg.key_data_length));
+}
+
+TEST(RoundTrip, ImportKeyResponse) {
+ ImportKeyResponse msg;
+ msg.error = KM_ERROR_OK;
+ msg.SetKeyMaterial("foo", 3);
+ msg.enforced.Reinitialize(params, array_length(params));
+ msg.unenforced.Reinitialize(params, array_length(params));
+
+ UniquePtr<ImportKeyResponse> deserialized(round_trip(msg, 167));
+ EXPECT_EQ(msg.error, deserialized->error);
+ EXPECT_EQ(msg.key_blob.key_material_size, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp(msg.key_blob.key_material, deserialized->key_blob.key_material,
+ msg.key_blob.key_material_size));
+ EXPECT_EQ(msg.enforced, deserialized->enforced);
+ EXPECT_EQ(msg.unenforced, deserialized->unenforced);
+}
+
+TEST(RoundTrip, ExportKeyRequest) {
+ ExportKeyRequest msg;
+ msg.additional_params.Reinitialize(params, array_length(params));
+ msg.key_format = KM_KEY_FORMAT_X509;
+ msg.SetKeyMaterial("foo", 3);
+
+ UniquePtr<ExportKeyRequest> deserialized(round_trip(msg, 89));
+ EXPECT_EQ(msg.additional_params, deserialized->additional_params);
+ EXPECT_EQ(msg.key_format, deserialized->key_format);
+ EXPECT_EQ(3U, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp("foo", deserialized->key_blob.key_material, 3));
+}
+
+TEST(RoundTrip, ExportKeyResponse) {
+ ExportKeyResponse msg;
+ msg.error = KM_ERROR_OK;
+ msg.SetKeyMaterial("foo", 3);
+
+ UniquePtr<ExportKeyResponse> deserialized(round_trip(msg, 11));
+ EXPECT_EQ(3U, deserialized->key_data_length);
+ EXPECT_EQ(0, memcmp("foo", deserialized->key_data, 3));
+}
+
+uint8_t msgbuf[] = {
+ 220, 88, 183, 255, 71, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 173, 0, 0, 0, 228, 174, 98, 187, 191, 135, 253, 200, 51, 230, 114, 247, 151, 109,
+ 237, 79, 87, 32, 94, 5, 204, 46, 154, 30, 91, 6, 103, 148, 254, 129, 65, 171, 228,
+ 167, 224, 163, 9, 15, 206, 90, 58, 11, 205, 55, 211, 33, 87, 178, 149, 91, 28, 236,
+ 218, 112, 231, 34, 82, 82, 134, 103, 137, 115, 27, 156, 102, 159, 220, 226, 89, 42, 25,
+ 37, 9, 84, 239, 76, 161, 198, 72, 167, 163, 39, 91, 148, 191, 17, 191, 87, 169, 179,
+ 136, 10, 194, 154, 4, 40, 107, 109, 61, 161, 20, 176, 247, 13, 214, 106, 229, 45, 17,
+ 5, 60, 189, 64, 39, 166, 208, 14, 57, 25, 140, 148, 25, 177, 246, 189, 43, 181, 88,
+ 204, 29, 126, 224, 100, 143, 93, 60, 57, 249, 55, 0, 87, 83, 227, 224, 166, 59, 214,
+ 81, 144, 129, 58, 6, 57, 46, 254, 232, 41, 220, 209, 230, 167, 138, 158, 94, 180, 125,
+ 247, 26, 162, 116, 238, 202, 187, 100, 65, 13, 180, 44, 245, 159, 83, 161, 176, 58, 72,
+ 236, 109, 105, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 11, 0, 0, 0, 98, 0, 0, 0, 1, 0, 0, 32, 2, 0, 0, 0, 1, 0,
+ 0, 32, 3, 0, 0, 0, 2, 0, 0, 16, 1, 0, 0, 0, 3, 0, 0, 48, 0,
+ 1, 0, 0, 200, 0, 0, 80, 3, 0, 0, 0, 0, 0, 0, 0, 244, 1, 0, 112,
+ 1, 246, 1, 0, 112, 1, 189, 2, 0, 96, 144, 178, 236, 250, 255, 255, 255, 255, 145,
+ 1, 0, 96, 144, 226, 33, 60, 222, 2, 0, 0, 189, 2, 0, 96, 0, 0, 0, 0,
+ 0, 0, 0, 0, 190, 2, 0, 16, 1, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, 11, 0,
+ 0, 0, 98, 0, 0, 0, 1, 0, 0, 32, 2, 0, 0, 0, 1, 0, 0, 32, 3,
+ 0, 0, 0, 2, 0, 0, 16, 1, 0, 0, 0, 3, 0, 0, 48, 0, 1, 0, 0,
+ 200, 0, 0, 80, 3, 0, 0, 0, 0, 0, 0, 0, 244, 1, 0, 112, 1, 246, 1,
+ 0, 112, 1, 189, 2, 0, 96, 144, 178, 236, 250, 255, 255, 255, 255, 145, 1, 0, 96,
+ 144, 226, 33, 60, 222, 2, 0, 0, 189, 2, 0, 96, 0, 0, 0, 0, 0, 0, 0,
+ 0, 190, 2, 0, 16, 1, 0, 0, 0,
+};
+
+/*
+ * These tests don't have any assertions or expectations. They just try to parse garbage, to see if
+ * the result will be a crash. This is especially informative when run under Valgrind memcheck.
+ */
+
+template <typename Message> void parse_garbage() {
+ Message msg;
+ const uint8_t* end = msgbuf + array_length(msgbuf);
+ for (size_t i = 0; i < array_length(msgbuf); ++i) {
+ const uint8_t* begin = msgbuf + i;
+ const uint8_t* p = begin;
+ msg.Deserialize(&p, end);
+ }
+}
+
+#define GARBAGE_TEST(Message) \
+ TEST(GarbageTest, Message) { parse_garbage<Message>(); }
+
+GARBAGE_TEST(SupportedAlgorithmsResponse)
+GARBAGE_TEST(GenerateKeyRequest);
+GARBAGE_TEST(GenerateKeyResponse);
+GARBAGE_TEST(GetKeyCharacteristicsRequest);
+GARBAGE_TEST(GetKeyCharacteristicsResponse);
+GARBAGE_TEST(BeginOperationRequest);
+GARBAGE_TEST(BeginOperationResponse);
+GARBAGE_TEST(UpdateOperationRequest);
+GARBAGE_TEST(UpdateOperationResponse);
+GARBAGE_TEST(FinishOperationRequest);
+GARBAGE_TEST(FinishOperationResponse);
+// GARBAGE_TEST(AddEntropyRequest);
+GARBAGE_TEST(ImportKeyRequest);
+GARBAGE_TEST(ImportKeyResponse);
+GARBAGE_TEST(ExportKeyRequest);
+GARBAGE_TEST(ExportKeyResponse);
+// GARBAGE_TEST(RescopeRequest);
+// GARBAGE_TEST(RescopeResponse);
+
+// The macro doesn't work on this one.
+TEST(GarbageTest, SupportedResponse) {
+ parse_garbage<SupportedResponse<keymaster_digest_t>>();
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/google_keymaster_test.cpp b/google_keymaster_test.cpp
new file mode 100644
index 0000000..a492743
--- /dev/null
+++ b/google_keymaster_test.cpp
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <string>
+#include <fstream>
+
+#include <gtest/gtest.h>
+
+#include <openssl/engine.h>
+
+#include <keymaster/google_keymaster_utils.h>
+#include <keymaster/keymaster_tags.h>
+
+#include "google_keymaster_test_utils.h"
+#include "google_softkeymaster.h"
+
+using std::string;
+using std::ifstream;
+using std::istreambuf_iterator;
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
+ CRYPTO_cleanup_all_ex_data();
+ ERR_free_strings();
+ return result;
+}
+
+namespace keymaster {
+namespace test {
+
+class KeymasterTest : public testing::Test {
+ protected:
+ KeymasterTest() : device(5, new StdoutLogger) { RAND_seed("foobar", 6); }
+ ~KeymasterTest() {}
+
+ GoogleSoftKeymaster device;
+};
+
+typedef KeymasterTest CheckSupported;
+TEST_F(CheckSupported, SupportedAlgorithms) {
+ // Shouldn't blow up on NULL.
+ device.SupportedAlgorithms(NULL);
+
+ SupportedResponse<keymaster_algorithm_t> response;
+ device.SupportedAlgorithms(&response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(3U, response.results_length);
+ EXPECT_EQ(KM_ALGORITHM_RSA, response.results[0]);
+ EXPECT_EQ(KM_ALGORITHM_DSA, response.results[1]);
+ EXPECT_EQ(KM_ALGORITHM_ECDSA, response.results[2]);
+}
+
+TEST_F(CheckSupported, SupportedBlockModes) {
+ // Shouldn't blow up on NULL.
+ device.SupportedBlockModes(KM_ALGORITHM_RSA, NULL);
+
+ SupportedResponse<keymaster_block_mode_t> response;
+ device.SupportedBlockModes(KM_ALGORITHM_RSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(0U, response.results_length);
+
+ device.SupportedBlockModes(KM_ALGORITHM_DSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(0U, response.results_length);
+
+ device.SupportedBlockModes(KM_ALGORITHM_ECDSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(0U, response.results_length);
+
+ device.SupportedBlockModes(KM_ALGORITHM_AES, &response);
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM, response.error);
+}
+
+TEST_F(CheckSupported, SupportedPaddingModes) {
+ // Shouldn't blow up on NULL.
+ device.SupportedPaddingModes(KM_ALGORITHM_RSA, NULL);
+
+ SupportedResponse<keymaster_padding_t> response;
+ device.SupportedPaddingModes(KM_ALGORITHM_RSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_PAD_NONE, response.results[0]);
+
+ device.SupportedPaddingModes(KM_ALGORITHM_DSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_PAD_NONE, response.results[0]);
+
+ device.SupportedPaddingModes(KM_ALGORITHM_ECDSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_PAD_NONE, response.results[0]);
+
+ device.SupportedPaddingModes(KM_ALGORITHM_AES, &response);
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM, response.error);
+}
+
+TEST_F(CheckSupported, SupportedDigests) {
+ // Shouldn't blow up on NULL.
+ device.SupportedDigests(KM_ALGORITHM_RSA, NULL);
+
+ SupportedResponse<keymaster_digest_t> response;
+ device.SupportedDigests(KM_ALGORITHM_RSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_DIGEST_NONE, response.results[0]);
+
+ device.SupportedDigests(KM_ALGORITHM_DSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_DIGEST_NONE, response.results[0]);
+
+ device.SupportedDigests(KM_ALGORITHM_ECDSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_DIGEST_NONE, response.results[0]);
+
+ device.SupportedDigests(KM_ALGORITHM_AES, &response);
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM, response.error);
+}
+
+TEST_F(CheckSupported, SupportedImportFormats) {
+ // Shouldn't blow up on NULL.
+ device.SupportedImportFormats(KM_ALGORITHM_RSA, NULL);
+
+ SupportedResponse<keymaster_key_format_t> response;
+ device.SupportedImportFormats(KM_ALGORITHM_RSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_KEY_FORMAT_PKCS8, response.results[0]);
+
+ device.SupportedImportFormats(KM_ALGORITHM_DSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_KEY_FORMAT_PKCS8, response.results[0]);
+
+ device.SupportedImportFormats(KM_ALGORITHM_ECDSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_KEY_FORMAT_PKCS8, response.results[0]);
+
+ device.SupportedImportFormats(KM_ALGORITHM_AES, &response);
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM, response.error);
+}
+
+TEST_F(CheckSupported, SupportedExportFormats) {
+ // Shouldn't blow up on NULL.
+ device.SupportedExportFormats(KM_ALGORITHM_RSA, NULL);
+
+ SupportedResponse<keymaster_key_format_t> response;
+ device.SupportedExportFormats(KM_ALGORITHM_RSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_KEY_FORMAT_X509, response.results[0]);
+
+ device.SupportedExportFormats(KM_ALGORITHM_DSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_KEY_FORMAT_X509, response.results[0]);
+
+ device.SupportedExportFormats(KM_ALGORITHM_ECDSA, &response);
+ EXPECT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_EQ(1U, response.results_length);
+ EXPECT_EQ(KM_KEY_FORMAT_X509, response.results[0]);
+
+ device.SupportedExportFormats(KM_ALGORITHM_AES, &response);
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM, response.error);
+}
+
+typedef KeymasterTest NewKeyGeneration;
+TEST_F(NewKeyGeneration, Rsa) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_APPLICATION_DATA, "app_data", 8),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ GenerateKeyRequest req;
+ req.key_description.Reinitialize(params, array_length(params));
+ GenerateKeyResponse rsp;
+
+ device.GenerateKey(req, &rsp);
+
+ ASSERT_EQ(KM_ERROR_OK, rsp.error);
+ EXPECT_EQ(0U, rsp.enforced.size());
+ EXPECT_EQ(12U, rsp.enforced.SerializedSize());
+ EXPECT_GT(rsp.unenforced.SerializedSize(), 12U);
+
+ // Check specified tags are all present in unenforced characteristics
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_SIGN));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_VERIFY));
+
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_ALGORITHM, KM_ALGORITHM_RSA));
+
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_USER_ID, 7));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_USER_AUTH_ID, 8));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_KEY_SIZE, 256));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_AUTH_TIMEOUT, 300));
+
+ // Verify that App ID, App data and ROT are NOT included.
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_ROOT_OF_TRUST));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_APPLICATION_ID));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_APPLICATION_DATA));
+
+ // Just for giggles, check that some unexpected tags/values are NOT present.
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_ENCRYPT));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_DECRYPT));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_AUTH_TIMEOUT, 301));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_RESCOPE_AUTH_TIMEOUT));
+
+ // Now check that unspecified, defaulted tags are correct.
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_RSA_PUBLIC_EXPONENT, 65537));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_ORIGIN, KM_ORIGIN_SOFTWARE));
+ EXPECT_TRUE(contains(rsp.unenforced, KM_TAG_CREATION_DATETIME));
+}
+
+TEST_F(NewKeyGeneration, Dsa) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_DSA),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_APPLICATION_DATA, "app_data", 8),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ GenerateKeyRequest req;
+ req.key_description.Reinitialize(params, array_length(params));
+ GenerateKeyResponse rsp;
+
+ device.GenerateKey(req, &rsp);
+
+ ASSERT_EQ(KM_ERROR_OK, rsp.error);
+ EXPECT_EQ(0U, rsp.enforced.size());
+ EXPECT_EQ(12U, rsp.enforced.SerializedSize());
+ EXPECT_GT(rsp.unenforced.SerializedSize(), 12U);
+
+ // Check specified tags are all present in unenforced characteristics
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_SIGN));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_VERIFY));
+
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_ALGORITHM, KM_ALGORITHM_DSA));
+
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_USER_ID, 7));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_USER_AUTH_ID, 8));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_KEY_SIZE, 256));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_AUTH_TIMEOUT, 300));
+
+ // Verify that App ID, App data and ROT are NOT included.
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_ROOT_OF_TRUST));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_APPLICATION_ID));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_APPLICATION_DATA));
+
+ // Just for giggles, check that some unexpected tags/values are NOT present.
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_ENCRYPT));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_DECRYPT));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_AUTH_TIMEOUT, 301));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_RESCOPE_AUTH_TIMEOUT));
+
+ // Now check that unspecified, defaulted tags are correct.
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_ORIGIN, KM_ORIGIN_SOFTWARE));
+ EXPECT_TRUE(contains(rsp.unenforced, KM_TAG_CREATION_DATETIME));
+
+ // Generator should have created DSA params.
+ keymaster_blob_t g, p, q;
+ EXPECT_TRUE(rsp.unenforced.GetTagValue(TAG_DSA_GENERATOR, &g));
+ EXPECT_TRUE(rsp.unenforced.GetTagValue(TAG_DSA_P, &p));
+ EXPECT_TRUE(rsp.unenforced.GetTagValue(TAG_DSA_Q, &q));
+ EXPECT_TRUE(g.data_length >= 63 && g.data_length <= 64);
+ EXPECT_EQ(64U, p.data_length);
+ EXPECT_EQ(20U, q.data_length);
+}
+
+TEST_F(NewKeyGeneration, Ecdsa) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_ECDSA),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_APPLICATION_DATA, "app_data", 8),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ GenerateKeyRequest req;
+ req.key_description.Reinitialize(params, array_length(params));
+ GenerateKeyResponse rsp;
+
+ device.GenerateKey(req, &rsp);
+
+ ASSERT_EQ(KM_ERROR_OK, rsp.error);
+ EXPECT_EQ(0U, rsp.enforced.size());
+ EXPECT_EQ(12U, rsp.enforced.SerializedSize());
+ EXPECT_GT(rsp.unenforced.SerializedSize(), 12U);
+
+ // Check specified tags are all present in unenforced characteristics
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_SIGN));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_VERIFY));
+
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_ALGORITHM, KM_ALGORITHM_ECDSA));
+
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_USER_ID, 7));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_USER_AUTH_ID, 8));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_KEY_SIZE, 256));
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_AUTH_TIMEOUT, 300));
+
+ // Verify that App ID, App data and ROT are NOT included.
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_ROOT_OF_TRUST));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_APPLICATION_ID));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_APPLICATION_DATA));
+
+ // Just for giggles, check that some unexpected tags/values are NOT present.
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_ENCRYPT));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_PURPOSE, KM_PURPOSE_DECRYPT));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_AUTH_TIMEOUT, 301));
+ EXPECT_FALSE(contains(rsp.unenforced, TAG_RESCOPE_AUTH_TIMEOUT));
+
+ // Now check that unspecified, defaulted tags are correct.
+ EXPECT_TRUE(contains(rsp.unenforced, TAG_ORIGIN, KM_ORIGIN_SOFTWARE));
+ EXPECT_TRUE(contains(rsp.unenforced, KM_TAG_CREATION_DATETIME));
+}
+
+typedef KeymasterTest GetKeyCharacteristics;
+TEST_F(GetKeyCharacteristics, SimpleRsa) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+
+ GenerateKeyRequest gen_req;
+ gen_req.key_description.Reinitialize(params, array_length(params));
+ GenerateKeyResponse gen_rsp;
+
+ device.GenerateKey(gen_req, &gen_rsp);
+ ASSERT_EQ(KM_ERROR_OK, gen_rsp.error);
+
+ GetKeyCharacteristicsRequest req;
+ req.SetKeyMaterial(gen_rsp.key_blob);
+ req.additional_params.push_back(TAG_APPLICATION_ID, "app_id", 6);
+
+ GetKeyCharacteristicsResponse rsp;
+ device.GetKeyCharacteristics(req, &rsp);
+ ASSERT_EQ(KM_ERROR_OK, rsp.error);
+
+ EXPECT_EQ(gen_rsp.enforced, rsp.enforced);
+ EXPECT_EQ(gen_rsp.unenforced, rsp.unenforced);
+}
+
+/**
+ * Test class that provides some infrastructure for generating keys and signing messages.
+ */
+class SigningOperationsTest : public KeymasterTest {
+ protected:
+ void GenerateKey(keymaster_algorithm_t algorithm, keymaster_digest_t digest,
+ keymaster_padding_t padding, uint32_t key_size) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, algorithm),
+ Authorization(TAG_KEY_SIZE, key_size),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ GenerateKeyRequest generate_request;
+ generate_request.key_description.Reinitialize(params, array_length(params));
+ if (static_cast<int>(digest) != -1)
+ generate_request.key_description.push_back(TAG_DIGEST, digest);
+ if (static_cast<int>(padding) != -1)
+ generate_request.key_description.push_back(TAG_PADDING, padding);
+ device.GenerateKey(generate_request, &generate_response_);
+ EXPECT_EQ(KM_ERROR_OK, generate_response_.error);
+ }
+
+ void SignMessage(const void* message, size_t size) {
+ SignMessage(generate_response_.key_blob, message, size);
+ }
+
+ void SignMessage(const keymaster_key_blob_t& key_blob, const void* message, size_t size) {
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(key_blob);
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(message, size);
+ EXPECT_EQ(size, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ device.FinishOperation(finish_request, &finish_response_);
+ ASSERT_EQ(KM_ERROR_OK, finish_response_.error);
+ EXPECT_GT(finish_response_.output.available_read(), 0U);
+ }
+
+ void AddClientParams(AuthorizationSet* set) { set->push_back(TAG_APPLICATION_ID, "app_id", 6); }
+
+ const keymaster_key_blob_t& key_blob() { return generate_response_.key_blob; }
+
+ const keymaster_key_blob_t& corrupt_key_blob() {
+ uint8_t* tmp = const_cast<uint8_t*>(generate_response_.key_blob.key_material);
+ ++tmp[generate_response_.key_blob.key_material_size / 2];
+ return generate_response_.key_blob;
+ }
+
+ Buffer* signature() {
+ if (finish_response_.error == KM_ERROR_OK)
+ return &finish_response_.output;
+ return NULL;
+ }
+
+ private:
+ GenerateKeyResponse generate_response_;
+ FinishOperationResponse finish_response_;
+};
+
+TEST_F(SigningOperationsTest, RsaSuccess) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
+ const char message[] = "12345678901234567890123456789012";
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(key_blob());
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(message, array_size(message) - 1);
+ EXPECT_EQ(array_size(message) - 1, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_OK, finish_response.error);
+ EXPECT_GT(finish_response.output.available_read(), 0U);
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(SigningOperationsTest, DsaSuccess) {
+ GenerateKey(KM_ALGORITHM_DSA, KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(key_blob());
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize("123456789012345678901234567890123456789012345678", 48);
+ EXPECT_EQ(48U, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_OK, finish_response.error);
+ EXPECT_GT(finish_response.output.available_read(), 0U);
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(SigningOperationsTest, EcdsaSuccess) {
+ GenerateKey(KM_ALGORITHM_ECDSA, KM_DIGEST_NONE, KM_PAD_NONE, 192 /* key size */);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(key_blob());
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize("123456789012345678901234567890123456789012345678", 48);
+ EXPECT_EQ(48U, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_OK, finish_response.error);
+ EXPECT_GT(finish_response.output.available_read(), 0U);
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(SigningOperationsTest, RsaAbort) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(key_blob());
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ EXPECT_EQ(KM_ERROR_OK, device.AbortOperation(begin_response.op_handle));
+
+ // Another abort should fail
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(SigningOperationsTest, RsaUnsupportedDigest) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_SHA_2_256, KM_PAD_NONE, 256 /* key size */);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ begin_request.SetKeyMaterial(key_blob());
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_DIGEST, begin_response.error);
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(SigningOperationsTest, RsaUnsupportedPadding) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, KM_PAD_RSA_OAEP, 256 /* key size */);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ begin_request.SetKeyMaterial(key_blob());
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_PADDING_MODE, begin_response.error);
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(SigningOperationsTest, RsaNoDigest) {
+ GenerateKey(KM_ALGORITHM_RSA, static_cast<keymaster_digest_t>(-1), KM_PAD_NONE,
+ 256 /* key size */);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ begin_request.SetKeyMaterial(key_blob());
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_DIGEST, begin_response.error);
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(SigningOperationsTest, RsaNoPadding) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, static_cast<keymaster_padding_t>(-1),
+ 256 /* key size */);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ begin_request.SetKeyMaterial(key_blob());
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_PADDING_MODE, begin_response.error);
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(SigningOperationsTest, RsaTooShortMessage) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(key_blob());
+ begin_request.purpose = KM_PURPOSE_SIGN;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize("01234567890123456789012345678901", 31);
+ EXPECT_EQ(31U, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, finish_response.error);
+ EXPECT_EQ(0U, finish_response.output.available_read());
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+typedef SigningOperationsTest VerificationOperationsTest;
+TEST_F(VerificationOperationsTest, RsaSuccess) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
+ const char message[] = "12345678901234567890123456789012";
+ SignMessage(message, array_size(message) - 1);
+ ASSERT_TRUE(signature() != NULL);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(key_blob());
+ begin_request.purpose = KM_PURPOSE_VERIFY;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(message, array_size(message) - 1);
+ EXPECT_EQ(array_size(message) - 1, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ finish_request.signature.Reinitialize(*signature());
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_OK, finish_response.error);
+ EXPECT_EQ(0U, finish_response.output.available_read());
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(VerificationOperationsTest, DsaSuccess) {
+ GenerateKey(KM_ALGORITHM_DSA, KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
+ const char message[] = "123456789012345678901234567890123456789012345678";
+ SignMessage(message, array_size(message) - 1);
+ ASSERT_TRUE(signature() != NULL);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(key_blob());
+ begin_request.purpose = KM_PURPOSE_VERIFY;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(message, array_size(message) - 1);
+ EXPECT_EQ(array_size(message) - 1, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ finish_request.signature.Reinitialize(*signature());
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_OK, finish_response.error);
+ EXPECT_EQ(0U, finish_response.output.available_read());
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(VerificationOperationsTest, EcdsaSuccess) {
+ GenerateKey(KM_ALGORITHM_ECDSA, KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
+ const char message[] = "123456789012345678901234567890123456789012345678";
+ SignMessage(message, array_size(message) - 1);
+ ASSERT_TRUE(signature() != NULL);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(key_blob());
+ begin_request.purpose = KM_PURPOSE_VERIFY;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(message, array_size(message) - 1);
+ EXPECT_EQ(array_size(message) - 1, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ finish_request.signature.Reinitialize(*signature());
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_OK, finish_response.error);
+ EXPECT_EQ(0U, finish_response.output.available_read());
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+typedef SigningOperationsTest ExportKeyTest;
+TEST_F(ExportKeyTest, RsaSuccess) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
+
+ ExportKeyRequest request;
+ ExportKeyResponse response;
+ AddClientParams(&request.additional_params);
+ request.key_format = KM_KEY_FORMAT_X509;
+ request.SetKeyMaterial(key_blob());
+
+ device.ExportKey(request, &response);
+ ASSERT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_TRUE(response.key_data != NULL);
+
+ // TODO(swillden): Verify that the exported key is actually usable to verify signatures.
+}
+
+TEST_F(ExportKeyTest, DsaSuccess) {
+ GenerateKey(KM_ALGORITHM_DSA, KM_DIGEST_NONE, KM_PAD_NONE, 1024 /* key size */);
+
+ ExportKeyRequest request;
+ ExportKeyResponse response;
+ AddClientParams(&request.additional_params);
+ request.key_format = KM_KEY_FORMAT_X509;
+ request.SetKeyMaterial(key_blob());
+
+ device.ExportKey(request, &response);
+ ASSERT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_TRUE(response.key_data != NULL);
+
+ // TODO(swillden): Verify that the exported key is actually usable to verify signatures.
+}
+
+TEST_F(ExportKeyTest, EcdsaSuccess) {
+ GenerateKey(KM_ALGORITHM_ECDSA, KM_DIGEST_NONE, KM_PAD_NONE, 192 /* key size */);
+
+ ExportKeyRequest request;
+ ExportKeyResponse response;
+ AddClientParams(&request.additional_params);
+ request.key_format = KM_KEY_FORMAT_X509;
+ request.SetKeyMaterial(key_blob());
+
+ device.ExportKey(request, &response);
+ ASSERT_EQ(KM_ERROR_OK, response.error);
+ EXPECT_TRUE(response.key_data != NULL);
+
+ // TODO(swillden): Verify that the exported key is actually usable to verify signatures.
+}
+
+TEST_F(ExportKeyTest, RsaUnsupportedKeyFormat) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, KM_PAD_NONE, 256);
+
+ ExportKeyRequest request;
+ ExportKeyResponse response;
+ AddClientParams(&request.additional_params);
+
+ /* We have no other defined export formats defined. */
+ request.key_format = KM_KEY_FORMAT_PKCS8;
+ request.SetKeyMaterial(key_blob());
+
+ device.ExportKey(request, &response);
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_KEY_FORMAT, response.error);
+ EXPECT_TRUE(response.key_data == NULL);
+}
+
+TEST_F(ExportKeyTest, RsaCorruptedKeyBlob) {
+ GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, KM_PAD_NONE, 256);
+
+ ExportKeyRequest request;
+ ExportKeyResponse response;
+ AddClientParams(&request.additional_params);
+ request.key_format = KM_KEY_FORMAT_X509;
+ request.SetKeyMaterial(corrupt_key_blob());
+
+ device.ExportKey(request, &response);
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, response.error);
+ ASSERT_TRUE(response.key_data == NULL);
+}
+
+static string read_file(const string& file_name) {
+ ifstream file_stream(file_name, std::ios::binary);
+ istreambuf_iterator<char> file_begin(file_stream);
+ istreambuf_iterator<char> file_end;
+ return string(file_begin, file_end);
+}
+
+typedef SigningOperationsTest ImportKeyTest;
+TEST_F(ImportKeyTest, RsaSuccess) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_DIGEST, KM_DIGEST_NONE),
+ Authorization(TAG_PADDING, KM_PAD_NONE),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+
+ string pk8_key = read_file("rsa_privkey_pk8.der");
+ ASSERT_EQ(633U, pk8_key.size());
+
+ ImportKeyRequest import_request;
+ import_request.key_description.Reinitialize(params, array_length(params));
+ import_request.key_format = KM_KEY_FORMAT_PKCS8;
+ import_request.SetKeyMaterial(pk8_key.data(), pk8_key.size());
+
+ ImportKeyResponse import_response;
+ device.ImportKey(import_request, &import_response);
+ ASSERT_EQ(KM_ERROR_OK, import_response.error);
+ EXPECT_EQ(0U, import_response.enforced.size());
+ EXPECT_GT(import_response.unenforced.size(), 0U);
+
+ // Check values derived from the key.
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_KEY_SIZE, 1024));
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+ // And values provided by GoogleKeymaster
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_ORIGIN, KM_ORIGIN_IMPORTED));
+ EXPECT_TRUE(contains(import_response.unenforced, KM_TAG_CREATION_DATETIME));
+
+ size_t message_len = 1024 / 8;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::fill(message.get(), message.get() + message_len, 'a');
+ SignMessage(import_response.key_blob, message.get(), message_len);
+ ASSERT_TRUE(signature() != NULL);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(import_response.key_blob);
+ begin_request.purpose = KM_PURPOSE_VERIFY;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(message.get(), message_len);
+ EXPECT_EQ(message_len, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ finish_request.signature.Reinitialize(*signature());
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_OK, finish_response.error);
+ EXPECT_EQ(0U, finish_response.output.available_read());
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(ImportKeyTest, DsaSuccess) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_DIGEST, KM_DIGEST_NONE),
+ Authorization(TAG_PADDING, KM_PAD_NONE),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+
+ string pk8_key = read_file("dsa_privkey_pk8.der");
+ ASSERT_EQ(335U, pk8_key.size());
+
+ ImportKeyRequest import_request;
+ import_request.key_description.Reinitialize(params, array_length(params));
+ import_request.key_format = KM_KEY_FORMAT_PKCS8;
+ import_request.SetKeyMaterial(pk8_key.data(), pk8_key.size());
+
+ ImportKeyResponse import_response;
+ device.ImportKey(import_request, &import_response);
+ ASSERT_EQ(KM_ERROR_OK, import_response.error);
+ EXPECT_EQ(0U, import_response.enforced.size());
+ EXPECT_GT(import_response.unenforced.size(), 0U);
+
+ // Check values derived from the key.
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_ALGORITHM, KM_ALGORITHM_DSA));
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_KEY_SIZE, 1024));
+
+ // And values provided by GoogleKeymaster
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_ORIGIN, KM_ORIGIN_IMPORTED));
+ EXPECT_TRUE(contains(import_response.unenforced, KM_TAG_CREATION_DATETIME));
+
+ size_t message_len = 48;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::fill(message.get(), message.get() + message_len, 'a');
+ SignMessage(import_response.key_blob, message.get(), message_len);
+ ASSERT_TRUE(signature() != NULL);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(import_response.key_blob);
+ begin_request.purpose = KM_PURPOSE_VERIFY;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(message.get(), message_len);
+ EXPECT_EQ(message_len, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ finish_request.signature.Reinitialize(*signature());
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_OK, finish_response.error);
+ EXPECT_EQ(0U, finish_response.output.available_read());
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+TEST_F(ImportKeyTest, EcdsaSuccess) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_DIGEST, KM_DIGEST_NONE),
+ Authorization(TAG_PADDING, KM_PAD_NONE),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_ID, 8),
+ Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+
+ string pk8_key = read_file("ec_privkey_pk8.der");
+ ASSERT_EQ(138U, pk8_key.size());
+
+ ImportKeyRequest import_request;
+ import_request.key_description.Reinitialize(params, array_length(params));
+ import_request.key_format = KM_KEY_FORMAT_PKCS8;
+ import_request.SetKeyMaterial(pk8_key.data(), pk8_key.size());
+
+ ImportKeyResponse import_response;
+ device.ImportKey(import_request, &import_response);
+ ASSERT_EQ(KM_ERROR_OK, import_response.error);
+ EXPECT_EQ(0U, import_response.enforced.size());
+ EXPECT_GT(import_response.unenforced.size(), 0U);
+
+ // Check values derived from the key.
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_ALGORITHM, KM_ALGORITHM_ECDSA));
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_KEY_SIZE, 256));
+
+ // And values provided by GoogleKeymaster
+ EXPECT_TRUE(contains(import_response.unenforced, TAG_ORIGIN, KM_ORIGIN_IMPORTED));
+ EXPECT_TRUE(contains(import_response.unenforced, KM_TAG_CREATION_DATETIME));
+
+ size_t message_len = 1024 / 8;
+ UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::fill(message.get(), message.get() + message_len, 'a');
+ SignMessage(import_response.key_blob, message.get(), message_len);
+ ASSERT_TRUE(signature() != NULL);
+
+ BeginOperationRequest begin_request;
+ BeginOperationResponse begin_response;
+ begin_request.SetKeyMaterial(import_response.key_blob);
+ begin_request.purpose = KM_PURPOSE_VERIFY;
+ AddClientParams(&begin_request.additional_params);
+
+ device.BeginOperation(begin_request, &begin_response);
+ ASSERT_EQ(KM_ERROR_OK, begin_response.error);
+
+ UpdateOperationRequest update_request;
+ UpdateOperationResponse update_response;
+ update_request.op_handle = begin_response.op_handle;
+ update_request.input.Reinitialize(message.get(), message_len);
+ EXPECT_EQ(message_len, update_request.input.available_read());
+
+ device.UpdateOperation(update_request, &update_response);
+ ASSERT_EQ(KM_ERROR_OK, update_response.error);
+ EXPECT_EQ(0U, update_response.output.available_read());
+
+ FinishOperationRequest finish_request;
+ finish_request.op_handle = begin_response.op_handle;
+ finish_request.signature.Reinitialize(*signature());
+ FinishOperationResponse finish_response;
+ device.FinishOperation(finish_request, &finish_response);
+ ASSERT_EQ(KM_ERROR_OK, finish_response.error);
+ EXPECT_EQ(0U, finish_response.output.available_read());
+
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device.AbortOperation(begin_response.op_handle));
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/google_keymaster_test_utils.cpp b/google_keymaster_test_utils.cpp
new file mode 100644
index 0000000..d0457ec
--- /dev/null
+++ b/google_keymaster_test_utils.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include "google_keymaster_test_utils.h"
+
+std::ostream& operator<<(std::ostream& os, const keymaster_key_param_t& param) {
+ os << "Tag: " << keymaster_tag_mask_type(param.tag);
+ switch (keymaster_tag_get_type(param.tag)) {
+ case KM_INVALID:
+ os << " Invalid";
+ break;
+ case KM_INT_REP:
+ os << " (Rep)";
+ /* Falls through */
+ case KM_INT:
+ os << " Int: " << param.integer;
+ break;
+ case KM_ENUM_REP:
+ os << " (Rep)";
+ /* Falls through */
+ case KM_ENUM:
+ os << " Enum: " << param.enumerated;
+ break;
+ case KM_LONG:
+ os << " Long: " << param.long_integer;
+ break;
+ case KM_DATE:
+ os << " Date: " << param.date_time;
+ break;
+ case KM_BOOL:
+ os << " Bool: " << param.boolean;
+ break;
+ case KM_BIGNUM:
+ os << " Bignum: ";
+ break;
+ case KM_BYTES:
+ os << " Bytes: ";
+ break;
+ }
+ return os;
+}
+
+bool operator==(const keymaster_key_param_t& a, const keymaster_key_param_t& b) {
+ if (a.tag != b.tag) {
+ return false;
+ }
+
+ switch (keymaster_tag_get_type(a.tag)) {
+ default:
+ return false;
+ case KM_INVALID:
+ return true;
+ case KM_INT_REP:
+ case KM_INT:
+ return a.integer == b.integer;
+ case KM_ENUM_REP:
+ case KM_ENUM:
+ return a.enumerated == b.enumerated;
+ case KM_LONG:
+ return a.long_integer == b.long_integer;
+ case KM_DATE:
+ return a.date_time == b.date_time;
+ case KM_BOOL:
+ return a.boolean == b.boolean;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ if ((a.blob.data == NULL || b.blob.data == NULL) && a.blob.data != b.blob.data)
+ return false;
+ return a.blob.data_length == b.blob.data_length &&
+ (memcmp(a.blob.data, b.blob.data, a.blob.data_length) == 0);
+ }
+}
+
+namespace keymaster {
+
+bool operator==(const AuthorizationSet& a, const AuthorizationSet& b) {
+ if (a.size() != b.size())
+ return false;
+
+ for (size_t i = 0; i < a.size(); ++i)
+ if (!(a[i] == b[i]))
+ return false;
+ return true;
+}
+
+std::ostream& operator<<(std::ostream& os, const AuthorizationSet& set) {
+ if (set.size() == 0)
+ os << "(Empty)" << std::endl;
+ for (size_t i = 0; i < set.size(); ++i) {
+ os << set[i] << std::endl;
+ }
+ return os;
+}
+
+} // namespace keymaster
diff --git a/google_keymaster_test_utils.h b/google_keymaster_test_utils.h
new file mode 100644
index 0000000..e681b3f
--- /dev/null
+++ b/google_keymaster_test_utils.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_TEST_UTILS_H_
+#define SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_TEST_UTILS_H_
+
+/*
+ * Utilities used to help with testing. Not used in production code.
+ */
+
+#include <stdarg.h>
+
+#include <ostream>
+
+#include <keymaster/authorization_set.h>
+#include <keymaster/keymaster_defs.h>
+#include <keymaster/logger.h>
+
+std::ostream& operator<<(std::ostream& os, const keymaster_key_param_t& param);
+bool operator==(const keymaster_key_param_t& a, const keymaster_key_param_t& b);
+
+namespace keymaster {
+
+bool operator==(const AuthorizationSet& a, const AuthorizationSet& b);
+
+std::ostream& operator<<(std::ostream& os, const AuthorizationSet& set);
+
+namespace test {
+
+template <keymaster_tag_t Tag, typename KeymasterEnum>
+bool contains(const AuthorizationSet& set, TypedEnumTag<KM_ENUM, Tag, KeymasterEnum> tag,
+ KeymasterEnum val) {
+ int pos = set.find(tag);
+ return pos != -1 && set[pos].enumerated == val;
+}
+
+template <keymaster_tag_t Tag, typename KeymasterEnum>
+bool contains(const AuthorizationSet& set, TypedEnumTag<KM_ENUM_REP, Tag, KeymasterEnum> tag,
+ KeymasterEnum val) {
+ int pos = -1;
+ while ((pos = set.find(tag, pos)) != -1)
+ if (set[pos].enumerated == val)
+ return true;
+ return false;
+}
+
+template <keymaster_tag_t Tag>
+bool contains(const AuthorizationSet& set, TypedTag<KM_INT, Tag> tag, uint32_t val) {
+ int pos = set.find(tag);
+ return pos != -1 && set[pos].integer == val;
+}
+
+template <keymaster_tag_t Tag>
+bool contains(const AuthorizationSet& set, TypedTag<KM_INT_REP, Tag> tag, uint32_t val) {
+ int pos = -1;
+ while ((pos = set.find(tag, pos)) != -1)
+ if (set[pos].integer == val)
+ return true;
+ return false;
+}
+
+template <keymaster_tag_t Tag>
+bool contains(const AuthorizationSet& set, TypedTag<KM_LONG, Tag> tag, uint64_t val) {
+ int pos = set.find(tag);
+ return pos != -1 && set[pos].long_integer == val;
+}
+
+template <keymaster_tag_t Tag>
+bool contains(const AuthorizationSet& set, TypedTag<KM_BYTES, Tag> tag, const std::string& val) {
+ int pos = set.find(tag);
+ return pos != -1 &&
+ std::string(reinterpret_cast<const char*>(set[pos].blob.data),
+ set[pos].blob.data_length) == val;
+}
+
+inline bool contains(const AuthorizationSet& set, keymaster_tag_t tag) {
+ return set.find(tag) != -1;
+}
+
+class StdoutLogger : public Logger {
+ public:
+ int log(const char* fmt, ...) const {
+ va_list args;
+ va_start(args, fmt);
+ int result = vprintf(fmt, args);
+ va_end(args);
+ return result;
+ }
+
+ int info(const char* fmt, ...) const {
+ va_list args;
+ va_start(args, fmt);
+ int result = vprintf(fmt, args);
+ va_end(args);
+ return result;
+ }
+
+ int error(const char* fmt, ...) const {
+ va_list args;
+ va_start(args, fmt);
+ int result = vfprintf(stderr, fmt, args);
+ va_end(args);
+ return result;
+ }
+
+ int severe(const char* fmt, ...) const {
+ va_list args;
+ va_start(args, fmt);
+ int result = vfprintf(stderr, fmt, args);
+ va_end(args);
+ return result;
+ }
+};
+
+} // namespace test
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_TEST_UTILS_H_
diff --git a/google_keymaster_utils.cpp b/google_keymaster_utils.cpp
new file mode 100644
index 0000000..9e06d95
--- /dev/null
+++ b/google_keymaster_utils.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <keymaster/google_keymaster_utils.h>
+
+namespace keymaster {
+
+uint8_t* dup_buffer(const void* buf, size_t size) {
+ uint8_t* retval = new uint8_t[size];
+ if (retval != NULL)
+ memcpy(retval, buf, size);
+ return retval;
+}
+
+int memcmp_s(const void* p1, const void* p2, size_t length) {
+ const uint8_t* s1 = static_cast<const uint8_t*>(p1);
+ const uint8_t* s2 = static_cast<const uint8_t*>(p2);
+ uint8_t result = 0;
+ while (length-- > 0)
+ result |= *s1++ ^ *s2++;
+ return result == 0 ? 0 : 1;
+}
+
+} // namespace keymaster
diff --git a/google_softkeymaster.h b/google_softkeymaster.h
new file mode 100644
index 0000000..fa96d14
--- /dev/null
+++ b/google_softkeymaster.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_GOOGLE_SOFT_KEYMASTER_H_
+#define SYSTEM_KEYMASTER_GOOGLE_SOFT_KEYMASTER_H_
+
+#include <keymaster/google_keymaster.h>
+
+namespace keymaster {
+
+class GoogleSoftKeymaster : public GoogleKeymaster {
+ public:
+ GoogleSoftKeymaster(size_t operation_table_size, Logger* logger)
+ : GoogleKeymaster(operation_table_size, logger) {
+ root_of_trust.tag = KM_TAG_ROOT_OF_TRUST;
+ root_of_trust.blob.data = reinterpret_cast<const uint8_t*>("SW");
+ root_of_trust.blob.data_length = 2;
+ }
+ bool is_enforced(keymaster_tag_t /* tag */) { return false; }
+ keymaster_key_origin_t origin() { return KM_ORIGIN_SOFTWARE; }
+
+ private:
+ static uint8_t master_key_[];
+
+ keymaster_key_blob_t MasterKey() {
+ keymaster_key_blob_t blob;
+ blob.key_material = master_key_;
+ blob.key_material_size = 16;
+ return blob;
+ }
+
+ void GenerateNonce(uint8_t* nonce, size_t length) {
+ for (size_t i = 0; i < length; ++i)
+ nonce[i] = 0;
+ }
+
+ keymaster_key_param_t RootOfTrustTag() { return root_of_trust; }
+
+ keymaster_key_param_t root_of_trust;
+};
+
+uint8_t GoogleSoftKeymaster::master_key_[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+} // namespace
+
+#endif // SYSTEM_KEYMASTER_GOOGLE_SOFT_KEYMASTER_H_
diff --git a/include/keymaster/authorization_set.h b/include/keymaster/authorization_set.h
new file mode 100644
index 0000000..a4db081
--- /dev/null
+++ b/include/keymaster/authorization_set.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_AUTHORIZATION_SET_H_
+#define SYSTEM_KEYMASTER_AUTHORIZATION_SET_H_
+
+#include <UniquePtr.h>
+
+#include <keymaster/keymaster_defs.h>
+#include <keymaster/keymaster_tags.h>
+#include <keymaster/serializable.h>
+
+namespace keymaster {
+
+/**
+ * A container that manages a set of keymaster_key_param_t objects, providing serialization,
+ * de-serialization and accessors.
+ */
+class AuthorizationSet : public Serializable {
+ public:
+ /**
+ * Construct an empty, dynamically-allocated, growable AuthorizationSet. Does not actually
+ * allocate any storage until elements are added, so there is no cost to creating an
+ * AuthorizationSet with this constructor and then reinitializing it to point at pre-allocated
+ * buffers, with \p Reinitialize.
+ */
+ AuthorizationSet()
+ : elems_(NULL), elems_size_(0), elems_capacity_(0), indirect_data_(NULL),
+ indirect_data_size_(0), indirect_data_capacity_(0), error_(OK) {}
+
+ /**
+ * Construct an AuthorizationSet from the provided array. The AuthorizationSet copies the data
+ * from the provided array (and the data referenced by its embedded pointers, if any) into
+ * dynamically-allocated storage. If allocation of the needed storage fails, \p is_valid() will
+ * return ALLOCATION_FAILURE. It is the responsibility of the caller to check before using the
+ * set, if allocations might fail.
+ */
+ AuthorizationSet(const keymaster_key_param_t* elems, size_t count)
+ : elems_(NULL), indirect_data_(NULL) {
+ Reinitialize(elems, count);
+ }
+
+ AuthorizationSet(const uint8_t* serialized_set, size_t serialized_size)
+ : elems_(NULL), indirect_data_(NULL) {
+ Deserialize(&serialized_set, serialized_set + serialized_size);
+ }
+
+ // Copy constructor.
+ AuthorizationSet(const AuthorizationSet&);
+
+ /**
+ * Reinitialize an AuthorizationSet as a dynamically-allocated, growable copy of the data in the
+ * provided array (and the data referenced by its embedded pointers, if any). If the allocation
+ * of the needed storage fails this method will return false and \p is_valid() will return
+ * ALLOCATION_FAILURE.
+ */
+ bool Reinitialize(const keymaster_key_param_t* elems, size_t count);
+
+ bool Reinitialize(const AuthorizationSet& set) {
+ return Reinitialize(set.elems_, set.elems_size_);
+ }
+
+ ~AuthorizationSet();
+
+ enum Error {
+ OK,
+ ALLOCATION_FAILURE,
+ MALFORMED_DATA,
+ };
+
+ Error is_valid() const { return error_; }
+
+ /**
+ * Returns the size of the set.
+ */
+ size_t size() const { return elems_size_; }
+
+ /**
+ * Returns the total size of all indirect data referenced by set elements.
+ */
+ size_t indirect_size() const { return indirect_data_size_; }
+
+ /**
+ * Returns the data in the set, directly. Be careful with this.
+ */
+ const keymaster_key_param_t* data() const;
+
+ /**
+ * Returns the offset of the next entry that matches \p tag, starting from the element after \p
+ * begin. If not found, returns -1.
+ */
+ int find(keymaster_tag_t tag, int begin = -1) const;
+
+ /**
+ * Returns the nth element of the set.
+ */
+ keymaster_key_param_t operator[](int n) const;
+
+ /**
+ * If the specified integer-typed \p tag exists, places its value in \p val and returns true.
+ * If \p tag is not present, leaves \p val unmodified and returns false.
+ */
+ template <keymaster_tag_t T>
+ inline bool GetTagValue(TypedTag<KM_INT, T> tag, uint32_t* val) const {
+ return GetTagValueInt(tag, val);
+ }
+
+ /**
+ * If the specified instance of the specified integer-typed \p tag exists, places its value
+ * in \p val and returns true. If \p tag is not present, leaves \p val unmodified and returns
+ * false.
+ */
+ template <keymaster_tag_t Tag>
+ bool GetTagValue(TypedTag<KM_INT_REP, Tag> tag, size_t instance, uint32_t* val) const {
+ return GetTagValueIntRep(tag, instance, val);
+ }
+
+ /**
+ * If the specified long-typed \p tag exists, places its value in \p val and returns true.
+ * If \p tag is not present, leaves \p val unmodified and returns false.
+ */
+ template <keymaster_tag_t T>
+ inline bool GetTagValue(TypedTag<KM_LONG, T> tag, uint64_t* val) const {
+ return GetTagValueLong(tag, val);
+ }
+
+ /**
+ * If the specified enumeration-typed \p tag exists, places its value in \p val and returns
+ * true. If \p tag is not present, leaves \p val unmodified and returns false.
+ */
+ template <keymaster_tag_t Tag, typename T>
+ bool GetTagValue(TypedEnumTag<KM_ENUM, Tag, T> tag, T* val) const {
+ return GetTagValueEnum(tag, reinterpret_cast<uint32_t*>(val));
+ }
+
+ /**
+ * If the specified instance of the specified enumeration-typed \p tag exists, places its value
+ * in \p val and returns true. If \p tag is not present, leaves \p val unmodified and returns
+ * false.
+ */
+ template <keymaster_tag_t Tag, typename T>
+ bool GetTagValue(TypedEnumTag<KM_ENUM_REP, Tag, T> tag, size_t instance, T* val) const {
+ return GetTagValueEnumRep(tag, instance, reinterpret_cast<uint32_t*>(val));
+ }
+
+ /**
+ * If the specified date-typed \p tag exists, places its value in \p val and returns
+ * true. If \p tag is not present, leaves \p val unmodified and returns false.
+ */
+ template <keymaster_tag_t Tag>
+ bool GetTagValue(TypedTag<KM_INT_REP, Tag> tag, size_t instance,
+ typename TypedTag<KM_INT_REP, Tag>::value_type* val) const {
+ return GetTagValueIntRep(tag, instance, val);
+ }
+
+ /**
+ * If the specified bytes-typed \p tag exists, places its value in \p val and returns
+ * true. If \p tag is not present, leaves \p val unmodified and returns false.
+ */
+ template <keymaster_tag_t Tag>
+ bool GetTagValue(TypedTag<KM_BYTES, Tag> tag, keymaster_blob_t* val) const {
+ return GetTagValueBlob(tag, val);
+ }
+
+ /**
+ * If the specified bignum-typed \p tag exists, places its value in \p val and returns
+ * true. If \p tag is not present, leaves \p val unmodified and returns false.
+ */
+ template <keymaster_tag_t Tag>
+ bool GetTagValue(TypedTag<KM_BIGNUM, Tag> tag, keymaster_blob_t* val) const {
+ return GetTagValueBlob(tag, val);
+ }
+
+ /**
+ * If the specified \p tag exists, places its value in \p val and returns true. If \p tag is
+ * not present, leaves \p val unmodified and returns false.
+ */
+ template <keymaster_tag_t Tag, keymaster_tag_type_t Type>
+ bool GetTagValue(TypedTag<Type, Tag> tag, typename TagValueType<Type>::value_type* val) const {
+ return GetTagValueLong(tag, val);
+ }
+
+ bool push_back(keymaster_key_param_t elem);
+
+ /**
+ * Grow the elements array to ensure it can contain \p count entries. Preserves any existing
+ * entries.
+ */
+ bool reserve_elems(size_t count);
+
+ /**
+ * Grow the indirect data array to ensure it can contain \p length bytes. Preserves any
+ * existing indirect data.
+ */
+ bool reserve_indirect(size_t length);
+
+ bool push_back(const AuthorizationSet& set);
+
+ template <keymaster_tag_t Tag, keymaster_tag_type_t Type, typename KeymasterEnum>
+ bool push_back(TypedEnumTag<Type, Tag, KeymasterEnum> tag, KeymasterEnum val) {
+ return push_back(Authorization(tag, val));
+ }
+
+ template <keymaster_tag_t Tag> bool push_back(TypedTag<KM_BOOL, Tag> tag) {
+ return push_back(Authorization(tag));
+ }
+
+ template <keymaster_tag_t Tag>
+ bool push_back(TypedTag<KM_BYTES, Tag> tag, const void* bytes, size_t bytes_len) {
+ return push_back(keymaster_param_blob(tag, static_cast<const uint8_t*>(bytes), bytes_len));
+ }
+
+ template <keymaster_tag_t Tag>
+ bool push_back(TypedTag<KM_BIGNUM, Tag> tag, const void* bytes, size_t bytes_len) {
+ return push_back(keymaster_param_blob(tag, static_cast<const uint8_t*>(bytes), bytes_len));
+ }
+
+ template <keymaster_tag_t Tag, keymaster_tag_type_t Type>
+ bool push_back(TypedTag<Type, Tag> tag, typename TypedTag<Type, Tag>::value_type val) {
+ return push_back(Authorization(tag, val));
+ }
+
+ template <keymaster_tag_t Tag, keymaster_tag_type_t Type>
+ bool push_back(TypedTag<Type, Tag> tag, const void* bytes, size_t bytes_len) {
+ return push_back(Authorization(tag, bytes, bytes_len));
+ }
+
+ /* Virtual methods from Serializable */
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* serialized_set, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ size_t SerializedSizeOfElements() const;
+
+ private:
+ // Disallow assignment
+ void operator=(const AuthorizationSet&);
+
+ void FreeData();
+ void set_invalid(Error err);
+
+ static size_t ComputeIndirectDataSize(const keymaster_key_param_t* elems, size_t count);
+ void CopyIndirectData();
+ bool CheckIndirectData();
+
+ bool DeserializeIndirectData(const uint8_t** buf_ptr, const uint8_t* end);
+ bool DeserializeElementsData(const uint8_t** buf_ptr, const uint8_t* end);
+
+ bool GetTagValueEnum(keymaster_tag_t tag, uint32_t* val) const;
+ bool GetTagValueEnumRep(keymaster_tag_t tag, size_t instance, uint32_t* val) const;
+ bool GetTagValueInt(keymaster_tag_t tag, uint32_t* val) const;
+ bool GetTagValueIntRep(keymaster_tag_t tag, size_t instance, uint32_t* val) const;
+ bool GetTagValueLong(keymaster_tag_t tag, uint64_t* val) const;
+ bool GetTagValueDate(keymaster_tag_t tag, uint64_t* val) const;
+ bool GetTagValueBlob(keymaster_tag_t tag, keymaster_blob_t* val) const;
+
+ keymaster_key_param_t* elems_;
+ size_t elems_size_;
+ size_t elems_capacity_;
+ uint8_t* indirect_data_;
+ size_t indirect_data_size_;
+ size_t indirect_data_capacity_;
+ Error error_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_KEY_AUTHORIZATION_SET_H_
diff --git a/include/keymaster/google_keymaster.h b/include/keymaster/google_keymaster.h
new file mode 100644
index 0000000..61714f0
--- /dev/null
+++ b/include/keymaster/google_keymaster.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_H_
+#define SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_H_
+
+#include <keymaster/authorization_set.h>
+#include <keymaster/google_keymaster_messages.h>
+#include <keymaster/logger.h>
+
+namespace keymaster {
+
+class Key;
+class KeyBlob;
+class Operation;
+
+/**
+ * OpenSSL-based Keymaster backing implementation, for use as a pure software implmentation
+ * (softkeymaster) and in a trusted execution environment (TEE), like ARM TrustZone. This class
+ * doesn't actually implement the Keymaster HAL interface, instead it implements an alternative API
+ * which is similar to and based upon the HAL, but uses C++ "message" classes which support
+ * serialization.
+ *
+ * For non-secure, pure software implementation there is a HAL translation layer that converts the
+ * HAL's parameters to and from the message representations, which are then passed in to this
+ * API.
+ *
+ * For secure implementation there is another HAL translation layer that serializes the messages to
+ * the TEE. In the TEE implementation there's another component which deserializes the messages,
+ * extracts the relevant parameters and calls this API.
+ */
+class GoogleKeymaster {
+ public:
+ GoogleKeymaster(size_t operation_table_size, Logger* logger);
+ virtual ~GoogleKeymaster();
+
+ void SupportedAlgorithms(SupportedResponse<keymaster_algorithm_t>* response) const;
+ void SupportedBlockModes(keymaster_algorithm_t algorithm,
+ SupportedResponse<keymaster_block_mode_t>* response) const;
+ void SupportedPaddingModes(keymaster_algorithm_t algorithm,
+ SupportedResponse<keymaster_padding_t>* response) const;
+ void SupportedDigests(keymaster_algorithm_t algorithm,
+ SupportedResponse<keymaster_digest_t>* response) const;
+ void SupportedImportFormats(keymaster_algorithm_t algorithm,
+ SupportedResponse<keymaster_key_format_t>* response) const;
+ void SupportedExportFormats(keymaster_algorithm_t algorithm,
+ SupportedResponse<keymaster_key_format_t>* response) const;
+
+ virtual keymaster_error_t AddRngEntropy(AddEntropyRequest& /* request */) {
+ // Not going to implement until post-L.
+ return KM_ERROR_UNIMPLEMENTED;
+ }
+ void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
+ void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
+ GetKeyCharacteristicsResponse* response);
+ void Rescope(const RescopeRequest& /* request */, RescopeResponse* response) {
+ // Not going to implement until post-L.
+ response->error = KM_ERROR_UNIMPLEMENTED;
+ }
+ void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
+ void ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response);
+ void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response);
+ void UpdateOperation(const UpdateOperationRequest& request, UpdateOperationResponse* response);
+ void FinishOperation(const FinishOperationRequest& request, FinishOperationResponse* response);
+ keymaster_error_t AbortOperation(const keymaster_operation_handle_t op_handle);
+
+ const Logger& logger() const { return *logger_; }
+
+ private:
+ virtual bool is_enforced(keymaster_tag_t tag) = 0;
+ virtual keymaster_key_origin_t origin() = 0;
+ virtual keymaster_key_param_t RootOfTrustTag() = 0;
+ virtual keymaster_key_blob_t MasterKey() = 0;
+ virtual void GenerateNonce(uint8_t* nonce, size_t length) = 0;
+
+ keymaster_error_t SerializeKey(const Key* key, keymaster_key_origin_t origin,
+ keymaster_key_blob_t* keymaster_blob, AuthorizationSet* enforced,
+ AuthorizationSet* unenforced);
+ Key* LoadKey(const keymaster_key_blob_t& key, const AuthorizationSet& client_params,
+ keymaster_error_t* error);
+ KeyBlob* LoadKeyBlob(const keymaster_key_blob_t& key, const AuthorizationSet& client_params,
+ keymaster_error_t* error);
+
+ keymaster_error_t SetAuthorizations(const AuthorizationSet& key_description,
+ keymaster_key_origin_t origin, AuthorizationSet* enforced,
+ AuthorizationSet* unenforced);
+ keymaster_error_t BuildHiddenAuthorizations(const AuthorizationSet& input_set,
+ AuthorizationSet* hidden);
+
+ void AddAuthorization(const keymaster_key_param_t& auth, AuthorizationSet* enforced,
+ AuthorizationSet* unenforced);
+ bool GenerateRsa(const AuthorizationSet& key_auths, GenerateKeyResponse* response,
+ AuthorizationSet* hidden_auths);
+ bool GenerateDsa(const AuthorizationSet& key_auths, GenerateKeyResponse* response,
+ AuthorizationSet* hidden_auths);
+ bool GenerateEcdsa(const AuthorizationSet& key_auths, GenerateKeyResponse* response,
+ AuthorizationSet* hidden_auths);
+ keymaster_error_t WrapKey(const uint8_t* key_material, size_t key_material_length,
+ KeyBlob* blob);
+ keymaster_error_t UnwrapKey(const KeyBlob* blob, uint8_t* key, size_t key_length);
+
+ struct OpTableEntry {
+ OpTableEntry() {
+ handle = 0;
+ operation = NULL;
+ }
+ keymaster_operation_handle_t handle;
+ Operation* operation;
+ };
+
+ keymaster_error_t AddOperation(Operation* operation, keymaster_operation_handle_t* op_handle);
+ OpTableEntry* FindOperation(keymaster_operation_handle_t op_handle);
+ void DeleteOperation(OpTableEntry* entry);
+ bool is_supported_export_format(keymaster_key_format_t test_format);
+ bool is_supported_import_format(keymaster_key_format_t test_format);
+
+ UniquePtr<OpTableEntry[]> operation_table_;
+ size_t operation_table_size_;
+ UniquePtr<Logger> logger_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_H_
diff --git a/include/keymaster/google_keymaster_messages.h b/include/keymaster/google_keymaster_messages.h
new file mode 100644
index 0000000..622d791
--- /dev/null
+++ b/include/keymaster/google_keymaster_messages.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_MESSAGES_H_
+#define SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_MESSAGES_H_
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <keymaster/authorization_set.h>
+#include <keymaster/google_keymaster_utils.h>
+
+namespace keymaster {
+
+// Commands
+const uint32_t GENERATE_KEY = 0;
+const uint32_t BEGIN_OPERATION = 1;
+const uint32_t UPDATE_OPERATION = 2;
+const uint32_t FINISH_OPERATION = 3;
+const uint32_t ABORT_OPERATION = 4;
+const uint32_t IMPORT_KEY = 5;
+const uint32_t EXPORT_KEY = 6;
+
+/**
+ * All responses include an error value, and if the error is not KM_ERROR_OK, return no additional
+ * data. This abstract class factors out the common serialization functionality for all of the
+ * responses, so we only have to implement it once. Inheritance for reuse is generally not a great
+ * structure, but in this case it's the cleanest option.
+ */
+struct KeymasterResponse : public Serializable {
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ virtual size_t NonErrorSerializedSize() const = 0;
+ virtual uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const = 0;
+ virtual bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) = 0;
+
+ keymaster_error_t error;
+};
+
+struct SupportedAlgorithmsResponse : public KeymasterResponse {
+ SupportedAlgorithmsResponse() : algorithms(NULL), algorithms_length(0) {}
+ ~SupportedAlgorithmsResponse() { delete[] algorithms; }
+
+ size_t NonErrorSerializedSize() const;
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const;
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ keymaster_algorithm_t* algorithms;
+ size_t algorithms_length;
+};
+
+template <typename T> struct SupportedResponse : public KeymasterResponse {
+ SupportedResponse() : results(NULL), results_length(0) {}
+ ~SupportedResponse() { delete[] results; }
+
+ template <size_t N> void SetResults(const T (&arr)[N]) {
+ delete[] results;
+ results_length = 0;
+ results = dup_array(arr);
+ if (results == NULL) {
+ error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ } else {
+ results_length = N;
+ error = KM_ERROR_OK;
+ }
+ }
+
+ size_t NonErrorSerializedSize() const {
+ return sizeof(uint32_t) + results_length * sizeof(uint32_t);
+ }
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const {
+ return append_uint32_array_to_buf(buf, end, results, results_length);
+ }
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ delete[] results;
+ results = NULL;
+ UniquePtr<T[]> tmp;
+ if (!copy_uint32_array_from_buf(buf_ptr, end, &tmp, &results_length))
+ return false;
+ results = tmp.release();
+ return true;
+ }
+
+ T* results;
+ size_t results_length;
+};
+
+struct GenerateKeyRequest : public Serializable {
+ GenerateKeyRequest() {}
+ GenerateKeyRequest(uint8_t* buf, size_t size) : key_description(buf, size) {}
+
+ size_t SerializedSize() const { return key_description.SerializedSize(); }
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const {
+ return key_description.Serialize(buf, end);
+ }
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ return key_description.Deserialize(buf_ptr, end);
+ }
+
+ AuthorizationSet key_description;
+};
+
+struct GenerateKeyResponse : public KeymasterResponse {
+ GenerateKeyResponse() {
+ error = KM_ERROR_OK;
+ key_blob.key_material = NULL;
+ key_blob.key_material_size = 0;
+ }
+ ~GenerateKeyResponse();
+
+ size_t NonErrorSerializedSize() const;
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const;
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ keymaster_key_blob_t key_blob;
+ AuthorizationSet enforced;
+ AuthorizationSet unenforced;
+};
+
+struct GetKeyCharacteristicsRequest : public Serializable {
+ GetKeyCharacteristicsRequest() { key_blob.key_material = NULL; }
+ ~GetKeyCharacteristicsRequest();
+
+ void SetKeyMaterial(const void* key_material, size_t length);
+ void SetKeyMaterial(const keymaster_key_blob_t& blob) {
+ SetKeyMaterial(blob.key_material, blob.key_material_size);
+ }
+
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ keymaster_key_blob_t key_blob;
+ AuthorizationSet additional_params;
+};
+
+struct GetKeyCharacteristicsResponse : public KeymasterResponse {
+ size_t NonErrorSerializedSize() const;
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const;
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ AuthorizationSet enforced;
+ AuthorizationSet unenforced;
+};
+
+struct BeginOperationRequest : public Serializable {
+ BeginOperationRequest() { key_blob.key_material = NULL; }
+ ~BeginOperationRequest() { delete[] key_blob.key_material; }
+
+ void SetKeyMaterial(const void* key_material, size_t length);
+ void SetKeyMaterial(const keymaster_key_blob_t& blob) {
+ SetKeyMaterial(blob.key_material, blob.key_material_size);
+ }
+
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ keymaster_purpose_t purpose;
+ keymaster_key_blob_t key_blob;
+ AuthorizationSet additional_params;
+};
+
+struct BeginOperationResponse : public KeymasterResponse {
+ size_t NonErrorSerializedSize() const;
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const;
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ keymaster_operation_handle_t op_handle;
+};
+
+struct UpdateOperationRequest : public Serializable {
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ keymaster_operation_handle_t op_handle;
+ Buffer input;
+};
+
+struct UpdateOperationResponse : public KeymasterResponse {
+ size_t NonErrorSerializedSize() const;
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const;
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ Buffer output;
+};
+
+struct FinishOperationRequest : public Serializable {
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ keymaster_operation_handle_t op_handle;
+ Buffer signature;
+};
+
+struct FinishOperationResponse : public KeymasterResponse {
+ size_t NonErrorSerializedSize() const;
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const;
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ Buffer output;
+};
+
+struct AddEntropyRequest : public Serializable {
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ Buffer random_data;
+};
+
+struct ImportKeyRequest : public Serializable {
+ ImportKeyRequest() : key_data(NULL) {}
+ ~ImportKeyRequest() { delete[] key_data; }
+
+ void SetKeyMaterial(const void* key_material, size_t length);
+
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ AuthorizationSet key_description;
+ keymaster_key_format_t key_format;
+ uint8_t* key_data;
+ size_t key_data_length;
+};
+
+struct ImportKeyResponse : public KeymasterResponse {
+ ImportKeyResponse() { key_blob.key_material = NULL; }
+ ~ImportKeyResponse() { delete[] key_blob.key_material; }
+
+ void SetKeyMaterial(const void* key_material, size_t length);
+ void SetKeyMaterial(const keymaster_key_blob_t& blob) {
+ SetKeyMaterial(blob.key_material, blob.key_material_size);
+ }
+
+ size_t NonErrorSerializedSize() const;
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const;
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ keymaster_key_blob_t key_blob;
+ AuthorizationSet enforced;
+ AuthorizationSet unenforced;
+};
+
+struct ExportKeyRequest : public Serializable {
+ ExportKeyRequest() { key_blob.key_material = NULL; }
+ ~ExportKeyRequest() { delete[] key_blob.key_material; }
+
+ void SetKeyMaterial(const void* key_material, size_t length);
+ void SetKeyMaterial(const keymaster_key_blob_t& blob) {
+ SetKeyMaterial(blob.key_material, blob.key_material_size);
+ }
+
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ AuthorizationSet additional_params;
+ keymaster_key_format_t key_format;
+ keymaster_key_blob_t key_blob;
+};
+
+struct ExportKeyResponse : public KeymasterResponse {
+ ExportKeyResponse() : key_data(NULL) {}
+ ~ExportKeyResponse() { delete[] key_data; }
+
+ void SetKeyMaterial(const void* key_material, size_t length);
+
+ size_t NonErrorSerializedSize() const;
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const;
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ uint8_t* key_data;
+ size_t key_data_length;
+};
+
+// The structs below are trivial because they're not implemented yet.
+struct RescopeRequest : public Serializable {};
+struct RescopeResponse : public KeymasterResponse {};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_MESSAGES_H_
diff --git a/include/keymaster/google_keymaster_utils.h b/include/keymaster/google_keymaster_utils.h
new file mode 100644
index 0000000..922ec2d
--- /dev/null
+++ b/include/keymaster/google_keymaster_utils.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_UTILS_H_
+#define SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_UTILS_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <time.h> // for time_t.
+
+#include <UniquePtr.h>
+
+#include <keymaster/serializable.h>
+
+namespace keymaster {
+
+/**
+ * Convert the specified time value into "Java time", which is a signed 64-bit integer representing
+ * elapsed milliseconds since Jan 1, 1970.
+ */
+inline int64_t java_time(time_t time) {
+ // The exact meaning of a time_t value is implementation-dependent. If this code is ported to a
+ // platform that doesn't define it as "seconds since Jan 1, 1970 UTC", this function will have
+ // to be revised.
+ return time * 1000;
+}
+
+/*
+ * Array Manipulation functions. This set of templated inline functions provides some nice tools
+ * for operating on c-style arrays. C-style arrays actually do have a defined size associated with
+ * them, as long as they are not allowed to decay to a pointer. These template methods exploit this
+ * to allow size-based array operations without explicitly specifying the size. If passed a pointer
+ * rather than an array, they'll fail to compile.
+ */
+
+/**
+ * Return the size in bytes of the array \p a.
+ */
+template <typename T, size_t N> inline size_t array_size(const T (&a)[N]) {
+ return sizeof(a);
+}
+
+/**
+ * Return the number of elements in array \p a.
+ */
+template <typename T, size_t N> inline size_t array_length(const T (&)[N]) {
+ return N;
+}
+
+/**
+ * Duplicate the array \p a. The memory for the new array is allocated and the caller takes
+ * responsibility. Note that the dup is necessarily returned as a pointer, so size is lost. Call
+ * array_length() on the original array to discover the size.
+ */
+template <typename T, size_t N> inline T* dup_array(const T (&a)[N]) {
+ T* dup = new T[N];
+ if (dup != NULL) {
+ memcpy(dup, &a, array_size(a));
+ }
+ return dup;
+}
+
+/**
+ * Duplicate the buffer \p buf. The memory for the new buffer is allocated and the caller takes
+ * responsibility.
+ */
+uint8_t* dup_buffer(const void* buf, size_t size);
+
+/**
+ * Copy the contents of array \p arr to \p dest.
+ */
+template <typename T, size_t N> inline void copy_array(const T (&arr)[N], T* dest) {
+ for (size_t i = 0; i < N; ++i)
+ dest[i] = arr[i];
+}
+
+/**
+ * Search array \p a for value \p val, returning true if found. Note that this function is
+ * early-exit, meaning that it should not be used in contexts where timing analysis attacks could be
+ * a concern.
+ */
+template <typename T, size_t N> inline bool array_contains(const T (&a)[N], T val) {
+ for (size_t i = 0; i < N; ++i) {
+ if (a[i] == val) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Variant of memset() that uses GCC-specific pragmas to disable optimizations, so effect is not
+ * optimized away. This is important because we often need to wipe blocks of sensitive data from
+ * memory. As an additional convenience, this implementation avoids writing to NULL pointers.
+ */
+#ifdef KEYMASTER_CLANG_TEST_BUILD
+#define OPTIMIZE(x)
+#else // not KEYMASTER_CLANG_TEST_BUILD
+#define OPTIMIZE(x) __attribute__((optimize(x)))
+#endif // not KEYMASTER_CLANG_TEST_BUILD
+inline OPTIMIZE("O0") void* memset_s(void* s, int c, size_t n) {
+ if (!s)
+ return s;
+ return memset(s, c, n);
+}
+#undef OPTIMIZE
+
+/**
+ * Variant of memcmp that has the same runtime regardless of whether the data matches (i.e. doesn't
+ * short-circuit). Not an exact equivalent to memcmp because it doesn't return <0 if p1 < p2, just
+ * 0 for match and non-zero for non-match.
+ */
+int memcmp_s(const void* p1, const void* p2, size_t length);
+
+/**
+ * Eraser clears buffers. Construct it with a buffer or object and the destructor will ensure that
+ * it is zeroed.
+ */
+class Eraser {
+ public:
+ /* Not implemented. If this gets used, we want a link error. */
+ template <typename T> explicit Eraser(T* t);
+
+ template <typename T>
+ explicit Eraser(T& t)
+ : buf_(reinterpret_cast<uint8_t*>(&t)), size_(sizeof(t)) {}
+
+ template <size_t N> explicit Eraser(uint8_t (&arr)[N]) : buf_(arr), size_(N) {}
+
+ Eraser(void* buf, size_t size) : buf_(static_cast<uint8_t*>(buf)), size_(size) {}
+ ~Eraser() { memset_s(buf_, 0, size_); }
+
+ private:
+ Eraser(const Eraser&);
+ void operator=(const Eraser&);
+
+ uint8_t* buf_;
+ size_t size_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_GOOGLE_KEYMASTER_UTILS_H_
diff --git a/include/keymaster/key_blob.h b/include/keymaster/key_blob.h
new file mode 100644
index 0000000..8d4bb42
--- /dev/null
+++ b/include/keymaster/key_blob.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_KEY_BLOB_H_
+#define SYSTEM_KEYMASTER_KEY_BLOB_H_
+
+#include <cstddef>
+
+#include <stdint.h>
+
+#include <UniquePtr.h>
+
+#include <keymaster/authorization_set.h>
+#include <keymaster/google_keymaster_utils.h>
+#include <keymaster/keymaster_defs.h>
+#include <keymaster/serializable.h>
+
+namespace keymaster {
+
+/**
+ * This class represents a Keymaster key blob, including authorization sets and key material, both
+ * encrypted and unencrypted. It's primary purpose is to serialize and deserialize blob arrays, and
+ * provide access to the data in the blob.
+ */
+class KeyBlob : public Serializable {
+ public:
+ static const size_t NONCE_LENGTH = 12;
+ static const size_t TAG_LENGTH = 128 / 8;
+
+ /**
+ * Create a KeyBlob containing the specified authorization data and key material. The copy of
+ * \p key will be encrypted with key derived from \p master_key, using OCB authenticated
+ * encryption with \p nonce. It is critically important that nonces NEVER be reused. The most
+ * convenient way to accomplish that is to choose them randomly (assuming good randomness, that
+ * means there's a probability of reuse of one in 2^96).
+ *
+ * Note that this interface abuses \p keymaster_key_blob_t a bit. Normally, that struct is used
+ * to contain a full Keymaster blob, i.e. what KeyBlob is designed to create and manage. In
+ * this case we're using it to hold pure key material without any of the additional structure
+ * needed for a true Keymaster key.
+ *
+ * IMPORTANT: After constructing a KeyBlob, call error() to verify that the blob is usable.
+ */
+ KeyBlob(const AuthorizationSet& enforced, const AuthorizationSet& unenforced,
+ const AuthorizationSet& hidden, const keymaster_key_blob_t& key,
+ const keymaster_key_blob_t& master_key, const uint8_t nonce[NONCE_LENGTH]);
+
+ /**
+ * Create a KeyBlob, reconstituting it from the encrypted material in \p encrypted_key,
+ * decrypted with key derived from \p master_key. The KeyBlob takes ownership of the \p
+ * keymaster_blob.key_material.
+ *
+ * Note, again, that \p master_key here is an abuse of \p keymaster_key_blob_t, since it
+ * is just key material, not a full Keymaster blob.
+ *
+ * IMPORTANT: After constructing a KeyBlob, call error() to verify that the blob is usable.
+ */
+ KeyBlob(const keymaster_key_blob_t& keymaster_blob, const AuthorizationSet& hidden,
+ const keymaster_key_blob_t& master_key);
+
+ /**
+ * Create a KeyBlob, extracting the enforced and unenforced sets, but not decrypting the key, or
+ * even keeping it. The KeyBlob does *not* take ownership of key_blob.
+ *
+ * IMPORTANT: After constructing a KeyBlob, call error() to verify that the blob is usable.
+ */
+ KeyBlob(const uint8_t* key_blob, size_t blob_size);
+
+ ~KeyBlob() {
+ memset_s(key_material_.get(), 0, key_material_length_);
+ // The following aren't sensitive, but clear them anyway.
+ memset_s(encrypted_key_material_.get(), 0, key_material_length_);
+ memset_s(nonce_.get(), 0, NONCE_LENGTH);
+ memset_s(tag_.get(), 0, TAG_LENGTH);
+ // AuthorizationSets clear themselves.
+ }
+
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ /**
+ * Decrypt encrypted key. Call this after calling "Deserialize". Until it's called,
+ * key_material() will return a pointer to an uninitialized buffer. Sets error if there is a
+ * problem.
+ */
+ void DecryptKey(const keymaster_key_blob_t& master_key);
+
+ /**
+ * Returns KM_ERROR_OK if all is well, or an appropriate error code if there is a problem. This
+ * error code should be checked after constructing or deserializing/decrypting, and if it does
+ * not return KM_ERROR_OK, then don't call any other methods.
+ */
+ inline keymaster_error_t error() { return error_; }
+
+ inline const uint8_t* nonce() const { return nonce_.get(); }
+ inline const uint8_t* key_material() const { return key_material_.get(); }
+ inline const uint8_t* encrypted_key_material() const { return encrypted_key_material_.get(); }
+ inline size_t key_material_length() const { return key_material_length_; }
+ inline const uint8_t* tag() const { return tag_.get(); }
+
+ inline const AuthorizationSet& enforced() const { return enforced_; }
+ inline const AuthorizationSet& unenforced() const { return unenforced_; }
+ inline const AuthorizationSet& hidden() const { return hidden_; }
+ inline keymaster_algorithm_t algorithm() const { return algorithm_; }
+ inline size_t key_size_bits() const { return key_size_bits_; }
+
+ private:
+ void EncryptKey(const keymaster_key_blob_t& master_key);
+ bool ExtractKeyCharacteristics();
+
+ /**
+ * Create an AES_OCB context initialized with a key derived using \p master_key and the
+ * authorizations.
+ */
+ class AeCtx;
+ AeCtx* InitializeKeyWrappingContext(const keymaster_key_blob_t& master_key,
+ keymaster_error_t* error) const;
+
+ const uint8_t* BuildDerivationData(size_t* derivation_data_len) const;
+
+ keymaster_error_t error_;
+ UniquePtr<uint8_t[]> nonce_;
+ UniquePtr<uint8_t[]> key_material_;
+ UniquePtr<uint8_t[]> encrypted_key_material_;
+ UniquePtr<uint8_t[]> tag_;
+ size_t key_material_length_;
+ AuthorizationSet enforced_;
+ AuthorizationSet unenforced_;
+ AuthorizationSet hidden_;
+ keymaster_algorithm_t algorithm_;
+ uint32_t key_size_bits_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_KEY_BLOB_H_
diff --git a/include/keymaster/keymaster_defs.h b/include/keymaster/keymaster_defs.h
new file mode 100644
index 0000000..c35f48b
--- /dev/null
+++ b/include/keymaster/keymaster_defs.h
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_HARDWARE_KEYMASTER_DEFS_H
+#define ANDROID_HARDWARE_KEYMASTER_DEFS_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif // defined(__cplusplus)
+
+/**
+ * Authorization tags each have an associated type. This enumeration facilitates tagging each with
+ * a type, by using the high four bits (of an implied 32-bit unsigned enum value) to specify up to
+ * 16 data types. These values are ORed with tag IDs to generate the final tag ID values.
+ */
+typedef enum {
+ KM_INVALID = 0 << 28, /* Invalid type, used to designate a tag as uninitialized */
+ KM_ENUM = 1 << 28,
+ KM_ENUM_REP = 2 << 28, /* Repeatable enumeration value. */
+ KM_INT = 3 << 28,
+ KM_INT_REP = 4 << 28, /* Repeatable integer value */
+ KM_LONG = 5 << 28,
+ KM_DATE = 6 << 28,
+ KM_BOOL = 7 << 28,
+ KM_BIGNUM = 8 << 28,
+ KM_BYTES = 9 << 28,
+} keymaster_tag_type_t;
+
+typedef enum {
+ KM_TAG_INVALID = KM_INVALID | 0,
+
+ /*
+ * Tags that must be semantically enforced by hardware and software implementations.
+ */
+
+ /* Crypto parameters */
+ KM_TAG_PURPOSE = KM_ENUM_REP | 1, /* keymaster_purpose_t. */
+ KM_TAG_ALGORITHM = KM_ENUM | 2, /* keymaster_algorithm_t. */
+ KM_TAG_KEY_SIZE = KM_INT | 3, /* Key size in bits. */
+ KM_TAG_BLOCK_MODE = KM_ENUM | 4, /* keymaster_block_mode_t. */
+ KM_TAG_DIGEST = KM_ENUM | 5, /* keymaster_digest_t. */
+ KM_TAG_MAC_LENGTH = KM_INT | 6, /* MAC length in bits. */
+ KM_TAG_PADDING = KM_ENUM | 7, /* keymaster_padding_t. */
+ KM_TAG_CHUNK_LENGTH = KM_INT | 8, /* AEAD mode minimum decryption chunk size, in bytes. */
+
+ /* Other hardware-enforced. */
+ KM_TAG_RESCOPING_ADD = KM_ENUM_REP | 101, /* Tags authorized for addition via rescoping. */
+ KM_TAG_RESCOPING_DEL = KM_ENUM_REP | 102, /* Tags authorized for removal via rescoping. */
+ KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705, /* keymaster_key_blob_usage_requirements_t */
+
+ /* Algorithm-specific. */
+ KM_TAG_RSA_PUBLIC_EXPONENT = KM_LONG | 200, /* Defaults to 2^16+1 */
+ KM_TAG_DSA_GENERATOR = KM_BIGNUM | 201,
+ KM_TAG_DSA_P = KM_BIGNUM | 202,
+ KM_TAG_DSA_Q = KM_BIGNUM | 203,
+ /* Note there are no EC-specific params. Field size is defined by KM_TAG_KEY_SIZE, and the
+ curve is chosen from NIST recommendations for field size */
+
+ /*
+ * Tags that should be semantically enforced by hardware if possible and will otherwise be
+ * enforced by software (keystore).
+ */
+
+ /* Key validity period */
+ KM_TAG_ACTIVE_DATETIME = KM_DATE | 400, /* Start of validity */
+ KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401, /* Date when new "messages" should no
+ longer be created. */
+ KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402, /* Date when existing "messages" should no
+ longer be trusted. */
+ KM_TAG_MIN_SECONDS_BETWEEN_OPS = KM_INT | 403, /* Minimum elapsed time between
+ cryptographic operations with the key. */
+ KM_TAG_SINGLE_USE_PER_BOOT = KM_BOOL | 404, /* If true, the key can only be used once
+ per boot. */
+
+ /* User authentication */
+ KM_TAG_ALL_USERS = KM_BOOL | 500, /* If key is usable by all users. */
+ KM_TAG_USER_ID = KM_INT | 501, /* ID of authorized user. Disallowed if KM_TAG_ALL_USERS is
+ present. */
+ KM_TAG_NO_AUTH_REQUIRED = KM_BOOL | 502, /* If key is usable without authentication. */
+ KM_TAG_USER_AUTH_ID = KM_INT_REP | 503, /* ID of the authenticator to use (e.g. password,
+ fingerprint, etc.). Repeatable to support
+ multi-factor auth. Disallowed if
+ KM_TAG_NO_AUTH_REQUIRED is present. */
+ KM_TAG_AUTH_TIMEOUT = KM_INT | 504, /* Required freshness of user authentication for
+ private/secret key operations, in seconds.
+ Public key operations require no authentication.
+ If absent, authentication is required for every
+ use. Authentication state is lost when the
+ device is powered off. */
+ KM_TAG_RESCOPE_AUTH_TIMEOUT = KM_INT | 505, /* Required freshness of user authentication for key
+ rescoping operations, in seconds. Public key
+ operations require no authentication. If absent,
+ authentication required for every rescoping. */
+
+ /* Application access control */
+ KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600, /* If key is usable by all applications. */
+ KM_TAG_APPLICATION_ID = KM_BYTES | 601, /* ID of authorized application. Disallowed if
+ KM_TAG_ALL_APPLICATIONS is present. */
+
+ /*
+ * Semantically unenforceable tags, either because they have no specific meaning or because
+ * they're informational only.
+ */
+ KM_TAG_APPLICATION_DATA = KM_BYTES | 700, /* Data provided by authorized application. */
+ KM_TAG_CREATION_DATETIME = KM_DATE | 701, /* Key creation time */
+ KM_TAG_ORIGIN = KM_ENUM | 702, /* keymaster_key_origin_t. */
+ KM_TAG_ROLLBACK_RESISTANT = KM_BOOL | 703, /* Whether key is rollback-resistant. */
+ KM_TAG_ROOT_OF_TRUST = KM_BYTES | 704, /* Root of trust ID. Empty array means usable by all
+ roots. */
+
+ /* Tags used only to provide data to operations */
+ KM_TAG_ADDITIONAL_DATA = KM_BYTES | 1000, /* Used to provide additional data for AEAD modes. */
+} keymaster_tag_t;
+
+/**
+ * Algorithms that may be provided by keymaster implementations. Those that must be provided by all
+ * implementations are tagged as "required". Note that where the values in this enumeration overlap
+ * with the values for the deprecated keymaster_keypair_t, the same algorithm must be
+ * specified. This type is new in 0_4 and replaces the deprecated keymaster_keypair_t.
+ */
+typedef enum {
+ /* Asymmetric algorithms. */
+ KM_ALGORITHM_RSA = 1, /* required */
+ KM_ALGORITHM_DSA = 2, /* required */
+ KM_ALGORITHM_ECDSA = 3, /* required */
+ KM_ALGORITHM_ECIES = 4,
+ /* FIPS Approved Ciphers */
+ KM_ALGORITHM_AES = 32, /* required */
+ KM_ALGORITHM_3DES = 33,
+ KM_ALGORITHM_SKIPJACK = 34,
+ /* AES Finalists */
+ KM_ALGORITHM_MARS = 48,
+ KM_ALGORITHM_RC6 = 49,
+ KM_ALGORITHM_SERPENT = 50,
+ KM_ALGORITHM_TWOFISH = 51,
+ /* Other common block ciphers */
+ KM_ALGORITHM_IDEA = 52,
+ KM_ALGORITHM_RC5 = 53,
+ KM_ALGORITHM_CAST5 = 54,
+ KM_ALGORITHM_BLOWFISH = 55,
+ /* Common stream ciphers */
+ KM_ALGORITHM_RC4 = 64,
+ KM_ALGORITHM_CHACHA20 = 65,
+ /* MAC algorithms */
+ KM_ALGORITHM_HMAC = 128, /* required */
+} keymaster_algorithm_t;
+
+/**
+ * Symmetric block cipher modes that may be provided by keymaster implementations. Those that must
+ * be provided by all implementations are tagged as "required". This type is new in 0_4.
+ */
+typedef enum {
+ /* Unauthenticated modes, usable only for encryption/decryption and not generally recommended
+ * except for compatibility with existing other protocols. */
+ KM_MODE_ECB = 1, /* required */
+ KM_MODE_CBC = 2, /* required */
+ KM_MODE_CBC_CTS = 3, /* recommended */
+ KM_MODE_CTR = 4, /* recommended */
+ KM_MODE_OFB = 5,
+ KM_MODE_CFB = 6,
+ KM_MODE_XTS = 7, /* Note: requires double-length keys */
+ /* Authenticated modes, usable for encryption/decryption and signing/verification. Recommended
+ * over unauthenticated modes for all purposes. One of KM_MODE_GCM and KM_MODE_OCB is
+ * required. */
+ KM_MODE_GCM = 32,
+ KM_MODE_OCB = 33,
+ KM_MODE_CCM = 34,
+ /* MAC modes -- only for signing/verification */
+ KM_MODE_CMAC = 128,
+ KM_MODE_POLY1305 = 129,
+} keymaster_block_mode_t;
+
+/**
+ * Padding modes that may be applied to plaintext for encryption operations. This list includes
+ * padding modes for both symmetric and asymmetric algorithms. Note that implementations should not
+ * provide all possible combinations of algorithm and padding, only the
+ * cryptographically-appropriate pairs.
+ */
+typedef enum {
+ KM_PAD_NONE = 1, /* required, deprecated */
+ KM_PAD_RSA_OAEP = 2, /* required */
+ KM_PAD_RSA_PSS = 3, /* required */
+ KM_PAD_RSA_PKCS1_1_5_ENCRYPT = 4,
+ KM_PAD_RSA_PKCS1_1_5_SIGN = 5,
+ KM_PAD_ANSI_X923 = 32,
+ KM_PAD_ISO_10126 = 33,
+ KM_PAD_ZERO = 64, /* required */
+ KM_PAD_PKCS7 = 65, /* required */
+ KM_PAD_ISO_7816_4 = 66,
+} keymaster_padding_t;
+
+#ifndef ANDROID_HARDWARE_KEYMASTER_H
+
+/**
+ * Digests that may be provided by keymaster implementations. Those that must be provided by all
+ * implementations are tagged as "required". Those that have been added since version 0_2 of the
+ * API are tagged as "new".
+ */
+typedef enum {
+ KM_DIGEST_NONE = 0, /* new, required */
+ DIGEST_NONE = KM_DIGEST_NONE, /* For 0_2 compatibility */
+ KM_DIGEST_MD5 = 1, /* new, for compatibility with old protocols only */
+ KM_DIGEST_SHA1 = 2, /* new */
+ KM_DIGEST_SHA_2_224 = 3, /* new */
+ KM_DIGEST_SHA_2_256 = 4, /* new, required */
+ KM_DIGEST_SHA_2_384 = 5, /* new, recommended */
+ KM_DIGEST_SHA_2_512 = 6, /* new, recommended */
+ KM_DIGEST_SHA_3_256 = 7, /* new */
+ KM_DIGEST_SHA_3_384 = 8, /* new */
+ KM_DIGEST_SHA_3_512 = 9, /* new */
+} keymaster_digest_t;
+
+#endif // ANDROID_HARDWARE_KEYMASTER_H
+
+/**
+ * The origin of a key (or pair), i.e. where it was generated. Origin and can be used together to
+ * determine whether a key may have existed outside of secure hardware. This type is new in 0_4.
+ */
+typedef enum {
+ KM_ORIGIN_HARDWARE = 0, /* Generated in secure hardware */
+ KM_ORIGIN_SOFTWARE = 1, /* Generated in non-secure software */
+ KM_ORIGIN_IMPORTED = 2, /* Imported, origin unknown */
+} keymaster_key_origin_t;
+
+/**
+ * Usability requirements of key blobs. This defines what system functionality must be available
+ * for the key to function. For example, key "blobs" which are actually handles referencing
+ * encrypted key material stored in the file system cannot be used until the file system is
+ * available, and should have BLOB_REQUIRES_FILE_SYSTEM. Other requirements entries will be added
+ * as needed for implementations. This type is new in 0_4.
+ */
+typedef enum {
+ KM_BLOB_STANDALONE = 0,
+ KM_BLOB_REQUIRES_FILE_SYSTEM = 1,
+} keymaster_key_blob_usage_requirements_t;
+
+/**
+ * Possible purposes of a key (or pair). This type is new in 0_4.
+ */
+typedef enum {
+ KM_PURPOSE_ENCRYPT = 0,
+ KM_PURPOSE_DECRYPT = 1,
+ KM_PURPOSE_SIGN = 2,
+ KM_PURPOSE_VERIFY = 3,
+} keymaster_purpose_t;
+
+typedef struct {
+ const uint8_t* data;
+ size_t data_length;
+} keymaster_blob_t;
+
+typedef struct {
+ keymaster_tag_t tag;
+ union {
+ uint32_t enumerated; /* KM_ENUM and KM_ENUM_REP */
+ bool boolean; /* KM_BOOL */
+ uint32_t integer; /* KM_INT and KM_INT_REP */
+ uint64_t long_integer; /* KM_LONG */
+ uint64_t date_time; /* KM_DATE */
+ keymaster_blob_t blob; /* KM_BIGNUM and KM_BYTES*/
+ };
+} keymaster_key_param_t;
+
+/**
+ * Parameters that define a key's characteristics, including authorized modes of usage and access
+ * control restrictions. The parameters are divided into two categories, those that are enforced by
+ * secure hardware, and those that are not. For a software-only keymaster implementation the
+ * enforced array must NULL. Hardware implementations must enforce everything in the enforced
+ * array.
+ */
+typedef struct {
+ keymaster_key_param_t* enforced; /* NULL if enforced_length == 0 */
+ size_t enforced_length;
+ keymaster_key_param_t* unenforced; /* NULL if unenforced_length == 0 */
+ size_t unenforced_length;
+} keymaster_key_characteristics_t;
+
+typedef struct {
+ const uint8_t* key_material;
+ size_t key_material_size;
+} keymaster_key_blob_t;
+
+/**
+ * Formats for key import and export. At present, only asymmetric key import/export is supported.
+ * In the future this list will expand greatly to accommodate asymmetric key import/export.
+ */
+typedef enum {
+ KM_KEY_FORMAT_X509, /* for public key export, required */
+ KM_KEY_FORMAT_PKCS8, /* for asymmetric key pair import, required */
+ KM_KEY_FORMAT_PKCS12, /* for asymmetric key pair import, not required */
+} keymaster_key_format_t;
+
+/**
+ * The keymaster operation API consists of begin, update, finish and abort. This is the type of the
+ * handle used to tie the sequence of calls together. A 64-bit value is used because it's important
+ * that handles not be predictable. Implementations must use strong random numbers for handle
+ * values.
+ */
+typedef uint64_t keymaster_operation_handle_t;
+
+typedef enum {
+ KM_ERROR_OK = 0,
+ KM_ERROR_ROOT_OF_TRUST_ALREADY_SET = -1,
+ KM_ERROR_UNSUPPORTED_PURPOSE = -2,
+ KM_ERROR_INCOMPATIBLE_PURPOSE = -3,
+ KM_ERROR_UNSUPPORTED_ALGORITHM = -4,
+ KM_ERROR_INCOMPATIBLE_ALGORITHM = -5,
+ KM_ERROR_UNSUPPORTED_KEY_SIZE = -6,
+ KM_ERROR_UNSUPPORTED_BLOCK_MODE = -7,
+ KM_ERROR_INCOMPATIBLE_BLOCK_MODE = -8,
+ KM_ERROR_UNSUPPORTED_TAG_LENGTH = -9,
+ KM_ERROR_UNSUPPORTED_PADDING_MODE = -10,
+ KM_ERROR_INCOMPATIBLE_PADDING_MODE = -11,
+ KM_ERROR_UNSUPPORTED_DIGEST = -12,
+ KM_ERROR_INCOMPATIBLE_DIGEST = -13,
+ KM_ERROR_INVALID_EXPIRATION_TIME = -14,
+ KM_ERROR_INVALID_USER_ID = -15,
+ KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT = -16,
+ KM_ERROR_UNSUPPORTED_KEY_FORMAT = -17,
+ KM_ERROR_INCOMPATIBLE_KEY_FORMAT = -18,
+ KM_ERROR_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = -19, /* For PKCS8 & PKCS12 */
+ KM_ERROR_UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = -20, /* For PKCS8 & PKCS12 */
+ KM_ERROR_INVALID_INPUT_LENGTH = -21,
+ KM_ERROR_KEY_EXPORT_OPTIONS_INVALID = -22,
+ KM_ERROR_DELEGATION_NOT_ALLOWED = -23,
+ KM_ERROR_KEY_NOT_YET_VALID = -24,
+ KM_ERROR_KEY_EXPIRED = -25,
+ KM_ERROR_KEY_USER_NOT_AUTHENTICATED = -26,
+ KM_ERROR_OUTPUT_PARAMETER_NULL = -27,
+ KM_ERROR_INVALID_OPERATION_HANDLE = -28,
+ KM_ERROR_INSUFFICIENT_BUFFER_SPACE = -29,
+ KM_ERROR_VERIFICATION_FAILED = -30,
+ KM_ERROR_TOO_MANY_OPERATIONS = -31,
+ KM_ERROR_UNEXPECTED_NULL_POINTER = -32,
+ KM_ERROR_INVALID_KEY_BLOB = -33,
+ KM_ERROR_IMPORTED_KEY_NOT_ENCRYPTED = -34,
+ KM_ERROR_IMPORTED_KEY_DECRYPTION_FAILED = -35,
+ KM_ERROR_IMPORTED_KEY_NOT_SIGNED = -36,
+ KM_ERROR_IMPORTED_KEY_VERIFICATION_FAILED = -37,
+ KM_ERROR_INVALID_ARGUMENT = -38,
+ KM_ERROR_UNSUPPORTED_TAG = -39,
+ KM_ERROR_INVALID_TAG = -40,
+ KM_ERROR_MEMORY_ALLOCATION_FAILED = -41,
+ KM_ERROR_INVALID_RESCOPING = -42,
+ KM_ERROR_INVALID_DSA_PARAMS = -43,
+ KM_ERROR_IMPORT_PARAMETER_MISMATCH =-44,
+ KM_ERROR_SECURE_HW_ACCESS_DENIED = -45,
+ KM_ERROR_OPERATION_CANCELLED =-46,
+ KM_ERROR_CONCURRENT_ACCESS_CONFLICT = -47,
+ KM_ERROR_SECURE_HW_BUSY =-48,
+ KM_ERROR_SECURE_HW_COMMUNICATION_FAILED = -49,
+ KM_ERROR_UNSUPPORTED_EC_FIELD = -50,
+ KM_ERROR_UNIMPLEMENTED = -100,
+
+ /* Additional error codes may be added by implementations, but implementers should coordinate
+ * with Google to avoid code collision. */
+ KM_ERROR_UNKNOWN_ERROR = -1000,
+} keymaster_error_t;
+
+/* Convenience functions for manipulating keymaster tag types */
+
+static inline keymaster_tag_type_t keymaster_tag_get_type(keymaster_tag_t tag) {
+ return (keymaster_tag_type_t)(tag & (0xF << 28));
+}
+
+static inline uint32_t keymaster_tag_mask_type(keymaster_tag_t tag) {
+ return tag & 0x0FFFFFFF;
+}
+
+static inline bool keymaster_tag_type_repeatable(keymaster_tag_type_t type) {
+ switch (type) {
+ case KM_INT_REP:
+ case KM_ENUM_REP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool keymaster_tag_repeatable(keymaster_tag_t tag) {
+ return keymaster_tag_type_repeatable(keymaster_tag_get_type(tag));
+}
+
+/* Convenience functions for manipulating keymaster_key_param_t structs */
+
+inline keymaster_key_param_t keymaster_param_enum(keymaster_tag_t tag, uint32_t value) {
+ // assert(keymaster_tag_get_type(tag) == KM_ENUM || keymaster_tag_get_type(tag) == KM_ENUM_REP);
+ keymaster_key_param_t param;
+ memset(¶m, 0, sizeof(param));
+ param.tag = tag;
+ param.enumerated = value;
+ return param;
+}
+
+inline keymaster_key_param_t keymaster_param_int(keymaster_tag_t tag, uint32_t value) {
+ // assert(keymaster_tag_get_type(tag) == KM_INT || keymaster_tag_get_type(tag) == KM_INT_REP);
+ keymaster_key_param_t param;
+ memset(¶m, 0, sizeof(param));
+ param.tag = tag;
+ param.integer = value;
+ return param;
+}
+
+inline keymaster_key_param_t keymaster_param_long(keymaster_tag_t tag, uint64_t value) {
+ // assert(keymaster_tag_get_type(tag) == KM_LONG);
+ keymaster_key_param_t param;
+ memset(¶m, 0, sizeof(param));
+ param.tag = tag;
+ param.long_integer = value;
+ return param;
+}
+
+inline keymaster_key_param_t keymaster_param_blob(keymaster_tag_t tag, const uint8_t* bytes,
+ size_t bytes_len) {
+ // assert(keymaster_tag_get_type(tag) == KM_BYTES || keymaster_tag_get_type(tag) == KM_BIGNUM);
+ keymaster_key_param_t param;
+ memset(¶m, 0, sizeof(param));
+ param.tag = tag;
+ param.blob.data = bytes;
+ param.blob.data_length = bytes_len;
+ return param;
+}
+
+inline keymaster_key_param_t keymaster_param_bool(keymaster_tag_t tag) {
+ // assert(keymaster_tag_get_type(tag) == KM_BOOL);
+ keymaster_key_param_t param;
+ memset(¶m, 0, sizeof(param));
+ param.tag = tag;
+ param.boolean = true;
+ return param;
+}
+
+inline keymaster_key_param_t keymaster_param_date(keymaster_tag_t tag, uint64_t value) {
+ // assert(keymaster_tag_get_type(tag) == KM_DATE);
+ keymaster_key_param_t param;
+ memset(¶m, 0, sizeof(param));
+ param.tag = tag;
+ param.date_time = value;
+ return param;
+}
+
+static inline void keymaster_free_param_values(keymaster_key_param_t* param, size_t param_count) {
+ while (param_count-- > 0) {
+ switch (keymaster_tag_get_type(param->tag)) {
+ case KM_BIGNUM:
+ case KM_BYTES:
+ free((void*)param->blob.data);
+ break;
+ default:
+ // NOP
+ break;
+ }
+ ++param;
+ }
+}
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif // defined(__cplusplus)
+
+#endif // ANDROID_HARDWARE_KEYMASTER_DEFS_H
diff --git a/include/keymaster/keymaster_tags.h b/include/keymaster/keymaster_tags.h
new file mode 100644
index 0000000..7c33849
--- /dev/null
+++ b/include/keymaster/keymaster_tags.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_KEYMASTER_TAGS_H_
+#define SYSTEM_KEYMASTER_KEYMASTER_TAGS_H_
+
+/**
+ * This header contains various definitions that make working with keymaster tags safer and easier.
+ * It makes use of a fair amount of template metaprogramming, which is genarally a bad idea for
+ * maintainability, but in this case all of the metaprogramming serves the purpose of making it
+ * impossible to make certain classes of mistakes when operating on keymaster authorizations. For
+ * example, it's an error to create a keymaster_param_t with tag == KM_TAG_PURPOSE and then to
+ * assign KM_ALGORITHM_RSA to the enumerated element of its union, but because "enumerated" is a
+ * uint32_t, there's no way for the compiler, ordinarily, to diagnose it. Also, generic functions
+ * to manipulate authorizations of multiple types can't be written, because they need to know which
+ * union parameter to modify.
+ *
+ * The machinery in this header solves these problems. The core elements are two templated classes,
+ * TypedTag and TypedEnumTag. These classes are templated on a tag type and a tag value, and in the
+ * case of TypedEnumTag, an enumeration type as well. Specializations are created for each
+ * keymaster tag, associating the tag type with the tag, and an instance of each specialization is
+ * created, and named the same as the keymaster tag, but with the KM_ prefix omitted. Because the
+ * classes include a conversion operator to keymaster_tag_t, they can be used anywhere a
+ * keymaster_tag_t is expected.
+ *
+ * They also define a "value_type" typedef, which specifies the type of values associated with that
+ * particular tag. This enables template functions to be written that check that the correct
+ * parameter type is used for a given tag, and that use the correct union entry for the tag type. A
+ * very useful example is the overloaded "Authorization" function defined below, which takes tag and
+ * value arguments and correctly constructs a keyamster_param_t struct.
+ *
+ * Because the classes have no data members and all of their methods are inline, they have ZERO
+ * run-time cost in and of themselves. The one way in which they can create code bloat is when
+ * template functions using them are expanded multiple times. The standard method of creating
+ * trivial, inlined template functions which call non-templated functions which are compact but not
+ * type-safe, allows the program to have both the type-safety of the templates and the compactness
+ * of the non-templated functions, at the same time.
+ *
+ * For debugging purposes, one additional element of TypedTag and TypedEnumTag can be conditionally
+ * compiled in. If the "KEYMASTER_NAME_TAGS" macro symbol is defined, both classes will have a
+ * name() method which returns a string equal to the tame of the tag (e.g. TAG_PURPOSE). Activating
+ * this option means the classes _do_ contain a data member, a pointer to the string, and also
+ * causes static data space to be allocated for the strings. So the run-time cost of these classes
+ * is no longer zero. Note that it can cause problems if KEYMASTER_NAME_TAGS is defined for some
+ * compilation units and not others.
+ */
+
+#include <keymaster/keymaster_defs.h>
+
+namespace keymaster {
+
+// Until we have C++11, fake std::static_assert.
+template <bool b> struct StaticAssert {};
+template <> struct StaticAssert<true> {
+ static void check() {}
+};
+
+// An unusable type that we can associate with tag types that don't have a simple value type.
+// That will prevent the associated type from being used inadvertently.
+class Void {
+ Void();
+ ~Void();
+};
+
+/**
+ * A template that defines the association between non-enumerated tag types and their value
+ * types. For each tag type we define a specialized struct that contains a typedef "value_type".
+ */
+template <keymaster_tag_type_t tag_type> struct TagValueType {};
+template <> struct TagValueType<KM_LONG> { typedef uint64_t value_type; };
+template <> struct TagValueType<KM_DATE> { typedef uint64_t value_type; };
+template <> struct TagValueType<KM_INT> { typedef uint32_t value_type; };
+template <> struct TagValueType<KM_INT_REP> { typedef uint32_t value_type; };
+template <> struct TagValueType<KM_INVALID> { typedef Void value_type; };
+template <> struct TagValueType<KM_BOOL> { typedef bool value_type; };
+template <> struct TagValueType<KM_BYTES> { typedef keymaster_blob_t value_type; };
+template <> struct TagValueType<KM_BIGNUM> { typedef keymaster_blob_t value_type; };
+
+/**
+ * TypedTag is a templatized version of keymaster_tag_t, which provides compile-time checking of
+ * keymaster tag types. Instances are convertible to keymaster_tag_t, so they can be used wherever
+ * keymaster_tag_t is expected, and because they encode the tag type it's possible to create
+ * function overloadings that only operate on tags with a particular type.
+ */
+template <keymaster_tag_type_t tag_type, keymaster_tag_t tag> class TypedTag {
+ public:
+ typedef typename TagValueType<tag_type>::value_type value_type;
+
+#ifdef KEYMASTER_NAME_TAGS
+ inline TypedTag(const char* name) : name_(name) {
+#else
+ inline TypedTag() {
+#endif
+ // Ensure that it's impossible to create a TypedTag instance whose 'tag' doesn't have type
+ // 'tag_type'. Attempting to instantiate a tag with the wrong type will result in a compile
+ // error (no match for template specialization StaticAssert<false>), with no run-time cost.
+ StaticAssert<(tag & tag_type) == tag_type>::check();
+ StaticAssert<(tag_type != KM_ENUM) && (tag_type != KM_ENUM_REP)>::check();
+ }
+ inline operator keymaster_tag_t() { return tag; }
+#ifdef KEYMASTER_NAME_TAGS
+ const char* name() { return name_; }
+
+ private:
+ const char* name_;
+#endif
+};
+
+template <keymaster_tag_type_t tag_type, keymaster_tag_t tag, typename KeymasterEnum>
+class TypedEnumTag {
+ public:
+ typedef KeymasterEnum value_type;
+
+#ifdef KEYMASTER_NAME_TAGS
+ inline TypedEnumTag(const char* name) : name_(name) {
+#else
+ inline TypedEnumTag() {
+#endif
+ // Ensure that it's impossible to create a TypedTag instance whose 'tag' doesn't have type
+ // 'tag_type'. Attempting to instantiate a tag with the wrong type will result in a compile
+ // error (no match for template specialization StaticAssert<false>), with no run-time cost.
+ StaticAssert<(tag & tag_type) == tag_type>::check();
+ StaticAssert<(tag_type == KM_ENUM) || (tag_type == KM_ENUM_REP)>::check();
+ }
+ inline operator keymaster_tag_t() { return tag; }
+#ifdef KEYMASTER_NAME_TAGS
+ const char* name() { return name_; }
+
+ private:
+ const char* name_;
+#endif
+};
+
+// DEFINE_KEYMASTER_TAG is used to create TypedTag instances for each non-enum keymaster tag.
+#ifdef KEYMASTER_NAME_TAGS
+#define DEFINE_KEYMASTER_TAG(type, name) static TypedTag<type, KM_##name> name(#name)
+#else
+#define DEFINE_KEYMASTER_TAG(type, name) static TypedTag<type, KM_##name> name
+#endif
+
+DEFINE_KEYMASTER_TAG(KM_INVALID, TAG_INVALID);
+DEFINE_KEYMASTER_TAG(KM_INT, TAG_KEY_SIZE);
+DEFINE_KEYMASTER_TAG(KM_INT, TAG_MAC_LENGTH);
+DEFINE_KEYMASTER_TAG(KM_INT, TAG_CHUNK_LENGTH);
+DEFINE_KEYMASTER_TAG(KM_LONG, TAG_RSA_PUBLIC_EXPONENT);
+DEFINE_KEYMASTER_TAG(KM_BIGNUM, TAG_DSA_GENERATOR);
+DEFINE_KEYMASTER_TAG(KM_BIGNUM, TAG_DSA_P);
+DEFINE_KEYMASTER_TAG(KM_BIGNUM, TAG_DSA_Q);
+DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ACTIVE_DATETIME);
+DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ORIGINATION_EXPIRE_DATETIME);
+DEFINE_KEYMASTER_TAG(KM_DATE, TAG_USAGE_EXPIRE_DATETIME);
+DEFINE_KEYMASTER_TAG(KM_INT, TAG_MIN_SECONDS_BETWEEN_OPS);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_SINGLE_USE_PER_BOOT);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ALL_USERS);
+DEFINE_KEYMASTER_TAG(KM_INT, TAG_USER_ID);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_NO_AUTH_REQUIRED);
+DEFINE_KEYMASTER_TAG(KM_INT_REP, TAG_USER_AUTH_ID);
+DEFINE_KEYMASTER_TAG(KM_INT, TAG_AUTH_TIMEOUT);
+DEFINE_KEYMASTER_TAG(KM_INT, TAG_RESCOPE_AUTH_TIMEOUT);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ALL_APPLICATIONS);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_APPLICATION_ID);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_APPLICATION_DATA);
+DEFINE_KEYMASTER_TAG(KM_DATE, TAG_CREATION_DATETIME);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ROLLBACK_RESISTANT);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_ROOT_OF_TRUST);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_ADDITIONAL_DATA);
+
+#ifdef KEYMASTER_NAME_TAGS
+#define DEFINE_KEYMASTER_ENUM_TAG(type, name, enumtype) \
+ static TypedEnumTag<type, KM_##name, enumtype> name(#name)
+#else
+#define DEFINE_KEYMASTER_ENUM_TAG(type, name, enumtype) \
+ static TypedEnumTag<type, KM_##name, enumtype> name
+#endif
+
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_PURPOSE, keymaster_purpose_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_ALGORITHM, keymaster_algorithm_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_BLOCK_MODE, keymaster_block_mode_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_DIGEST, keymaster_digest_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_PADDING, keymaster_padding_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_RESCOPING_ADD, keymaster_tag_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_RESCOPING_DEL, keymaster_tag_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_BLOB_USAGE_REQUIREMENTS,
+ keymaster_key_blob_usage_requirements_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_ORIGIN, keymaster_key_origin_t);
+
+//
+// Overloaded function "Authorization" to create keymaster_key_param_t objects for all of tags.
+//
+
+template <keymaster_tag_t Tag>
+inline keymaster_key_param_t Authorization(TypedTag<KM_BOOL, Tag> tag) {
+ return keymaster_param_bool(tag);
+}
+
+template <keymaster_tag_t Tag>
+inline keymaster_key_param_t Authorization(TypedTag<KM_INT, Tag> tag, uint32_t value) {
+ return keymaster_param_int(tag, value);
+}
+
+template <keymaster_tag_t Tag>
+inline keymaster_key_param_t Authorization(TypedTag<KM_INT_REP, Tag> tag, uint32_t value) {
+ return keymaster_param_int(tag, value);
+}
+
+template <keymaster_tag_t Tag>
+inline keymaster_key_param_t Authorization(TypedTag<KM_LONG, Tag> tag, uint64_t value) {
+ return keymaster_param_long(tag, value);
+}
+
+template <keymaster_tag_t Tag>
+inline keymaster_key_param_t Authorization(TypedTag<KM_DATE, Tag> tag, uint64_t value) {
+ return keymaster_param_date(tag, value);
+}
+
+template <keymaster_tag_t Tag>
+inline keymaster_key_param_t Authorization(TypedTag<KM_BYTES, Tag> tag, const void* bytes,
+ size_t bytes_len) {
+ return keymaster_param_blob(tag, reinterpret_cast<const uint8_t*>(bytes), bytes_len);
+}
+
+template <keymaster_tag_t Tag>
+inline keymaster_key_param_t Authorization(TypedTag<KM_BYTES, Tag> tag,
+ const keymaster_blob_t& blob) {
+ return keymaster_param_blob(tag, blob.data, blob.data_length);
+}
+
+template <keymaster_tag_t Tag>
+inline keymaster_key_param_t Authorization(TypedTag<KM_BIGNUM, Tag> tag, const void* bytes,
+ size_t bytes_len) {
+ return keymaster_param_blob(tag, reinterpret_cast<const uint8_t*>(bytes), bytes_len);
+}
+
+template <keymaster_tag_t Tag>
+inline keymaster_key_param_t Authorization(TypedTag<KM_BIGNUM, Tag> tag,
+ const keymaster_blob_t& blob) {
+ return keymaster_param_blob(tag, blob.data, blob.data_length);
+}
+
+template <keymaster_tag_t Tag, typename KeymasterEnum>
+inline keymaster_key_param_t Authorization(TypedEnumTag<KM_ENUM, Tag, KeymasterEnum> tag,
+ KeymasterEnum value) {
+ return keymaster_param_enum(tag, value);
+}
+
+template <keymaster_tag_t Tag, typename KeymasterEnum>
+inline keymaster_key_param_t Authorization(TypedEnumTag<KM_ENUM_REP, Tag, KeymasterEnum> tag,
+ KeymasterEnum value) {
+ return keymaster_param_enum(tag, value);
+}
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_KEYMASTER_TAGS_H_
diff --git a/include/keymaster/logger.h b/include/keymaster/logger.h
new file mode 100644
index 0000000..7dde77f
--- /dev/null
+++ b/include/keymaster/logger.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_LOGGER_H_
+#define SYSTEM_KEYMASTER_LOGGER_H_
+
+namespace keymaster {
+
+class Logger {
+ public:
+ Logger() {}
+ virtual ~Logger() {}
+ virtual int log(const char* fmt, ...) const = 0;
+ virtual int info(const char* fmt, ...) const = 0;
+ virtual int error(const char* fmt, ...) const = 0;
+ virtual int severe(const char* fmt, ...) const = 0;
+
+ private:
+ // Disallow copying.
+ Logger(const Logger&);
+ void operator=(const Logger&);
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_LOGGER_H_
diff --git a/include/keymaster/serializable.h b/include/keymaster/serializable.h
new file mode 100644
index 0000000..b183367
--- /dev/null
+++ b/include/keymaster/serializable.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_SERIALIZABLE_H_
+#define SYSTEM_KEYMASTER_SERIALIZABLE_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cstddef>
+
+#include <UniquePtr.h>
+
+namespace keymaster {
+
+class Serializable {
+ public:
+ Serializable() {}
+ virtual ~Serializable() {}
+
+ /**
+ * Return the size of the serialized representation of this object.
+ */
+ virtual size_t SerializedSize() const = 0;
+
+ /**
+ * Serialize this object into the provided buffer. Returns a pointer to the byte after the last
+ * written. Will not write past \p end, which should point to \p buf + size of the buffer
+ * (i.e. one past the end of the buffer).
+ */
+ virtual uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const = 0;
+
+ /**
+ * Deserialize from the provided buffer, copying the data into newly-allocated storage. Returns
+ * true if successful, and advances *buf past the bytes read.
+ */
+ virtual bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) = 0;
+
+ private:
+ // Disallow copying and assignment.
+ Serializable(const Serializable&);
+ void operator=(const Serializable&);
+};
+
+/*
+ * Utility functions for writing Serialize() methods
+ */
+
+/**
+ * Append a byte array to a buffer. Note that by itself this function isn't very useful, because it
+ * provides no indication in the serialized buffer of what the array size is. For writing arrays,
+ * see \p append_size_and_data_to_buf().
+ *
+ * Returns a pointer to the first byte after the data written.
+ */
+uint8_t* append_to_buf(uint8_t* buf, const uint8_t* end, const void* data, size_t data_len);
+
+/**
+ * Append some type of value convertible to a uint32_t to a buffer. This is primarily used for
+ * writing enumerated values, and uint32_ts.
+ *
+ * Returns a pointer to the first byte after the data written.
+ */
+template <typename T>
+inline uint8_t* append_uint32_to_buf(uint8_t* buf, const uint8_t* end, T value) {
+ uint32_t val = static_cast<uint32_t>(value);
+ return append_to_buf(buf, end, &val, sizeof(val));
+}
+
+/**
+ * Append a uint64_t to a buffer. Returns a pointer to the first byte after the data written.
+ */
+inline uint8_t* append_uint64_to_buf(uint8_t* buf, const uint8_t* end, uint64_t value) {
+ return append_to_buf(buf, end, &value, sizeof(value));
+}
+
+/**
+ * Appends a byte array to a buffer, prefixing it with a 32-bit size field. Returns a pointer to
+ * the first byte after the data written.
+ *
+ * See copy_size_and_data_from_buf().
+ */
+inline uint8_t* append_size_and_data_to_buf(uint8_t* buf, const uint8_t* end, const void* data,
+ size_t data_len) {
+ buf = append_uint32_to_buf(buf, end, data_len);
+ return append_to_buf(buf, end, data, data_len);
+}
+
+/**
+ * Appends an array of values that are convertible to uint32_t as uint32ts to a buffer, prefixing a
+ * count so deserialization knows how many values to read.
+ *
+ * See copy_uint32_array_from_buf().
+ */
+template <typename T>
+inline uint8_t* append_uint32_array_to_buf(uint8_t* buf, const uint8_t* end, const T* data,
+ size_t count) {
+ buf = append_uint32_to_buf(buf, end, count);
+ for (size_t i = 0; i < count; ++i)
+ buf = append_uint32_to_buf(buf, end, static_cast<uint32_t>(data[i]));
+ return buf;
+}
+
+/*
+ * Utility functions for writing Deserialize() methods.
+ */
+
+/**
+ * Copy \p size bytes from \p *buf_ptr into \p dest. If there are fewer than \p size bytes to read,
+ * returns false. Advances *buf_ptr to the next byte to be read.
+ */
+bool copy_from_buf(const uint8_t** buf_ptr, const uint8_t* end, void* dest, size_t size);
+
+/**
+ * Extracts a uint32_t size from *buf_ptr, placing it in \p *size, and then reads *size bytes from
+ * *buf_ptr, placing them in newly-allocated storage in *dest. If there aren't enough bytes in
+ * *buf_ptr, returns false. Advances \p *buf_ptr to the next byte to be read.
+ *
+ * See \p append_size_and_data_to_buf().
+ */
+bool copy_size_and_data_from_buf(const uint8_t** buf_ptr, const uint8_t* end, size_t* size,
+ UniquePtr<uint8_t[]>* dest);
+
+/**
+ * Copies a value convertible from uint32_t from \p *buf_ptr. Returns false if there are less than
+ * four bytes remaining in \p *buf_ptr. Advances \p *buf_ptr to the next byte to be read.
+ */
+template <typename T>
+inline bool copy_uint32_from_buf(const uint8_t** buf_ptr, const uint8_t* end, T* value) {
+ uint32_t val;
+ if (!copy_from_buf(buf_ptr, end, &val, sizeof(val)))
+ return false;
+ *value = static_cast<T>(val);
+ return true;
+}
+
+/**
+ * Copies a uint64_t from \p *buf_ptr. Returns false if there are less than eight bytes remaining
+ * in \p *buf_ptr. Advances \p *buf_ptr to the next byte to be read.
+ */
+inline bool copy_uint64_from_buf(const uint8_t** buf_ptr, const uint8_t* end, uint64_t* value) {
+ return copy_from_buf(buf_ptr, end, value, sizeof(*value));
+}
+
+/**
+ * Copies an array of values convertible to uint32_t from \p *buf_ptr, first reading a count of
+ * values to read. The count is returned in \p *count and the values returned in newly-allocated
+ * storage at *data. Returns false if there are insufficient bytes at \p *buf_ptr. Advances \p
+ * *buf_ptr to the next byte to be read.
+ */
+template <typename T>
+inline bool copy_uint32_array_from_buf(const uint8_t** buf_ptr, const uint8_t* end,
+ UniquePtr<T[]>* data, size_t* count) {
+ if (!copy_uint32_from_buf(buf_ptr, end, count) || *buf_ptr + *count * sizeof(uint32_t) > end)
+ return false;
+ data->reset(new T[*count]);
+ for (size_t i = 0; i < *count; ++i)
+ if (!copy_uint32_from_buf(buf_ptr, end, &(*data)[i]))
+ return false;
+ return true;
+}
+
+/**
+ * A simple buffer that supports reading and writing. Manages its own memory.
+ */
+class Buffer : public Serializable {
+ public:
+ Buffer() : buffer_(NULL), buffer_size_(0), read_position_(0), write_position_(0) {}
+ Buffer(size_t size) : buffer_(NULL) { Reinitialize(size); }
+ Buffer(const void* buf, size_t size) : buffer_(NULL) { Reinitialize(buf, size); }
+
+ // Grow the buffer so that at least \p size bytes can be written.
+ bool reserve(size_t size);
+
+ bool Reinitialize(size_t size);
+ bool Reinitialize(const void* buf, size_t size);
+
+ // Reinitialize with a copy of the provided buffer's readable data.
+ bool Reinitialize(const Buffer& buffer) {
+ return Reinitialize(buffer.peek_read(), buffer.available_read());
+ }
+
+ void Clear();
+
+ size_t available_write() const;
+ size_t available_read() const;
+ size_t buffer_size() const { return buffer_size_; }
+
+ bool write(const uint8_t* src, size_t write_length);
+ bool read(uint8_t* dest, size_t read_length);
+ const uint8_t* peek_read() const { return buffer_.get() + read_position_; }
+ void advance_read(int distance) { read_position_ += distance; }
+ uint8_t* peek_write() { return buffer_.get() + write_position_; }
+ void advance_write(int distance) { write_position_ += distance; }
+
+ size_t SerializedSize() const;
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const;
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end);
+
+ private:
+ // Disallow copy construction and assignment.
+ void operator=(const Buffer& other);
+ Buffer(const Buffer&);
+
+ UniquePtr<uint8_t[]> buffer_;
+ size_t buffer_size_;
+ int read_position_;
+ int write_position_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_SERIALIZABLE_H_
diff --git a/key.cpp b/key.cpp
new file mode 100644
index 0000000..74cb508
--- /dev/null
+++ b/key.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <openssl/x509.h>
+
+#include <keymaster/key_blob.h>
+
+#include "asymmetric_key.h"
+#include "openssl_utils.h"
+
+#include "key.h"
+
+namespace keymaster {
+
+struct PKCS8_PRIV_KEY_INFO_Delete {
+ void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
+};
+
+Key::Key(const KeyBlob& blob, const Logger& logger) : logger_(logger) {
+ authorizations_.push_back(blob.unenforced());
+ authorizations_.push_back(blob.enforced());
+}
+
+/* static */
+Key* Key::CreateKey(const KeyBlob& blob, const Logger& logger, keymaster_error_t* error) {
+ switch (blob.algorithm()) {
+ case KM_ALGORITHM_RSA:
+ return new RsaKey(blob, logger, error);
+ case KM_ALGORITHM_DSA:
+ return new DsaKey(blob, logger, error);
+ case KM_ALGORITHM_ECDSA:
+ return new EcdsaKey(blob, logger, error);
+ default:
+ *error = KM_ERROR_UNSUPPORTED_ALGORITHM;
+ return NULL;
+ }
+}
+
+/* static */
+Key* Key::GenerateKey(const AuthorizationSet& key_description, const Logger& logger,
+ keymaster_error_t* error) {
+ keymaster_algorithm_t algorithm;
+ if (!key_description.GetTagValue(TAG_ALGORITHM, &algorithm)) {
+ *error = KM_ERROR_UNSUPPORTED_ALGORITHM;
+ return NULL;
+ }
+
+ switch (algorithm) {
+ case KM_ALGORITHM_RSA:
+ return RsaKey::GenerateKey(key_description, logger, error);
+ case KM_ALGORITHM_DSA:
+ return DsaKey::GenerateKey(key_description, logger, error);
+ case KM_ALGORITHM_ECDSA:
+ return EcdsaKey::GenerateKey(key_description, logger, error);
+ default:
+ *error = KM_ERROR_UNSUPPORTED_ALGORITHM;
+ return NULL;
+ }
+}
+
+/* static */
+Key* Key::ImportKey(const AuthorizationSet& key_description, keymaster_key_format_t key_format,
+ const uint8_t* key_data, size_t key_data_length, const Logger& logger,
+ keymaster_error_t* error) {
+ *error = KM_ERROR_OK;
+
+ if (key_data == NULL || key_data_length <= 0) {
+ *error = KM_ERROR_INVALID_KEY_BLOB;
+ return NULL;
+ }
+
+ if (key_format != KM_KEY_FORMAT_PKCS8) {
+ *error = KM_ERROR_UNSUPPORTED_KEY_FORMAT;
+ return NULL;
+ }
+
+ UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> pkcs8(
+ d2i_PKCS8_PRIV_KEY_INFO(NULL, &key_data, key_data_length));
+ if (pkcs8.get() == NULL) {
+ *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return NULL;
+ }
+
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKCS82PKEY(pkcs8.get()));
+ if (pkey.get() == NULL) {
+ *error = KM_ERROR_INVALID_KEY_BLOB;
+ return NULL;
+ }
+
+ UniquePtr<Key> key;
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_RSA:
+ return RsaKey::ImportKey(key_description, pkey.get(), logger, error);
+ case EVP_PKEY_DSA:
+ return DsaKey::ImportKey(key_description, pkey.get(), logger, error);
+ case EVP_PKEY_EC:
+ return EcdsaKey::ImportKey(key_description, pkey.get(), logger, error);
+ default:
+ *error = KM_ERROR_UNSUPPORTED_ALGORITHM;
+ return NULL;
+ }
+
+ *error = KM_ERROR_UNIMPLEMENTED;
+ return NULL;
+}
+
+} // namespace keymaster
diff --git a/key.h b/key.h
new file mode 100644
index 0000000..5fd824f
--- /dev/null
+++ b/key.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_KEY_H_
+#define SYSTEM_KEYMASTER_KEY_H_
+
+#include <keymaster/authorization_set.h>
+#include <keymaster/keymaster_defs.h>
+#include <keymaster/logger.h>
+
+namespace keymaster {
+
+class KeyBlob;
+class Operation;
+
+class Key {
+ public:
+ static Key* CreateKey(const KeyBlob& blob, const Logger& logger, keymaster_error_t* error);
+ static Key* GenerateKey(const AuthorizationSet& key_description, const Logger& logger,
+ keymaster_error_t* error);
+ static Key* ImportKey(const AuthorizationSet& key_description,
+ keymaster_key_format_t key_format, const uint8_t* key_data,
+ size_t key_data_length, const Logger& logger, keymaster_error_t* error);
+
+ virtual ~Key() {}
+ virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error) = 0;
+
+ /**
+ * Return a copy of raw key material, in the key's preferred binary format.
+ */
+ virtual keymaster_error_t key_material(UniquePtr<uint8_t[]>*, size_t* size) const = 0;
+
+ /**
+ * Return a copy of raw key material, in the specified format.
+ */
+ virtual keymaster_error_t formatted_key_material(keymaster_key_format_t format,
+ UniquePtr<uint8_t[]>* material,
+ size_t* size) const = 0;
+
+ const AuthorizationSet& authorizations() const { return authorizations_; }
+
+ protected:
+ Key(const KeyBlob& blob, const Logger& logger);
+ Key(const AuthorizationSet& authorizations, const Logger& logger)
+ : logger_(logger), authorizations_(authorizations) {}
+
+ const Logger& logger_;
+
+ private:
+ AuthorizationSet authorizations_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_KEY_H_
diff --git a/key_blob.cpp b/key_blob.cpp
new file mode 100644
index 0000000..4eb99f3
--- /dev/null
+++ b/key_blob.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <assert.h>
+
+#include <openssl/aes.h>
+#include <openssl/sha.h>
+
+#include <keymaster/google_keymaster_utils.h>
+#include <keymaster/key_blob.h>
+
+#include "ae.h"
+
+namespace keymaster {
+
+class KeyBlob::AeCtx {
+ public:
+ AeCtx() : ctx_(ae_allocate(NULL)) {}
+ ~AeCtx() {
+ ae_clear(ctx_);
+ ae_free(ctx_);
+ }
+
+ ae_ctx* get() { return ctx_; }
+
+ private:
+ ae_ctx* ctx_;
+};
+
+const size_t KeyBlob::NONCE_LENGTH;
+const size_t KeyBlob::TAG_LENGTH;
+
+KeyBlob::KeyBlob(const AuthorizationSet& enforced, const AuthorizationSet& unenforced,
+ const AuthorizationSet& hidden, const keymaster_key_blob_t& key,
+ const keymaster_key_blob_t& master_key, const uint8_t nonce[NONCE_LENGTH])
+ : error_(KM_ERROR_OK), nonce_(new uint8_t[NONCE_LENGTH]), tag_(new uint8_t[TAG_LENGTH]),
+ enforced_(enforced), unenforced_(unenforced), hidden_(hidden) {
+ if (!nonce_.get() || !tag_.get()) {
+ error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return;
+ }
+ error_ = KM_ERROR_OK;
+
+ if (enforced_.is_valid() == AuthorizationSet::ALLOCATION_FAILURE ||
+ unenforced_.is_valid() == AuthorizationSet::ALLOCATION_FAILURE ||
+ hidden_.is_valid() == AuthorizationSet::ALLOCATION_FAILURE) {
+ error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return;
+ }
+
+ if (enforced_.is_valid() != AuthorizationSet::OK ||
+ unenforced_.is_valid() != AuthorizationSet::OK ||
+ hidden_.is_valid() != AuthorizationSet::OK) {
+ error_ = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+
+ if (!ExtractKeyCharacteristics())
+ return;
+
+ key_material_length_ = key.key_material_size;
+ key_material_.reset(new uint8_t[key_material_length_]);
+ encrypted_key_material_.reset(new uint8_t[key_material_length_]);
+
+ if (!key_material_.get() || !encrypted_key_material_.get() || !nonce_.get() || !tag_.get()) {
+ error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return;
+ }
+
+ memcpy(nonce_.get(), nonce, NONCE_LENGTH);
+ memcpy(key_material_.get(), key.key_material, key_material_length_);
+ EncryptKey(master_key);
+}
+
+KeyBlob::KeyBlob(const keymaster_key_blob_t& key, const AuthorizationSet& hidden,
+ const keymaster_key_blob_t& master_key)
+ : nonce_(new uint8_t[NONCE_LENGTH]), tag_(new uint8_t[TAG_LENGTH]), hidden_(hidden) {
+ if (!nonce_.get() || !tag_.get()) {
+ error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return;
+ }
+ error_ = KM_ERROR_OK;
+
+ const uint8_t* p = key.key_material;
+ if (!Deserialize(&p, key.key_material + key.key_material_size))
+ return;
+ DecryptKey(master_key);
+}
+
+KeyBlob::KeyBlob(const uint8_t* key_blob, size_t blob_size)
+ : nonce_(new uint8_t[NONCE_LENGTH]), tag_(new uint8_t[TAG_LENGTH]) {
+ if (!nonce_.get() || !tag_.get()) {
+ error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return;
+ }
+ error_ = KM_ERROR_OK;
+
+ if (!Deserialize(&key_blob, key_blob + blob_size))
+ return;
+}
+
+size_t KeyBlob::SerializedSize() const {
+ return NONCE_LENGTH + sizeof(uint32_t) + key_material_length() + TAG_LENGTH +
+ enforced_.SerializedSize() + unenforced_.SerializedSize();
+}
+
+uint8_t* KeyBlob::Serialize(uint8_t* buf, const uint8_t* end) const {
+ const uint8_t* start = buf;
+ buf = append_to_buf(buf, end, nonce(), NONCE_LENGTH);
+ buf = append_size_and_data_to_buf(buf, end, encrypted_key_material(), key_material_length());
+ buf = append_to_buf(buf, end, tag(), TAG_LENGTH);
+ buf = enforced_.Serialize(buf, end);
+ buf = unenforced_.Serialize(buf, end);
+ assert(buf - start == static_cast<ptrdiff_t>(SerializedSize()));
+ return buf;
+}
+
+bool KeyBlob::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ UniquePtr<uint8_t[]> tmp_key_ptr;
+ if (!copy_from_buf(buf_ptr, end, nonce_.get(), NONCE_LENGTH) ||
+ !copy_size_and_data_from_buf(buf_ptr, end, &key_material_length_, &tmp_key_ptr) ||
+ !copy_from_buf(buf_ptr, end, tag_.get(), TAG_LENGTH) ||
+ !enforced_.Deserialize(buf_ptr, end) || !unenforced_.Deserialize(buf_ptr, end)) {
+ error_ = KM_ERROR_INVALID_KEY_BLOB;
+ return false;
+ }
+
+ if (!ExtractKeyCharacteristics())
+ return false;
+
+ encrypted_key_material_.reset(tmp_key_ptr.release());
+ key_material_.reset(new uint8_t[key_material_length_]);
+ return true;
+}
+
+void KeyBlob::EncryptKey(const keymaster_key_blob_t& master_key) {
+ UniquePtr<AeCtx> ctx(InitializeKeyWrappingContext(master_key, &error_));
+ if (error_ != KM_ERROR_OK)
+ return;
+
+ int ae_err = ae_encrypt(ctx->get(), nonce_.get(), key_material(), key_material_length(),
+ NULL /* additional data */, 0 /* additional data length */,
+ encrypted_key_material_.get(), tag_.get(), 1 /* final */);
+ if (ae_err < 0) {
+ error_ = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ assert(ae_err == static_cast<int>(key_material_length_));
+ error_ = KM_ERROR_OK;
+}
+
+void KeyBlob::DecryptKey(const keymaster_key_blob_t& master_key) {
+ UniquePtr<AeCtx> ctx(InitializeKeyWrappingContext(master_key, &error_));
+ if (error_ != KM_ERROR_OK)
+ return;
+
+ int ae_err =
+ ae_decrypt(ctx->get(), nonce_.get(), encrypted_key_material(), key_material_length(),
+ NULL /* additional data */, 0 /* additional data length */, key_material_.get(),
+ tag_.get(), 1 /* final */);
+ if (ae_err == AE_INVALID) {
+ // Authentication failed! Decryption probably succeeded(ish), but we don't want to return
+ // any data when the authentication fails, so clear it.
+ memset_s(key_material_.get(), 0, key_material_length());
+ error_ = KM_ERROR_INVALID_KEY_BLOB;
+ return;
+ } else if (ae_err < 0) {
+ error_ = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ assert(ae_err == static_cast<int>(key_material_length()));
+ error_ = KM_ERROR_OK;
+}
+
+KeyBlob::AeCtx* KeyBlob::InitializeKeyWrappingContext(const keymaster_key_blob_t& master_key,
+ keymaster_error_t* error) const {
+ size_t derivation_data_length;
+ UniquePtr<const uint8_t[]> derivation_data(BuildDerivationData(&derivation_data_length));
+ if (derivation_data.get() == NULL) {
+ *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return NULL;
+ }
+
+ *error = KM_ERROR_OK;
+ UniquePtr<AeCtx> ctx(new AeCtx);
+
+ SHA256_CTX sha256_ctx;
+ UniquePtr<uint8_t[]> hash_buf(new uint8_t[SHA256_DIGEST_LENGTH]);
+ Eraser hash_eraser(hash_buf.get(), SHA256_DIGEST_LENGTH);
+ UniquePtr<uint8_t[]> derived_key(new uint8_t[AES_BLOCK_SIZE]);
+ Eraser derived_key_eraser(derived_key.get(), AES_BLOCK_SIZE);
+
+ if (ctx.get() == NULL || hash_buf.get() == NULL || derived_key.get() == NULL) {
+ *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return NULL;
+ }
+
+ Eraser sha256_ctx_eraser(sha256_ctx);
+
+ // Hash derivation data.
+ SHA256_Init(&sha256_ctx);
+ SHA256_Update(&sha256_ctx, derivation_data.get(), derivation_data_length);
+ SHA256_Final(hash_buf.get(), &sha256_ctx);
+
+ // Encrypt hash with master key to build derived key.
+ AES_KEY aes_key;
+ Eraser aes_key_eraser(AES_KEY);
+ if (AES_set_encrypt_key(master_key.key_material, master_key.key_material_size * 8, &aes_key) !=
+ 0) {
+ *error = KM_ERROR_UNKNOWN_ERROR;
+ return NULL;
+ }
+ AES_encrypt(hash_buf.get(), derived_key.get(), &aes_key);
+
+ // Set up AES OCB context using derived key.
+ if (ae_init(ctx->get(), derived_key.get(), AES_BLOCK_SIZE, NONCE_LENGTH, TAG_LENGTH) ==
+ AE_SUCCESS)
+ return ctx.release();
+ else {
+ memset_s(ctx.get(), 0, ae_ctx_sizeof());
+ return NULL;
+ }
+}
+
+const uint8_t* KeyBlob::BuildDerivationData(size_t* derivation_data_length) const {
+ *derivation_data_length =
+ hidden_.SerializedSize() + enforced_.SerializedSize() + unenforced_.SerializedSize();
+ uint8_t* derivation_data = new uint8_t[*derivation_data_length];
+ if (derivation_data != NULL) {
+ uint8_t* buf = derivation_data;
+ uint8_t* end = derivation_data + *derivation_data_length;
+ buf = hidden_.Serialize(buf, end);
+ buf = enforced_.Serialize(buf, end);
+ buf = unenforced_.Serialize(buf, end);
+ }
+ return derivation_data;
+}
+
+bool KeyBlob::ExtractKeyCharacteristics() {
+ if (!enforced_.GetTagValue(TAG_ALGORITHM, &algorithm_) &&
+ !unenforced_.GetTagValue(TAG_ALGORITHM, &algorithm_)) {
+ error_ = KM_ERROR_UNSUPPORTED_ALGORITHM;
+ return false;
+ }
+ if (!enforced_.GetTagValue(TAG_KEY_SIZE, &key_size_bits_) &&
+ !unenforced_.GetTagValue(TAG_KEY_SIZE, &key_size_bits_)) {
+ error_ = KM_ERROR_UNSUPPORTED_KEY_SIZE;
+ return false;
+ }
+ return true;
+}
+
+} // namespace keymaster
diff --git a/key_blob_test.cpp b/key_blob_test.cpp
new file mode 100644
index 0000000..275da8b
--- /dev/null
+++ b/key_blob_test.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <algorithm>
+
+#include <gtest/gtest.h>
+
+#include <openssl/engine.h>
+
+#include <keymaster/authorization_set.h>
+#include <keymaster/google_keymaster_utils.h>
+#include <keymaster/keymaster_tags.h>
+
+#include <keymaster/key_blob.h>
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
+ CRYPTO_cleanup_all_ex_data();
+ ERR_free_strings();
+ return result;
+}
+
+namespace keymaster {
+
+namespace test {
+
+const uint8_t master_key_data[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+const uint8_t key_data[5] = {21, 22, 23, 24, 25};
+const uint8_t nonce[KeyBlob::NONCE_LENGTH]{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+
+class KeyBlobTest : public testing::Test {
+ protected:
+ KeyBlobTest() {
+ key_.key_material = const_cast<uint8_t*>(key_data);
+ key_.key_material_size = array_size(key_data);
+ master_key_.key_material = const_cast<uint8_t*>(master_key_data);
+ master_key_.key_material_size = array_size(master_key_data);
+
+ enforced_.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
+ enforced_.push_back(TAG_KEY_SIZE, 256);
+ enforced_.push_back(TAG_BLOB_USAGE_REQUIREMENTS, KM_BLOB_STANDALONE);
+ enforced_.push_back(TAG_MIN_SECONDS_BETWEEN_OPS, 10);
+ enforced_.push_back(TAG_ALL_USERS);
+ enforced_.push_back(TAG_NO_AUTH_REQUIRED);
+ enforced_.push_back(TAG_ORIGIN, KM_ORIGIN_HARDWARE);
+
+ unenforced_.push_back(TAG_ACTIVE_DATETIME, 10);
+ unenforced_.push_back(TAG_ORIGINATION_EXPIRE_DATETIME, 100);
+ unenforced_.push_back(TAG_CREATION_DATETIME, 10);
+ unenforced_.push_back(TAG_CHUNK_LENGTH, 10);
+
+ hidden_.push_back(TAG_ROOT_OF_TRUST, "foo", 3);
+ hidden_.push_back(TAG_APPLICATION_ID, "my_app", 6);
+
+ blob_.reset(new KeyBlob(enforced_, unenforced_, hidden_, key_, master_key_, nonce));
+ }
+
+ AuthorizationSet enforced_;
+ AuthorizationSet unenforced_;
+ AuthorizationSet hidden_;
+
+ UniquePtr<KeyBlob> blob_;
+
+ keymaster_key_blob_t key_;
+ keymaster_key_blob_t master_key_;
+};
+
+TEST_F(KeyBlobTest, EncryptDecrypt) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ // key_data shouldn't be anywhere in the blob.
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+ EXPECT_EQ(end, std::search(begin, end, key_data, key_data + array_size(key_data)));
+
+ // Recover the key material.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, hidden_, master_key_);
+ EXPECT_EQ(KM_ERROR_OK, deserialized.error());
+ EXPECT_EQ(0, memcmp(deserialized.key_material(), key_data, array_size(key_data)));
+}
+
+TEST_F(KeyBlobTest, WrongKeyLength) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ // Modify the key length
+ serialized_blob[KeyBlob::NONCE_LENGTH]++;
+
+ // Decrypting with wrong nonce should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, hidden_, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+}
+
+TEST_F(KeyBlobTest, WrongNonce) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ // Find the nonce, then modify it.
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+ auto nonce_ptr = std::search(begin, end, nonce, nonce + array_size(nonce));
+ ASSERT_NE(nonce_ptr, end);
+ EXPECT_EQ(end, std::search(nonce_ptr + 1, end, nonce, nonce + array_size(nonce)));
+ (*nonce_ptr)++;
+
+ // Decrypting with wrong nonce should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, hidden_, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+ EXPECT_NE(0, memcmp(deserialized.key_material(), key_data, array_size(key_data)));
+}
+
+TEST_F(KeyBlobTest, WrongTag) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ // Find the tag, them modify it.
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+ auto tag_ptr = std::search(begin, end, blob_->tag(), blob_->tag() + KeyBlob::TAG_LENGTH);
+ ASSERT_NE(tag_ptr, end);
+ EXPECT_EQ(end, std::search(tag_ptr + 1, end, blob_->tag(), blob_->tag() + KeyBlob::TAG_LENGTH));
+ (*tag_ptr)++;
+
+ // Decrypting with wrong tag should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, hidden_, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+ EXPECT_NE(0, memcmp(deserialized.key_material(), key_data, array_size(key_data)));
+}
+
+TEST_F(KeyBlobTest, WrongCiphertext) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ // Find the ciphertext, them modify it.
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+ auto ciphertext_ptr =
+ std::search(begin, end, blob_->encrypted_key_material(),
+ blob_->encrypted_key_material() + blob_->key_material_length());
+ ASSERT_NE(ciphertext_ptr, end);
+ EXPECT_EQ(end, std::search(ciphertext_ptr + 1, end, blob_->encrypted_key_material(),
+ blob_->encrypted_key_material() + blob_->key_material_length()));
+ (*ciphertext_ptr)++;
+
+ // Decrypting with wrong tag should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, hidden_, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+ EXPECT_NE(0, memcmp(deserialized.key_material(), key_data, array_size(key_data)));
+}
+
+TEST_F(KeyBlobTest, WrongMasterKey) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ uint8_t wrong_master_data[] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ keymaster_key_blob_t wrong_master;
+ wrong_master.key_material = wrong_master_data;
+ wrong_master.key_material_size = array_size(wrong_master_data);
+
+ // Decrypting with wrong master key should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, hidden_, wrong_master);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+ EXPECT_NE(0, memcmp(deserialized.key_material(), key_data, array_size(key_data)));
+}
+
+TEST_F(KeyBlobTest, WrongEnforced) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+
+ // Find enforced serialization data and modify it.
+ size_t enforced_size = enforced_.SerializedSize();
+ UniquePtr<uint8_t[]> enforced_data(new uint8_t[enforced_size]);
+ enforced_.Serialize(enforced_data.get(), enforced_data.get() + enforced_size);
+
+ auto enforced_ptr =
+ std::search(begin, end, enforced_data.get(), enforced_data.get() + enforced_size);
+ ASSERT_NE(end, enforced_ptr);
+ EXPECT_EQ(end, std::search(enforced_ptr + 1, end, enforced_data.get(),
+ enforced_data.get() + enforced_size));
+ (*(enforced_ptr + enforced_size - 1))++;
+
+ // Decrypting with wrong unenforced data should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, hidden_, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+}
+
+TEST_F(KeyBlobTest, WrongUnenforced) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+
+ // Find unenforced serialization data and modify it.
+ size_t unenforced_size = unenforced_.SerializedSize();
+ UniquePtr<uint8_t[]> unenforced_data(new uint8_t[unenforced_size]);
+ unenforced_.Serialize(unenforced_data.get(), unenforced_data.get() + unenforced_size);
+
+ auto unenforced_ptr =
+ std::search(begin, end, unenforced_data.get(), unenforced_data.get() + unenforced_size);
+ ASSERT_NE(end, unenforced_ptr);
+ EXPECT_EQ(end, std::search(unenforced_ptr + 1, end, unenforced_data.get(),
+ unenforced_data.get() + unenforced_size));
+ (*(unenforced_ptr + unenforced_size - 1))++;
+
+ // Decrypting with wrong unenforced data should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, hidden_, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+}
+
+TEST_F(KeyBlobTest, EmptyHidden) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+
+ AuthorizationSet wrong_hidden;
+
+ // Decrypting with wrong hidden data should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, wrong_hidden, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+}
+
+TEST_F(KeyBlobTest, WrongRootOfTrust) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+
+ AuthorizationSet wrong_hidden;
+ wrong_hidden.push_back(TAG_ROOT_OF_TRUST, "bar", 3);
+ wrong_hidden.push_back(TAG_APPLICATION_ID, "my_app", 6);
+
+ // Decrypting with wrong hidden data should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, wrong_hidden, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+}
+
+TEST_F(KeyBlobTest, WrongAppId) {
+ size_t size = blob_->SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+
+ AuthorizationSet wrong_hidden;
+ wrong_hidden.push_back(TAG_ROOT_OF_TRUST, "foo", 3);
+ wrong_hidden.push_back(TAG_APPLICATION_ID, "your_app", 7);
+
+ // Decrypting with wrong hidden data should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, wrong_hidden, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/ocb.c b/ocb.c
new file mode 100644
index 0000000..2af174e
--- /dev/null
+++ b/ocb.c
@@ -0,0 +1,1481 @@
+/*------------------------------------------------------------------------
+/ OCB Version 3 Reference Code (Optimized C) Last modified 12-JUN-2013
+/-------------------------------------------------------------------------
+/ Copyright (c) 2013 Ted Krovetz.
+/
+/ Permission to use, copy, modify, and/or distribute this software for any
+/ purpose with or without fee is hereby granted, provided that the above
+/ copyright notice and this permission notice appear in all copies.
+/
+/ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+/ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+/ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+/ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+/ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+/ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+/ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+/
+/ Phillip Rogaway holds patents relevant to OCB. See the following for
+/ his patent grant: http://www.cs.ucdavis.edu/~rogaway/ocb/grant.htm
+/
+/ Special thanks to Keegan McAllister for suggesting several good improvements
+/
+/ Comments are welcome: Ted Krovetz <ted@krovetz.net> - Dedicated to Laurel K
+/------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------- */
+/* Usage notes */
+/* ----------------------------------------------------------------------- */
+
+/* - When AE_PENDING is passed as the 'final' parameter of any function,
+/ the length parameters must be a multiple of (BPI*16).
+/ - When available, SSE or AltiVec registers are used to manipulate data.
+/ So, when on machines with these facilities, all pointers passed to
+/ any function should be 16-byte aligned.
+/ - Plaintext and ciphertext pointers may be equal (ie, plaintext gets
+/ encrypted in-place), but no other pair of pointers may be equal.
+/ - This code assumes all x86 processors have SSE2 and SSSE3 instructions
+/ when compiling under MSVC. If untrue, alter the #define.
+/ - This code is tested for C99 and recent versions of GCC and MSVC. */
+
+/* ----------------------------------------------------------------------- */
+/* User configuration options */
+/* ----------------------------------------------------------------------- */
+
+/* Set the AES key length to use and length of authentication tag to produce.
+/ Setting either to 0 requires the value be set at runtime via ae_init().
+/ Some optimizations occur for each when set to a fixed value. */
+#define OCB_KEY_LEN 16 /* 0, 16, 24 or 32. 0 means set in ae_init */
+#define OCB_TAG_LEN 16 /* 0 to 16. 0 means set in ae_init */
+
+/* This implementation has built-in support for multiple AES APIs. Set any
+/ one of the following to non-zero to specify which to use. */
+#define USE_OPENSSL_AES 1 /* http://openssl.org */
+#define USE_REFERENCE_AES 0 /* Internet search: rijndael-alg-fst.c */
+#define USE_AES_NI 0 /* Uses compiler's intrinsics */
+
+/* During encryption and decryption, various "L values" are required.
+/ The L values can be precomputed during initialization (requiring extra
+/ space in ae_ctx), generated as needed (slightly slowing encryption and
+/ decryption), or some combination of the two. L_TABLE_SZ specifies how many
+/ L values to precompute. L_TABLE_SZ must be at least 3. L_TABLE_SZ*16 bytes
+/ are used for L values in ae_ctx. Plaintext and ciphertexts shorter than
+/ 2^L_TABLE_SZ blocks need no L values calculated dynamically. */
+#define L_TABLE_SZ 16
+
+/* Set L_TABLE_SZ_IS_ENOUGH non-zero iff you know that all plaintexts
+/ will be shorter than 2^(L_TABLE_SZ+4) bytes in length. This results
+/ in better performance. */
+#define L_TABLE_SZ_IS_ENOUGH 1
+
+/* ----------------------------------------------------------------------- */
+/* Includes and compiler specific definitions */
+/* ----------------------------------------------------------------------- */
+
+#include "ae.h"
+#include <stdlib.h>
+#include <string.h>
+
+/* Define standard sized integers */
+#if defined(_MSC_VER) && (_MSC_VER < 1600)
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+typedef __int64 int64_t;
+#else
+#include <stdint.h>
+#endif
+
+/* Compiler-specific intrinsics and fixes: bswap64, ntz */
+#if _MSC_VER
+#define inline __inline /* MSVC doesn't recognize "inline" in C */
+#define restrict __restrict /* MSVC doesn't recognize "restrict" in C */
+#define __SSE2__ (_M_IX86 || _M_AMD64 || _M_X64) /* Assume SSE2 */
+#define __SSSE3__ (_M_IX86 || _M_AMD64 || _M_X64) /* Assume SSSE3 */
+#include <intrin.h>
+#pragma intrinsic(_byteswap_uint64, _BitScanForward, memcpy)
+#define bswap64(x) _byteswap_uint64(x)
+static inline unsigned ntz(unsigned x) {
+ _BitScanForward(&x, x);
+ return x;
+}
+#elif __GNUC__
+#define inline __inline__ /* No "inline" in GCC ansi C mode */
+#define restrict __restrict__ /* No "restrict" in GCC ansi C mode */
+#define bswap64(x) __builtin_bswap64(x) /* Assuming GCC 4.3+ */
+#define ntz(x) __builtin_ctz((unsigned)(x)) /* Assuming GCC 3.4+ */
+#else /* Assume some C99 features: stdint.h, inline, restrict */
+#define bswap32(x) \
+ ((((x)&0xff000000u) >> 24) | (((x)&0x00ff0000u) >> 8) | (((x)&0x0000ff00u) << 8) | \
+ (((x)&0x000000ffu) << 24))
+
+static inline uint64_t bswap64(uint64_t x) {
+ union {
+ uint64_t u64;
+ uint32_t u32[2];
+ } in, out;
+ in.u64 = x;
+ out.u32[0] = bswap32(in.u32[1]);
+ out.u32[1] = bswap32(in.u32[0]);
+ return out.u64;
+}
+
+#if (L_TABLE_SZ <= 9) && (L_TABLE_SZ_IS_ENOUGH) /* < 2^13 byte texts */
+static inline unsigned ntz(unsigned x) {
+ static const unsigned char tz_table[] = {
+ 0, 2, 3, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, 3, 2, 6, 2, 3, 2, 4, 2, 3, 2, 5, 2,
+ 3, 2, 4, 2, 3, 2, 7, 2, 3, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, 3, 2, 6, 2, 3, 2,
+ 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, 3, 2, 8, 2, 3, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 2,
+ 3, 2, 6, 2, 3, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, 3, 2, 7, 2, 3, 2, 4, 2, 3, 2,
+ 5, 2, 3, 2, 4, 2, 3, 2, 6, 2, 3, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, 3, 2};
+ return tz_table[x / 4];
+}
+#else /* From http://supertech.csail.mit.edu/papers/debruijn.pdf */
+static inline unsigned ntz(unsigned x) {
+ static const unsigned char tz_table[32] = {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20,
+ 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19,
+ 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
+ return tz_table[((uint32_t)((x & -x) * 0x077CB531u)) >> 27];
+}
+#endif
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* Define blocks and operations -- Patch if incorrect on your compiler. */
+/* ----------------------------------------------------------------------- */
+
+#if __SSE2__ && !KEYMASTER_CLANG_TEST_BUILD
+#include <xmmintrin.h> /* SSE instructions and _mm_malloc */
+#include <emmintrin.h> /* SSE2 instructions */
+typedef __m128i block;
+#define xor_block(x, y) _mm_xor_si128(x, y)
+#define zero_block() _mm_setzero_si128()
+#define unequal_blocks(x, y) (_mm_movemask_epi8(_mm_cmpeq_epi8(x, y)) != 0xffff)
+#if __SSSE3__ || USE_AES_NI
+#include <tmmintrin.h> /* SSSE3 instructions */
+#define swap_if_le(b) \
+ _mm_shuffle_epi8(b, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15))
+#else
+static inline block swap_if_le(block b) {
+ block a = _mm_shuffle_epi32(b, _MM_SHUFFLE(0, 1, 2, 3));
+ a = _mm_shufflehi_epi16(a, _MM_SHUFFLE(2, 3, 0, 1));
+ a = _mm_shufflelo_epi16(a, _MM_SHUFFLE(2, 3, 0, 1));
+ return _mm_xor_si128(_mm_srli_epi16(a, 8), _mm_slli_epi16(a, 8));
+}
+#endif
+static inline block gen_offset(uint64_t KtopStr[3], unsigned bot) {
+ block hi = _mm_load_si128((__m128i*)(KtopStr + 0)); /* hi = B A */
+ block lo = _mm_loadu_si128((__m128i*)(KtopStr + 1)); /* lo = C B */
+ __m128i lshift = _mm_cvtsi32_si128(bot);
+ __m128i rshift = _mm_cvtsi32_si128(64 - bot);
+ lo = _mm_xor_si128(_mm_sll_epi64(hi, lshift), _mm_srl_epi64(lo, rshift));
+#if __SSSE3__ || USE_AES_NI
+ return _mm_shuffle_epi8(lo, _mm_set_epi8(8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7));
+#else
+ return swap_if_le(_mm_shuffle_epi32(lo, _MM_SHUFFLE(1, 0, 3, 2)));
+#endif
+}
+static inline block double_block(block bl) {
+ const __m128i mask = _mm_set_epi32(135, 1, 1, 1);
+ __m128i tmp = _mm_srai_epi32(bl, 31);
+ tmp = _mm_and_si128(tmp, mask);
+ tmp = _mm_shuffle_epi32(tmp, _MM_SHUFFLE(2, 1, 0, 3));
+ bl = _mm_slli_epi32(bl, 1);
+ return _mm_xor_si128(bl, tmp);
+}
+#elif __ALTIVEC__
+#include <altivec.h>
+typedef vector unsigned block;
+#define xor_block(x, y) vec_xor(x, y)
+#define zero_block() vec_splat_u32(0)
+#define unequal_blocks(x, y) vec_any_ne(x, y)
+#define swap_if_le(b) (b)
+#if __PPC64__
+block gen_offset(uint64_t KtopStr[3], unsigned bot) {
+ union {
+ uint64_t u64[2];
+ block bl;
+ } rval;
+ rval.u64[0] = (KtopStr[0] << bot) | (KtopStr[1] >> (64 - bot));
+ rval.u64[1] = (KtopStr[1] << bot) | (KtopStr[2] >> (64 - bot));
+ return rval.bl;
+}
+#else
+/* Special handling: Shifts are mod 32, and no 64-bit types */
+block gen_offset(uint64_t KtopStr[3], unsigned bot) {
+ const vector unsigned k32 = {32, 32, 32, 32};
+ vector unsigned hi = *(vector unsigned*)(KtopStr + 0);
+ vector unsigned lo = *(vector unsigned*)(KtopStr + 2);
+ vector unsigned bot_vec;
+ if (bot < 32) {
+ lo = vec_sld(hi, lo, 4);
+ } else {
+ vector unsigned t = vec_sld(hi, lo, 4);
+ lo = vec_sld(hi, lo, 8);
+ hi = t;
+ bot = bot - 32;
+ }
+ if (bot == 0)
+ return hi;
+ *(unsigned*)&bot_vec = bot;
+ vector unsigned lshift = vec_splat(bot_vec, 0);
+ vector unsigned rshift = vec_sub(k32, lshift);
+ hi = vec_sl(hi, lshift);
+ lo = vec_sr(lo, rshift);
+ return vec_xor(hi, lo);
+}
+#endif
+static inline block double_block(block b) {
+ const vector unsigned char mask = {135, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+ const vector unsigned char perm = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
+ const vector unsigned char shift7 = vec_splat_u8(7);
+ const vector unsigned char shift1 = vec_splat_u8(1);
+ vector unsigned char c = (vector unsigned char)b;
+ vector unsigned char t = vec_sra(c, shift7);
+ t = vec_and(t, mask);
+ t = vec_perm(t, t, perm);
+ c = vec_sl(c, shift1);
+ return (block)vec_xor(c, t);
+}
+#elif __ARM_NEON__
+#include <arm_neon.h>
+typedef int8x16_t block; /* Yay! Endian-neutral reads! */
+#define xor_block(x, y) veorq_s8(x, y)
+#define zero_block() vdupq_n_s8(0)
+static inline int unequal_blocks(block a, block b) {
+ int64x2_t t = veorq_s64((int64x2_t)a, (int64x2_t)b);
+ return (vgetq_lane_s64(t, 0) | vgetq_lane_s64(t, 1)) != 0;
+}
+#define swap_if_le(b) (b) /* Using endian-neutral int8x16_t */
+/* KtopStr is reg correct by 64 bits, return mem correct */
+block gen_offset(uint64_t KtopStr[3], unsigned bot) {
+ const union {
+ unsigned x;
+ unsigned char endian;
+ } little = {1};
+ const int64x2_t k64 = {-64, -64};
+ uint64x2_t hi = *(uint64x2_t*)(KtopStr + 0); /* hi = A B */
+ uint64x2_t lo = *(uint64x2_t*)(KtopStr + 1); /* hi = B C */
+ int64x2_t ls = vdupq_n_s64(bot);
+ int64x2_t rs = vqaddq_s64(k64, ls);
+ block rval = (block)veorq_u64(vshlq_u64(hi, ls), vshlq_u64(lo, rs));
+ if (little.endian)
+ rval = vrev64q_s8(rval);
+ return rval;
+}
+static inline block double_block(block b) {
+ const block mask = {135, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+ block tmp = vshrq_n_s8(b, 7);
+ tmp = vandq_s8(tmp, mask);
+ tmp = vextq_s8(tmp, tmp, 1); /* Rotate high byte to end */
+ b = vshlq_n_s8(b, 1);
+ return veorq_s8(tmp, b);
+}
+#else
+typedef struct { uint64_t l, r; } block;
+static inline block xor_block(block x, block y) {
+ x.l ^= y.l;
+ x.r ^= y.r;
+ return x;
+}
+static inline block zero_block(void) {
+ const block t = {0, 0};
+ return t;
+}
+#define unequal_blocks(x, y) ((((x).l ^ (y).l) | ((x).r ^ (y).r)) != 0)
+static inline block swap_if_le(block b) {
+ const union {
+ unsigned x;
+ unsigned char endian;
+ } little = {1};
+ if (little.endian) {
+ block r;
+ r.l = bswap64(b.l);
+ r.r = bswap64(b.r);
+ return r;
+ } else
+ return b;
+}
+
+/* KtopStr is reg correct by 64 bits, return mem correct */
+block gen_offset(uint64_t KtopStr[3], unsigned bot) {
+ block rval;
+ if (bot != 0) {
+ rval.l = (KtopStr[0] << bot) | (KtopStr[1] >> (64 - bot));
+ rval.r = (KtopStr[1] << bot) | (KtopStr[2] >> (64 - bot));
+ } else {
+ rval.l = KtopStr[0];
+ rval.r = KtopStr[1];
+ }
+ return swap_if_le(rval);
+}
+
+#if __GNUC__ && __arm__
+static inline block double_block(block b) {
+ __asm__("adds %1,%1,%1\n\t"
+ "adcs %H1,%H1,%H1\n\t"
+ "adcs %0,%0,%0\n\t"
+ "adcs %H0,%H0,%H0\n\t"
+ "it cs\n\t"
+ "eorcs %1,%1,#135"
+ : "+r"(b.l), "+r"(b.r)
+ :
+ : "cc");
+ return b;
+}
+#else
+static inline block double_block(block b) {
+ uint64_t t = (uint64_t)((int64_t)b.l >> 63);
+ b.l = (b.l + b.l) ^ (b.r >> 63);
+ b.r = (b.r + b.r) ^ (t & 135);
+ return b;
+}
+#endif
+
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* AES - Code uses OpenSSL API. Other implementations get mapped to it. */
+/* ----------------------------------------------------------------------- */
+
+/*---------------*/
+#if USE_OPENSSL_AES
+/*---------------*/
+
+#include <openssl/aes.h> /* http://openssl.org/ */
+
+/* How to ECB encrypt an array of blocks, in place */
+static inline void AES_ecb_encrypt_blks(block* blks, unsigned nblks, AES_KEY* key) {
+ while (nblks) {
+ --nblks;
+ AES_encrypt((unsigned char*)(blks + nblks), (unsigned char*)(blks + nblks), key);
+ }
+}
+
+static inline void AES_ecb_decrypt_blks(block* blks, unsigned nblks, AES_KEY* key) {
+ while (nblks) {
+ --nblks;
+ AES_decrypt((unsigned char*)(blks + nblks), (unsigned char*)(blks + nblks), key);
+ }
+}
+
+#define BPI 4 /* Number of blocks in buffer per ECB call */
+
+/*-------------------*/
+#elif USE_REFERENCE_AES
+/*-------------------*/
+
+#include "rijndael-alg-fst.h" /* Barreto's Public-Domain Code */
+#if (OCB_KEY_LEN == 0)
+typedef struct {
+ uint32_t rd_key[60];
+ int rounds;
+} AES_KEY;
+#define ROUNDS(ctx) ((ctx)->rounds)
+#define AES_set_encrypt_key(x, y, z) \
+ do { \
+ rijndaelKeySetupEnc((z)->rd_key, x, y); \
+ (z)->rounds = y / 32 + 6; \
+ } while (0)
+#define AES_set_decrypt_key(x, y, z) \
+ do { \
+ rijndaelKeySetupDec((z)->rd_key, x, y); \
+ (z)->rounds = y / 32 + 6; \
+ } while (0)
+#else
+typedef struct { uint32_t rd_key[OCB_KEY_LEN + 28]; } AES_KEY;
+#define ROUNDS(ctx) (6 + OCB_KEY_LEN / 4)
+#define AES_set_encrypt_key(x, y, z) rijndaelKeySetupEnc((z)->rd_key, x, y)
+#define AES_set_decrypt_key(x, y, z) rijndaelKeySetupDec((z)->rd_key, x, y)
+#endif
+#define AES_encrypt(x, y, z) rijndaelEncrypt((z)->rd_key, ROUNDS(z), x, y)
+#define AES_decrypt(x, y, z) rijndaelDecrypt((z)->rd_key, ROUNDS(z), x, y)
+
+static void AES_ecb_encrypt_blks(block* blks, unsigned nblks, AES_KEY* key) {
+ while (nblks) {
+ --nblks;
+ AES_encrypt((unsigned char*)(blks + nblks), (unsigned char*)(blks + nblks), key);
+ }
+}
+
+void AES_ecb_decrypt_blks(block* blks, unsigned nblks, AES_KEY* key) {
+ while (nblks) {
+ --nblks;
+ AES_decrypt((unsigned char*)(blks + nblks), (unsigned char*)(blks + nblks), key);
+ }
+}
+
+#define BPI 4 /* Number of blocks in buffer per ECB call */
+
+/*----------*/
+#elif USE_AES_NI
+/*----------*/
+
+#include <wmmintrin.h>
+
+#if (OCB_KEY_LEN == 0)
+typedef struct {
+ __m128i rd_key[15];
+ int rounds;
+} AES_KEY;
+#define ROUNDS(ctx) ((ctx)->rounds)
+#else
+typedef struct { __m128i rd_key[7 + OCB_KEY_LEN / 4]; } AES_KEY;
+#define ROUNDS(ctx) (6 + OCB_KEY_LEN / 4)
+#endif
+
+#define EXPAND_ASSIST(v1, v2, v3, v4, shuff_const, aes_const) \
+ v2 = _mm_aeskeygenassist_si128(v4, aes_const); \
+ v3 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(v3), _mm_castsi128_ps(v1), 16)); \
+ v1 = _mm_xor_si128(v1, v3); \
+ v3 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(v3), _mm_castsi128_ps(v1), 140)); \
+ v1 = _mm_xor_si128(v1, v3); \
+ v2 = _mm_shuffle_epi32(v2, shuff_const); \
+ v1 = _mm_xor_si128(v1, v2)
+
+#define EXPAND192_STEP(idx, aes_const) \
+ EXPAND_ASSIST(x0, x1, x2, x3, 85, aes_const); \
+ x3 = _mm_xor_si128(x3, _mm_slli_si128(x3, 4)); \
+ x3 = _mm_xor_si128(x3, _mm_shuffle_epi32(x0, 255)); \
+ kp[idx] = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(tmp), _mm_castsi128_ps(x0), 68)); \
+ kp[idx + 1] = \
+ _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x0), _mm_castsi128_ps(x3), 78)); \
+ EXPAND_ASSIST(x0, x1, x2, x3, 85, (aes_const * 2)); \
+ x3 = _mm_xor_si128(x3, _mm_slli_si128(x3, 4)); \
+ x3 = _mm_xor_si128(x3, _mm_shuffle_epi32(x0, 255)); \
+ kp[idx + 2] = x0; \
+ tmp = x3
+
+static void AES_128_Key_Expansion(const unsigned char* userkey, void* key) {
+ __m128i x0, x1, x2;
+ __m128i* kp = (__m128i*)key;
+ kp[0] = x0 = _mm_loadu_si128((__m128i*)userkey);
+ x2 = _mm_setzero_si128();
+ EXPAND_ASSIST(x0, x1, x2, x0, 255, 1);
+ kp[1] = x0;
+ EXPAND_ASSIST(x0, x1, x2, x0, 255, 2);
+ kp[2] = x0;
+ EXPAND_ASSIST(x0, x1, x2, x0, 255, 4);
+ kp[3] = x0;
+ EXPAND_ASSIST(x0, x1, x2, x0, 255, 8);
+ kp[4] = x0;
+ EXPAND_ASSIST(x0, x1, x2, x0, 255, 16);
+ kp[5] = x0;
+ EXPAND_ASSIST(x0, x1, x2, x0, 255, 32);
+ kp[6] = x0;
+ EXPAND_ASSIST(x0, x1, x2, x0, 255, 64);
+ kp[7] = x0;
+ EXPAND_ASSIST(x0, x1, x2, x0, 255, 128);
+ kp[8] = x0;
+ EXPAND_ASSIST(x0, x1, x2, x0, 255, 27);
+ kp[9] = x0;
+ EXPAND_ASSIST(x0, x1, x2, x0, 255, 54);
+ kp[10] = x0;
+}
+
+static void AES_192_Key_Expansion(const unsigned char* userkey, void* key) {
+ __m128i x0, x1, x2, x3, tmp, *kp = (__m128i*)key;
+ kp[0] = x0 = _mm_loadu_si128((__m128i*)userkey);
+ tmp = x3 = _mm_loadu_si128((__m128i*)(userkey + 16));
+ x2 = _mm_setzero_si128();
+ EXPAND192_STEP(1, 1);
+ EXPAND192_STEP(4, 4);
+ EXPAND192_STEP(7, 16);
+ EXPAND192_STEP(10, 64);
+}
+
+static void AES_256_Key_Expansion(const unsigned char* userkey, void* key) {
+ __m128i x0, x1, x2, x3, *kp = (__m128i*)key;
+ kp[0] = x0 = _mm_loadu_si128((__m128i*)userkey);
+ kp[1] = x3 = _mm_loadu_si128((__m128i*)(userkey + 16));
+ x2 = _mm_setzero_si128();
+ EXPAND_ASSIST(x0, x1, x2, x3, 255, 1);
+ kp[2] = x0;
+ EXPAND_ASSIST(x3, x1, x2, x0, 170, 1);
+ kp[3] = x3;
+ EXPAND_ASSIST(x0, x1, x2, x3, 255, 2);
+ kp[4] = x0;
+ EXPAND_ASSIST(x3, x1, x2, x0, 170, 2);
+ kp[5] = x3;
+ EXPAND_ASSIST(x0, x1, x2, x3, 255, 4);
+ kp[6] = x0;
+ EXPAND_ASSIST(x3, x1, x2, x0, 170, 4);
+ kp[7] = x3;
+ EXPAND_ASSIST(x0, x1, x2, x3, 255, 8);
+ kp[8] = x0;
+ EXPAND_ASSIST(x3, x1, x2, x0, 170, 8);
+ kp[9] = x3;
+ EXPAND_ASSIST(x0, x1, x2, x3, 255, 16);
+ kp[10] = x0;
+ EXPAND_ASSIST(x3, x1, x2, x0, 170, 16);
+ kp[11] = x3;
+ EXPAND_ASSIST(x0, x1, x2, x3, 255, 32);
+ kp[12] = x0;
+ EXPAND_ASSIST(x3, x1, x2, x0, 170, 32);
+ kp[13] = x3;
+ EXPAND_ASSIST(x0, x1, x2, x3, 255, 64);
+ kp[14] = x0;
+}
+
+static int AES_set_encrypt_key(const unsigned char* userKey, const int bits, AES_KEY* key) {
+ if (bits == 128) {
+ AES_128_Key_Expansion(userKey, key);
+ } else if (bits == 192) {
+ AES_192_Key_Expansion(userKey, key);
+ } else if (bits == 256) {
+ AES_256_Key_Expansion(userKey, key);
+ }
+#if (OCB_KEY_LEN == 0)
+ key->rounds = 6 + bits / 32;
+#endif
+ return 0;
+}
+
+static void AES_set_decrypt_key_fast(AES_KEY* dkey, const AES_KEY* ekey) {
+ int j = 0;
+ int i = ROUNDS(ekey);
+#if (OCB_KEY_LEN == 0)
+ dkey->rounds = i;
+#endif
+ dkey->rd_key[i--] = ekey->rd_key[j++];
+ while (i)
+ dkey->rd_key[i--] = _mm_aesimc_si128(ekey->rd_key[j++]);
+ dkey->rd_key[i] = ekey->rd_key[j];
+}
+
+static int AES_set_decrypt_key(const unsigned char* userKey, const int bits, AES_KEY* key) {
+ AES_KEY temp_key;
+ AES_set_encrypt_key(userKey, bits, &temp_key);
+ AES_set_decrypt_key_fast(key, &temp_key);
+ return 0;
+}
+
+static inline void AES_encrypt(const unsigned char* in, unsigned char* out, const AES_KEY* key) {
+ int j, rnds = ROUNDS(key);
+ const __m128i* sched = ((__m128i*)(key->rd_key));
+ __m128i tmp = _mm_load_si128((__m128i*)in);
+ tmp = _mm_xor_si128(tmp, sched[0]);
+ for (j = 1; j < rnds; j++)
+ tmp = _mm_aesenc_si128(tmp, sched[j]);
+ tmp = _mm_aesenclast_si128(tmp, sched[j]);
+ _mm_store_si128((__m128i*)out, tmp);
+}
+
+static inline void AES_decrypt(const unsigned char* in, unsigned char* out, const AES_KEY* key) {
+ int j, rnds = ROUNDS(key);
+ const __m128i* sched = ((__m128i*)(key->rd_key));
+ __m128i tmp = _mm_load_si128((__m128i*)in);
+ tmp = _mm_xor_si128(tmp, sched[0]);
+ for (j = 1; j < rnds; j++)
+ tmp = _mm_aesdec_si128(tmp, sched[j]);
+ tmp = _mm_aesdeclast_si128(tmp, sched[j]);
+ _mm_store_si128((__m128i*)out, tmp);
+}
+
+static inline void AES_ecb_encrypt_blks(block* blks, unsigned nblks, AES_KEY* key) {
+ unsigned i, j, rnds = ROUNDS(key);
+ const __m128i* sched = ((__m128i*)(key->rd_key));
+ for (i = 0; i < nblks; ++i)
+ blks[i] = _mm_xor_si128(blks[i], sched[0]);
+ for (j = 1; j < rnds; ++j)
+ for (i = 0; i < nblks; ++i)
+ blks[i] = _mm_aesenc_si128(blks[i], sched[j]);
+ for (i = 0; i < nblks; ++i)
+ blks[i] = _mm_aesenclast_si128(blks[i], sched[j]);
+}
+
+static inline void AES_ecb_decrypt_blks(block* blks, unsigned nblks, AES_KEY* key) {
+ unsigned i, j, rnds = ROUNDS(key);
+ const __m128i* sched = ((__m128i*)(key->rd_key));
+ for (i = 0; i < nblks; ++i)
+ blks[i] = _mm_xor_si128(blks[i], sched[0]);
+ for (j = 1; j < rnds; ++j)
+ for (i = 0; i < nblks; ++i)
+ blks[i] = _mm_aesdec_si128(blks[i], sched[j]);
+ for (i = 0; i < nblks; ++i)
+ blks[i] = _mm_aesdeclast_si128(blks[i], sched[j]);
+}
+
+#define BPI 8 /* Number of blocks in buffer per ECB call */
+/* Set to 4 for Westmere, 8 for Sandy Bridge */
+
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* Define OCB context structure. */
+/* ----------------------------------------------------------------------- */
+
+/*------------------------------------------------------------------------
+/ Each item in the OCB context is stored either "memory correct" or
+/ "register correct". On big-endian machines, this is identical. On
+/ little-endian machines, one must choose whether the byte-string
+/ is in the correct order when it resides in memory or in registers.
+/ It must be register correct whenever it is to be manipulated
+/ arithmetically, but must be memory correct whenever it interacts
+/ with the plaintext or ciphertext.
+/------------------------------------------------------------------------- */
+
+struct _ae_ctx {
+ block offset; /* Memory correct */
+ block checksum; /* Memory correct */
+ block Lstar; /* Memory correct */
+ block Ldollar; /* Memory correct */
+ block L[L_TABLE_SZ]; /* Memory correct */
+ block ad_checksum; /* Memory correct */
+ block ad_offset; /* Memory correct */
+ block cached_Top; /* Memory correct */
+ uint64_t KtopStr[3]; /* Register correct, each item */
+ uint32_t ad_blocks_processed;
+ uint32_t blocks_processed;
+ AES_KEY decrypt_key;
+ AES_KEY encrypt_key;
+#if (OCB_TAG_LEN == 0)
+ unsigned tag_len;
+#endif
+};
+
+/* ----------------------------------------------------------------------- */
+/* L table lookup (or on-the-fly generation) */
+/* ----------------------------------------------------------------------- */
+
+#if L_TABLE_SZ_IS_ENOUGH
+#define getL(_ctx, _tz) ((_ctx)->L[_tz])
+#else
+static block getL(const ae_ctx* ctx, unsigned tz) {
+ if (tz < L_TABLE_SZ)
+ return ctx->L[tz];
+ else {
+ unsigned i;
+ /* Bring L[MAX] into registers, make it register correct */
+ block rval = swap_if_le(ctx->L[L_TABLE_SZ - 1]);
+ rval = double_block(rval);
+ for (i = L_TABLE_SZ; i < tz; i++)
+ rval = double_block(rval);
+ return swap_if_le(rval); /* To memory correct */
+ }
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* Public functions */
+/* ----------------------------------------------------------------------- */
+
+/* 32-bit SSE2 and Altivec systems need to be forced to allocate memory
+ on 16-byte alignments. (I believe all major 64-bit systems do already.) */
+
+ae_ctx* ae_allocate(void* misc) {
+ void* p;
+ (void)misc; /* misc unused in this implementation */
+#if (__SSE2__ && !_M_X64 && !_M_AMD64 && !__amd64__)
+ p = _mm_malloc(sizeof(ae_ctx), 16);
+#elif(__ALTIVEC__ && !__PPC64__)
+ if (posix_memalign(&p, 16, sizeof(ae_ctx)) != 0)
+ p = NULL;
+#else
+ p = malloc(sizeof(ae_ctx));
+#endif
+ return (ae_ctx*)p;
+}
+
+void ae_free(ae_ctx* ctx) {
+#if (__SSE2__ && !_M_X64 && !_M_AMD64 && !__amd64__)
+ _mm_free(ctx);
+#else
+ free(ctx);
+#endif
+}
+
+/* ----------------------------------------------------------------------- */
+
+int ae_clear(ae_ctx* ctx) /* Zero ae_ctx and undo initialization */
+{
+ memset(ctx, 0, sizeof(ae_ctx));
+ return AE_SUCCESS;
+}
+
+int ae_ctx_sizeof(void) {
+ return (int)sizeof(ae_ctx);
+}
+
+/* ----------------------------------------------------------------------- */
+
+int ae_init(ae_ctx* ctx, const void* key, int key_len, int nonce_len, int tag_len) {
+ unsigned i;
+ block tmp_blk;
+
+ if (nonce_len != 12)
+ return AE_NOT_SUPPORTED;
+
+/* Initialize encryption & decryption keys */
+#if (OCB_KEY_LEN > 0)
+ key_len = OCB_KEY_LEN;
+#endif
+ AES_set_encrypt_key((unsigned char*)key, key_len * 8, &ctx->encrypt_key);
+#if USE_AES_NI
+ AES_set_decrypt_key_fast(&ctx->decrypt_key, &ctx->encrypt_key);
+#else
+ AES_set_decrypt_key((unsigned char*)key, (int)(key_len * 8), &ctx->decrypt_key);
+#endif
+
+ /* Zero things that need zeroing */
+ ctx->cached_Top = ctx->ad_checksum = zero_block();
+ ctx->ad_blocks_processed = 0;
+
+ /* Compute key-dependent values */
+ AES_encrypt((unsigned char*)&ctx->cached_Top, (unsigned char*)&ctx->Lstar, &ctx->encrypt_key);
+ tmp_blk = swap_if_le(ctx->Lstar);
+ tmp_blk = double_block(tmp_blk);
+ ctx->Ldollar = swap_if_le(tmp_blk);
+ tmp_blk = double_block(tmp_blk);
+ ctx->L[0] = swap_if_le(tmp_blk);
+ for (i = 1; i < L_TABLE_SZ; i++) {
+ tmp_blk = double_block(tmp_blk);
+ ctx->L[i] = swap_if_le(tmp_blk);
+ }
+
+#if (OCB_TAG_LEN == 0)
+ ctx->tag_len = tag_len;
+#else
+ (void)tag_len; /* Suppress var not used error */
+#endif
+
+ return AE_SUCCESS;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static block gen_offset_from_nonce(ae_ctx* ctx, const void* nonce) {
+ const union {
+ unsigned x;
+ unsigned char endian;
+ } little = {1};
+ union {
+ uint32_t u32[4];
+ uint8_t u8[16];
+ block bl;
+ } tmp;
+ unsigned idx;
+ uint32_t tagadd;
+
+/* Replace cached nonce Top if needed */
+#if (OCB_TAG_LEN > 0)
+ if (little.endian)
+ tmp.u32[0] = 0x01000000 + ((OCB_TAG_LEN * 8 % 128) << 1);
+ else
+ tmp.u32[0] = 0x00000001 + ((OCB_TAG_LEN * 8 % 128) << 25);
+#else
+ if (little.endian)
+ tmp.u32[0] = 0x01000000 + ((ctx->tag_len * 8 % 128) << 1);
+ else
+ tmp.u32[0] = 0x00000001 + ((ctx->tag_len * 8 % 128) << 25);
+#endif
+ tmp.u32[1] = ((uint32_t*)nonce)[0];
+ tmp.u32[2] = ((uint32_t*)nonce)[1];
+ tmp.u32[3] = ((uint32_t*)nonce)[2];
+ idx = (unsigned)(tmp.u8[15] & 0x3f); /* Get low 6 bits of nonce */
+ tmp.u8[15] = tmp.u8[15] & 0xc0; /* Zero low 6 bits of nonce */
+ if (unequal_blocks(tmp.bl, ctx->cached_Top)) { /* Cached? */
+ ctx->cached_Top = tmp.bl; /* Update cache, KtopStr */
+ AES_encrypt(tmp.u8, (unsigned char*)&ctx->KtopStr, &ctx->encrypt_key);
+ if (little.endian) { /* Make Register Correct */
+ ctx->KtopStr[0] = bswap64(ctx->KtopStr[0]);
+ ctx->KtopStr[1] = bswap64(ctx->KtopStr[1]);
+ }
+ ctx->KtopStr[2] = ctx->KtopStr[0] ^ (ctx->KtopStr[0] << 8) ^ (ctx->KtopStr[1] >> 56);
+ }
+ return gen_offset(ctx->KtopStr, idx);
+}
+
+static void process_ad(ae_ctx* ctx, const void* ad, int ad_len, int final) {
+ union {
+ uint32_t u32[4];
+ uint8_t u8[16];
+ block bl;
+ } tmp;
+ block ad_offset, ad_checksum;
+ const block* adp = (block*)ad;
+ unsigned i, k, tz, remaining;
+
+ ad_offset = ctx->ad_offset;
+ ad_checksum = ctx->ad_checksum;
+ i = ad_len / (BPI * 16);
+ if (i) {
+ unsigned ad_block_num = ctx->ad_blocks_processed;
+ do {
+ block ta[BPI], oa[BPI];
+ ad_block_num += BPI;
+ tz = ntz(ad_block_num);
+ oa[0] = xor_block(ad_offset, ctx->L[0]);
+ ta[0] = xor_block(oa[0], adp[0]);
+ oa[1] = xor_block(oa[0], ctx->L[1]);
+ ta[1] = xor_block(oa[1], adp[1]);
+ oa[2] = xor_block(ad_offset, ctx->L[1]);
+ ta[2] = xor_block(oa[2], adp[2]);
+#if BPI == 4
+ ad_offset = xor_block(oa[2], getL(ctx, tz));
+ ta[3] = xor_block(ad_offset, adp[3]);
+#elif BPI == 8
+ oa[3] = xor_block(oa[2], ctx->L[2]);
+ ta[3] = xor_block(oa[3], adp[3]);
+ oa[4] = xor_block(oa[1], ctx->L[2]);
+ ta[4] = xor_block(oa[4], adp[4]);
+ oa[5] = xor_block(oa[0], ctx->L[2]);
+ ta[5] = xor_block(oa[5], adp[5]);
+ oa[6] = xor_block(ad_offset, ctx->L[2]);
+ ta[6] = xor_block(oa[6], adp[6]);
+ ad_offset = xor_block(oa[6], getL(ctx, tz));
+ ta[7] = xor_block(ad_offset, adp[7]);
+#endif
+ AES_ecb_encrypt_blks(ta, BPI, &ctx->encrypt_key);
+ ad_checksum = xor_block(ad_checksum, ta[0]);
+ ad_checksum = xor_block(ad_checksum, ta[1]);
+ ad_checksum = xor_block(ad_checksum, ta[2]);
+ ad_checksum = xor_block(ad_checksum, ta[3]);
+#if (BPI == 8)
+ ad_checksum = xor_block(ad_checksum, ta[4]);
+ ad_checksum = xor_block(ad_checksum, ta[5]);
+ ad_checksum = xor_block(ad_checksum, ta[6]);
+ ad_checksum = xor_block(ad_checksum, ta[7]);
+#endif
+ adp += BPI;
+ } while (--i);
+ ctx->ad_blocks_processed = ad_block_num;
+ ctx->ad_offset = ad_offset;
+ ctx->ad_checksum = ad_checksum;
+ }
+
+ if (final) {
+ block ta[BPI];
+
+ /* Process remaining associated data, compute its tag contribution */
+ remaining = ((unsigned)ad_len) % (BPI * 16);
+ if (remaining) {
+ k = 0;
+#if (BPI == 8)
+ if (remaining >= 64) {
+ tmp.bl = xor_block(ad_offset, ctx->L[0]);
+ ta[0] = xor_block(tmp.bl, adp[0]);
+ tmp.bl = xor_block(tmp.bl, ctx->L[1]);
+ ta[1] = xor_block(tmp.bl, adp[1]);
+ ad_offset = xor_block(ad_offset, ctx->L[1]);
+ ta[2] = xor_block(ad_offset, adp[2]);
+ ad_offset = xor_block(ad_offset, ctx->L[2]);
+ ta[3] = xor_block(ad_offset, adp[3]);
+ remaining -= 64;
+ k = 4;
+ }
+#endif
+ if (remaining >= 32) {
+ ad_offset = xor_block(ad_offset, ctx->L[0]);
+ ta[k] = xor_block(ad_offset, adp[k]);
+ ad_offset = xor_block(ad_offset, getL(ctx, ntz(k + 2)));
+ ta[k + 1] = xor_block(ad_offset, adp[k + 1]);
+ remaining -= 32;
+ k += 2;
+ }
+ if (remaining >= 16) {
+ ad_offset = xor_block(ad_offset, ctx->L[0]);
+ ta[k] = xor_block(ad_offset, adp[k]);
+ remaining = remaining - 16;
+ ++k;
+ }
+ if (remaining) {
+ ad_offset = xor_block(ad_offset, ctx->Lstar);
+ tmp.bl = zero_block();
+ memcpy(tmp.u8, adp + k, remaining);
+ tmp.u8[remaining] = (unsigned char)0x80u;
+ ta[k] = xor_block(ad_offset, tmp.bl);
+ ++k;
+ }
+ AES_ecb_encrypt_blks(ta, k, &ctx->encrypt_key);
+ switch (k) {
+#if (BPI == 8)
+ case 8:
+ ad_checksum = xor_block(ad_checksum, ta[7]);
+ case 7:
+ ad_checksum = xor_block(ad_checksum, ta[6]);
+ case 6:
+ ad_checksum = xor_block(ad_checksum, ta[5]);
+ case 5:
+ ad_checksum = xor_block(ad_checksum, ta[4]);
+#endif
+ case 4:
+ ad_checksum = xor_block(ad_checksum, ta[3]);
+ case 3:
+ ad_checksum = xor_block(ad_checksum, ta[2]);
+ case 2:
+ ad_checksum = xor_block(ad_checksum, ta[1]);
+ case 1:
+ ad_checksum = xor_block(ad_checksum, ta[0]);
+ }
+ ctx->ad_checksum = ad_checksum;
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+int ae_encrypt(ae_ctx* ctx, const void* nonce, const void* pt, int pt_len, const void* ad,
+ int ad_len, void* ct, void* tag, int final) {
+ union {
+ uint32_t u32[4];
+ uint8_t u8[16];
+ block bl;
+ } tmp;
+ block offset, checksum;
+ unsigned i, k;
+ block* ctp = (block*)ct;
+ const block* ptp = (block*)pt;
+
+ /* Non-null nonce means start of new message, init per-message values */
+ if (nonce) {
+ ctx->offset = gen_offset_from_nonce(ctx, nonce);
+ ctx->ad_offset = ctx->checksum = zero_block();
+ ctx->ad_blocks_processed = ctx->blocks_processed = 0;
+ if (ad_len >= 0)
+ ctx->ad_checksum = zero_block();
+ }
+
+ /* Process associated data */
+ if (ad_len > 0)
+ process_ad(ctx, ad, ad_len, final);
+
+ /* Encrypt plaintext data BPI blocks at a time */
+ offset = ctx->offset;
+ checksum = ctx->checksum;
+ i = pt_len / (BPI * 16);
+ if (i) {
+ block oa[BPI];
+ unsigned block_num = ctx->blocks_processed;
+ oa[BPI - 1] = offset;
+ do {
+ block ta[BPI];
+ block_num += BPI;
+ oa[0] = xor_block(oa[BPI - 1], ctx->L[0]);
+ ta[0] = xor_block(oa[0], ptp[0]);
+ checksum = xor_block(checksum, ptp[0]);
+ oa[1] = xor_block(oa[0], ctx->L[1]);
+ ta[1] = xor_block(oa[1], ptp[1]);
+ checksum = xor_block(checksum, ptp[1]);
+ oa[2] = xor_block(oa[1], ctx->L[0]);
+ ta[2] = xor_block(oa[2], ptp[2]);
+ checksum = xor_block(checksum, ptp[2]);
+#if BPI == 4
+ oa[3] = xor_block(oa[2], getL(ctx, ntz(block_num)));
+ ta[3] = xor_block(oa[3], ptp[3]);
+ checksum = xor_block(checksum, ptp[3]);
+#elif BPI == 8
+ oa[3] = xor_block(oa[2], ctx->L[2]);
+ ta[3] = xor_block(oa[3], ptp[3]);
+ checksum = xor_block(checksum, ptp[3]);
+ oa[4] = xor_block(oa[1], ctx->L[2]);
+ ta[4] = xor_block(oa[4], ptp[4]);
+ checksum = xor_block(checksum, ptp[4]);
+ oa[5] = xor_block(oa[0], ctx->L[2]);
+ ta[5] = xor_block(oa[5], ptp[5]);
+ checksum = xor_block(checksum, ptp[5]);
+ oa[6] = xor_block(oa[7], ctx->L[2]);
+ ta[6] = xor_block(oa[6], ptp[6]);
+ checksum = xor_block(checksum, ptp[6]);
+ oa[7] = xor_block(oa[6], getL(ctx, ntz(block_num)));
+ ta[7] = xor_block(oa[7], ptp[7]);
+ checksum = xor_block(checksum, ptp[7]);
+#endif
+ AES_ecb_encrypt_blks(ta, BPI, &ctx->encrypt_key);
+ ctp[0] = xor_block(ta[0], oa[0]);
+ ctp[1] = xor_block(ta[1], oa[1]);
+ ctp[2] = xor_block(ta[2], oa[2]);
+ ctp[3] = xor_block(ta[3], oa[3]);
+#if (BPI == 8)
+ ctp[4] = xor_block(ta[4], oa[4]);
+ ctp[5] = xor_block(ta[5], oa[5]);
+ ctp[6] = xor_block(ta[6], oa[6]);
+ ctp[7] = xor_block(ta[7], oa[7]);
+#endif
+ ptp += BPI;
+ ctp += BPI;
+ } while (--i);
+ ctx->offset = offset = oa[BPI - 1];
+ ctx->blocks_processed = block_num;
+ ctx->checksum = checksum;
+ }
+
+ if (final) {
+ block ta[BPI + 1], oa[BPI];
+
+ /* Process remaining plaintext and compute its tag contribution */
+ unsigned remaining = ((unsigned)pt_len) % (BPI * 16);
+ k = 0; /* How many blocks in ta[] need ECBing */
+ if (remaining) {
+#if (BPI == 8)
+ if (remaining >= 64) {
+ oa[0] = xor_block(offset, ctx->L[0]);
+ ta[0] = xor_block(oa[0], ptp[0]);
+ checksum = xor_block(checksum, ptp[0]);
+ oa[1] = xor_block(oa[0], ctx->L[1]);
+ ta[1] = xor_block(oa[1], ptp[1]);
+ checksum = xor_block(checksum, ptp[1]);
+ oa[2] = xor_block(oa[1], ctx->L[0]);
+ ta[2] = xor_block(oa[2], ptp[2]);
+ checksum = xor_block(checksum, ptp[2]);
+ offset = oa[3] = xor_block(oa[2], ctx->L[2]);
+ ta[3] = xor_block(offset, ptp[3]);
+ checksum = xor_block(checksum, ptp[3]);
+ remaining -= 64;
+ k = 4;
+ }
+#endif
+ if (remaining >= 32) {
+ oa[k] = xor_block(offset, ctx->L[0]);
+ ta[k] = xor_block(oa[k], ptp[k]);
+ checksum = xor_block(checksum, ptp[k]);
+ offset = oa[k + 1] = xor_block(oa[k], ctx->L[1]);
+ ta[k + 1] = xor_block(offset, ptp[k + 1]);
+ checksum = xor_block(checksum, ptp[k + 1]);
+ remaining -= 32;
+ k += 2;
+ }
+ if (remaining >= 16) {
+ offset = oa[k] = xor_block(offset, ctx->L[0]);
+ ta[k] = xor_block(offset, ptp[k]);
+ checksum = xor_block(checksum, ptp[k]);
+ remaining -= 16;
+ ++k;
+ }
+ if (remaining) {
+ tmp.bl = zero_block();
+ memcpy(tmp.u8, ptp + k, remaining);
+ tmp.u8[remaining] = (unsigned char)0x80u;
+ checksum = xor_block(checksum, tmp.bl);
+ ta[k] = offset = xor_block(offset, ctx->Lstar);
+ ++k;
+ }
+ }
+ offset = xor_block(offset, ctx->Ldollar); /* Part of tag gen */
+ ta[k] = xor_block(offset, checksum); /* Part of tag gen */
+ AES_ecb_encrypt_blks(ta, k + 1, &ctx->encrypt_key);
+ offset = xor_block(ta[k], ctx->ad_checksum); /* Part of tag gen */
+ if (remaining) {
+ --k;
+ tmp.bl = xor_block(tmp.bl, ta[k]);
+ memcpy(ctp + k, tmp.u8, remaining);
+ }
+ switch (k) {
+#if (BPI == 8)
+ case 7:
+ ctp[6] = xor_block(ta[6], oa[6]);
+ case 6:
+ ctp[5] = xor_block(ta[5], oa[5]);
+ case 5:
+ ctp[4] = xor_block(ta[4], oa[4]);
+ case 4:
+ ctp[3] = xor_block(ta[3], oa[3]);
+#endif
+ case 3:
+ ctp[2] = xor_block(ta[2], oa[2]);
+ case 2:
+ ctp[1] = xor_block(ta[1], oa[1]);
+ case 1:
+ ctp[0] = xor_block(ta[0], oa[0]);
+ }
+
+ /* Tag is placed at the correct location
+ */
+ if (tag) {
+#if (OCB_TAG_LEN == 16)
+ *(block*)tag = offset;
+#elif(OCB_TAG_LEN > 0)
+ memcpy((char*)tag, &offset, OCB_TAG_LEN);
+#else
+ memcpy((char*)tag, &offset, ctx->tag_len);
+#endif
+ } else {
+#if (OCB_TAG_LEN > 0)
+ memcpy((char*)ct + pt_len, &offset, OCB_TAG_LEN);
+ pt_len += OCB_TAG_LEN;
+#else
+ memcpy((char*)ct + pt_len, &offset, ctx->tag_len);
+ pt_len += ctx->tag_len;
+#endif
+ }
+ }
+ return (int)pt_len;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* Compare two regions of memory, taking a constant amount of time for a
+ given buffer size -- under certain assumptions about the compiler
+ and machine, of course.
+
+ Use this to avoid timing side-channel attacks.
+
+ Returns 0 for memory regions with equal contents; non-zero otherwise. */
+static int constant_time_memcmp(const void* av, const void* bv, size_t n) {
+ const uint8_t* a = (const uint8_t*)av;
+ const uint8_t* b = (const uint8_t*)bv;
+ uint8_t result = 0;
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ result |= *a ^ *b;
+ a++;
+ b++;
+ }
+
+ return (int)result;
+}
+
+int ae_decrypt(ae_ctx* ctx, const void* nonce, const void* ct, int ct_len, const void* ad,
+ int ad_len, void* pt, const void* tag, int final) {
+ union {
+ uint32_t u32[4];
+ uint8_t u8[16];
+ block bl;
+ } tmp;
+ block offset, checksum;
+ unsigned i, k;
+ block* ctp = (block*)ct;
+ block* ptp = (block*)pt;
+
+ /* Reduce ct_len tag bundled in ct */
+ if ((final) && (!tag))
+#if (OCB_TAG_LEN > 0)
+ ct_len -= OCB_TAG_LEN;
+#else
+ ct_len -= ctx->tag_len;
+#endif
+
+ /* Non-null nonce means start of new message, init per-message values */
+ if (nonce) {
+ ctx->offset = gen_offset_from_nonce(ctx, nonce);
+ ctx->ad_offset = ctx->checksum = zero_block();
+ ctx->ad_blocks_processed = ctx->blocks_processed = 0;
+ if (ad_len >= 0)
+ ctx->ad_checksum = zero_block();
+ }
+
+ /* Process associated data */
+ if (ad_len > 0)
+ process_ad(ctx, ad, ad_len, final);
+
+ /* Encrypt plaintext data BPI blocks at a time */
+ offset = ctx->offset;
+ checksum = ctx->checksum;
+ i = ct_len / (BPI * 16);
+ if (i) {
+ block oa[BPI];
+ unsigned block_num = ctx->blocks_processed;
+ oa[BPI - 1] = offset;
+ do {
+ block ta[BPI];
+ block_num += BPI;
+ oa[0] = xor_block(oa[BPI - 1], ctx->L[0]);
+ ta[0] = xor_block(oa[0], ctp[0]);
+ oa[1] = xor_block(oa[0], ctx->L[1]);
+ ta[1] = xor_block(oa[1], ctp[1]);
+ oa[2] = xor_block(oa[1], ctx->L[0]);
+ ta[2] = xor_block(oa[2], ctp[2]);
+#if BPI == 4
+ oa[3] = xor_block(oa[2], getL(ctx, ntz(block_num)));
+ ta[3] = xor_block(oa[3], ctp[3]);
+#elif BPI == 8
+ oa[3] = xor_block(oa[2], ctx->L[2]);
+ ta[3] = xor_block(oa[3], ctp[3]);
+ oa[4] = xor_block(oa[1], ctx->L[2]);
+ ta[4] = xor_block(oa[4], ctp[4]);
+ oa[5] = xor_block(oa[0], ctx->L[2]);
+ ta[5] = xor_block(oa[5], ctp[5]);
+ oa[6] = xor_block(oa[7], ctx->L[2]);
+ ta[6] = xor_block(oa[6], ctp[6]);
+ oa[7] = xor_block(oa[6], getL(ctx, ntz(block_num)));
+ ta[7] = xor_block(oa[7], ctp[7]);
+#endif
+ AES_ecb_decrypt_blks(ta, BPI, &ctx->decrypt_key);
+ ptp[0] = xor_block(ta[0], oa[0]);
+ checksum = xor_block(checksum, ptp[0]);
+ ptp[1] = xor_block(ta[1], oa[1]);
+ checksum = xor_block(checksum, ptp[1]);
+ ptp[2] = xor_block(ta[2], oa[2]);
+ checksum = xor_block(checksum, ptp[2]);
+ ptp[3] = xor_block(ta[3], oa[3]);
+ checksum = xor_block(checksum, ptp[3]);
+#if (BPI == 8)
+ ptp[4] = xor_block(ta[4], oa[4]);
+ checksum = xor_block(checksum, ptp[4]);
+ ptp[5] = xor_block(ta[5], oa[5]);
+ checksum = xor_block(checksum, ptp[5]);
+ ptp[6] = xor_block(ta[6], oa[6]);
+ checksum = xor_block(checksum, ptp[6]);
+ ptp[7] = xor_block(ta[7], oa[7]);
+ checksum = xor_block(checksum, ptp[7]);
+#endif
+ ptp += BPI;
+ ctp += BPI;
+ } while (--i);
+ ctx->offset = offset = oa[BPI - 1];
+ ctx->blocks_processed = block_num;
+ ctx->checksum = checksum;
+ }
+
+ if (final) {
+ block ta[BPI + 1], oa[BPI];
+
+ /* Process remaining plaintext and compute its tag contribution */
+ unsigned remaining = ((unsigned)ct_len) % (BPI * 16);
+ k = 0; /* How many blocks in ta[] need ECBing */
+ if (remaining) {
+#if (BPI == 8)
+ if (remaining >= 64) {
+ oa[0] = xor_block(offset, ctx->L[0]);
+ ta[0] = xor_block(oa[0], ctp[0]);
+ oa[1] = xor_block(oa[0], ctx->L[1]);
+ ta[1] = xor_block(oa[1], ctp[1]);
+ oa[2] = xor_block(oa[1], ctx->L[0]);
+ ta[2] = xor_block(oa[2], ctp[2]);
+ offset = oa[3] = xor_block(oa[2], ctx->L[2]);
+ ta[3] = xor_block(offset, ctp[3]);
+ remaining -= 64;
+ k = 4;
+ }
+#endif
+ if (remaining >= 32) {
+ oa[k] = xor_block(offset, ctx->L[0]);
+ ta[k] = xor_block(oa[k], ctp[k]);
+ offset = oa[k + 1] = xor_block(oa[k], ctx->L[1]);
+ ta[k + 1] = xor_block(offset, ctp[k + 1]);
+ remaining -= 32;
+ k += 2;
+ }
+ if (remaining >= 16) {
+ offset = oa[k] = xor_block(offset, ctx->L[0]);
+ ta[k] = xor_block(offset, ctp[k]);
+ remaining -= 16;
+ ++k;
+ }
+ if (remaining) {
+ block pad;
+ offset = xor_block(offset, ctx->Lstar);
+ AES_encrypt((unsigned char*)&offset, tmp.u8, &ctx->encrypt_key);
+ pad = tmp.bl;
+ memcpy(tmp.u8, ctp + k, remaining);
+ tmp.bl = xor_block(tmp.bl, pad);
+ tmp.u8[remaining] = (unsigned char)0x80u;
+ memcpy(ptp + k, tmp.u8, remaining);
+ checksum = xor_block(checksum, tmp.bl);
+ }
+ }
+ AES_ecb_decrypt_blks(ta, k, &ctx->decrypt_key);
+ switch (k) {
+#if (BPI == 8)
+ case 7:
+ ptp[6] = xor_block(ta[6], oa[6]);
+ checksum = xor_block(checksum, ptp[6]);
+ case 6:
+ ptp[5] = xor_block(ta[5], oa[5]);
+ checksum = xor_block(checksum, ptp[5]);
+ case 5:
+ ptp[4] = xor_block(ta[4], oa[4]);
+ checksum = xor_block(checksum, ptp[4]);
+ case 4:
+ ptp[3] = xor_block(ta[3], oa[3]);
+ checksum = xor_block(checksum, ptp[3]);
+#endif
+ case 3:
+ ptp[2] = xor_block(ta[2], oa[2]);
+ checksum = xor_block(checksum, ptp[2]);
+ case 2:
+ ptp[1] = xor_block(ta[1], oa[1]);
+ checksum = xor_block(checksum, ptp[1]);
+ case 1:
+ ptp[0] = xor_block(ta[0], oa[0]);
+ checksum = xor_block(checksum, ptp[0]);
+ }
+
+ /* Calculate expected tag */
+ offset = xor_block(offset, ctx->Ldollar);
+ tmp.bl = xor_block(offset, checksum);
+ AES_encrypt(tmp.u8, tmp.u8, &ctx->encrypt_key);
+ tmp.bl = xor_block(tmp.bl, ctx->ad_checksum); /* Full tag */
+
+ /* Compare with proposed tag, change ct_len if invalid */
+ if ((OCB_TAG_LEN == 16) && tag) {
+ if (unequal_blocks(tmp.bl, *(block*)tag))
+ ct_len = AE_INVALID;
+ } else {
+#if (OCB_TAG_LEN > 0)
+ int len = OCB_TAG_LEN;
+#else
+ int len = ctx->tag_len;
+#endif
+ if (tag) {
+ if (constant_time_memcmp(tag, tmp.u8, len) != 0)
+ ct_len = AE_INVALID;
+ } else {
+ if (constant_time_memcmp((char*)ct + ct_len, tmp.u8, len) != 0)
+ ct_len = AE_INVALID;
+ }
+ }
+ }
+ return ct_len;
+}
+
+/* ----------------------------------------------------------------------- */
+/* Simple test program */
+/* ----------------------------------------------------------------------- */
+
+#if 0
+
+#include <stdio.h>
+#include <time.h>
+
+#if __GNUC__
+#define ALIGN(n) __attribute__((aligned(n)))
+#elif _MSC_VER
+#define ALIGN(n) __declspec(align(n))
+#else /* Not GNU/Microsoft: delete alignment uses. */
+#define ALIGN(n)
+#endif
+
+static void pbuf(void *p, unsigned len, const void *s)
+{
+ unsigned i;
+ if (s)
+ printf("%s", (char *)s);
+ for (i = 0; i < len; i++)
+ printf("%02X", (unsigned)(((unsigned char *)p)[i]));
+ printf("\n");
+}
+
+static void vectors(ae_ctx *ctx, int len)
+{
+ ALIGN(16) char pt[128];
+ ALIGN(16) char ct[144];
+ ALIGN(16) char nonce[] = {0,1,2,3,4,5,6,7,8,9,10,11};
+ int i;
+ for (i=0; i < 128; i++) pt[i] = i;
+ i = ae_encrypt(ctx,nonce,pt,len,pt,len,ct,NULL,AE_FINALIZE);
+ printf("P=%d,A=%d: ",len,len); pbuf(ct, i, NULL);
+ i = ae_encrypt(ctx,nonce,pt,0,pt,len,ct,NULL,AE_FINALIZE);
+ printf("P=%d,A=%d: ",0,len); pbuf(ct, i, NULL);
+ i = ae_encrypt(ctx,nonce,pt,len,pt,0,ct,NULL,AE_FINALIZE);
+ printf("P=%d,A=%d: ",len,0); pbuf(ct, i, NULL);
+}
+
+void validate()
+{
+ ALIGN(16) char pt[1024];
+ ALIGN(16) char ct[1024];
+ ALIGN(16) char tag[16];
+ ALIGN(16) char nonce[12] = {0,};
+ ALIGN(16) char key[32] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
+ ae_ctx ctx;
+ char *val_buf, *next;
+ int i, len;
+
+ val_buf = (char *)malloc(22400 + 16);
+ next = val_buf = (char *)(((size_t)val_buf + 16) & ~((size_t)15));
+
+ if (0) {
+ ae_init(&ctx, key, 16, 12, 16);
+ /* pbuf(&ctx, sizeof(ctx), "CTX: "); */
+ vectors(&ctx,0);
+ vectors(&ctx,8);
+ vectors(&ctx,16);
+ vectors(&ctx,24);
+ vectors(&ctx,32);
+ vectors(&ctx,40);
+ }
+
+ memset(key,0,32);
+ memset(pt,0,128);
+ ae_init(&ctx, key, OCB_KEY_LEN, 12, OCB_TAG_LEN);
+
+ /* RFC Vector test */
+ for (i = 0; i < 128; i++) {
+ int first = ((i/3)/(BPI*16))*(BPI*16);
+ int second = first;
+ int third = i - (first + second);
+
+ nonce[11] = i;
+
+ if (0) {
+ ae_encrypt(&ctx,nonce,pt,i,pt,i,ct,NULL,AE_FINALIZE);
+ memcpy(next,ct,(size_t)i+OCB_TAG_LEN);
+ next = next+i+OCB_TAG_LEN;
+
+ ae_encrypt(&ctx,nonce,pt,i,pt,0,ct,NULL,AE_FINALIZE);
+ memcpy(next,ct,(size_t)i+OCB_TAG_LEN);
+ next = next+i+OCB_TAG_LEN;
+
+ ae_encrypt(&ctx,nonce,pt,0,pt,i,ct,NULL,AE_FINALIZE);
+ memcpy(next,ct,OCB_TAG_LEN);
+ next = next+OCB_TAG_LEN;
+ } else {
+ ae_encrypt(&ctx,nonce,pt,first,pt,first,ct,NULL,AE_PENDING);
+ ae_encrypt(&ctx,NULL,pt+first,second,pt+first,second,ct+first,NULL,AE_PENDING);
+ ae_encrypt(&ctx,NULL,pt+first+second,third,pt+first+second,third,ct+first+second,NULL,AE_FINALIZE);
+ memcpy(next,ct,(size_t)i+OCB_TAG_LEN);
+ next = next+i+OCB_TAG_LEN;
+
+ ae_encrypt(&ctx,nonce,pt,first,pt,0,ct,NULL,AE_PENDING);
+ ae_encrypt(&ctx,NULL,pt+first,second,pt,0,ct+first,NULL,AE_PENDING);
+ ae_encrypt(&ctx,NULL,pt+first+second,third,pt,0,ct+first+second,NULL,AE_FINALIZE);
+ memcpy(next,ct,(size_t)i+OCB_TAG_LEN);
+ next = next+i+OCB_TAG_LEN;
+
+ ae_encrypt(&ctx,nonce,pt,0,pt,first,ct,NULL,AE_PENDING);
+ ae_encrypt(&ctx,NULL,pt,0,pt+first,second,ct,NULL,AE_PENDING);
+ ae_encrypt(&ctx,NULL,pt,0,pt+first+second,third,ct,NULL,AE_FINALIZE);
+ memcpy(next,ct,OCB_TAG_LEN);
+ next = next+OCB_TAG_LEN;
+ }
+
+ }
+ nonce[11] = 0;
+ ae_encrypt(&ctx,nonce,NULL,0,val_buf,next-val_buf,ct,tag,AE_FINALIZE);
+ pbuf(tag,OCB_TAG_LEN,0);
+
+
+ /* Encrypt/Decrypt test */
+ for (i = 0; i < 128; i++) {
+ int first = ((i/3)/(BPI*16))*(BPI*16);
+ int second = first;
+ int third = i - (first + second);
+
+ nonce[11] = i%128;
+
+ if (1) {
+ len = ae_encrypt(&ctx,nonce,val_buf,i,val_buf,i,ct,tag,AE_FINALIZE);
+ len = ae_encrypt(&ctx,nonce,val_buf,i,val_buf,-1,ct,tag,AE_FINALIZE);
+ len = ae_decrypt(&ctx,nonce,ct,len,val_buf,-1,pt,tag,AE_FINALIZE);
+ if (len == -1) { printf("Authentication error: %d\n", i); return; }
+ if (len != i) { printf("Length error: %d\n", i); return; }
+ if (memcmp(val_buf,pt,i)) { printf("Decrypt error: %d\n", i); return; }
+ } else {
+ len = ae_encrypt(&ctx,nonce,val_buf,i,val_buf,i,ct,NULL,AE_FINALIZE);
+ ae_decrypt(&ctx,nonce,ct,first,val_buf,first,pt,NULL,AE_PENDING);
+ ae_decrypt(&ctx,NULL,ct+first,second,val_buf+first,second,pt+first,NULL,AE_PENDING);
+ len = ae_decrypt(&ctx,NULL,ct+first+second,len-(first+second),val_buf+first+second,third,pt+first+second,NULL,AE_FINALIZE);
+ if (len == -1) { printf("Authentication error: %d\n", i); return; }
+ if (memcmp(val_buf,pt,i)) { printf("Decrypt error: %d\n", i); return; }
+ }
+
+ }
+ printf("Decrypt: PASS\n");
+}
+
+int main()
+{
+ validate();
+ return 0;
+}
+#endif
+
+#if USE_AES_NI
+char infoString[] = "OCB3 (AES-NI)";
+#elif USE_REFERENCE_AES
+char infoString[] = "OCB3 (Reference)";
+#elif USE_OPENSSL_AES
+char infoString[] = "OCB3 (OpenSSL)";
+#endif
diff --git a/openssl_utils.h b/openssl_utils.h
new file mode 100644
index 0000000..7ea015f
--- /dev/null
+++ b/openssl_utils.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_OPENSSL_UTILS_H_
+#define SYSTEM_KEYMASTER_OPENSSL_UTILS_H_
+
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
+};
+
+struct BIGNUM_Delete {
+ void operator()(BIGNUM* p) const { BN_free(p); }
+};
+
+/**
+ * Many OpenSSL APIs take ownership of an argument on success but don't free the argument on
+ * failure. This means we need to tell our scoped pointers when we've transferred ownership, without
+ * triggering a warning by not using the result of release().
+ */
+template <typename T, typename Delete_T>
+inline void release_because_ownership_transferred(UniquePtr<T, Delete_T>& p) {
+ T* val __attribute__((unused)) = p.release();
+}
+
+inline void convert_bn_to_blob(BIGNUM* bn, keymaster_blob_t* blob) {
+ blob->data_length = BN_num_bytes(bn);
+ blob->data = new uint8_t[blob->data_length];
+ BN_bn2bin(bn, const_cast<uint8_t*>(blob->data));
+}
+
+#endif // SYSTEM_KEYMASTER_OPENSSL_UTILS_H_
diff --git a/operation.h b/operation.h
new file mode 100644
index 0000000..5d20541
--- /dev/null
+++ b/operation.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_OPERATION_H_
+#define SYSTEM_KEYMASTER_OPERATION_H_
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <keymaster/google_keymaster_utils.h>
+#include <keymaster/keymaster_defs.h>
+
+namespace keymaster {
+
+/**
+ * Abstract base for all cryptographic operations.
+ */
+class Operation {
+ public:
+ Operation(keymaster_purpose_t purpose) : purpose_(purpose) {
+ }
+ virtual ~Operation() {
+ }
+
+ keymaster_purpose_t purpose() const {
+ return purpose_;
+ }
+
+ virtual keymaster_error_t Begin() = 0;
+ virtual keymaster_error_t Update(const Buffer& input, Buffer* output) = 0;
+ virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output) = 0;
+ virtual keymaster_error_t Abort() = 0;
+
+ private:
+ const keymaster_purpose_t purpose_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_OPERATION_H_
diff --git a/rsa_operation.cpp b/rsa_operation.cpp
new file mode 100644
index 0000000..816c234
--- /dev/null
+++ b/rsa_operation.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+
+#include "rsa_operation.h"
+#include "openssl_utils.h"
+
+namespace keymaster {
+
+struct RSA_Delete {
+ void operator()(RSA* p) const { RSA_free(p); }
+};
+
+RsaOperation::~RsaOperation() {
+ if (rsa_key_ != NULL)
+ RSA_free(rsa_key_);
+}
+
+keymaster_error_t RsaOperation::Update(const Buffer& input, Buffer* /* output */) {
+ switch (purpose()) {
+ default:
+ return KM_ERROR_UNIMPLEMENTED;
+ case KM_PURPOSE_SIGN:
+ case KM_PURPOSE_VERIFY:
+ return StoreData(input);
+ }
+}
+
+keymaster_error_t RsaOperation::StoreData(const Buffer& input) {
+ if (!data_.reserve(data_.available_read() + input.available_read()) ||
+ !data_.write(input.peek_read(), input.available_read()))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t RsaSignOperation::Finish(const Buffer& /* signature */, Buffer* output) {
+ output->Reinitialize(RSA_size(rsa_key_));
+ if (data_.available_read() != output->buffer_size())
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+
+ int bytes_encrypted = RSA_private_encrypt(data_.available_read(), data_.peek_read(),
+ output->peek_write(), rsa_key_, RSA_NO_PADDING);
+ if (bytes_encrypted < 0)
+ return KM_ERROR_UNKNOWN_ERROR;
+ assert(bytes_encrypted == RSA_size(rsa_key_));
+ output->advance_write(bytes_encrypted);
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t RsaVerifyOperation::Finish(const Buffer& signature, Buffer* /* output */) {
+
+ if ((int)data_.available_read() != RSA_size(rsa_key_))
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ if (data_.available_read() != signature.available_read())
+ return KM_ERROR_VERIFICATION_FAILED;
+
+ UniquePtr<uint8_t[]> decrypted_data(new uint8_t[RSA_size(rsa_key_)]);
+ int bytes_decrypted = RSA_public_decrypt(signature.available_read(), signature.peek_read(),
+ decrypted_data.get(), rsa_key_, RSA_NO_PADDING);
+ if (bytes_decrypted < 0)
+ return KM_ERROR_UNKNOWN_ERROR;
+ assert(bytes_decrypted == RSA_size(rsa_key_));
+
+ if (memcmp_s(decrypted_data.get(), data_.peek_read(), data_.available_read()) == 0)
+ return KM_ERROR_OK;
+ return KM_ERROR_VERIFICATION_FAILED;
+}
+
+} // namespace keymaster
diff --git a/rsa_operation.h b/rsa_operation.h
new file mode 100644
index 0000000..ad0ebfc
--- /dev/null
+++ b/rsa_operation.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_KEYMASTER_RSA_OPERATION_H_
+#define SYSTEM_KEYMASTER_RSA_OPERATION_H_
+
+#include <UniquePtr.h>
+
+#include <keymaster/key_blob.h>
+
+#include "operation.h"
+
+namespace keymaster {
+
+class RsaOperation : public Operation {
+ public:
+ RsaOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, RSA* key)
+ : Operation(purpose), rsa_key_(key), digest_(digest), padding_(padding) {}
+ ~RsaOperation();
+
+ virtual keymaster_error_t Begin() { return KM_ERROR_OK; }
+ virtual keymaster_error_t Update(const Buffer& input, Buffer* output);
+ virtual keymaster_error_t Abort() { return KM_ERROR_OK; }
+
+ protected:
+ keymaster_error_t StoreData(const Buffer& input);
+
+ RSA* rsa_key_;
+ keymaster_digest_t digest_;
+ keymaster_padding_t padding_;
+ Buffer data_;
+};
+
+class RsaSignOperation : public RsaOperation {
+ public:
+ RsaSignOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, RSA* key)
+ : RsaOperation(purpose, digest, padding, key) {}
+ virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
+};
+
+class RsaVerifyOperation : public RsaOperation {
+ public:
+ RsaVerifyOperation(keymaster_purpose_t purpose, keymaster_digest_t digest,
+ keymaster_padding_t padding, RSA* key)
+ : RsaOperation(purpose, digest, padding, key) {}
+ virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_RSA_OPERATION_H_
diff --git a/rsa_privkey_pk8.der b/rsa_privkey_pk8.der
new file mode 100644
index 0000000..0336f80
--- /dev/null
+++ b/rsa_privkey_pk8.der
Binary files differ
diff --git a/serializable.cpp b/serializable.cpp
new file mode 100644
index 0000000..9cfbac7
--- /dev/null
+++ b/serializable.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include <keymaster/serializable.h>
+#include <keymaster/google_keymaster_utils.h>
+
+namespace keymaster {
+
+uint8_t* append_to_buf(uint8_t* buf, const uint8_t* end, const void* data, size_t data_len) {
+ if (buf + data_len <= end)
+ memcpy(buf, data, data_len);
+ return buf + data_len;
+}
+
+bool copy_from_buf(const uint8_t** buf_ptr, const uint8_t* end, void* dest, size_t size) {
+ if (end < *buf_ptr + size)
+ return false;
+ memcpy(dest, *buf_ptr, size);
+ *buf_ptr += size;
+ return true;
+}
+
+bool copy_size_and_data_from_buf(const uint8_t** buf_ptr, const uint8_t* end, size_t* size,
+ UniquePtr<uint8_t[]>* dest) {
+ if (!copy_uint32_from_buf(buf_ptr, end, size) || *buf_ptr + *size > end) {
+ return false;
+ }
+ if (*size == 0) {
+ dest->reset();
+ return true;
+ }
+ dest->reset(new uint8_t[*size]);
+ if (dest->get() == NULL)
+ return false;
+ return copy_from_buf(buf_ptr, end, dest->get(), *size);
+}
+
+bool Buffer::reserve(size_t size) {
+ if (available_write() < size) {
+ size_t new_size = buffer_size_ + size - available_write();
+ uint8_t* new_buffer = new uint8_t[new_size];
+ if (!new_buffer)
+ return false;
+ memcpy(new_buffer, buffer_.get() + read_position_, available_read());
+ memset_s(buffer_.get(), 0, buffer_size_);
+ buffer_.reset(new_buffer);
+ buffer_size_ = new_size;
+ write_position_ -= read_position_;
+ read_position_ = 0;
+ }
+ return true;
+}
+
+bool Buffer::Reinitialize(size_t size) {
+ Clear();
+ buffer_.reset(new uint8_t[size]);
+ if (buffer_.get() == NULL)
+ return false;
+ buffer_size_ = size;
+ read_position_ = 0;
+ write_position_ = 0;
+ return true;
+}
+
+bool Buffer::Reinitialize(const void* data, size_t data_len) {
+ Clear();
+ buffer_.reset(new uint8_t[data_len]);
+ if (buffer_.get() == NULL)
+ return false;
+ buffer_size_ = data_len;
+ memcpy(buffer_.get(), data, data_len);
+ read_position_ = 0;
+ write_position_ = buffer_size_;
+ return true;
+}
+
+size_t Buffer::available_write() const {
+ return buffer_size_ - write_position_;
+}
+
+size_t Buffer::available_read() const {
+ return write_position_ - read_position_;
+}
+
+bool Buffer::write(const uint8_t* src, size_t write_length) {
+ if (available_write() < write_length)
+ return false;
+ memcpy(buffer_.get() + write_position_, src, write_length);
+ write_position_ += write_length;
+ return true;
+}
+
+bool Buffer::read(uint8_t* dest, size_t read_length) {
+ if (available_read() < read_length)
+ return false;
+ memcpy(dest, buffer_.get() + read_position_, read_length);
+ read_position_ += read_length;
+ return true;
+}
+
+size_t Buffer::SerializedSize() const {
+ return sizeof(uint32_t) + available_read();
+}
+
+uint8_t* Buffer::Serialize(uint8_t* buf, const uint8_t* end) const {
+ return append_size_and_data_to_buf(buf, end, peek_read(), available_read());
+}
+
+bool Buffer::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ Clear();
+ if (!copy_size_and_data_from_buf(buf_ptr, end, &buffer_size_, &buffer_)) {
+ buffer_.reset();
+ buffer_size_ = 0;
+ return false;
+ }
+ write_position_ = buffer_size_;
+ return true;
+}
+
+void Buffer::Clear() {
+ if (buffer_.get())
+ memset_s(buffer_.get(), 0, buffer_size_);
+ buffer_.reset();
+ read_position_ = 0;
+ write_position_ = 0;
+ buffer_size_ = 0;
+}
+
+} // namespace keymaster