// Copyright 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Cryptography library for LMP procedures.
//!
//! IMPORTANT
//! These cryptography methods do not provide any security or correctness
//! ensurance. They should be used only in Bluetooth emulation, not including
//! any production environment.
use num_bigint::{BigInt, Sign};
use num_integer::Integer;
use num_traits::{One, Signed, Zero};
use rand::{thread_rng, Rng};
use std::convert::TryInto;
use std::marker::PhantomData;
#[ derive(Debug, Clone, PartialEq, Eq)]
pub enum PublicKey {
P192([u8; P192r1::PUBLIC_KEY_SIZE]),
P256([u8; P256r1::PUBLIC_KEY_SIZE]),
}
impl PublicKey {
pub fn new(size: usize) -> Option<
Self > {
match size {
P192r1::PUBLIC_KEY_SIZE => Some(
Self ::P192([
0 ; P192r1::PUBLIC_KEY_SIZE])),
P256r1::PUBLIC_KEY_SIZE => Some(
Self ::P256([
0 ; P256r1::PUBLIC_KEY_SIZE])),
_ => None,
}
}
fn from_bytes(bytes: &[u8]) -> Option<
Self > {
if let Ok(inner) = bytes.try_into() {
Some(PublicKey::P192(inner))
}
else if let Ok(inner) = bytes.try_into() {
Some(PublicKey::P256(inner))
}
else {
None
}
}
pub fn as_slice(&
self ) -> &[u8] {
match self {
PublicKey::P192(inner) => inner,
PublicKey::P256(inner) => inner,
}
}
pub fn size(&
self ) -> usize {
self .as_slice().len()
}
pub fn as_mut_slice(&
mut self ) -> &
mut [u8]
{
match self {
PublicKey::P192(inner) => inner,
PublicKey::P256(inner) => inner,
}
}
fn get_x(&self ) -> BigInt {
BigInt::from_signed_bytes_le(&self .as_slice()[0 ..self .size() / 2 ])
}
fn get_y(&self ) -> BigInt {
BigInt::from_signed_bytes_le(&self .as_slice()[self .size() / 2 ..self .size()])
}
fn to_point<Curve: EllipticCurve>(&self ) -> Point<Curve> {
Point::from_affine(self .get_x(), self .get_y())
}
}
#[ derive(Debug, Clone, PartialEq, Eq)]
pub enum PrivateKey {
P192([u8; P192r1::PRIVATE_KEY_SIZE]),
P256([u8; P256r1::PRIVATE_KEY_SIZE]),
}
#[ derive(Debug, Clone, PartialEq, Eq)]
pub enum DhKey {
P192([u8; P192r1::PUBLIC_KEY_SIZE]),
P256([u8; P256r1::PUBLIC_KEY_SIZE]),
}
impl DhKey {
fn from_bytes(bytes: &[u8]) -> Option<Self > {
if let Ok(inner) = bytes.try_into() {
Some(DhKey::P192(inner))
} else if let Ok(inner) = bytes.try_into() {
Some(DhKey::P256(inner))
} else {
None
}
}
}
impl PrivateKey {
// Generate a private key in range[1,2**191]
pub fn generate_p192() -> Self {
let random_bytes: [u8; P192r1::PRIVATE_KEY_SIZE] = thread_rng().gen ();
let mut key = BigInt::from_signed_bytes_le(&random_bytes);
if key.is_negative() {
key = -key;
}
if key < BigInt::one() {
key = BigInt::one();
}
let buf = key.to_signed_bytes_le();
let mut inner = [0 ; P192r1::PRIVATE_KEY_SIZE];
inner[0 ..buf.len()].copy_from_slice(&buf);
Self ::P192(inner)
}
pub fn generate_p256() -> Self {
let random_bytes: [u8; P256r1::PRIVATE_KEY_SIZE] = thread_rng().gen ();
let mut key = BigInt::from_signed_bytes_le(&random_bytes);
if key.is_negative() {
key = -key;
}
if key < BigInt::one() {
key = BigInt::one();
}
let buf = key.to_signed_bytes_le();
let mut inner = [0 ; P256r1::PRIVATE_KEY_SIZE];
inner[0 ..buf.len()].copy_from_slice(&buf);
Self ::P256(inner)
}
pub fn as_slice(&self ) -> &[u8] {
match self {
PrivateKey::P192(inner) => inner,
PrivateKey::P256(inner) => inner,
}
}
fn to_bigint(&self ) -> BigInt {
BigInt::from_signed_bytes_le(self .as_slice())
}
pub fn derive(&self ) -> PublicKey {
let bytes = match self {
PrivateKey::P192(_) => {
Point::<P192r1>::generate_public_key(&self .to_bigint()).to_bytes()
}
PrivateKey::P256(_) => {
Point::<P256r1>::generate_public_key(&self .to_bigint()).to_bytes()
}
}
.unwrap();
PublicKey::from_bytes(&bytes).unwrap()
}
pub fn shared_secret(&self , peer_public_key: PublicKey) -> DhKey {
let bytes = match self {
PrivateKey::P192(_) => {
(&peer_public_key.to_point::<P192r1>() * &self .to_bigint()).to_bytes()
}
PrivateKey::P256(_) => {
(&peer_public_key.to_point::<P256r1>() * &self .to_bigint()).to_bytes()
}
}
.unwrap();
DhKey::from_bytes(&bytes).unwrap()
}
}
// Modular Inverse
fn mod_inv(x: &BigInt, m: &BigInt) -> Option<BigInt> {
let egcd = x.extended_gcd(m);
if !egcd.gcd.is_one() {
None
} else {
Some(egcd.x % m)
}
}
trait EllipticCurve {
type Param: AsRef<[u8]>;
const A: i32;
const P: Self ::Param;
const G_X: Self ::Param;
const G_Y: Self ::Param;
const PRIVATE_KEY_SIZE: usize;
const PUBLIC_KEY_SIZE: usize;
fn p() -> BigInt {
BigInt::from_bytes_be(Sign::Plus, Self ::P.as_ref())
}
}
#[ derive(Debug, Clone, PartialEq)]
struct P192r1;
impl EllipticCurve for P192r1 {
type Param = [u8; 24 ];
const A: i32 = -3 ;
const P: Self ::Param = [
0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff,
0 xfe, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff,
];
const G_X: Self ::Param = [
0 x18, 0 x8d, 0 xa8, 0 x0e, 0 xb0, 0 x30, 0 x90, 0 xf6, 0 x7c, 0 xbf, 0 x20, 0 xeb, 0 x43, 0 xa1, 0 x88,
0 x00, 0 xf4, 0 xff, 0 x0a, 0 xfd, 0 x82, 0 xff, 0 x10, 0 x12,
];
const G_Y: Self ::Param = [
0 x07, 0 x19, 0 x2b, 0 x95, 0 xff, 0 xc8, 0 xda, 0 x78, 0 x63, 0 x10, 0 x11, 0 xed, 0 x6b, 0 x24, 0 xcd,
0 xd5, 0 x73, 0 xf9, 0 x77, 0 xa1, 0 x1e, 0 x79, 0 x48, 0 x11,
];
const PRIVATE_KEY_SIZE: usize = 24 ;
const PUBLIC_KEY_SIZE: usize = 48 ;
}
#[ derive(Debug, Clone, PartialEq)]
struct P256r1;
impl EllipticCurve for P256r1 {
type Param = [u8; 32 ];
const A: i32 = -3 ;
const P: Self ::Param = [
0 xff, 0 xff, 0 xff, 0 xff, 0 x00, 0 x00, 0 x00, 0 x01, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00,
0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff,
0 xff, 0 xff,
];
const G_X: Self ::Param = [
0 x6b, 0 x17, 0 xd1, 0 xf2, 0 xe1, 0 x2c, 0 x42, 0 x47, 0 xf8, 0 xbc, 0 xe6, 0 xe5, 0 x63, 0 xa4, 0 x40,
0 xf2, 0 x77, 0 x03, 0 x7d, 0 x81, 0 x2d, 0 xeb, 0 x33, 0 xa0, 0 xf4, 0 xa1, 0 x39, 0 x45, 0 xd8, 0 x98,
0 xc2, 0 x96,
];
const G_Y: Self ::Param = [
0 x4f, 0 xe3, 0 x42, 0 xe2, 0 xfe, 0 x1a, 0 x7f, 0 x9b, 0 x8e, 0 xe7, 0 xeb, 0 x4a, 0 x7c, 0 x0f, 0 x9e,
0 x16, 0 x2b, 0 xce, 0 x33, 0 x57, 0 x6b, 0 x31, 0 x5e, 0 xce, 0 xcb, 0 xb6, 0 x40, 0 x68, 0 x37, 0 xbf,
0 x51, 0 xf5,
];
const PRIVATE_KEY_SIZE: usize = 32 ;
const PUBLIC_KEY_SIZE: usize = 64 ;
}
// https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
#[ derive(Debug, PartialEq)]
enum Point<Curve> {
Infinite(PhantomData<Curve>),
Finite { x: BigInt, y: BigInt, z: BigInt, _curve: PhantomData<Curve> },
}
impl <Curve> Point<Curve>
where
Curve: EllipticCurve,
{
fn o() -> Self {
Point::Infinite(PhantomData)
}
fn generate_public_key(private_key: &BigInt) -> Self {
&Self ::g() * private_key
}
fn new(x: BigInt, y: BigInt, z: BigInt) -> Self {
Point::Finite { x, y, z, _curve: PhantomData }
}
fn from_affine(x: BigInt, y: BigInt) -> Self {
Self ::new(x, y, BigInt::from(1 ))
}
fn g() -> Self {
Self ::from_affine(
BigInt::from_bytes_be(Sign::Plus, Curve::G_X.as_ref()),
BigInt::from_bytes_be(Sign::Plus, Curve::G_Y.as_ref()),
)
}
fn to_affine(&self ) -> Option<(BigInt, BigInt)> {
match self {
Point::Infinite(_) => None,
Point::Finite { x, y, z, _curve } => {
let p = &Curve::p();
let inv_z = mod_inv(z, p).unwrap();
let affine_x = (x * inv_z.pow(2 )) % p;
let affine_y = (y * inv_z.pow(3 )) % p;
Some((affine_x, affine_y))
}
}
}
fn to_bytes(&self ) -> Option<Vec<u8>> {
self .to_affine().map(|(x, y)| {
let mut x = x.to_signed_bytes_le();
x.resize(Curve::PRIVATE_KEY_SIZE, 0 );
let mut y = y.to_signed_bytes_le();
y.resize(Curve::PRIVATE_KEY_SIZE, 0 );
x.append(&mut y);
x
})
}
fn double(&self ) -> Self {
// https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates#Point_Doubling_(4M_+_6S_or_4M_+_4S)
match self {
Point::Infinite(_) => Point::o(),
Point::Finite { y, .. } if y.is_zero() => Point::o(),
Point::Finite { x, y, z, _curve } => {
let s = 4 * x * y.pow(2 );
let m: BigInt = 3 * x.pow(2 ) + Curve::A * z.pow(4 );
let rx = m.pow(2 ) - 2 * &s;
let ry = m * (s - &rx) - 8 * y.pow(4 );
let rz = 2 * y * z;
let p = &Curve::p();
Point::new(rx % p, ry % p, rz % p)
}
}
}
}
impl <Curve> Clone for Point<Curve>
where
Curve: EllipticCurve,
{
fn clone(&self ) -> Self {
match self {
Point::Infinite(_) => Point::o(),
Point::Finite { x, y, z, _curve } => Point::new(x.clone(), y.clone(), z.clone()),
}
}
}
// Elliptic Curve Group Addition
// https://mathworld.wolfram.com/EllipticCurve.html
impl <Curve> std::ops::Add<&Point<Curve>> for &Point<Curve>
where
Curve: EllipticCurve,
{
type Output = Point<Curve>;
fn add(self , rhs: &Point<Curve>) -> Self ::Output {
// P + O = O + P = P
match (self , rhs) {
(Point::Infinite(_), Point::Infinite(_)) => Point::o(),
(Point::Infinite(_), Point::Finite { .. }) => rhs.clone(),
(Point::Finite { .. }, Point::Infinite(_)) => self .clone(),
(
Point::Finite { _curve: _, x: x1, y: y1, z: z1 },
Point::Finite { _curve: _, x: x2, y: y2, z: z2 },
) => {
// https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates#Point_Addition_(12M_+_4S)
let p = &Curve::p();
let u1 = (x1 * z2.pow(2 )) % p;
let u2 = (x2 * z1.pow(2 )) % p;
let s1 = (y1 * z2.pow(3 )) % p;
let s2 = (y2 * z1.pow(3 )) % p;
if u1 == u2 {
if s1 != s2 {
Point::o()
} else {
self .double()
}
} else {
let h = &u2 - &u1;
let r = &s2 - &s1;
let h3 = h.pow(3 ) % p;
let u1h2 = (u1 * h.pow(2 )) % p;
let x3 = r.pow(2 ) - &h3 - 2 * &u1h2;
let y3 = r * (u1h2 - &x3) - s1 * h3;
let z3 = h * z1 * z2;
Point::new(x3 % p, y3 % p, z3 % p)
}
}
}
}
}
impl <Curve> std::ops::Mul<&BigInt> for &Point<Curve>
where
Curve: EllipticCurve,
{
type Output = Point<Curve>;
fn mul(self , rhs: &BigInt) -> Self ::Output {
let mut addend = self .clone();
let mut result = Point::o();
let mut i = rhs.clone();
// O(logN) double-and-add multiplication
while !i.is_zero() {
if i.is_odd() {
result = &result + &addend;
}
addend = addend.double();
i /= 2 ;
}
result
}
}
#[ cfg(test)]
mod tests {
use crate ::lmp::ec::*;
use num_bigint::BigInt;
struct EcTestCase<const N: usize> {
pub priv_a: [u8; N],
pub priv_b: [u8; N],
pub pub_a: [u8; N],
pub dh_x: [u8; N],
}
// Private A, Private B, Public A(x), DHKey
const P192_TEST_CASES: [EcTestCase<48 >; 4 ] = [
EcTestCase::<48 > {
priv_a: *b"07915f86918ddc27005df1d6cf0c142b625ed2eff4a518ff" ,
priv_b: *b"1e636ca790b50f68f15d8dbe86244e309211d635de00e16d" ,
pub_a: *b"15207009984421a6586f9fc3fe7e4329d2809ea51125f8ed" ,
dh_x: *b"fb3ba2012c7e62466e486e229290175b4afebc13fdccee46" ,
},
EcTestCase::<48 > {
priv_a: *b"52ec1ca6e0ec973c29065c3ca10be80057243002f09bb43e" ,
priv_b: *b"57231203533e9efe18cc622fd0e34c6a29c6e0fa3ab3bc53" ,
pub_a: *b"45571f027e0d690795d61560804da5de789a48f94ab4b07e" ,
dh_x: *b"a20a34b5497332aa7a76ab135cc0c168333be309d463c0c0" ,
},
EcTestCase::<48 > {
priv_a: *b"00a0df08eaf51e6e7be519d67c6749ea3f4517cdd2e9e821" ,
priv_b: *b"2bf5e0d1699d50ca5025e8e2d9b13244b4d322a328be1821" ,
pub_a: *b"2ed35b430fa45f9d329186d754eeeb0495f0f653127f613d" ,
dh_x: *b"3b3986ba70790762f282a12a6d3bcae7a2ca01e25b87724e" ,
},
EcTestCase::<48 > {
priv_a: *b"030a4af66e1a4d590a83e0284fca5cdf83292b84f4c71168" ,
priv_b: *b"12448b5c69ecd10c0471060f2bf86345c5e83c03d16bae2c" ,
pub_a: *b"f24a6899218fa912e7e4a8ba9357cb8182958f9fa42c968c" ,
dh_x: *b"4a78f83fba757c35f94abea43e92effdd2bc700723c61939" ,
},
];
// Private A, Private B, Public A(x), DHKey
const P256_TEST_CASES: [EcTestCase<64 >; 2 ] = [
EcTestCase::<64 > {
priv_a: *b"3f49f6d4a3c55f3874c9b3e3d2103f504aff607beb40b7995899b8a6cd3c1abd" ,
priv_b: *b"55188b3d32f6bb9a900afcfbeed4e72a59cb9ac2f19d7cfb6b4fdd49f47fc5fd" ,
pub_a: *b"20b003d2f297be2c5e2c83a7e9f9a5b9eff49111acf4fddbcc0301480e359de6" ,
dh_x: *b"ec0234a357c8ad05341010a60a397d9b99796b13b4f866f1868d34f373bfa698" ,
},
EcTestCase::<64 > {
priv_a: *b"06a516693c9aa31a6084545d0c5db641b48572b97203ddffb7ac73f7d0457663" ,
priv_b: *b"529aa0670d72cd6497502ed473502b037e8803b5c60829a5a3caa219505530ba" ,
pub_a: *b"2c31a47b5779809ef44cb5eaaf5c3e43d5f8faad4a8794cb987e9b03745c78dd" ,
dh_x: *b"ab85843a2f6d883f62e5684b38e307335fe6e1945ecd19604105c6f23221eb69" ,
},
];
#[ test]
fn p192() {
for test_case in P192_TEST_CASES {
let priv_a = BigInt::parse_bytes(&test_case.priv_a, 16 ).unwrap();
let priv_b = BigInt::parse_bytes(&test_case.priv_b, 16 ).unwrap();
let pub_a = Point::<P192r1>::generate_public_key(&priv_a);
let pub_b = Point::<P192r1>::generate_public_key(&priv_b);
assert_eq!(
pub_a.to_affine().unwrap().0 ,
BigInt::parse_bytes(&test_case.pub_a, 16 ).unwrap()
);
let shared = &pub_a * &priv_b;
assert_eq!(
shared.to_affine().unwrap().0 ,
BigInt::parse_bytes(&test_case.dh_x, 16 ).unwrap()
);
assert_eq!(
(&pub_a * &priv_b).to_affine().unwrap().0 ,
(&pub_b * &priv_a).to_affine().unwrap().0
);
}
}
#[ test]
fn p256() {
for test_case in P256_TEST_CASES {
let priv_a = BigInt::parse_bytes(&test_case.priv_a, 16 ).unwrap();
let priv_b = BigInt::parse_bytes(&test_case.priv_b, 16 ).unwrap();
let pub_a = Point::<P256r1>::generate_public_key(&priv_a);
let pub_b = Point::<P256r1>::generate_public_key(&priv_b);
assert_eq!(
pub_a.to_affine().unwrap().0 ,
BigInt::parse_bytes(&test_case.pub_a, 16 ).unwrap()
);
let shared = &pub_a * &priv_b;
assert_eq!(
shared.to_affine().unwrap().0 ,
BigInt::parse_bytes(&test_case.dh_x, 16 ).unwrap()
);
assert_eq!(
(&pub_a * &priv_b).to_affine().unwrap().0 ,
(&pub_b * &priv_a).to_affine().unwrap().0
);
}
}
}
Messung V0.5 in Prozent C=86 H=96 G=90
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-27)
¤
*© Formatika GbR, Deutschland