secp256k1/
ecdh.rs

1// Bitcoin secp256k1 bindings
2// Written in 2015 by
3//   Andrew Poelstra
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the CC0 Public Domain Dedication
11// along with this software.
12// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13//
14
15//! # ECDH
16//! Support for shared secret computations
17//!
18
19use std::ops;
20
21use super::Secp256k1;
22use key::{SecretKey, PublicKey};
23use ffi;
24
25/// A tag used for recovering the public key from a compact signature
26#[derive(Copy, Clone, PartialEq, Eq, Debug)]
27#[repr(C)]
28pub struct SharedSecret(ffi::SharedSecret);
29
30impl SharedSecret {
31    /// Creates a new shared secret from a pubkey and secret key
32    #[inline]
33    pub fn new(secp: &Secp256k1, point: &PublicKey, scalar: &SecretKey) -> SharedSecret {
34        unsafe {
35            let mut ss = ffi::SharedSecret::blank();
36            let res = ffi::secp256k1_ecdh(secp.ctx, &mut ss, point.as_ptr(), scalar.as_ptr());
37            debug_assert_eq!(res, 1);
38            SharedSecret(ss)
39        }
40    }
41
42    /// Creates a new unhashed shared secret from a pubkey and secret key
43    #[inline]
44    pub fn new_raw(secp: &Secp256k1, point: &PublicKey, scalar: &SecretKey) -> SharedSecret {
45        unsafe {
46            let mut ss = ffi::SharedSecret::blank();
47            let res = ffi::secp256k1_ecdh_raw(secp.ctx, &mut ss, point.as_ptr(), scalar.as_ptr());
48            debug_assert_eq!(res, 1);
49            SharedSecret(ss)
50        }
51    }
52
53    /// Obtains a raw pointer suitable for use with FFI functions
54    #[inline]
55    pub fn as_ptr(&self) -> *const ffi::SharedSecret {
56        &self.0 as *const _
57    }
58}
59
60/// Creates a new shared secret from a FFI shared secret
61impl From<ffi::SharedSecret> for SharedSecret {
62    #[inline]
63    fn from(ss: ffi::SharedSecret) -> SharedSecret {
64        SharedSecret(ss)
65    }
66}
67
68
69impl ops::Index<usize> for SharedSecret {
70    type Output = u8;
71
72    #[inline]
73    fn index(&self, index: usize) -> &u8 {
74        &self.0[index]
75    }
76}
77
78impl ops::Index<ops::Range<usize>> for SharedSecret {
79    type Output = [u8];
80
81    #[inline]
82    fn index(&self, index: ops::Range<usize>) -> &[u8] {
83        &self.0[index]
84    }
85}
86
87impl ops::Index<ops::RangeFrom<usize>> for SharedSecret {
88    type Output = [u8];
89
90    #[inline]
91    fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
92        &self.0[index.start..]
93    }
94}
95
96impl ops::Index<ops::RangeFull> for SharedSecret {
97    type Output = [u8];
98
99    #[inline]
100    fn index(&self, _: ops::RangeFull) -> &[u8] {
101        &self.0[..]
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use rand::thread_rng;
108    use super::SharedSecret;
109    use super::super::Secp256k1;
110
111    #[test]
112    fn ecdh() {
113        let s = Secp256k1::with_caps(::ContextFlag::SignOnly);
114        let (sk1, pk1) = s.generate_keypair(&mut thread_rng()).unwrap();
115        let (sk2, pk2) = s.generate_keypair(&mut thread_rng()).unwrap();
116
117        let sec1 = SharedSecret::new(&s, &pk1, &sk2);
118        let sec2 = SharedSecret::new(&s, &pk2, &sk1);
119        let sec_odd = SharedSecret::new(&s, &pk1, &sk1);
120        assert_eq!(sec1, sec2);
121        assert!(sec_odd != sec2);
122    }
123}
124
125#[cfg(all(test, feature = "unstable"))]
126mod benches {
127    use rand::thread_rng;
128    use test::{Bencher, black_box};
129
130    use super::SharedSecret;
131    use super::super::Secp256k1;
132
133    #[bench]
134    pub fn bench_ecdh(bh: &mut Bencher) {
135        let s = Secp256k1::with_caps(::ContextFlag::SignOnly);
136        let (sk, pk) = s.generate_keypair(&mut thread_rng()).unwrap();
137
138        let s = Secp256k1::new();
139        bh.iter( || {
140            let res = SharedSecret::new(&s, &pk, &sk);
141            black_box(res);
142        });
143    }
144}
145