mirror of
https://github.com/FeitianTech/OpenSK.git
synced 2026-08-28 10:41:01 -07:00
Initial commit
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
[package]
|
||||
name = "crypto"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Fabian Kaczmarczyck <kaczmarczyck@google.com>",
|
||||
"Guillaume Endignoux <guillaumee@google.com>",
|
||||
"Jean-Michel Picod <jmichel@google.com>",
|
||||
]
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libtock = { path = "../../third_party/libtock-rs" }
|
||||
cbor = { path = "../cbor" }
|
||||
arrayref = "0.3.5"
|
||||
subtle = { version = "2.2", default-features = false, features = ["nightly"] }
|
||||
byteorder = { version = "1", default-features = false }
|
||||
hex = { version = "0.3.2", default-features = false, optional = true }
|
||||
ring = { version = "0.14.6", optional = true }
|
||||
untrusted = { version = "0.6.2", optional = true }
|
||||
rand = { version = "0.6.5", optional = true }
|
||||
serde = { version = "1.0", optional = true, features = ["derive"] }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
regex = { version = "1", optional = true }
|
||||
|
||||
[features]
|
||||
std = ["cbor/std", "hex", "rand", "ring", "untrusted", "serde", "serde_json", "regex"]
|
||||
derive_debug = []
|
||||
with_ctap1 = []
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,258 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::util::{xor_block_16, Block16};
|
||||
use super::{Decrypt16BytesBlock, Encrypt16BytesBlock};
|
||||
|
||||
pub fn cbc_encrypt<K>(key: &K, mut iv: Block16, blocks: &mut [Block16])
|
||||
where
|
||||
K: Encrypt16BytesBlock,
|
||||
{
|
||||
for block in blocks {
|
||||
xor_block_16(block, &iv);
|
||||
key.encrypt_block(block);
|
||||
iv = *block;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cbc_decrypt<K>(key: &K, mut iv: Block16, blocks: &mut [Block16])
|
||||
where
|
||||
K: Decrypt16BytesBlock,
|
||||
{
|
||||
for block in blocks {
|
||||
let tmp = *block;
|
||||
key.decrypt_block(block);
|
||||
xor_block_16(block, &iv);
|
||||
iv = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::super::aes256;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_cbc_encrypt_decrypt() {
|
||||
// Test that cbc_decrypt is the inverse of cbc_encrypt for a bunch of block values.
|
||||
let enc_key = aes256::EncryptionKey::new(&[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]);
|
||||
let dec_key = aes256::DecryptionKey::new(&enc_key);
|
||||
|
||||
for len in 0..16 {
|
||||
let mut blocks: Vec<Block16> = vec![Default::default(); len];
|
||||
for i in 0..len {
|
||||
for j in 0..16 {
|
||||
blocks[i][j] = ((len + i) * 16 + j) as u8;
|
||||
}
|
||||
}
|
||||
let iv = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
];
|
||||
let expected = blocks.clone();
|
||||
|
||||
cbc_encrypt(&enc_key, iv, &mut blocks);
|
||||
cbc_decrypt(&dec_key, iv, &mut blocks);
|
||||
assert_eq!(blocks, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbc_encrypt_1block_zero_iv() {
|
||||
let key = aes256::EncryptionKey::new(&[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]);
|
||||
|
||||
let mut blocks = [[
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
]];
|
||||
let iv = [0; 16];
|
||||
cbc_encrypt(&key, iv, &mut blocks);
|
||||
|
||||
let mut expected = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
];
|
||||
key.encrypt_block(&mut expected);
|
||||
|
||||
assert_eq!(blocks, [expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbc_decrypt_1block_zero_iv() {
|
||||
let key = aes256::DecryptionKey::new(&aes256::EncryptionKey::new(&[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]));
|
||||
|
||||
let mut blocks = [[
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
]];
|
||||
let iv = [0; 16];
|
||||
cbc_decrypt(&key, iv, &mut blocks);
|
||||
|
||||
let mut expected = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
];
|
||||
key.decrypt_block(&mut expected);
|
||||
|
||||
assert_eq!(blocks, [expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbc_encrypt_1block() {
|
||||
let key = aes256::EncryptionKey::new(&[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]);
|
||||
|
||||
let mut blocks = [[
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
]];
|
||||
let iv = [
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
|
||||
0x3e, 0x3f,
|
||||
];
|
||||
cbc_encrypt(&key, iv, &mut blocks);
|
||||
|
||||
let mut expected = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
];
|
||||
xor_block_16(&mut expected, &iv);
|
||||
key.encrypt_block(&mut expected);
|
||||
|
||||
assert_eq!(blocks, [expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbc_decrypt_1block() {
|
||||
let key = aes256::DecryptionKey::new(&aes256::EncryptionKey::new(&[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]));
|
||||
|
||||
let mut blocks = [[
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
]];
|
||||
let iv = [
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
|
||||
0x3e, 0x3f,
|
||||
];
|
||||
cbc_decrypt(&key, iv, &mut blocks);
|
||||
|
||||
let mut expected = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
];
|
||||
key.decrypt_block(&mut expected);
|
||||
xor_block_16(&mut expected, &iv);
|
||||
|
||||
assert_eq!(blocks, [expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbc_encrypt_2blocks() {
|
||||
let key = aes256::EncryptionKey::new(&[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]);
|
||||
|
||||
let mut blocks = [
|
||||
[
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
],
|
||||
[
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
|
||||
0x4e, 0x4f,
|
||||
],
|
||||
];
|
||||
let iv = [
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
|
||||
0x3e, 0x3f,
|
||||
];
|
||||
cbc_encrypt(&key, iv, &mut blocks);
|
||||
|
||||
let mut expected0 = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
];
|
||||
let mut expected1 = [
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
|
||||
0x4e, 0x4f,
|
||||
];
|
||||
xor_block_16(&mut expected0, &iv);
|
||||
key.encrypt_block(&mut expected0);
|
||||
xor_block_16(&mut expected1, &expected0);
|
||||
key.encrypt_block(&mut expected1);
|
||||
|
||||
assert_eq!(blocks, [expected0, expected1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbc_decrypt_2blocks() {
|
||||
let key = aes256::DecryptionKey::new(&aes256::EncryptionKey::new(&[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]));
|
||||
|
||||
let mut blocks = [
|
||||
[
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
],
|
||||
[
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
|
||||
0x4e, 0x4f,
|
||||
],
|
||||
];
|
||||
let iv = [
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
|
||||
0x3e, 0x3f,
|
||||
];
|
||||
cbc_decrypt(&key, iv, &mut blocks);
|
||||
|
||||
let mut expected0 = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
];
|
||||
let mut expected1 = [
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
|
||||
0x4e, 0x4f,
|
||||
];
|
||||
key.decrypt_block(&mut expected1);
|
||||
xor_block_16(&mut expected1, &expected0);
|
||||
key.decrypt_block(&mut expected0);
|
||||
xor_block_16(&mut expected0, &iv);
|
||||
|
||||
assert_eq!(blocks, [expected0, expected1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::super::rng256::Rng256;
|
||||
use super::int256::{Digit, Int256};
|
||||
use core::ops::Mul;
|
||||
use subtle::{self, Choice, ConditionallySelectable, CtOption};
|
||||
|
||||
// An exponent on the elliptic curve, that is an element modulo the curve order N.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
// TODO: remove this Default once https://github.com/dalek-cryptography/subtle/issues/63 is
|
||||
// resolved.
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "derive_debug", derive(Debug))]
|
||||
pub struct ExponentP256 {
|
||||
int: Int256,
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for ExponentP256 {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Self {
|
||||
int: Int256::conditional_select(&a.int, &b.int, choice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExponentP256 {
|
||||
/** Constructors **/
|
||||
pub fn from_int_checked(int: Int256) -> CtOption<ExponentP256> {
|
||||
CtOption::new(ExponentP256 { int }, int.ct_lt(&Int256::N))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
// Normally the ExponentP256 type guarantees that its values stay in [0, N[ because N is the
|
||||
// curve order and therefore exponents >= N are equivalent to their reduction modulo N.
|
||||
// This unsafe function is only used in tests to check that N is indeed the curve order.
|
||||
pub unsafe fn from_int_unchecked(int: Int256) -> ExponentP256 {
|
||||
ExponentP256 { int }
|
||||
}
|
||||
|
||||
pub fn modn(int: Int256) -> ExponentP256 {
|
||||
ExponentP256 {
|
||||
int: int.modd(&Int256::N),
|
||||
}
|
||||
}
|
||||
|
||||
/** Helpful getters **/
|
||||
pub fn bit(&self, i: usize) -> Digit {
|
||||
self.int.bit(i)
|
||||
}
|
||||
|
||||
pub fn to_int(self) -> Int256 {
|
||||
self.int
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> subtle::Choice {
|
||||
self.int.is_zero()
|
||||
}
|
||||
|
||||
pub fn non_zero(self) -> CtOption<NonZeroExponentP256> {
|
||||
CtOption::new(NonZeroExponentP256 { e: self }, !self.is_zero())
|
||||
}
|
||||
|
||||
/** Arithmetic **/
|
||||
pub fn mul_top(&self, other: &Int256, other_top: Digit) -> ExponentP256 {
|
||||
ExponentP256 {
|
||||
int: Int256::modmul_top(&self.int, other, other_top, &Int256::N),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Arithmetic operators **/
|
||||
impl Mul for &ExponentP256 {
|
||||
type Output = ExponentP256;
|
||||
|
||||
fn mul(self, other: &ExponentP256) -> ExponentP256 {
|
||||
ExponentP256 {
|
||||
int: Int256::modmul(&self.int, &other.int, &Int256::N),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A non-zero exponent on the elliptic curve.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
// TODO: remove this Default once https://github.com/dalek-cryptography/subtle/issues/63 is
|
||||
// resolved.
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "derive_debug", derive(Debug))]
|
||||
pub struct NonZeroExponentP256 {
|
||||
e: ExponentP256,
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for NonZeroExponentP256 {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Self {
|
||||
e: ExponentP256::conditional_select(&a.e, &b.e, choice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NonZeroExponentP256 {
|
||||
/** RNG **/
|
||||
// Generates a uniformly distributed element 0 < k < N
|
||||
pub fn gen_uniform<R>(r: &mut R) -> NonZeroExponentP256
|
||||
where
|
||||
R: Rng256,
|
||||
{
|
||||
loop {
|
||||
let x = Int256::gen_uniform_256(r);
|
||||
if bool::from(Int256::N_MIN_2.ct_lt(&x)) {
|
||||
continue;
|
||||
}
|
||||
// At this point, x <= n - 2.
|
||||
// We add 1 so that 0 < result < n.
|
||||
return NonZeroExponentP256 {
|
||||
e: ExponentP256 { int: (&x + 1).0 },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructors **/
|
||||
pub fn from_int_checked(int: Int256) -> CtOption<NonZeroExponentP256> {
|
||||
ExponentP256::from_int_checked(int)
|
||||
.and_then(|e| CtOption::new(NonZeroExponentP256 { e }, !e.is_zero()))
|
||||
}
|
||||
|
||||
/** Helpful getters **/
|
||||
pub fn to_int(self) -> Int256 {
|
||||
self.e.to_int()
|
||||
}
|
||||
|
||||
pub fn as_exponent(&self) -> &ExponentP256 {
|
||||
&self.e
|
||||
}
|
||||
|
||||
/** Arithmetic **/
|
||||
// Compute the inverse modulo N. This uses Fermat's little theorem for constant-timeness.
|
||||
pub fn inv(&self) -> NonZeroExponentP256 {
|
||||
NonZeroExponentP256 {
|
||||
e: ExponentP256 {
|
||||
int: self.e.int.modpow(&Int256::N_MIN_2, &Int256::N),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn inv_vartime(&self) -> NonZeroExponentP256 {
|
||||
NonZeroExponentP256 {
|
||||
e: ExponentP256 {
|
||||
int: self.e.int.modinv_vartime(&Int256::N),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Arithmetic operators **/
|
||||
impl Mul for &NonZeroExponentP256 {
|
||||
type Output = NonZeroExponentP256;
|
||||
|
||||
// The product of two non-zero elements is also non-zero, because the curve order N is prime.
|
||||
fn mul(self, other: &NonZeroExponentP256) -> NonZeroExponentP256 {
|
||||
NonZeroExponentP256 {
|
||||
e: &self.e * &other.e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::super::montgomery::Montgomery;
|
||||
use super::*;
|
||||
use crate::util::ToOption;
|
||||
|
||||
const ZERO: ExponentP256 = ExponentP256 { int: Int256::ZERO };
|
||||
const ONE: NonZeroExponentP256 = NonZeroExponentP256 {
|
||||
e: ExponentP256 { int: Int256::ONE },
|
||||
};
|
||||
const N_MIN_1_INT: Int256 = Int256::new([
|
||||
0xfc632550, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, 0xffffffff, 0xffffffff, 0x00000000,
|
||||
0xffffffff,
|
||||
]);
|
||||
const N_MIN_1: NonZeroExponentP256 = NonZeroExponentP256 {
|
||||
e: ExponentP256 { int: N_MIN_1_INT },
|
||||
};
|
||||
|
||||
fn get_nonzero_test_values() -> Vec<NonZeroExponentP256> {
|
||||
let mut values: Vec<NonZeroExponentP256> = Montgomery::PRECOMPUTED
|
||||
.iter()
|
||||
.flatten()
|
||||
.flatten()
|
||||
.map(|x| {
|
||||
ExponentP256::modn(x.montgomery_to_field().to_int())
|
||||
.non_zero()
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
values.extend(
|
||||
super::super::int256::test::get_nonzero_test_values()
|
||||
.iter()
|
||||
.filter_map(|&x| {
|
||||
let y = ExponentP256::modn(x).non_zero();
|
||||
if bool::from(y.is_some()) {
|
||||
Some(y.unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
);
|
||||
values.push(ONE);
|
||||
values
|
||||
}
|
||||
|
||||
pub fn get_test_values() -> Vec<ExponentP256> {
|
||||
let mut values: Vec<ExponentP256> = get_nonzero_test_values()
|
||||
.iter()
|
||||
.map(|x| *x.as_exponent())
|
||||
.collect();
|
||||
values.push(ZERO);
|
||||
values
|
||||
}
|
||||
|
||||
/** Constructors **/
|
||||
#[test]
|
||||
fn test_from_int_checked() {
|
||||
assert_eq!(
|
||||
ExponentP256::from_int_checked(Int256::ZERO).to_option(),
|
||||
Some(ExponentP256 { int: Int256::ZERO })
|
||||
);
|
||||
assert_eq!(
|
||||
ExponentP256::from_int_checked(Int256::ONE).to_option(),
|
||||
Some(ExponentP256 { int: Int256::ONE })
|
||||
);
|
||||
assert_eq!(
|
||||
ExponentP256::from_int_checked(N_MIN_1_INT).to_option(),
|
||||
Some(ExponentP256 { int: N_MIN_1_INT })
|
||||
);
|
||||
assert_eq!(ExponentP256::from_int_checked(Int256::N).to_option(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_modn() {
|
||||
assert_eq!(
|
||||
ExponentP256::modn(Int256::ZERO),
|
||||
ExponentP256 { int: Int256::ZERO }
|
||||
);
|
||||
assert_eq!(
|
||||
ExponentP256::modn(Int256::ONE),
|
||||
ExponentP256 { int: Int256::ONE }
|
||||
);
|
||||
assert_eq!(
|
||||
ExponentP256::modn(N_MIN_1_INT),
|
||||
ExponentP256 { int: N_MIN_1_INT }
|
||||
);
|
||||
assert_eq!(
|
||||
ExponentP256::modn(Int256::N),
|
||||
ExponentP256 { int: Int256::ZERO }
|
||||
);
|
||||
}
|
||||
|
||||
/** Arithmetic operations: inverse **/
|
||||
#[test]
|
||||
fn test_inv_is_inv_vartime() {
|
||||
for x in &get_nonzero_test_values() {
|
||||
assert_eq!(x.inv(), x.inv_vartime());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_self_times_inv_is_one() {
|
||||
for x in &get_nonzero_test_values() {
|
||||
assert_eq!(x * &x.inv(), ONE);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inv_inv() {
|
||||
for x in get_nonzero_test_values() {
|
||||
assert_eq!(x.inv().inv(), x);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_well_known_inverses() {
|
||||
assert_eq!(ONE.inv(), ONE);
|
||||
assert_eq!(N_MIN_1.inv(), N_MIN_1);
|
||||
}
|
||||
|
||||
/** RNG **/
|
||||
// Mock rng that samples through a list of values, then panics.
|
||||
struct StressTestingRng {
|
||||
values: Vec<Int256>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl StressTestingRng {
|
||||
pub fn new(values: Vec<Int256>) -> StressTestingRng {
|
||||
StressTestingRng { values, index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Rng256 for StressTestingRng {
|
||||
// This function is unused, as we redefine gen_uniform_u32x8.
|
||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn gen_uniform_u32x8(&mut self) -> [u32; 8] {
|
||||
let result = self.values[self.index].digits();
|
||||
self.index += 1;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uniform_non_zero_is_below_n() {
|
||||
let mut rng = StressTestingRng::new(vec![
|
||||
Int256::new([
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff,
|
||||
]),
|
||||
Int256::N,
|
||||
N_MIN_1.to_int(),
|
||||
Int256::N_MIN_2,
|
||||
]);
|
||||
|
||||
assert_eq!(NonZeroExponentP256::gen_uniform(&mut rng), N_MIN_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uniform_n_is_above_zero() {
|
||||
let mut rng = StressTestingRng::new(vec![Int256::ZERO]);
|
||||
|
||||
assert_eq!(NonZeroExponentP256::gen_uniform(&mut rng), ONE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::int256::{Digit, Int256};
|
||||
use core::ops::Mul;
|
||||
use subtle::Choice;
|
||||
|
||||
// A field element on the elliptic curve, that is an element modulo the prime P.
|
||||
// This is the format used to serialize coordinates of points on the curve.
|
||||
// This implements enough methods to validate points and to convert them to/from the Montgomery
|
||||
// form, which is more convenient to operate on.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct GFP256 {
|
||||
int: Int256,
|
||||
}
|
||||
|
||||
impl GFP256 {
|
||||
pub const ZERO: GFP256 = GFP256 { int: Int256::ZERO };
|
||||
pub const ONE: GFP256 = GFP256 { int: Int256::ONE };
|
||||
pub const B: GFP256 = GFP256 { int: Int256::B };
|
||||
pub const R: GFP256 = GFP256 { int: Int256::R };
|
||||
pub const R_INV: GFP256 = GFP256 { int: Int256::R_INV };
|
||||
|
||||
/** Constructors **/
|
||||
pub fn from_int_checked(int: Int256) -> Option<GFP256> {
|
||||
if bool::from(int.ct_lt(&Int256::P)) {
|
||||
Some(GFP256 { int })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/** Helpful getters **/
|
||||
pub fn to_int(self) -> Int256 {
|
||||
self.int
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> Choice {
|
||||
self.int.is_zero()
|
||||
}
|
||||
|
||||
/** Arithmetic **/
|
||||
pub fn mul_top(&self, other: &Int256, other_top: Digit) -> GFP256 {
|
||||
GFP256 {
|
||||
int: Int256::modmul_top(&self.int, other, other_top, &Int256::P),
|
||||
}
|
||||
}
|
||||
|
||||
/** Point validation **/
|
||||
// Verify that all of the following are true:
|
||||
// * y^2 == x^3 - 3x + b mod p
|
||||
// * 0 < x < p
|
||||
// * 0 < y < p
|
||||
//
|
||||
// Not constant time.
|
||||
pub fn is_valid_point_vartime(x: &GFP256, y: &GFP256) -> bool {
|
||||
if bool::from(x.is_zero()) || bool::from(y.is_zero()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// y^2
|
||||
let y2 = y * y;
|
||||
|
||||
// x^3
|
||||
let x2 = x * x;
|
||||
let x3 = &x2 * x;
|
||||
|
||||
// x^3 - 3x + b
|
||||
let mut xx = x3;
|
||||
xx = xx.sub_vartime(x);
|
||||
xx = xx.sub_vartime(x);
|
||||
xx = xx.sub_vartime(x);
|
||||
xx = xx.add_vartime(&GFP256::B);
|
||||
|
||||
xx == y2
|
||||
}
|
||||
|
||||
/** Arithmetic operators **/
|
||||
fn add_vartime(self, other: &GFP256) -> GFP256 {
|
||||
GFP256 {
|
||||
int: Int256::modadd_vartime(&self.int, &other.int, &Int256::P),
|
||||
}
|
||||
}
|
||||
|
||||
fn sub_vartime(self, other: &GFP256) -> GFP256 {
|
||||
GFP256 {
|
||||
int: Int256::modsub_vartime(&self.int, &other.int, &Int256::P),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Arithmetic operators **/
|
||||
impl Mul for &GFP256 {
|
||||
type Output = GFP256;
|
||||
|
||||
fn mul(self, other: &GFP256) -> GFP256 {
|
||||
GFP256 {
|
||||
int: Int256::modmul(&self.int, &other.int, &Int256::P),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "derive_debug")]
|
||||
impl core::fmt::Debug for GFP256 {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "GFP256::{:?}", self.int)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::super::montgomery::Montgomery;
|
||||
use super::*;
|
||||
use core::ops::{Add, Sub};
|
||||
|
||||
const P_MIN_1_INT: Int256 = Int256::new([
|
||||
0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
|
||||
0xffffffff,
|
||||
]);
|
||||
|
||||
fn get_test_values() -> Vec<GFP256> {
|
||||
let mut values: Vec<GFP256> = Montgomery::PRECOMPUTED
|
||||
.iter()
|
||||
.flatten()
|
||||
.flatten()
|
||||
.map(|x| x.montgomery_to_field())
|
||||
.collect();
|
||||
values.extend(
|
||||
super::super::int256::test::get_1bit_one_test_values()
|
||||
.iter()
|
||||
.filter_map(|&x| GFP256::from_int_checked(x)),
|
||||
);
|
||||
values.extend(
|
||||
super::super::int256::test::get_1bit_zero_test_values()
|
||||
.iter()
|
||||
.filter_map(|&x| GFP256::from_int_checked(x)),
|
||||
);
|
||||
values.push(GFP256::ZERO);
|
||||
values.push(GFP256::ONE);
|
||||
values.push(GFP256::B);
|
||||
values
|
||||
}
|
||||
|
||||
/** Arithmetic operators, only for tests as these are not constant time **/
|
||||
impl Add for &GFP256 {
|
||||
type Output = GFP256;
|
||||
|
||||
fn add(self, other: &GFP256) -> GFP256 {
|
||||
self.add_vartime(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for &GFP256 {
|
||||
type Output = GFP256;
|
||||
|
||||
fn sub(self, other: &GFP256) -> GFP256 {
|
||||
self.sub_vartime(other)
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructors **/
|
||||
#[test]
|
||||
fn test_from_int_checked() {
|
||||
assert_eq!(
|
||||
GFP256::from_int_checked(Int256::ZERO),
|
||||
Some(GFP256 { int: Int256::ZERO })
|
||||
);
|
||||
assert_eq!(
|
||||
GFP256::from_int_checked(Int256::ONE),
|
||||
Some(GFP256 { int: Int256::ONE })
|
||||
);
|
||||
assert_eq!(
|
||||
GFP256::from_int_checked(P_MIN_1_INT),
|
||||
Some(GFP256 { int: P_MIN_1_INT })
|
||||
);
|
||||
assert_eq!(GFP256::from_int_checked(Int256::P), None);
|
||||
}
|
||||
|
||||
/** Point validation **/
|
||||
// See point.rs
|
||||
|
||||
/** Arithmetic operators **/
|
||||
// Due to the 3 nested loops, this test is super slow with debug assertions enabled.
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[test]
|
||||
fn test_add_is_associative() {
|
||||
for x in &get_test_values() {
|
||||
for y in &get_test_values() {
|
||||
for z in &get_test_values() {
|
||||
assert_eq!(&(x + y) + z, x + &(y + z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_is_commutative() {
|
||||
for x in &get_test_values() {
|
||||
for y in &get_test_values() {
|
||||
assert_eq!(x + y, y + x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_sub() {
|
||||
for x in &get_test_values() {
|
||||
for y in &get_test_values() {
|
||||
assert_eq!(&(x - y) + y, *x);
|
||||
assert_eq!(&(x + y) - y, *x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Due to the 3 nested loops, this test is super slow with debug assertions enabled.
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[test]
|
||||
fn test_mul_is_associative() {
|
||||
for x in &get_test_values() {
|
||||
for y in &get_test_values() {
|
||||
for z in &get_test_values() {
|
||||
assert_eq!(&(x * y) * z, x * &(y * z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_is_commutative() {
|
||||
for x in &get_test_values() {
|
||||
for y in &get_test_values() {
|
||||
assert_eq!(x * y, y * x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Due to the 3 nested loops, this test is super slow with debug assertions enabled.
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[test]
|
||||
fn test_mul_is_distributive() {
|
||||
for x in &get_test_values() {
|
||||
for y in &get_test_values() {
|
||||
for z in &get_test_values() {
|
||||
assert_eq!(&(x + y) * z, &(x * z) + &(y * z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
pub mod exponent256;
|
||||
mod gfp256;
|
||||
pub mod int256;
|
||||
mod montgomery;
|
||||
pub mod point;
|
||||
mod precomputed;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,324 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::montgomery::{Montgomery, NLIMBS};
|
||||
|
||||
pub const PRECOMPUTED: [[[Montgomery; 2]; 15]; 2] = [
|
||||
[
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[0]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[1]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[2]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[3]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[4]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[5]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[6]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[7]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[8]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[9]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[10]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[11]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[12]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[13]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[14]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[15]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[16]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[17]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[18]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[19]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[20]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[21]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[22]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[23]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[24]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[25]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[26]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[27]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[28]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[29]),
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[30]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[31]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[32]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[33]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[34]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[35]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[36]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[37]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[38]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[39]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[40]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[41]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[42]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[43]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[44]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[45]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[46]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[47]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[48]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[49]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[50]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[51]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[52]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[53]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[54]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[55]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[56]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[57]),
|
||||
],
|
||||
[
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[58]),
|
||||
Montgomery::new(PRECOMPUTED_LIMBS[59]),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
const PRECOMPUTED_LIMBS: [[u32; NLIMBS]; 60] = [
|
||||
[0x11522878, 0x0e730d41, 0x0db60179, 0x04afe2ff, 0x12883add, 0x0caddd88, 0x119e7edc, 0x0d4a6eab, 0x03120bee],
|
||||
[0x1d2aac15, 0x0f25357c, 0x19e45cdd, 0x05c721d0, 0x1992c5a5, 0x0a237487, 0x0154ba21, 0x014b10bb, 0x00ae3fe3],
|
||||
[0x0d41a576, 0x0922fc51, 0x0234994f, 0x060b60d3, 0x164586ae, 0x0ce95f18, 0x1fe49073, 0x03fa36cc, 0x05ebcd2c],
|
||||
[0x0b402f2f, 0x015c70bf, 0x1561925c, 0x05a26704, 0x0da91e90, 0x0cdc1c7f, 0x1ea12446, 0x0e1ade1e, 0x0ec91f22],
|
||||
[0x026f7778, 0x0566847e, 0x0a0bec9e, 0x0234f453, 0x1a31f21a, 0x0d85e75c, 0x056c7109, 0x0a267a00, 0x0b57c050],
|
||||
[0x0098fb57, 0x0aa837cc, 0x060c0792, 0x0cfa5e19, 0x061bab9e, 0x0589e39b, 0x00a324c5, 0x07d6dee7, 0x02976e4b],
|
||||
[0x1fc4124a, 0x0a8c244b, 0x1ce86762, 0x0cd61c7e, 0x1831c8e0, 0x075774e1, 0x1d96a5a9, 0x0843a649, 0x0c3ab0fa],
|
||||
[0x06e2e7d5, 0x07673a2a, 0x178b65e8, 0x04003e9b, 0x1a1f11c2, 0x007816ea, 0x0f643e11, 0x058c43df, 0x0f423fc2],
|
||||
[0x19633ffa, 0x0891f2b2, 0x123c231c, 0x046add8c, 0x054700dd, 0x059e2b17, 0x172db40f, 0x083e277d, 0x0b0dd609],
|
||||
[0x0fd1da12, 0x035c6e52, 0x19ede20c, 0x0d19e0c0, 0x097d0f40, 0x0b015b19, 0x0449e3f5, 0x00e10c9e, 0x033ab581],
|
||||
[0x056a67ab, 0x0577734d, 0x1dddc062, 0x0c57b10d, 0x0149b39d, 0x026a9e7b, 0x0c35df9f, 0x048764cd, 0x076dbcca],
|
||||
[0x0ca4b366, 0x0e9303ab, 0x1a7480e7, 0x057e9e81, 0x1e13eb50, 0x0f466cf3, 0x06f16b20, 0x04ba3173, 0x0c168c33],
|
||||
[0x15cb5439, 0x06a38e11, 0x073658bd, 0x0b29564f, 0x03f6dc5b, 0x0053b97e, 0x1322c4c0, 0x065dd7ff, 0x03a1e4f6],
|
||||
[0x14e614aa, 0x09246317, 0x1bc83aca, 0x0ad97eed, 0x0d38ce4a, 0x0f82b006, 0x0341f077, 0x0a6add89, 0x04894acd],
|
||||
[0x09f162d5, 0x0f8410ef, 0x1b266a56, 0x00d7f223, 0x03e0cb92, 0x0e39b672, 0x06a2901a, 0x069a8556, 0x0007e7c0],
|
||||
[0x09b7d8d3, 0x00309a80, 0x1ad05f7f, 0x0c2fb5dd, 0x0cbfd41d, 0x09ceb638, 0x1051825c, 0x0da0cf5b, 0x0812e881],
|
||||
[0x06f35669, 0x06a56f2c, 0x1df8d184, 0x00345820, 0x1477d477, 0x01645db1, 0x0be80c51, 0x0c22be3e, 0x0e35e65a],
|
||||
[0x1aeb7aa0, 0x0c375315, 0x0f67bc99, 0x07fdd7b9, 0x191fc1be, 0x0061235d, 0x02c184e9, 0x01c5a839, 0x047a1e26],
|
||||
[0x0b7cb456, 0x093e225d, 0x14f3c6ed, 0x0ccc1ac9, 0x17fe37f3, 0x04988989, 0x1a90c502, 0x02f32042, 0x0a17769b],
|
||||
[0x0afd8c7c, 0x08191c6e, 0x1dcdb237, 0x016200c0, 0x107b32a1, 0x066c08db, 0x10d06a02, 0x0003fc93, 0x05620023],
|
||||
[0x16722b27, 0x068b5c59, 0x0270fcfc, 0x0fad0ecc, 0x0e5de1c2, 0x0eab466b, 0x02fc513c, 0x0407f75c, 0x0baab133],
|
||||
[0x09705fe9, 0x0b88b8e7, 0x0734c993, 0x01e1ff8f, 0x19156970, 0x0abd0f00, 0x10469ea7, 0x03293ac0, 0x0cdc98aa],
|
||||
[0x01d843fd, 0x0e14bfe8, 0x15be825f, 0x008b5212, 0x0eb3fb67, 0x081cbd29, 0x0bc62f16, 0x02b6fcc7, 0x0f5a4e29],
|
||||
[0x13560b66, 0x0c0b6ac2, 0x051ae690, 0x0d41e271, 0x0f3e9bd4, 0x01d70aab, 0x01029f72, 0x073e1c35, 0x0ee70fbc],
|
||||
[0x0ad81baf, 0x09ecc49a, 0x086c741e, 0x0fe6be30, 0x176752e7, 0x0023d416, 0x1f83de85, 0x027de188, 0x066f70b8],
|
||||
[0x181cd51f, 0x096b6e4c, 0x188f2335, 0x0a5df759, 0x17a77eb6, 0x0feb0e73, 0x154ae914, 0x02f3ec51, 0x03826b59],
|
||||
[0x0b91f17d, 0x01c72949, 0x1362bf0a, 0x0e23fddf, 0x0a5614b0, 0x000f7d8f, 0x00079061, 0x0823d9d2, 0x08213f39],
|
||||
[0x1128ae0b, 0x0d095d05, 0x0b85c0c2, 0x01ecb2ef, 0x024ddc84, 0x0e35e901, 0x18411a4a, 0x0f5ddc3d, 0x03786689],
|
||||
[0x052260e8, 0x05ae3564, 0x0542b10d, 0x08d93a45, 0x19952aa4, 0x0996cc41, 0x1051a729, 0x04be3499, 0x052b23aa],
|
||||
[0x109f307e, 0x06f5b6bb, 0x1f84e1e7, 0x077a0cfa, 0x10c4df3f, 0x025a02ea, 0x0b048035, 0x0e31de66, 0x0c6ecaa3],
|
||||
[0x028ea335, 0x02886024, 0x1372f020, 0x00f55d35, 0x15e4684c, 0x0f2a9e17, 0x1a4a7529, 0x0cb7beb1, 0x0b2a78a1],
|
||||
[0x1ab21f1f, 0x06361ccf, 0x06c9179d, 0x0b135627, 0x1267b974, 0x04408bad, 0x1cbff658, 0x0e3d6511, 0x00c7d76f],
|
||||
[0x01cc7a69, 0x0e7ee31b, 0x054fab4f, 0x002b914f, 0x1ad27a30, 0x0cd3579e, 0x0c50124c, 0x050daa90, 0x00b13f72],
|
||||
[0x0b06aa75, 0x070f5cc6, 0x1649e5aa, 0x084a5312, 0x0329043c, 0x041c4011, 0x13d32411, 0x0b04a838, 0x0d760d2d],
|
||||
[0x1713b532, 0x0baa0c03, 0x084022ab, 0x06bcf5c1, 0x02f45379, 0x018ae070, 0x18c9e11e, 0x020bca9a, 0x066f496b],
|
||||
[0x03eef294, 0x067500d2, 0x0d7f613c, 0x002dbbeb, 0x0b741038, 0x0e04133f, 0x1582968d, 0x0be985f7, 0x01acbc1a],
|
||||
[0x1a6a939f, 0x033e50f6, 0x0d665ed4, 0x0b4b7bd6, 0x1e5a3799, 0x06b33847, 0x17fa56ff, 0x065ef930, 0x0021dc4a],
|
||||
[0x02b37659, 0x0450fe17, 0x0b357b65, 0x0df5efac, 0x15397bef, 0x09d35a7f, 0x112ac15f, 0x0624e62e, 0x0a90ae2f],
|
||||
[0x107eecd2, 0x01f69bbe, 0x077d6bce, 0x05741394, 0x13c684fc, 0x0950c910, 0x0725522b, 0x0dc78583, 0x040eeabb],
|
||||
[0x1fde328a, 0x0bd61d96, 0x0d28c387, 0x09e77d89, 0x12550c40, 0x0759cb7d, 0x0367ef34, 0x0ae2a960, 0x091b8bdc],
|
||||
[0x093462a9, 0x00f469ef, 0x0b2e9aef, 0x0d2ca771, 0x054e1f42, 0x007aaa49, 0x06316abb, 0x02413c8e, 0x05425bf9],
|
||||
[0x1bed3e3a, 0x0f272274, 0x1f5e7326, 0x06416517, 0x0ea27072, 0x09cedea7, 0x006e7633, 0x07c91952, 0x0d806dce],
|
||||
[0x08e2a7e1, 0x0e421e1a, 0x0418c9e1, 0x01dbc890, 0x1b395c36, 0x0a1dc175, 0x1dc4ef73, 0x08956f34, 0x0e4b5cf2],
|
||||
[0x1b0d3a18, 0x03194a36, 0x06c2641f, 0x0e44124c, 0x0a2f4eaa, 0x0a8c25ba, 0x0f927ed7, 0x0627b614, 0x07371cca],
|
||||
[0x0ba16694, 0x0417bc03, 0x07c0a7e3, 0x09c35c19, 0x1168a205, 0x08b6b00d, 0x10e3edc9, 0x09c19bf2, 0x05882229],
|
||||
[0x1b2b4162, 0x0a5cef1a, 0x1543622b, 0x09bd433e, 0x0364e04d, 0x07480792, 0x05c9b5b3, 0x0e85ff25, 0x0408ef57],
|
||||
[0x1814cfa4, 0x0121b41b, 0x0d248a0f, 0x03b05222, 0x039bb16a, 0x0c75966d, 0x0a038113, 0x0a4a1769, 0x011fbc6c],
|
||||
[0x0917e50e, 0x0eec3da8, 0x169d6eac, 0x010c1699, 0x0a416153, 0x0f724912, 0x15cd60b7, 0x04acbad9, 0x05efc5fa],
|
||||
[0x0f150ed7, 0x00122b51, 0x1104b40a, 0x0cb7f442, 0x0fbb28ff, 0x06ac53ca, 0x196142cc, 0x07bf0fa9, 0x00957651],
|
||||
[0x04e0f215, 0x0ed439f8, 0x03f46bd5, 0x05ace82f, 0x110916b6, 0x006db078, 0x0ffd7d57, 0x0f2ecaac, 0x0ca86dec],
|
||||
[0x15d6b2da, 0x0965ecc9, 0x1c92b4c2, 0x001f3811, 0x1cb080f5, 0x02d8b804, 0x19d1c12d, 0x0f20bd46, 0x01951fa7],
|
||||
[0x0a3656c3, 0x0523a425, 0x0fcd0692, 0x0d44ddc8, 0x131f0f5b, 0x0af80e4a, 0x0cd9fc74, 0x099bb618, 0x02db944c],
|
||||
[0x0a673090, 0x01c210e1, 0x178c8d23, 0x01474383, 0x10b8743d, 0x0985a55b, 0x02e74779, 0x00576138, 0x09587927],
|
||||
[0x133130fa, 0x0be05516, 0x09f4d619, 0x0bb62570, 0x099ec591, 0x0d9468fe, 0x1d07782d, 0x0fc72e0b, 0x0701b298],
|
||||
[0x1863863b, 0x085954b8, 0x121a0c36, 0x09e7fedf, 0x0f64b429, 0x09b9d71e, 0x14e2f5d8, 0x0f858d3a, 0x0942eea8],
|
||||
[0x0da5b765, 0x06edafff, 0x0a9d18cc, 0x0c65e4ba, 0x1c747e86, 0x0e4ea915, 0x1981d7a1, 0x08395659, 0x052ed4e2],
|
||||
[0x087d43b7, 0x037ab11b, 0x19d292ce, 0x0f8d4692, 0x18c3053f, 0x08863e13, 0x04c146c0, 0x06bdf55a, 0x04e4457d],
|
||||
[0x16152289, 0x0ac78ec2, 0x1a59c5a2, 0x02028b97, 0x071c2d01, 0x0295851f, 0x0404747b, 0x0878558d, 0x07d29aa4],
|
||||
[0x13d8341f, 0x08daefd7, 0x139c972d, 0x06b7ea75, 0x0d4a9dde, 0x0ff163d8, 0x081d55d7, 0x0a5bef68, 0x0b7b30d8],
|
||||
[0x0be73d6f, 0x0aa88141, 0x0d976c81, 0x07e7a9cc, 0x18beb771, 0x0d773cbd, 0x13f51951, 0x09d0c177, 0x01c49a78],
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::super::montgomery::{BOTTOM_28_BITS, BOTTOM_29_BITS};
|
||||
use super::super::point::test::{power_of_two, precomputed};
|
||||
use super::super::point::PointProjective;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_precomputed_bits() {
|
||||
for x in PRECOMPUTED_LIMBS.iter() {
|
||||
for (i, &limb) in x.iter().enumerate() {
|
||||
if i & 1 == 0 {
|
||||
assert_eq!(limb & BOTTOM_29_BITS, limb);
|
||||
} else {
|
||||
assert_eq!(limb & BOTTOM_28_BITS, limb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_precomputed_powers_of_g_are_correct() {
|
||||
let gen = PointProjective::from_affine(&precomputed(0, 0));
|
||||
let g32 = power_of_two(gen, 32);
|
||||
let g64 = power_of_two(gen, 64);
|
||||
let g96 = power_of_two(gen, 96);
|
||||
let g128 = power_of_two(gen, 128);
|
||||
let g160 = power_of_two(gen, 160);
|
||||
let g192 = power_of_two(gen, 192);
|
||||
let g224 = power_of_two(gen, 224);
|
||||
|
||||
assert_eq!(
|
||||
PointProjective::from_affine(&precomputed(0, 0b0001 - 1)),
|
||||
gen
|
||||
);
|
||||
assert_eq!(
|
||||
PointProjective::from_affine(&precomputed(0, 0b0010 - 1)),
|
||||
g64
|
||||
);
|
||||
assert_eq!(
|
||||
PointProjective::from_affine(&precomputed(0, 0b0100 - 1)),
|
||||
g128
|
||||
);
|
||||
assert_eq!(
|
||||
PointProjective::from_affine(&precomputed(0, 0b1000 - 1)),
|
||||
g192
|
||||
);
|
||||
assert_eq!(
|
||||
PointProjective::from_affine(&precomputed(1, 0b0001 - 1)),
|
||||
g32
|
||||
);
|
||||
assert_eq!(
|
||||
PointProjective::from_affine(&precomputed(1, 0b0010 - 1)),
|
||||
g96
|
||||
);
|
||||
assert_eq!(
|
||||
PointProjective::from_affine(&precomputed(1, 0b0100 - 1)),
|
||||
g160
|
||||
);
|
||||
assert_eq!(
|
||||
PointProjective::from_affine(&precomputed(1, 0b1000 - 1)),
|
||||
g224
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_precomputed_table_0_is_correct() {
|
||||
let gen = PointProjective::from_affine(&precomputed(0, 0));
|
||||
let g64 = power_of_two(gen, 64);
|
||||
let g128 = power_of_two(gen, 128);
|
||||
let g192 = power_of_two(gen, 192);
|
||||
|
||||
for i in 1..16 {
|
||||
let mut x = PointProjective::INFINITY;
|
||||
if i & 1 != 0 {
|
||||
x = &x + &gen;
|
||||
}
|
||||
if i & 2 != 0 {
|
||||
x = &x + &g64;
|
||||
}
|
||||
if i & 4 != 0 {
|
||||
x = &x + &g128;
|
||||
}
|
||||
if i & 8 != 0 {
|
||||
x = &x + &g192;
|
||||
}
|
||||
assert_eq!(PointProjective::from_affine(&precomputed(0, i - 1)), x);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_precomputed_table_1_is_correct() {
|
||||
let gen = PointProjective::from_affine(&precomputed(0, 0));
|
||||
let g32 = power_of_two(gen, 32);
|
||||
let g96 = power_of_two(gen, 96);
|
||||
let g160 = power_of_two(gen, 160);
|
||||
let g224 = power_of_two(gen, 224);
|
||||
|
||||
for i in 1..16 {
|
||||
let mut x = PointProjective::INFINITY;
|
||||
if i & 1 != 0 {
|
||||
x = &x + &g32;
|
||||
}
|
||||
if i & 2 != 0 {
|
||||
x = &x + &g96;
|
||||
}
|
||||
if i & 4 != 0 {
|
||||
x = &x + &g160;
|
||||
}
|
||||
if i & 8 != 0 {
|
||||
x = &x + &g224;
|
||||
}
|
||||
assert_eq!(PointProjective::from_affine(&precomputed(1, i - 1)), x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::ec::exponent256::NonZeroExponentP256;
|
||||
use super::ec::int256;
|
||||
use super::ec::int256::Int256;
|
||||
use super::ec::point::PointP256;
|
||||
use super::rng256::Rng256;
|
||||
use super::sha256::Sha256;
|
||||
use super::Hash256;
|
||||
|
||||
pub const NBYTES: usize = int256::NBYTES;
|
||||
|
||||
pub struct SecKey {
|
||||
a: NonZeroExponentP256,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "derive_debug", derive(Clone, PartialEq, Debug))]
|
||||
pub struct PubKey {
|
||||
p: PointP256,
|
||||
}
|
||||
|
||||
impl SecKey {
|
||||
pub fn gensk<R>(rng: &mut R) -> SecKey
|
||||
where
|
||||
R: Rng256,
|
||||
{
|
||||
SecKey {
|
||||
a: NonZeroExponentP256::gen_uniform(rng),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn genpk(&self) -> PubKey {
|
||||
PubKey {
|
||||
p: PointP256::base_point_mul(self.a.as_exponent()),
|
||||
}
|
||||
}
|
||||
|
||||
fn exchange_raw(&self, other: &PubKey) -> PointP256 {
|
||||
// At this point, the PubKey type guarantees that other.p is a valid point on the curve.
|
||||
// It's the responsibility of the caller to handle errors when converting serialized bytes
|
||||
// to a PubKey.
|
||||
other.p.mul(self.a.as_exponent())
|
||||
// TODO: Do we need to check that the exchanged point is not infinite, and if yes handle
|
||||
// the error? The following argument should be reviewed:
|
||||
//
|
||||
// In principle this isn't needed on the P-256 curve, which has a prime order and a
|
||||
// cofactor of 1.
|
||||
//
|
||||
// Some pointers on this:
|
||||
// - https://www.secg.org/sec1-v2.pdf
|
||||
}
|
||||
|
||||
// DH key agreement method defined in the FIDO2 specification, Section 5.5.4. "Getting
|
||||
// sharedSecret from Authenticator"
|
||||
pub fn exchange_x_sha256(&self, other: &PubKey) -> [u8; 32] {
|
||||
let p = self.exchange_raw(other);
|
||||
let mut x: [u8; 32] = [Default::default(); 32];
|
||||
p.getx().to_int().to_bin(&mut x);
|
||||
Sha256::hash(&x)
|
||||
}
|
||||
}
|
||||
|
||||
impl PubKey {
|
||||
#[cfg(test)]
|
||||
fn from_bytes_uncompressed(bytes: &[u8]) -> Option<PubKey> {
|
||||
PointP256::from_bytes_uncompressed_vartime(bytes).map(|p| PubKey { p })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn to_bytes_uncompressed(&self, bytes: &mut [u8; 65]) {
|
||||
self.p.to_bytes_uncompressed(bytes);
|
||||
}
|
||||
|
||||
pub fn from_coordinates(x: &[u8; NBYTES], y: &[u8; NBYTES]) -> Option<PubKey> {
|
||||
PointP256::new_checked_vartime(Int256::from_bin(x), Int256::from_bin(y))
|
||||
.map(|p| PubKey { p })
|
||||
}
|
||||
|
||||
pub fn to_coordinates(&self, x: &mut [u8; NBYTES], y: &mut [u8; NBYTES]) {
|
||||
self.p.getx().to_int().to_bin(x);
|
||||
self.p.gety().to_int().to_bin(y);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::super::rng256::ThreadRng256;
|
||||
use super::*;
|
||||
|
||||
// Run more test iterations in release mode, as the code should be faster.
|
||||
#[cfg(not(debug_assertions))]
|
||||
const ITERATIONS: u32 = 10000;
|
||||
#[cfg(debug_assertions)]
|
||||
const ITERATIONS: u32 = 1000;
|
||||
|
||||
/** Test that key generation creates valid keys **/
|
||||
#[test]
|
||||
fn test_gen_pub_is_valid_random() {
|
||||
let mut rng = ThreadRng256 {};
|
||||
|
||||
for _ in 0..ITERATIONS {
|
||||
let sk = SecKey::gensk(&mut rng);
|
||||
let pk = sk.genpk();
|
||||
assert!(pk.p.is_valid_vartime());
|
||||
}
|
||||
}
|
||||
|
||||
/** Test that the exchanged key is the same on both sides **/
|
||||
#[test]
|
||||
fn test_exchange_x_sha256_is_symmetric() {
|
||||
let mut rng = ThreadRng256 {};
|
||||
|
||||
for _ in 0..ITERATIONS {
|
||||
let sk_a = SecKey::gensk(&mut rng);
|
||||
let pk_a = sk_a.genpk();
|
||||
let sk_b = SecKey::gensk(&mut rng);
|
||||
let pk_b = sk_b.genpk();
|
||||
assert_eq!(sk_a.exchange_x_sha256(&pk_b), sk_b.exchange_x_sha256(&pk_a));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exchange_x_sha256_bytes_is_symmetric() {
|
||||
let mut rng = ThreadRng256 {};
|
||||
|
||||
for _ in 0..ITERATIONS {
|
||||
let sk_a = SecKey::gensk(&mut rng);
|
||||
let mut pk_bytes_a = [Default::default(); 65];
|
||||
sk_a.genpk().to_bytes_uncompressed(&mut pk_bytes_a);
|
||||
|
||||
let sk_b = SecKey::gensk(&mut rng);
|
||||
let mut pk_bytes_b = [Default::default(); 65];
|
||||
sk_b.genpk().to_bytes_uncompressed(&mut pk_bytes_b);
|
||||
|
||||
let pk_a = PubKey::from_bytes_uncompressed(&pk_bytes_a).unwrap();
|
||||
let pk_b = PubKey::from_bytes_uncompressed(&pk_bytes_b).unwrap();
|
||||
assert_eq!(sk_a.exchange_x_sha256(&pk_b), sk_b.exchange_x_sha256(&pk_a));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: tests with invalid public shares.
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,281 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::{Hash256, HashBlockSize64Bytes};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
const BLOCK_SIZE: usize = 64;
|
||||
const HASH_SIZE: usize = 32;
|
||||
|
||||
pub fn verify_hmac_256<H>(key: &[u8], contents: &[u8], mac: &[u8; HASH_SIZE]) -> bool
|
||||
where
|
||||
H: Hash256 + HashBlockSize64Bytes,
|
||||
{
|
||||
let expected_mac = hmac_256::<H>(key, contents);
|
||||
bool::from(expected_mac.ct_eq(mac))
|
||||
}
|
||||
|
||||
// FIDO2's PIN verification is just matching the first 16 bytes of the HMAC
|
||||
// against the pin ¯\_(ツ)_/¯
|
||||
pub fn verify_hmac_256_first_128bits<H>(key: &[u8], contents: &[u8], pin: &[u8; 16]) -> bool
|
||||
where
|
||||
H: Hash256 + HashBlockSize64Bytes,
|
||||
{
|
||||
let expected_mac = hmac_256::<H>(key, contents);
|
||||
bool::from(array_ref![expected_mac, 0, 16].ct_eq(pin))
|
||||
}
|
||||
|
||||
pub fn hmac_256<H>(key: &[u8], contents: &[u8]) -> [u8; HASH_SIZE]
|
||||
where
|
||||
H: Hash256 + HashBlockSize64Bytes,
|
||||
{
|
||||
let mut ipad: [u8; BLOCK_SIZE] = [0x36; BLOCK_SIZE];
|
||||
let mut opad: [u8; BLOCK_SIZE] = [0x5c; BLOCK_SIZE];
|
||||
if key.len() <= BLOCK_SIZE {
|
||||
xor_pads(&mut ipad, &mut opad, key);
|
||||
} else {
|
||||
xor_pads(&mut ipad, &mut opad, &H::hash(key));
|
||||
}
|
||||
|
||||
let mut ihasher = H::new();
|
||||
ihasher.update(&ipad);
|
||||
ihasher.update(contents);
|
||||
let ihash = ihasher.finalize();
|
||||
|
||||
let mut ohasher = H::new();
|
||||
ohasher.update(&opad);
|
||||
ohasher.update(&ihash);
|
||||
|
||||
ohasher.finalize()
|
||||
}
|
||||
|
||||
fn xor_pads(ipad: &mut [u8; BLOCK_SIZE], opad: &mut [u8; BLOCK_SIZE], key: &[u8]) {
|
||||
for (i, k) in key.iter().enumerate() {
|
||||
ipad[i] ^= k;
|
||||
opad[i] ^= k;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::super::sha256::Sha256;
|
||||
use super::*;
|
||||
extern crate hex;
|
||||
|
||||
#[test]
|
||||
fn test_verify_hmac_valid() {
|
||||
// Test for various lengths of the key and contents.
|
||||
for len in 0..128 {
|
||||
let key = vec![0; len];
|
||||
let contents = vec![0; len];
|
||||
let mac = hmac_256::<Sha256>(&key, &contents);
|
||||
assert!(verify_hmac_256::<Sha256>(&key, &contents, &mac));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_hmac_invalid() {
|
||||
// Test for various lengths of the key and contents.
|
||||
for len in 0..128 {
|
||||
let key = vec![0; len];
|
||||
let contents = vec![0; len];
|
||||
let mac = hmac_256::<Sha256>(&key, &contents);
|
||||
|
||||
// Check that invalid MACs don't verify, by changing any byte of the valid MAC.
|
||||
for i in 0..HASH_SIZE {
|
||||
let mut bad_mac = mac;
|
||||
bad_mac[i] ^= 0x01;
|
||||
assert!(!verify_hmac_256::<Sha256>(&key, &contents, &bad_mac));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac_sha256_empty() {
|
||||
let mut buf = [0; 96];
|
||||
buf[..64].copy_from_slice(&[0x5c; 64]);
|
||||
buf[64..].copy_from_slice(&Sha256::hash(&[0x36; 64]));
|
||||
assert_eq!(hmac_256::<Sha256>(&[], &[]), Sha256::hash(&buf));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac_sha256_examples() {
|
||||
assert_eq!(
|
||||
hmac_256::<Sha256>(&[], &[]),
|
||||
hex::decode("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad")
|
||||
.unwrap()
|
||||
.as_slice()
|
||||
);
|
||||
assert_eq!(
|
||||
hmac_256::<Sha256>(b"key", b"The quick brown fox jumps over the lazy dog"),
|
||||
hex::decode("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8")
|
||||
.unwrap()
|
||||
.as_slice()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_sha256_for_various_lengths() {
|
||||
// This test makes sure that the key hashing and hash padding are implemented properly.
|
||||
//
|
||||
// Test vectors generated with the following Python script:
|
||||
//
|
||||
// import hashlib
|
||||
// import hmac
|
||||
// for n in range(128):
|
||||
// print('b"' + hmac.new('A' * n, 'A' * n, hashlib.sha256).hexdigest() + '",')
|
||||
//
|
||||
let hashes: [&[u8; 64]; 128] = [
|
||||
b"b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad",
|
||||
b"ff333794c5afd126a4e75aef1a35595a846e9e1d4053017d78d7aa3d8373182a",
|
||||
b"7c1d0c177d40e7fa03cd1fa9cfc314888985aecde8f586edfb05e71e5f109c20",
|
||||
b"8f7afbaef39ad155ba09bcbb386a08257731fc06fb2c1d3cff451af5386c0e7d",
|
||||
b"a4dbd2915084ba3bed9306ba674c1eb22fae1dffd971c95b62ab17f0711480cd",
|
||||
b"f59d521db107b4265d995f197b189de468b984816d2a01dc8ca3fcfc24d6ff37",
|
||||
b"29e0656a90f3975e41c8b7e55d225353c3bb8bc0b36328c8066a61763df70b83",
|
||||
b"25a85cdd13b683f3cac732fa1f19757afffe7013ddbea6e441af883379bf4232",
|
||||
b"5d0b0812ecb57fb4be7f29a8978cc4f2c35b32417cc9854e2702f7fb08f6d84d",
|
||||
b"d5e12df428ebd9d991a280c714c9197b977520a7fea87200177cf03618868cb3",
|
||||
b"fa311e0cd19106ab33f8ee84cafdc2f03be787679c1fe7b6e64df2b2e7466ca2",
|
||||
b"9a481f04a2f40d561e8e6c4850498699b36e915993153e9f9b5a4485cb0541b1",
|
||||
b"c8ae9a117e3a5c9125cc21c7f9df09de50de1a6846caf13586d1e5efdfcc1ce6",
|
||||
b"5ef3a488aaffb5db9173ef59e65c2dd27f4cd1a10e1b464ef5bf82d2e35eddd5",
|
||||
b"e5753e8febbb38a97c1dfee23a6103fc58416b7172bab834fe5684b00800abd5",
|
||||
b"0ed714c729a3db2ffde2c4ccb9f5da498c5a73a2c7010d463a7b5da8d18127aa",
|
||||
b"950918cfdb0cb20f327ba0b5095d53c0befb66b9f639d389020453fcf18e8c40",
|
||||
b"699470b9775ec45e9024394e7ebb16463534ed7a617bafddca11a3a0e61a3694",
|
||||
b"95e8c0520fc4dc4c85e00e3868c16ab22a82e8c71ca8ea329ae1ecef0ee3968a",
|
||||
b"7964fba6a123164167cbf51dc2c53948e13b2fee67d09f532e8b7bc56447c091",
|
||||
b"c7d07a5f715b3ea44c258de03f2000ba79a44dd8213feb6e6006e1c3becbedff",
|
||||
b"3eb1a7bf7e4f5c2d9b8cc12667ada8d7773cb9e9424104a30063778567df9422",
|
||||
b"19cc344ad67db96ea936ef15cc751b29fa0409f5c9894d2cfeb100d604653ffc",
|
||||
b"91001e472df16d68ee8033cd13d76a26543ccdfe1426cc6e969759b05ee7d115",
|
||||
b"65f1f4670f8537f37263dabedce049b0e7d1e71f741781413cc2d5c81ecd6ac3",
|
||||
b"0cf5de0ada8b717296a34fc386a8b77f60b607a48a899bee37650891a616dae7",
|
||||
b"de3d56f0c83c992163aac087c999ed8eb5a039f21b0536a1967e747868dafd56",
|
||||
b"0bc3fd1d46d2dc4e2d357d1f6b437d168051d6f19975223505fc2d0abddc3faa",
|
||||
b"1e30596f4144ca58f3e306548cd079cc92a51ab825a4ad3246393c22283af640",
|
||||
b"c2fbc726bf3eb7f0216c7e1d5f5b475a93b033b3f901a4a4dca6bbefc65b68b2",
|
||||
b"1c4ef56d530bb8b627df49ea7b31cf9520e067a2fd0c671896103e146dd4564a",
|
||||
b"9aeed0f7808bd6c13ce54aa5724484f41bd7cbb5e39fd1d7730befbbf056a957",
|
||||
b"e58aa85e83553b7fec841cbaf42cede943b6b7ded22661cd3b1867effe91b745",
|
||||
b"790fc5689193ac57c97dd25729003f2a0d8d3e5f9d2a39e007b794282a51cd3e",
|
||||
b"be067b95a45cdc0060d2c3757597d8d9b858128435e93346c28ce2e82a68e951",
|
||||
b"8734f497f5cd2c8667c7d8e1c2328e7e11fa80fc26c7d933937490d37718871f",
|
||||
b"e8a2d0e77dfad5b046bcff0340a975300b051a21ac403dd802348511a80ba8c8",
|
||||
b"4afec38787117ab6e71c34a4be7575643d8e74fc16b9d5666157fd9aee0f6e86",
|
||||
b"ac8b2831d636384d1d3ff5dd77b249860bd88d5aa9af3e1d4ee70be2a1b03db9",
|
||||
b"e92e1ab3934d3ed2073973204aaba03de3bc4e864cc74677b560c42971b26ab1",
|
||||
b"312effff0c19cf410b9e227654211133f29276c781baadbe00a1e8319e06e361",
|
||||
b"d46158da3c64439f8f176fd5e3d5ac59dcaa6c0835715c5ef30abedf34b13c59",
|
||||
b"693d18266ec508515b9a3839e5336918d41ec7891feadd684b49c03093bd2061",
|
||||
b"440b6a14be7bc2a1ec475a197fe4bea720d1214883e4d1e2029cf0f75fcdba29",
|
||||
b"ca8e30319508df21a851a06f543f59bf6fd3b241e5a52b36fb674b0d9d5e8f67",
|
||||
b"fa4f327635ff5c7051d6968daf5ffcc827bbd0da1e1c56d59918895bc418be98",
|
||||
b"0e6d0dd75bc42aa6e245a212ff027bc701a7ef61179b335365a29f545bcf45e0",
|
||||
b"57a08bef7822c2111ac79062240e725611322543f10758763c1c6467021c4fe7",
|
||||
b"29768d5fb640a46e0129303128669eb3c7fb357ef1c49506f6300cc8e451d5e2",
|
||||
b"009a868f1065ccb4227dc28084484263495ad90378dc928fc61d361eef2d072e",
|
||||
b"6808e39e343af0fd53309fc02b05da1a0e68b87d5cd434e94521c635a78d7fdb",
|
||||
b"8fe1668eba03be5444e0956396ac9d81ac1c4a7beb6a01151ad679f5ca0cd206",
|
||||
b"01f41c1cc6d9d260ec3d938d681fc476415aae96a318862a492ba1a56f1b0a88",
|
||||
b"3100bc758eeccb959376b334f999a2b4e5fcced5b5d4956510a86b654f1f0c04",
|
||||
b"6225330aedced9909d4a190dba55e807624f44e7c6e0c50cac5e4ccf8e2e0029",
|
||||
b"783111276f7218e3e525c83cdf117e2d5ce251f6a04beabb65ef565f513a9301",
|
||||
b"4b365ab05720ad517f46df03ff441f9e0769a2ce5279663b7d90eb7d980625ef",
|
||||
b"52431871c39a881c63df82860e32ccf05c1addbc630aaa580733f2e6a2fff5a6",
|
||||
b"f017486486b0e308a10862e910f22545c29670daa26bf0c6791827a7f9f625a4",
|
||||
b"85efd1eface951759a4642e882d0ef8d0be58afd483e0945d03a7a35fac789ac",
|
||||
b"880872df19c7ff14105ba59cebc07d9e9d7e67f4896a14acae5346c66c6ce2da",
|
||||
b"85ba2aa3634c619d604a964d62ae3c97f9eba7fcb7e4db2ebfa2bd23338c2d60",
|
||||
b"5aa9736d405016676878fcc63e84b286cfbc843799ea786d089b2200281d5a5c",
|
||||
b"30f43d84f3d5ffac60323a126fe321c6cc1e9c440249a8d69abe172494cba7ae",
|
||||
b"7dce62ce40f8f4cda666341730e7dfcde8839eed4236c58ae273e6687d229d85",
|
||||
b"83d670a41779f63fad0ece766a19920e0cfdd9d02f5a5900c888de21f6ae0526",
|
||||
b"bb2acf0d6d39e58b09204dedcc2fe68ad829ba471a077e6e03246d8a0b0c0858",
|
||||
b"1f94db9ee970a9fcc9089865eb2aa485765345f6d4de54815507ce363bc20711",
|
||||
b"b4bceedc935c574935117f7ad280a7d858da7ca5a6b0920b4975111206fcac77",
|
||||
b"b9da394c337e9aa150c12e54c574978773ff953270f5aefde88a766e9874c260",
|
||||
b"1769fcb5d31c8c09868e4e3dcd9db92b4cccefb5660e72bfc52159fda9da8518",
|
||||
b"d8ceb568f85e30cb48cabf8e84d577369033511cbbdac6a7996cf9f397c2e203",
|
||||
b"64c35795b87cbb02afcdc6a5e6bacb10d98cdd1bd810ccf12c847fbdcf4d2634",
|
||||
b"76ccd4e2b71a826128b60bc9fe33613d82f0ae1f57fa192f107ed54d25a842e0",
|
||||
b"095450e0f61a4201bb2197371247bb7ced09056d86e901202ecc561c3568e032",
|
||||
b"0e928f8e201fd3019f11037bf164cc3d719ecf08e6eac985f429702c41f25d0a",
|
||||
b"135f37174ff19409cef67f3263511ed9286901090eec2b54d9444036308a72cf",
|
||||
b"51eafe6978c32396022a31230b8b9120ac7bd79ad7a3303e6b6979063275c9b8",
|
||||
b"fd2fb9443156429b2bc042248bf022f18ee6ed11fefea222893c49e8fef6a17e",
|
||||
b"ed9a240e63644aa55c71fb339d79e1a38de71002c30baf14af38359c513de2e0",
|
||||
b"c925e74d4740558277a55fe57ba88ed05ce8f5d5c35c19e7228adc09351ecfa4",
|
||||
b"cd99660a7c2aded095152bc6d5fa160077355819fbf421acb95ab39ad8c27862",
|
||||
b"42cf7593a535a1e79299c2839f4e0ffba5b429cb0df6c77d3fe86b6a1535d505",
|
||||
b"348c6014dcb0cd583862c09ad24aa5d93e81e2f6ca4b04f4e77e50067f34e625",
|
||||
b"8b00c1d89bd10b24bda2c8f6e45c4112112baacab29d0330b377d811a7b0184e",
|
||||
b"dfe3960732f8e5958085a581859f6ccf40169df965bb8ff5feb4b0229a02c6f8",
|
||||
b"de0aa4189c97d17df38c0ea3bc9376287076939afd515336d5d7d851f81f2517",
|
||||
b"543190454858ff5396a1de3f753f84bdd2869fd2b59e3a89f090ce06bd94d626",
|
||||
b"541ddbe00a0237c5c8bd043e6cced9ce78209dd83b057272cd46c2cc2f39a88c",
|
||||
b"11c82017445f7e295d4c734a40cf28793df855dd321d507d4a0f3e212293ca2b",
|
||||
b"cda1ecd6ca2f3858352985b4833170589e17e1f7f464e279cc78051911b8e8d3",
|
||||
b"5feb471949318b26898b3339ac7a66e464a752223cf764aa0af9292fe087c39a",
|
||||
b"eb48e5de63ecd22f7b305c7e74ce8cc714ab858cf834cf485727868e017e473e",
|
||||
b"03428482b5208a35032777c7d628a6c8f3b07c5c87c641ddfb3adf46ffefe449",
|
||||
b"a8e0cd1dfcf6e1a29ba032d3889fca2f25e3fad1a9b1c36e4978e48945b06092",
|
||||
b"6cf4280b75da3bb22ddee9a9a3fdd1d0cd04627c00f73b608e44be9ce84869db",
|
||||
b"9fa7648f23c405938b6fb8eaf6a24c476dafd05625a9a8f6c52e1abb9d432a76",
|
||||
b"2906f0a6276e9a1ba4fbca2f335d9f7d3c331684814ac5407145d86891ba37f7",
|
||||
b"1b667ee18a909e3f828f2bd6a162e3aa1361f3801b7cbc863b1b54a1ccfbf580",
|
||||
b"a75e75962b3f950f7718839ef06cc09112806dbf88b142e369cf1ef99069c226",
|
||||
b"71b2e2f6d66f13b919a842843b201c25249ff4b4caa4c7ab079b2980de8a18f6",
|
||||
b"c4a9e73df196f876f5a3af1c8836504ee61daac5d9e15cc043a511310c22ddd8",
|
||||
b"88e3303a6a2ab03430652fea942ae7cd96618fae4addcf8e92989d542777e496",
|
||||
b"9a727095299564e2f5e1598f1a4095a2a000cb9196fe4eb13447932af777be3c",
|
||||
b"5643a1c72b189d462236a43afc7d0b9504cbf81b4fa9a6c9cca49bd50da299df",
|
||||
b"caebc47e2204fe19e757372c2e0469a34051415a09c927e63be7747903d80af5",
|
||||
b"e5c9fd4bd27e7a55958cb5f66ff88baf80ab40d9690e81a9cf03ada5bd08cd44",
|
||||
b"dc606eb9a631ca3f080f6d6f610444201608f47b5b49c75c2c0ec03ba9000009",
|
||||
b"bce230b32a7dc676fb98a4a3d41c4171e9e173e840b92c194fb1dc4accc079b9",
|
||||
b"51542ff3b57cd2ff6b1542649f7a44dbed32194b98c3af32b6ff3eefe6b490cf",
|
||||
b"703f5dea8c9e3ed75c691fc17699b9648a65a331add554fa3613ddcb7267565a",
|
||||
b"d74f9f60763467ce0b35ced0630ecf58605da0ca49e058de21524b5c532b5de8",
|
||||
b"e9dacaf1fefaad6d9d372affab8fb5216004fb3fa7a9b86b25ecd00e583c22a5",
|
||||
b"abe2c75f1ac881b743da99c543506c4b7532f3fc445fa8cbf1df22689770d66a",
|
||||
b"7be506d2fd6dc79f0b14ddfff0147c79d78d99140547efcd03d0a73819b84c5a",
|
||||
b"78dbc5c946ee8aa147ccfa8b5d9231c95c4257d8c79bd219ab95d53303367309",
|
||||
b"1d75ee600f2ba216d211189493d793aa610aa57ccbfc4d9a7f44194e7166a062",
|
||||
b"368a4d0ae1b139d71f35ba5f917a7ad4c18e6aa51b095cb135193dcd09e299b4",
|
||||
b"a9d3202db927be8f0d15d2dfb83ea09db32fec3b1fc10a6acfb91da8c3c5eaf3",
|
||||
b"c2284f8efeee554cc29a8802aadd7cd88e84561d353282ee31322ed497a3336c",
|
||||
b"7695648a111e2ea51013f03c9de91d81a2435a7777f303e51d027750f8381680",
|
||||
b"719fc44e0f64f7da9ac5d33d9ca912fdc839bb4535c66a21f0804f2cdf800666",
|
||||
b"6940c082413b0c1ced6f9cb6583588c472ff72b48b00a4fb6d2710d7ac4dad99",
|
||||
b"af1f26fcf070d9f5926dd41db3c09ec6b4c3f2208775cf983330cd0ff5aa239e",
|
||||
b"ae50df4404a7f46146d8112bfb1cde876e591abe5ef1640e27c4d178a84b6335",
|
||||
b"50f91f48df7f0f96af954be7e1b518bac537173cea38be300d98761da1d9b10f",
|
||||
b"9c9aee382b3e3417e87352bdcb48837e88335e9dd0112fc22ecf61e766a6ac43",
|
||||
b"d53cee1696c613f988520cf9c923c7cb6e6933b4faf57e640867d5f45a0f2569",
|
||||
];
|
||||
|
||||
let mut input = Vec::new();
|
||||
for i in 0..128 {
|
||||
assert_eq!(
|
||||
hmac_256::<Sha256>(&input, &input),
|
||||
hex::decode(hashes[i] as &[u8]).unwrap().as_slice()
|
||||
);
|
||||
input.push(b'A');
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: more tests
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![feature(wrapping_int_impl)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
extern crate subtle;
|
||||
#[macro_use]
|
||||
extern crate arrayref;
|
||||
extern crate byteorder;
|
||||
extern crate libtock;
|
||||
#[macro_use]
|
||||
extern crate cbor;
|
||||
|
||||
pub mod aes256;
|
||||
pub mod cbc;
|
||||
mod ec;
|
||||
pub mod ecdh;
|
||||
pub mod ecdsa;
|
||||
pub mod hmac;
|
||||
pub mod rng256;
|
||||
pub mod sha256;
|
||||
pub mod util;
|
||||
|
||||
// Trait for hash functions that returns a 256-bit hash.
|
||||
// The type must be Sized (size known at compile time) so that we can instanciate one on the stack
|
||||
// in the hash() method.
|
||||
pub trait Hash256: Sized {
|
||||
fn new() -> Self;
|
||||
fn update(&mut self, contents: &[u8]);
|
||||
fn finalize(self) -> [u8; 32];
|
||||
|
||||
fn hash(contents: &[u8]) -> [u8; 32] {
|
||||
let mut h = Self::new();
|
||||
h.update(contents);
|
||||
h.finalize()
|
||||
}
|
||||
}
|
||||
|
||||
// Traits for block ciphers that operate on 16-byte blocks.
|
||||
pub trait Encrypt16BytesBlock {
|
||||
fn encrypt_block(&self, block: &mut [u8; 16]);
|
||||
}
|
||||
|
||||
pub trait Decrypt16BytesBlock {
|
||||
fn decrypt_block(&self, block: &mut [u8; 16]);
|
||||
}
|
||||
|
||||
// Trait for hash functions that operate on 64-byte input blocks.
|
||||
pub trait HashBlockSize64Bytes {
|
||||
type State;
|
||||
|
||||
fn hash_block(state: &mut Self::State, block: &[u8; 64]);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
use libtock::rng;
|
||||
|
||||
// Lightweight RNG trait to generate uniformly distributed 256 bits.
|
||||
pub trait Rng256 {
|
||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32];
|
||||
|
||||
fn gen_uniform_u32x8(&mut self) -> [u32; 8] {
|
||||
bytes_to_u32(self.gen_uniform_u8x32())
|
||||
}
|
||||
}
|
||||
|
||||
// The TockOS rng driver fills a buffer of bytes, but we need 32-bit words for ECDSA.
|
||||
// This function does the conversion in safe Rust, using the native endianness to avoid unnecessary
|
||||
// instructions.
|
||||
// An unsafe one-line equivalent could be implemented with mem::transmute, but let's use safe Rust
|
||||
// when possible.
|
||||
fn bytes_to_u32(bytes: [u8; 32]) -> [u32; 8] {
|
||||
let mut result: [u32; 8] = [Default::default(); 8];
|
||||
for (i, r) in result.iter_mut().enumerate() {
|
||||
*r = u32::from_ne_bytes(*array_ref![bytes, 4 * i, 4]);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// RNG backed by the TockOS rng driver.
|
||||
pub struct TockRng256 {}
|
||||
|
||||
impl Rng256 for TockRng256 {
|
||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
||||
let mut buf: [u8; 32] = [Default::default(); 32];
|
||||
rng::fill_buffer(&mut buf);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
// For tests on the desktop, we use the cryptographically secure thread rng as entropy source.
|
||||
#[cfg(feature = "std")]
|
||||
pub struct ThreadRng256 {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Rng256 for ThreadRng256 {
|
||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
||||
use rand::Rng;
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut result = [Default::default(); 32];
|
||||
rng.fill(&mut result);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_bytes_to_u32() {
|
||||
// This tests that all bytes of the input are indeed used in the output, once each.
|
||||
// Otherwise the result of gen_uniform_u32x8 wouldn't be uniformly distributed.
|
||||
let bytes = b"\x00\x01\x02\x03\x04\x05\x06\x07\
|
||||
\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\
|
||||
\x10\x11\x12\x13\x14\x15\x16\x17\
|
||||
\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
|
||||
#[cfg(target_endian = "big")]
|
||||
let expected = [
|
||||
0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, 0x10111213, 0x14151617, 0x18191a1b,
|
||||
0x1c1d1e1f,
|
||||
];
|
||||
#[cfg(target_endian = "little")]
|
||||
let expected = [
|
||||
0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918,
|
||||
0x1f1e1d1c,
|
||||
];
|
||||
|
||||
assert_eq!(bytes_to_u32(*bytes), expected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::{Hash256, HashBlockSize64Bytes};
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use core::num::Wrapping;
|
||||
|
||||
const BLOCK_SIZE: usize = 64;
|
||||
|
||||
pub struct Sha256 {
|
||||
state: [Wrapping<u32>; 8],
|
||||
block: [u8; BLOCK_SIZE],
|
||||
total_len: usize,
|
||||
}
|
||||
|
||||
impl Hash256 for Sha256 {
|
||||
fn new() -> Self {
|
||||
Sha256 {
|
||||
state: Sha256::H,
|
||||
block: [0; BLOCK_SIZE],
|
||||
total_len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, mut contents: &[u8]) {
|
||||
let cursor_in_block = self.total_len % BLOCK_SIZE;
|
||||
let left_in_block = BLOCK_SIZE - cursor_in_block;
|
||||
|
||||
// Increment the total length before we mutate the contents slice.
|
||||
self.total_len += contents.len();
|
||||
|
||||
if contents.len() < left_in_block {
|
||||
// The contents don't fill the current block. Simply copy the bytes.
|
||||
self.block[cursor_in_block..(cursor_in_block + contents.len())]
|
||||
.copy_from_slice(contents);
|
||||
} else {
|
||||
// First, fill and process the current block.
|
||||
let (this_block, rest) = contents.split_at(left_in_block);
|
||||
self.block[cursor_in_block..].copy_from_slice(this_block);
|
||||
Sha256::hash_block(&mut self.state, &self.block);
|
||||
contents = rest;
|
||||
|
||||
// Process full blocks.
|
||||
while contents.len() >= 64 {
|
||||
let (block, rest) = contents.split_at(64);
|
||||
Sha256::hash_block(&mut self.state, array_ref![block, 0, 64]);
|
||||
contents = rest;
|
||||
}
|
||||
|
||||
// Copy the last block for further processing.
|
||||
self.block[..contents.len()].copy_from_slice(contents);
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize(mut self) -> [u8; 32] {
|
||||
// Last block and padding.
|
||||
let cursor_in_block = self.total_len % BLOCK_SIZE;
|
||||
self.block[cursor_in_block] = 0x80;
|
||||
// Clear the rest of the block.
|
||||
for byte in self.block[(cursor_in_block + 1)..].iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
|
||||
if cursor_in_block >= 56 {
|
||||
// Padding doesn't fit in this block, so we first hash this block and then hash a
|
||||
// padding block.
|
||||
Sha256::hash_block(&mut self.state, &self.block);
|
||||
// Clear buffer for the padding block.
|
||||
for byte in self.block.iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// The last 8 bytes of the last block contain the length of the contents. It must be
|
||||
// expressed in bits, whereas `total_len` is in bytes.
|
||||
BigEndian::write_u64(array_mut_ref![self.block, 56, 8], self.total_len as u64 * 8);
|
||||
Sha256::hash_block(&mut self.state, &self.block);
|
||||
|
||||
// Encode the state's 32-bit words into bytes, using big-endian.
|
||||
let mut result: [u8; 32] = [0; 32];
|
||||
for i in 0..8 {
|
||||
BigEndian::write_u32(array_mut_ref![result, 4 * i, 4], self.state[i].0);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl HashBlockSize64Bytes for Sha256 {
|
||||
type State = [Wrapping<u32>; 8];
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn hash_block(state: &mut Self::State, block: &[u8; 64]) {
|
||||
let mut w: [Wrapping<u32>; 64] = [Wrapping(0); 64];
|
||||
|
||||
// Read the block as big-endian 32-bit words.
|
||||
for (i, item) in w.iter_mut().take(16).enumerate() {
|
||||
*item = Wrapping(BigEndian::read_u32(array_ref![block, 4 * i, 4]));
|
||||
}
|
||||
|
||||
for i in 16..64 {
|
||||
w[i] = w[i - 16] + Sha256::ssig0(w[i - 15]) + w[i - 7] + Sha256::ssig1(w[i - 2]);
|
||||
}
|
||||
|
||||
let mut a = state[0];
|
||||
let mut b = state[1];
|
||||
let mut c = state[2];
|
||||
let mut d = state[3];
|
||||
let mut e = state[4];
|
||||
let mut f = state[5];
|
||||
let mut g = state[6];
|
||||
let mut h = state[7];
|
||||
|
||||
for (i, item) in w.iter().enumerate() {
|
||||
let tmp1 =
|
||||
h + Sha256::bsig1(e) + Sha256::choice(e, f, g) + Wrapping(Sha256::K[i]) + *item;
|
||||
let tmp2 = Sha256::bsig0(a) + Sha256::majority(a, b, c);
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + tmp1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = tmp1 + tmp2;
|
||||
}
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
state[5] += f;
|
||||
state[6] += g;
|
||||
state[7] += h;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sha256 {
|
||||
// SHA-256 constants.
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
const H: [Wrapping<u32>; 8] = [
|
||||
Wrapping(0x6a09e667),
|
||||
Wrapping(0xbb67ae85),
|
||||
Wrapping(0x3c6ef372),
|
||||
Wrapping(0xa54ff53a),
|
||||
Wrapping(0x510e527f),
|
||||
Wrapping(0x9b05688c),
|
||||
Wrapping(0x1f83d9ab),
|
||||
Wrapping(0x5be0cd19),
|
||||
];
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
const K: [u32; 64] = [
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
|
||||
0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
|
||||
0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
|
||||
0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
|
||||
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
||||
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
|
||||
0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
|
||||
0xc67178f2,
|
||||
];
|
||||
|
||||
// SHA-256 helper functions.
|
||||
#[inline(always)]
|
||||
fn choice(e: Wrapping<u32>, f: Wrapping<u32>, g: Wrapping<u32>) -> Wrapping<u32> {
|
||||
(e & f) ^ (!e & g)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn majority(a: Wrapping<u32>, b: Wrapping<u32>, c: Wrapping<u32>) -> Wrapping<u32> {
|
||||
(a & b) ^ (a & c) ^ (b & c)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn bsig0(x: Wrapping<u32>) -> Wrapping<u32> {
|
||||
x.rotate_right(2) ^ x.rotate_right(13) ^ x.rotate_right(22)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn bsig1(x: Wrapping<u32>) -> Wrapping<u32> {
|
||||
x.rotate_right(6) ^ x.rotate_right(11) ^ x.rotate_right(25)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ssig0(x: Wrapping<u32>) -> Wrapping<u32> {
|
||||
x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ssig1(x: Wrapping<u32>) -> Wrapping<u32> {
|
||||
x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
extern crate hex;
|
||||
|
||||
#[test]
|
||||
fn test_choice() {
|
||||
assert_eq!(
|
||||
Sha256::choice(
|
||||
Wrapping(0b00001111),
|
||||
Wrapping(0b00110011),
|
||||
Wrapping(0b01010101)
|
||||
),
|
||||
Wrapping(0b01010011)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_majority() {
|
||||
assert_eq!(
|
||||
Sha256::majority(
|
||||
Wrapping(0b00001111),
|
||||
Wrapping(0b00110011),
|
||||
Wrapping(0b01010101)
|
||||
),
|
||||
Wrapping(0b00010111)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_empty() {
|
||||
assert_eq!(
|
||||
Sha256::hash(&[]),
|
||||
hex::decode("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
|
||||
.unwrap()
|
||||
.as_slice()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_for_various_splits() {
|
||||
// Test vector generated with the following Python script:
|
||||
//
|
||||
// import hashlib
|
||||
// print(hashlib.sha256('A' * 512).hexdigest())
|
||||
//
|
||||
let input = vec![b'A'; 512];
|
||||
let hash = hex::decode("32beecb58a128af8248504600bd203dcc676adf41045300485655e6b8780a01d")
|
||||
.unwrap();
|
||||
|
||||
for i in 0..512 {
|
||||
for j in i..512 {
|
||||
let mut h = Sha256::new();
|
||||
h.update(&input[..i]);
|
||||
h.update(&input[i..j]);
|
||||
h.update(&input[j..]);
|
||||
assert_eq!(h.finalize(), hash.as_slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_for_various_lengths() {
|
||||
// This test makes sure that the padding is implemented properly.
|
||||
//
|
||||
// Test vectors generated with the following Python script:
|
||||
//
|
||||
// import hashlib
|
||||
// for n in range(128):
|
||||
// print('b"' + hashlib.sha256('A' * n).hexdigest() + '",')
|
||||
//
|
||||
let hashes: [&[u8; 64]; 128] = [
|
||||
b"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
b"559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd",
|
||||
b"58bb119c35513a451d24dc20ef0e9031ec85b35bfc919d263e7e5d9868909cb5",
|
||||
b"cb1ad2119d8fafb69566510ee712661f9f14b83385006ef92aec47f523a38358",
|
||||
b"63c1dd951ffedf6f7fd968ad4efa39b8ed584f162f46e715114ee184f8de9201",
|
||||
b"11770b3ea657fe68cba19675143e4715c8de9d763d3c21a85af6b7513d43997d",
|
||||
b"69dc6c3210e25e62c5938ff4e841e81ce3c7d2cde583553478a77d7fcb389f30",
|
||||
b"0f0cf9286f065a2f38e3c4e4886578e35af4050c108e507998a05888c98667ea",
|
||||
b"c34ab6abb7b2bb595bc25c3b388c872fd1d575819a8f55cc689510285e212385",
|
||||
b"e5f9176ecd90317cf2d4673926c9db65475b0b58e7f468586ddaef280a98cdbd",
|
||||
b"1d65bf29403e4fb1767522a107c827b8884d16640cf0e3b18c4c1dd107e0d49d",
|
||||
b"dd20088919031875b7bcca29995545dd40ca994be0558183f9b942b51b3b2249",
|
||||
b"0592cedeabbf836d8d1c7456417c7653ac208f71e904d3d0ab37faf711021aff",
|
||||
b"3461164897596e65b79bc0b7bee8cc7685487e37f52ecf0b34c000329675b859",
|
||||
b"14f99c4b0a6493e3a3f52022cd75276b4cff9a7c8eef74793267f687b600af96",
|
||||
b"6f9f84c09a5950e1ea7888f17922a69e5292dcbcb1e682ddfc977a9b4ea1a8c0",
|
||||
b"991204fba2b6216d476282d375ab88d20e6108d109aecded97ef424ddd114706",
|
||||
b"444074d5328d52b4e0036d37b1b6ea0a9fe3b0c96872d1157fbf01b6fdb2ce8d",
|
||||
b"d273c6b6de3f5260e123348e0feb270126fa06e164bb82818df7c71b30ab0ef5",
|
||||
b"234b7f9389f9b521f407805760775940d79a48188338d02a1fe654e826a83f69",
|
||||
b"edfcaac579024f574adbcaa3c13e4fd2b7f1797826afe679f2144af2cb5c062d",
|
||||
b"f48de1653fdfa9b637b7fb4da9c169a1f2be6a1ec001e3d2cca44c669a693ecf",
|
||||
b"8a5bdb4cc15164126c6ef2668de9dd240d299ce6397a42c95a9411b93d080ed8",
|
||||
b"1786ac1492c6c922c2734e4d3d8e9b030cfba291a72bc135989c49fc31171ac2",
|
||||
b"1bda9f0aed80857d43c9329457f28b1ca29f736a0c539901e1ba16a909eb07b4",
|
||||
b"6724431fc312ba42c98b38b8595a49749419526aa89722c77a85c6c813dfdb5a",
|
||||
b"06f469c97c14e84c74853bb96aa79305eb4f6635291bf1202c4fdadb82706204",
|
||||
b"568f214d529544bf4430513c2993495a5b434611533c63d1cf095b51c5e1f8af",
|
||||
b"c84f7630cbe823fc4d80f605b98294592f15b14db1f78d6f18e686c1f8cb5ded",
|
||||
b"a7951e0ca2e9612a985a36747309822a67a9b8c1a5abd848c03e82216c85f1b3",
|
||||
b"37b9403cf88cc2639d0a118d757a43a0ff6d4871823707ab6a8bb56bc68e8e79",
|
||||
b"55ee740f58335c97d42c32125218eb7c325fbe34206912f1aa7af7fd6580c9a1",
|
||||
b"22a48051594c1949deed7040850c1f0f8764537f5191be56732d16a54c1d8153",
|
||||
b"5d873590851b7b00b60490c8e6966b3409c385adcc9590d801f0e03e268b5ba5",
|
||||
b"1e98a405718c430a4067d75125015a947a971449bc433b078418438c48bc6046",
|
||||
b"015c50632207f69408c05d20e36facfad9bde74c727f933023f54cd6e8b87372",
|
||||
b"a3b99d59dbb025726312e812c2821cfbe55189f515414bdabd5e3d284c8ad6f9",
|
||||
b"7d24c321bfb2a5b6d2c7a3c2948855cef421d08352dc296ed95c6f645fcce441",
|
||||
b"876fc5bf6bde065afea543aceb645ce17ffe3c9d8df9c6073ab31f3a562f4257",
|
||||
b"b9b515854e040b8de31d85d597aba28db4467fd0a7d6eb77a31005f4a67a8fb3",
|
||||
b"f0a2fb80ac0699075fb6c7b0ee2bcc204a1d909ee3149571216ec9cc1d4b9f8e",
|
||||
b"b78244167af116f2b3597b4a81421bd2b28f3d8bf616025a5ae424f689fd7632",
|
||||
b"d85ce644bf4e82cee032eaa5c3d9030a090276d9bae3703112bdfc6f8fdde307",
|
||||
b"0f007385b6f9d4b7eeb2748605afe1a984a0a3bfa3f014d09e2a784ce9e5cd1a",
|
||||
b"b06b3f20c246db70a136e3ae4787d0df96db4f693d215c21883d3c19700fb276",
|
||||
b"ac752ced452069c55c7567a0717b87615824c568dc98c8626ac85fc34b234c3b",
|
||||
b"91a07088de2d0fe9f31567b05d290e65feb06758d000ec463f7f5a6e82ce00a5",
|
||||
b"abf6c5d1b6512e188f1da6a72e974f7b98b5bac62453f1748c8f9ab180803fdb",
|
||||
b"4739dcdbab0c377161c539af55d47c5c90c87807d0728aabe91697b66e29096c",
|
||||
b"ff85f0693c8e6bbeeaa1f90c32e1159b9b545d830ffe58cd80cb94d9d8140d21",
|
||||
b"509ddb85fdf92f197d32570c005cdcb6dffa398f088bd1a013459f6fb1f730ef",
|
||||
b"1d31616e307323bd80775ae7483fce654a3b65bced7134c22e179a2e25155009",
|
||||
b"3e1ae21112ec8fad05e3676e1940da52d56771162142aac4d73743e7df70b686",
|
||||
b"5f2671f97427c8873e5af72686d244e4c8126a4f618983bae880a48a834a0607",
|
||||
b"2d0009d7df28cdc6b5a4c36063d97415a8fe99515317458fcb0b0e2a821dbcb9",
|
||||
b"8963cc0afd622cc7574ac2011f93a3059b3d65548a77542a1559e3d202e6ab00",
|
||||
b"6ea719cefa4b31862035a7fa606b7cc3602f46231117d135cc7119b3c1412314",
|
||||
b"a00df74fbdadd9eb0e7742a019e5b2d77374de5417eba5b7a0730a60cce5e7bf",
|
||||
b"cee244d999f8cf49f2a4ee4d89695130c9c95c33538cedf0306881ebd42714d2",
|
||||
b"5b29354ee33cba5b924ded5e3c873a76e1d12527d824ace01ff9683d24e06816",
|
||||
b"c5fb235befd875b915fa6c4702a7abb93cacf3d7c414b71cbeff9e1b0a9fbd41",
|
||||
b"0ae45129ef1edf64309559f6cb7bb0af16eff14ad82f24d55fa029c1b4144078",
|
||||
b"5a2aafcacb9828e41fb7c8f8098952638645874b3a8ca45d2523fb2d5fc7166d",
|
||||
b"1b58d00f5b1fbd2a1884d666a2be33c2fa7463dff32cd60ef200c0f750a6b70f",
|
||||
b"d53eda7a637c99cc7fb566d96e9fa109bf15c478410a3f5eb4d4c4e26cd081f6",
|
||||
b"836203944f4c0280461ad73d31457c22ba19d1d99e232dc231000085899e00a2",
|
||||
b"fd8afe9151793a84a21af054ba985d1486a705561e2a50d4a50f814664f5e806",
|
||||
b"f495547fca5a5a2c40dccebefe40160efb8bc2888e8afef712b096b5f2585b44",
|
||||
b"ba31b89f9486439fdf551f597fede0c10260f9b404866dba4a6555375f486359",
|
||||
b"46f23cc7ccba8af67978bea568e63cd045be72aba974132b1b14cc59277329f1",
|
||||
b"01d3a187638cc1a7740a74fbeb57aa2648dbdec42d497321912bf393d283ccd1",
|
||||
b"96b437b3df7c62fc877a121b087899f5e36a58f6d87ba52d997e92bb016aa575",
|
||||
b"6a6d691ac9ba70955046757cd685b6257773ff3ad93f43d4d4812c5b106f4b5b",
|
||||
b"beb869adc22a7e8fbd5af12cbf3ad36dd92dca6ebf52ef3441ed6cd0dff24dc6",
|
||||
b"0f40cb2f3661d73dfaec511e8ebea082fb1f77db45bf8c9ba7c9708da6ba6301",
|
||||
b"ddd5d1ecc7af6a5b0d18e0825004d3bc9d52e2cdf14bc00c7474f16941a64acc",
|
||||
b"6a10b9a8a33d7814ce73679ace5c43657aa6d63169ce215fd85177c77a94147d",
|
||||
b"9d887d47c78267827dac4afb2cbdcc593d1b89c1d0c1f22c3800cae7916962cd",
|
||||
b"e45ca598e970afb0f1f57bd34e87065839d2fac524421048fbec489f68e1fd0d",
|
||||
b"1581baebc5f9dcfd89c658b3c3303203fc0e2f93e3f9e0b593d8b2b8112c6eda",
|
||||
b"d9b1f3e2c6d528668a73f22575c44ed9f98d9c684964761b621417efd80d7a60",
|
||||
b"9feacd760dfda20e5e0accf9ddeb8b5c01276a56dc3518046a26f5276fe15041",
|
||||
b"6aacf5279e24979684fab16fb5495c3ac1dfcf7138b0825376af83473d07cae9",
|
||||
b"cd2f0deb953014ee400eddef094602d9676e0fd2269d22818f0d5bc198d44d8d",
|
||||
b"ff9265df14681e44d170fd2b10c6cdf3991f731601d6b89cafe39691d3b42559",
|
||||
b"6c99e32b005a3a4956b9406ab15411e666c7f67982db170ae1fb111ec634b9c4",
|
||||
b"e1659ad54063a379f77fee108a376a6a7d5ae3d0c437bf847203963bd0078dfc",
|
||||
b"572d07a66fccf05d5f73c913552e12d9ffb39a15d01a8fd48cd6aaaab86f4f14",
|
||||
b"ca97d312ef8551820844548f300f9528f27d53f6ad3910ed2709f2b35c9591f3",
|
||||
b"97654dc78f4f7d4cec4b4870e6ee0a87abacc89337ed0629e2e511e4466df56d",
|
||||
b"fde923c1ed5e5cd32c629bdf341db32c0f72ba8f1e2afd9c194e87e0e3d9da5f",
|
||||
b"d6624a66f3bcc4adef8a17abf9eeb1fbf23746165b2f90f9cb3a679a58e4958e",
|
||||
b"8676909e9578a790f84be31fe94f4d22488f912b754ee816ba0a5c4a392305a5",
|
||||
b"57f65fd8a95ff738b95dba0f1606025535e34591f1b58b00d33958093808360c",
|
||||
b"f6afcaf794fe0e04d6ec18bbde55412a60c0c5ef55e75223b817e97f208bbccc",
|
||||
b"6121f27b52c1f17ddce365143ba58a720fa303707faa32a4e5e89029f34ac618",
|
||||
b"69d62c062d67d8d2ce9068c1898fb9746c911839aa88ad1628d090f4c8e47f05",
|
||||
b"eb9f8b69313e19e14b1043b3cac05d18d40321536ad485be8145007aefa9295d",
|
||||
b"b1cfb0f511886ca07ade919740ca95e1b3d998ba7cb66ba2badd53be28f5f509",
|
||||
b"d0118863549f990558685da9090ca8eae8c809c5545c4aa85f8e5eec413b2555",
|
||||
b"d82c6aa133a0fc25b087f46ad7ed2a3042772e612e015571e61753ff55ba6da8",
|
||||
b"aac76dab773c00c8ad4bb128147945c70798eae5a2511fef01e853c6e3051ab9",
|
||||
b"ccd77d7adb6178ee3e3560ba4583044a36b296257ae4c5cfead96d46af31fccd",
|
||||
b"3c4f48a886b2de7e908d6a626074e7515265cc9d1188c161cf159fd376d3d5f8",
|
||||
b"7f9578a31905e95a16cc9d3e7b57dc3158a23dccf359a1f2cf09e73eb13e5cde",
|
||||
b"770492ebaee89a20d19f9972c3e3d0c7d51c9baaedf06fdfe9a7b69da3394779",
|
||||
b"893785aaddea396621c31dc5d465e2775cbc6b7423dc3498e80aa5da7a6a819d",
|
||||
b"1b2b69fbec485ef3f347ee6dc9c87d73505e45b5c9b02599b823ac94f5d642a1",
|
||||
b"724b4b3d3e8ac7588561ca00eec11693f6b85c03bb6b1302d458a7a4ce4b39e4",
|
||||
b"6a30ef4094128b6fa463b70cb21d141da92711d80ea94c9b73fb8a0471cc49a9",
|
||||
b"50968cf735e3f6a47834ae3745816234f72fd156aef1bec4b6a7d3a3151773bc",
|
||||
b"3ed01dd816dc93ea2d445681df11aa24e9fd1441de429eb0ee7816ccc09a2b7a",
|
||||
b"64bdc48c731313c7b37c1f1d13d6265ac7a2604ff630b50f591a86e610cb3005",
|
||||
b"96666c386cf99a74ec9eb55a5545aa90a3e53a8bbbe74cd3334b32d4968a3214",
|
||||
b"33555a41335654a29d5b7799bf180915e09095d21991dacf071583957b9e3f35",
|
||||
b"ffd391d554ce0672ae818a149dd55325f4cb933c97017b8148934474355d5a88",
|
||||
b"cf2050114ccaefd8a0ea6cf31d85e0232eadc8fd61277ff16496d2234b55c7d7",
|
||||
b"e42142b4243d5a2c59a2977d0385d49eab288085f8d38ead3ae5d87145c562ec",
|
||||
b"3bb810492422cc5c7466d86dcd8095b0d87e97634656a3fa5fe2270a2244c16b",
|
||||
b"17d2f0f7197a6612e311d141781f2b9539c4aef7affd729246c401890e000dde",
|
||||
b"a4f4256159ea6fb23b27eb8c5eb9cfb9083475985f355a85c78de8f2fef2b3ac",
|
||||
b"a36c4cf85204c67047c00d5dcc16677978839af0f0fde7ff973c98b66e244552",
|
||||
b"4a596559f450ce5e3a777d952d8d2ed8611e9f3facc8400483371f6eadc4bdb2",
|
||||
b"64855e54c94d14ab53afc6109d3c0033c665fab85b57c0e7d4e8da55b3b26952",
|
||||
b"a2ee2228d4798988ce3ac273c0cd8b9bbc4e3e58413eb22dfbe6395758659a2b",
|
||||
b"35c28ee2e25f5ad70384f1ca9723f520c955fb5fe9f2e56b9dc809479a9ca8cc",
|
||||
b"da3f6a7f55f821760330dd14495e68e7d153b05e472d38459d4728d63ad9df26",
|
||||
b"026134f6117e45a37c5c2dc2f330bdd274c6dc087526b91ecec4d6dac9bb7346",
|
||||
];
|
||||
|
||||
let mut input = Vec::new();
|
||||
for i in 0..128 {
|
||||
assert_eq!(
|
||||
Sha256::hash(&input),
|
||||
hex::decode(hashes[i] as &[u8]).unwrap().as_slice()
|
||||
);
|
||||
input.push(b'A');
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: more tests
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#[cfg(test)]
|
||||
use subtle::CtOption;
|
||||
|
||||
pub type Block16 = [u8; 16];
|
||||
|
||||
#[inline(always)]
|
||||
pub fn xor_block_16(block: &mut Block16, mask: &Block16) {
|
||||
for i in 0..16 {
|
||||
block[i] ^= mask[i];
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub trait ToOption<T> {
|
||||
fn to_option(self) -> Option<T>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<T> ToOption<T> for CtOption<T> {
|
||||
fn to_option(self) -> Option<T> {
|
||||
if bool::from(self.is_some()) {
|
||||
Some(self.unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
/// Test vectors for AES-ECB from NIST's validation suite.
|
||||
///
|
||||
/// See also https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/aes/AESAVS.pdf
|
||||
#[macro_use]
|
||||
extern crate arrayref;
|
||||
extern crate hex;
|
||||
extern crate regex;
|
||||
|
||||
use crypto::{aes256, Decrypt16BytesBlock, Encrypt16BytesBlock};
|
||||
use regex::Regex;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn aesavs() {
|
||||
// These data files are taken from https://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip.
|
||||
test_aesavs_file("tests/data/ECBVarKey256.rsp");
|
||||
test_aesavs_file("tests/data/ECBVarTxt256.rsp");
|
||||
}
|
||||
|
||||
fn test_aesavs_file<P: AsRef<Path>>(path: P) {
|
||||
// Implements some custom parsing for NIST's test vectors.
|
||||
let re_count = Regex::new("^COUNT = ([0-9]+)$").unwrap();
|
||||
let re_key = Regex::new("^KEY = ([0-9a-f]{64})$").unwrap();
|
||||
let re_plaintext = Regex::new("^PLAINTEXT = ([0-9a-f]{32})$").unwrap();
|
||||
let re_ciphertext = Regex::new("^CIPHERTEXT = ([0-9a-f]{32})$").unwrap();
|
||||
|
||||
let file = BufReader::new(File::open(path).unwrap());
|
||||
let mut lines = file.lines();
|
||||
|
||||
loop {
|
||||
let line = lines.next().unwrap().unwrap();
|
||||
if line == "[ENCRYPT]" {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0.. {
|
||||
// empty line
|
||||
let line = lines.next().unwrap().unwrap();
|
||||
assert_eq!(line, "");
|
||||
|
||||
let line = lines.next().unwrap().unwrap();
|
||||
if line == "[DECRYPT]" {
|
||||
// Skip the decryption tests, they are the same as the encryption tests.
|
||||
break;
|
||||
}
|
||||
// "COUNT = "
|
||||
let captures = re_count.captures(&line).unwrap();
|
||||
let count = captures.get(1).unwrap().as_str().parse::<usize>().unwrap();
|
||||
assert_eq!(count, i);
|
||||
|
||||
// "KEY = "
|
||||
let line = lines.next().unwrap().unwrap();
|
||||
let captures = re_key.captures(&line).unwrap();
|
||||
let key = hex::decode(captures.get(1).unwrap().as_str()).unwrap();
|
||||
assert_eq!(key.len(), 32);
|
||||
|
||||
// "PLAINTEXT = "
|
||||
let line = lines.next().unwrap().unwrap();
|
||||
let captures = re_plaintext.captures(&line).unwrap();
|
||||
let plaintext = hex::decode(captures.get(1).unwrap().as_str()).unwrap();
|
||||
assert_eq!(plaintext.len(), 16);
|
||||
|
||||
// "CIPHERTEXT = "
|
||||
let line = lines.next().unwrap().unwrap();
|
||||
let captures = re_ciphertext.captures(&line).unwrap();
|
||||
let ciphertext = hex::decode(captures.get(1).unwrap().as_str()).unwrap();
|
||||
assert_eq!(ciphertext.len(), 16);
|
||||
|
||||
{
|
||||
let encryption_key = aes256::EncryptionKey::new(array_ref![key, 0, 32]);
|
||||
let mut block: [u8; 16] = [Default::default(); 16];
|
||||
block.copy_from_slice(&plaintext);
|
||||
encryption_key.encrypt_block(&mut block);
|
||||
assert_eq!(&block, ciphertext.as_slice());
|
||||
}
|
||||
|
||||
{
|
||||
let encryption_key = aes256::EncryptionKey::new(array_ref![key, 0, 32]);
|
||||
let decryption_key = aes256::DecryptionKey::new(&encryption_key);
|
||||
let mut block: [u8; 16] = [Default::default(); 16];
|
||||
block.copy_from_slice(&ciphertext);
|
||||
decryption_key.decrypt_block(&mut block);
|
||||
assert_eq!(&block, plaintext.as_slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
/// A minimalist parser for ASN.1 encoded ECDSA signatures in DER form.
|
||||
use arrayref::mut_array_refs;
|
||||
use crypto::ecdsa;
|
||||
use std::convert::TryFrom;
|
||||
use std::io::Read;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Asn1Error {
|
||||
IoError(std::io::Error),
|
||||
InvalidTagClass,
|
||||
InvalidLongFormEncoding,
|
||||
ArithmeticOverflow,
|
||||
ExpectedSequenceTag(Tag),
|
||||
ExpectedIntegerTag(Tag),
|
||||
InvalidSequenceLen(usize),
|
||||
InvalidIntegerLen(usize),
|
||||
UnexpectedTrailingBytes,
|
||||
InvalidSignature,
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Asn1Error {
|
||||
fn from(e: std::io::Error) -> Asn1Error {
|
||||
Asn1Error::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum TagClass {
|
||||
Universal = 0,
|
||||
Application = 1,
|
||||
ContextSpecific = 2,
|
||||
Private = 3,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for TagClass {
|
||||
type Error = Asn1Error;
|
||||
|
||||
fn try_from(x: u8) -> Result<TagClass, Asn1Error> {
|
||||
match x {
|
||||
0 => Ok(TagClass::Universal),
|
||||
1 => Ok(TagClass::Application),
|
||||
2 => Ok(TagClass::ContextSpecific),
|
||||
3 => Ok(TagClass::Private),
|
||||
_ => Err(Asn1Error::InvalidTagClass),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum UniversalTag {
|
||||
Boolean = 1,
|
||||
Integer = 2,
|
||||
BitString = 3,
|
||||
OctetString = 4,
|
||||
Null = 5,
|
||||
ObjectIdentifier = 6,
|
||||
Utf8String = 12,
|
||||
Sequence = 16,
|
||||
Set = 17,
|
||||
PrintableString = 19,
|
||||
UtcTime = 23,
|
||||
GeneralizedTime = 24,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tag {
|
||||
class: TagClass,
|
||||
constructed: bool,
|
||||
number: u64,
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
fn is_sequence(&self) -> bool {
|
||||
self.class == TagClass::Universal
|
||||
&& self.constructed
|
||||
&& self.number == UniversalTag::Sequence as u64
|
||||
}
|
||||
|
||||
fn is_number(&self) -> bool {
|
||||
self.class == TagClass::Universal
|
||||
&& !self.constructed
|
||||
&& self.number == UniversalTag::Integer as u64
|
||||
}
|
||||
|
||||
// Parse an ASN.1 tag encoded in DER form.
|
||||
fn parse<R: Read>(input: &mut R) -> Result<Tag, Asn1Error> {
|
||||
let mut buf = [0u8; 1];
|
||||
|
||||
input.read_exact(&mut buf)?;
|
||||
let mut tag = buf[0];
|
||||
let class = TagClass::try_from(tag >> 6)?;
|
||||
let constructed = tag & 0x20 != 0;
|
||||
tag &= 0x1F;
|
||||
|
||||
if tag < 31 {
|
||||
// Short tag number
|
||||
let number = tag as u64;
|
||||
Ok(Tag {
|
||||
class,
|
||||
constructed,
|
||||
number,
|
||||
})
|
||||
} else {
|
||||
// Long tag number
|
||||
let mut number: u64 = 0;
|
||||
loop {
|
||||
input.read_exact(&mut buf)?;
|
||||
let x = buf[0];
|
||||
if number == 0 && x == 0 {
|
||||
return Err(Asn1Error::InvalidLongFormEncoding);
|
||||
}
|
||||
if number >> ((8 * std::mem::size_of::<u64>()) - 7) != 0 {
|
||||
return Err(Asn1Error::ArithmeticOverflow);
|
||||
}
|
||||
number = (number << 7) | (x & 0x7F) as u64;
|
||||
if (x & 0x80) == 0 {
|
||||
if number < 31 {
|
||||
return Err(Asn1Error::InvalidLongFormEncoding);
|
||||
}
|
||||
return Ok(Tag {
|
||||
class,
|
||||
constructed,
|
||||
number,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse an ASN.1 length encoded in DER form.
|
||||
fn parse_len<R: Read>(input: &mut R) -> Result<usize, Asn1Error> {
|
||||
let mut buf = [0u8; 1];
|
||||
|
||||
input.read_exact(&mut buf)?;
|
||||
let first_byte = buf[0];
|
||||
if (first_byte & 0x80) == 0 {
|
||||
// Short form
|
||||
Ok(first_byte as usize)
|
||||
} else {
|
||||
// Long form
|
||||
let nbytes = (first_byte & 0x7F) as usize;
|
||||
|
||||
let mut length: usize = 0;
|
||||
for _ in 0..nbytes {
|
||||
input.read_exact(&mut buf)?;
|
||||
let x = buf[0];
|
||||
if length == 0 && x == 0 {
|
||||
return Err(Asn1Error::InvalidLongFormEncoding);
|
||||
}
|
||||
if length >> (8 * (std::mem::size_of::<usize>() - 1)) != 0 {
|
||||
return Err(Asn1Error::ArithmeticOverflow);
|
||||
}
|
||||
length = (length << 8) | x as usize;
|
||||
}
|
||||
if length < 0x80 {
|
||||
return Err(Asn1Error::InvalidLongFormEncoding);
|
||||
}
|
||||
Ok(length)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_coordinate<R: Read>(mut input: R, bytes: &mut [u8; 32]) -> Result<usize, Asn1Error> {
|
||||
let tag = Tag::parse(&mut input)?;
|
||||
if !tag.is_number() {
|
||||
return Err(Asn1Error::ExpectedIntegerTag(tag));
|
||||
}
|
||||
let len = parse_len(&mut input)?;
|
||||
if len > 33 {
|
||||
return Err(Asn1Error::InvalidIntegerLen(len));
|
||||
}
|
||||
|
||||
let mut buf = vec![0; len];
|
||||
input.read_exact(&mut buf)?;
|
||||
|
||||
if len == 33 {
|
||||
if buf.remove(0) != 0 {
|
||||
return Err(Asn1Error::InvalidIntegerLen(len));
|
||||
}
|
||||
}
|
||||
|
||||
bytes[(32 - buf.len())..].copy_from_slice(&buf);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn parse_signature<R: Read>(mut input: R) -> Result<ecdsa::Signature, Asn1Error> {
|
||||
let tag = Tag::parse(&mut input)?;
|
||||
if !tag.is_sequence() {
|
||||
return Err(Asn1Error::ExpectedSequenceTag(tag));
|
||||
}
|
||||
let len = parse_len(&mut input)?;
|
||||
|
||||
let mut bytes = [0; 64];
|
||||
let (xbytes, ybytes) = mut_array_refs![&mut bytes, 32, 32];
|
||||
|
||||
let xlen = parse_coordinate(&mut input, xbytes)?;
|
||||
let ylen = parse_coordinate(&mut input, ybytes)?;
|
||||
|
||||
// Each coordinate has, besides (x|y)len bytes of integer, one byte for the tag and one
|
||||
// byte for the length (the length is at most 33 and therefore encoded on one byte).
|
||||
if len != xlen + ylen + 4 {
|
||||
return Err(Asn1Error::InvalidSequenceLen(len));
|
||||
}
|
||||
|
||||
// Check for unexpected bytes at the end.
|
||||
let is_eof = {
|
||||
let mut buf = [0u8; 1];
|
||||
match input.read_exact(&mut buf) {
|
||||
Ok(_) => false,
|
||||
Err(e) => e.kind() == std::io::ErrorKind::UnexpectedEof,
|
||||
}
|
||||
};
|
||||
if !is_eof {
|
||||
return Err(Asn1Error::UnexpectedTrailingBytes);
|
||||
}
|
||||
|
||||
ecdsa::Signature::from_bytes(&bytes).ok_or(Asn1Error::InvalidSignature)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user