owl_crypto/crypto/
mod.rs

1// This file is part of the un-prim.
2//
3// Copyright (C) 2022 Ade M Ramdani
4//
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
7// This program is free software: you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
20use secp256k1::{
21    ecdsa::{self, RecoverableSignature},
22    Error, Message,
23};
24use tiny_keccak::Hasher;
25
26mod keys;
27pub use keys::*;
28
29/// Calculate and return keccak-256 hash of the given input.
30/// # Input
31/// * `input` - The input to be hashed in byte slices.
32/// # Returns
33/// * `hash` - The keccak-256 hash of the input.
34pub fn keccak256(input: &[u8]) -> [u8; 32] {
35    let mut buf = [0u8; 32];
36    let mut hasher = tiny_keccak::Keccak::v256();
37    hasher.update(input);
38    hasher.finalize(&mut buf);
39    buf
40}
41
42/// Calculate and return keccak-512 hash of the given input.
43/// # Input
44/// * `input` - The input to be hashed in byte slices.
45/// # Returns
46/// * `hash` - The keccak-512 hash of the input.
47pub fn keccak512(input: &[u8]) -> [u8; 64] {
48    let mut buf = [0u8; 64];
49    let mut hasher = tiny_keccak::Keccak::v512();
50    hasher.update(input);
51    hasher.finalize(&mut buf);
52    buf
53}
54
55pub type Signature = RecoverableSignature;
56
57/// Sign a message with the given secret key and return the recoverable signature.
58pub fn sign(msg: &[u8], secret_key: SecretKey) -> Result<Signature, Error> {
59    let secp = secp256k1::Secp256k1::new();
60    let hash = keccak256(msg);
61    let msg_bytes = Message::from_slice(&hash)?;
62    let sk = secp256k1::SecretKey::from_slice(secret_key.to_slice())?;
63    let sig = secp.sign_ecdsa_recoverable(&msg_bytes, &sk);
64    Ok(sig)
65}
66
67/// Verify a signature with the given public key and message.
68pub fn verify(sig: &Signature, pub_key: &PublicKey, msg: &[u8]) -> Result<bool, Error> {
69    let secp = secp256k1::Secp256k1::new();
70    let hash = keccak256(msg);
71    let msg_bytes = Message::from_slice(&hash)?;
72    let pk = secp256k1::PublicKey::from_slice(pub_key.to_slice())?;
73    let sig = ecdsa::Signature::from_compact(&sig.serialize_compact().1)?;
74
75    let verif = secp.verify_ecdsa(&msg_bytes, &sig, &pk);
76    Ok(verif.is_ok())
77}
78
79/// Recover the public key from the signature and message.
80pub fn ecrecover(msg: &[u8], sig: &Signature) -> Result<PublicKey, Error> {
81    let secp = secp256k1::Secp256k1::new();
82    let hash = keccak256(msg);
83    let msg_bytes = Message::from_slice(&hash)?;
84    let pk = secp.recover_ecdsa(&msg_bytes, sig)?;
85    let pub_key = PublicKey::from_slice(&pk.serialize_uncompressed());
86
87    match pub_key {
88        Ok(pk) => Ok(pk),
89        Err(_) => Err(Error::InvalidPublicKey),
90    }
91}
92
93#[cfg(test)]
94mod crypto_test {
95    use super::*;
96
97    #[test]
98    fn test_sign_and_verify() {
99        let (sk, pk) = new_key_pair().unwrap();
100        let msg = b"hello world";
101        let sig = sign(msg, sk).unwrap();
102        assert!(verify(&sig, &pk, msg).unwrap());
103    }
104
105    #[test]
106    fn test_ecrecover() {
107        let (sk, pk) = new_key_pair().unwrap();
108        let msg = b"hello world";
109        let sig = sign(msg, sk).unwrap();
110        let pk2 = ecrecover(msg, &sig).unwrap();
111        assert_eq!(pk, pk2);
112    }
113}