mirror of
https://fuchsia.googlesource.com/third_party/pigweed.googlesource.com/pigweed/pigweed
synced 2024-09-20 13:51:05 +00:00
cd2ce40d3f
Adds ECDSA signature verification support to keys.py. No-Docs-Update-Reason: module in early development Change-Id: I0e83c20fa76b9d52cf404111e1b283e4952dcb93 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/66882 Pigweed-Auto-Submit: Ali Zhang <alizhang@google.com> Reviewed-by: Joe Ethier <jethier@google.com> Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
194 lines
7.9 KiB
Python
194 lines
7.9 KiB
Python
# Copyright 2021 The Pigweed Authors
|
|
#
|
|
# 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
|
|
#
|
|
# https://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.
|
|
"""Unit tests for pw_software_update/keys.py."""
|
|
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
|
|
from pw_software_update import keys
|
|
from pw_software_update.tuf_pb2 import Key, KeyType, KeyScheme
|
|
|
|
|
|
class KeyGenTest(unittest.TestCase):
|
|
"""Test the generation of keys."""
|
|
def test_ecdsa_keygen(self):
|
|
"""Test ECDSA key generation."""
|
|
with tempfile.TemporaryDirectory() as tempdir_name:
|
|
temp_root = Path(tempdir_name)
|
|
private_key_filename = (temp_root / 'test_key')
|
|
public_key_filename = (temp_root / 'test_key.pub')
|
|
|
|
keys.gen_ecdsa_keypair(private_key_filename)
|
|
|
|
self.assertTrue(private_key_filename.exists())
|
|
self.assertTrue(public_key_filename.exists())
|
|
public_key = keys.import_ecdsa_public_key(
|
|
public_key_filename.read_bytes())
|
|
self.assertEqual(public_key.key.key_type,
|
|
KeyType.ECDSA_SHA2_NISTP256)
|
|
self.assertEqual(public_key.key.scheme,
|
|
KeyScheme.ECDSA_SHA2_NISTP256_SCHEME)
|
|
|
|
|
|
class KeyIdTest(unittest.TestCase):
|
|
"""Test Key ID generations """
|
|
def test_256bit_length(self):
|
|
key_id = keys.gen_key_id(
|
|
Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
|
|
scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
|
|
keyval=b'public_key bytes'))
|
|
self.assertEqual(len(key_id), 32)
|
|
|
|
def test_different_keyval(self):
|
|
key1 = Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
|
|
scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
|
|
keyval=b'key 1 bytes')
|
|
key2 = Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
|
|
scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
|
|
keyval=b'key 2 bytes')
|
|
|
|
key1_id, key2_id = keys.gen_key_id(key1), keys.gen_key_id(key2)
|
|
self.assertNotEqual(key1_id, key2_id)
|
|
|
|
def test_different_key_type(self):
|
|
key1 = Key(key_type=KeyType.RSA,
|
|
scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
|
|
keyval=b'key bytes')
|
|
key2 = Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
|
|
scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
|
|
keyval=b'key bytes')
|
|
|
|
key1_id, key2_id = keys.gen_key_id(key1), keys.gen_key_id(key2)
|
|
self.assertNotEqual(key1_id, key2_id)
|
|
|
|
def test_different_scheme(self):
|
|
key1 = Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
|
|
scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
|
|
keyval=b'key bytes')
|
|
key2 = Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
|
|
scheme=KeyScheme.ED25519_SCHEME,
|
|
keyval=b'key bytes')
|
|
|
|
key1_id, key2_id = keys.gen_key_id(key1), keys.gen_key_id(key2)
|
|
self.assertNotEqual(key1_id, key2_id)
|
|
|
|
|
|
class KeyImportTest(unittest.TestCase):
|
|
"""Test key importing"""
|
|
def setUp(self):
|
|
# Generated with:
|
|
# $> openssl ecparam -name prime256v1 -genkey -noout -out priv.pem
|
|
# $> openssl ec -in priv.pem -pubout -out pub.pem
|
|
# $> cat pub.pem
|
|
self.valid_nistp256_pem_bytes = (
|
|
b'-----BEGIN PUBLIC KEY-----\n'
|
|
b'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKmK5mJwMV7eimA6MfFQL2q6KbZDr'
|
|
b'SnWwoeHvXB/aZBnwF422OLifuOuMjEUEHrNMmoekcua+ulHW41X3AgbvIw==\n'
|
|
b'-----END PUBLIC KEY-----\n')
|
|
|
|
# Generated with:
|
|
# $> openssl ecparam -name secp384r1 -genkey -noout -out priv.pem
|
|
# $> openssl ec -in priv.pem -pubout -out pub.pem
|
|
# $> cat pub.pem
|
|
self.valid_secp384r1_pem_bytes = (
|
|
b'-----BEGIN PUBLIC KEY-----\n'
|
|
b'MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE6xs+TEjb2/vIzs4AzSm2CSUWpJMCPAts'
|
|
b'e+gwvGwFrr2bXKHVLNCxr5/Va6rD0nDmB2NOiJwAXX1Z8CB5wqLLB31emCBFRb5i'
|
|
b'1LjZu8Bp3hrWOL7uvXer8uExnSfTKAoT\n'
|
|
b'-----END PUBLIC KEY-----\n')
|
|
|
|
# Replaces "MF" with "MM"
|
|
self.tampered_nistp256_pem_bytes = (
|
|
b'-----BEGIN PUBLIC KEY-----\n'
|
|
b'MMkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKmK5mJwMV7eimA6MfFQL2q6KbZDr'
|
|
b'SnWwoeHvXB/aZBnwF422OLifuOuMjEUEHrNMmoekcua+ulHW41X3AgbvIw==\n'
|
|
b'-----END PUBLIC KEY-----\n')
|
|
|
|
self.rsa_2048_pem_bytes = (
|
|
b'-----BEGIN PUBLIC KEY-----\n'
|
|
b'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsu0+ol90Ri2BQ5TE9ife'
|
|
b'6aAmAUMzvAD2b3cnWaTBGXKpi7O9PKnfKbMVf/nJcWsyw2Bj8uStx3oV98U6owLO'
|
|
b'vsQwyFKVgLZdrXo2qv0L6ljBfCLJxnDhjesEV/oG04dwdN7qyPwAZtpVBCrC7Qi8'
|
|
b'2rkTnzTQi/1slUxRjliDDhgEdqP7dHbCr7QXNIAA0HFRiOqYmHGD7HNKl67iYmAX'
|
|
b'd/Jv8GfZL/ykZstP6Ow1/ByP1ZKvrZvg2iXjC686hZXiMJLqmp0sIqLire82oW+8'
|
|
b'XFc1uyr1j20m+NI5Siy0G3RbfPXrVKyXIgAYPW12+a/BXR9SrqYJYcWwuOGbHZCM'
|
|
b'pwIDAQAB\n'
|
|
b'-----END PUBLIC KEY-----\n')
|
|
|
|
def test_valid_nistp256_key(self):
|
|
keys.import_ecdsa_public_key(self.valid_nistp256_pem_bytes)
|
|
|
|
def test_tampered_nistp256_key(self):
|
|
with self.assertRaises(ValueError):
|
|
keys.import_ecdsa_public_key(self.tampered_nistp256_pem_bytes)
|
|
|
|
def test_non_ec_key(self):
|
|
with self.assertRaises(TypeError):
|
|
keys.import_ecdsa_public_key(self.rsa_2048_pem_bytes)
|
|
|
|
def test_wrong_curve(self):
|
|
with self.assertRaises(TypeError):
|
|
keys.import_ecdsa_public_key(self.valid_secp384r1_pem_bytes)
|
|
|
|
|
|
class SignatureVerificationTest(unittest.TestCase):
|
|
"""ECDSA signing and verification test."""
|
|
def setUp(self):
|
|
# Generated with:
|
|
# $> openssl ecparam -name prime256v1 -genkey -noout -out priv.pem
|
|
# $> openssl ec -in priv.pem -pubout -out pub.pem
|
|
# $> cat priv.pem pub.pem
|
|
self.private_key_pem = (
|
|
b'-----BEGIN EC PRIVATE KEY-----\n'
|
|
b'MHcCAQEEIH9u1n4qAT59f7KRRl/ZB0Y/BUfS4blba+LONlF4s3ltoAoGCCqGSM49'
|
|
b'AwEHoUQDQgAEgKf3kY9Hi3hxIyqm2EkfqQvJkCijjlJSmEAJ1oAp0Godi5x2af+m'
|
|
b'cSNuBjpRcC8iW8x1/gizqyWlfAVrZV0XdA==\n'
|
|
b'-----END EC PRIVATE KEY-----\n')
|
|
self.public_key_pem = (
|
|
b'-----BEGIN PUBLIC KEY-----\n'
|
|
b'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgKf3kY9Hi3hxIyqm2EkfqQvJkCij'
|
|
b'jlJSmEAJ1oAp0Godi5x2af+mcSNuBjpRcC8iW8x1/gizqyWlfAVrZV0XdA==\n'
|
|
b'-----END PUBLIC KEY-----\n')
|
|
|
|
self.message = b'Hello Pigweed!'
|
|
self.tampered_message = b'Hell0 Pigweed!'
|
|
|
|
def test_good_signature(self):
|
|
sig = keys.create_ecdsa_signature(self.message, self.private_key_pem)
|
|
self.assertTrue(
|
|
keys.verify_ecdsa_signature(
|
|
sig.sig, self.message,
|
|
keys.import_ecdsa_public_key(self.public_key_pem).key))
|
|
|
|
def test_tampered_message(self):
|
|
sig = keys.create_ecdsa_signature(self.message, self.private_key_pem)
|
|
self.assertFalse(
|
|
keys.verify_ecdsa_signature(
|
|
sig.sig, self.tampered_message,
|
|
keys.import_ecdsa_public_key(self.public_key_pem).key))
|
|
|
|
def test_tampered_signature(self):
|
|
sig = keys.create_ecdsa_signature(self.message, self.private_key_pem)
|
|
tampered_sig = bytearray(sig.sig)
|
|
tampered_sig[0] ^= 1
|
|
self.assertFalse(
|
|
keys.verify_ecdsa_signature(
|
|
tampered_sig, self.message,
|
|
keys.import_ecdsa_public_key(self.public_key_pem).key))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|