From 7bcd4041857c24cedd59720a8d641c299d8ec217 Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Thu, 1 Jun 2023 12:30:53 +0200 Subject: [PATCH] oaep rsa base --- src-py/plexcryptool/scripts/oaep_rsa.py | 92 +++++++++++++++++++++++++ src/math/ecc.rs | 2 +- src/math/gallois.rs | 55 ++++++++++++--- src/math/modexp.rs | 1 - 4 files changed, 137 insertions(+), 13 deletions(-) create mode 100755 src-py/plexcryptool/scripts/oaep_rsa.py diff --git a/src-py/plexcryptool/scripts/oaep_rsa.py b/src-py/plexcryptool/scripts/oaep_rsa.py new file mode 100755 index 0000000..8d2d7c5 --- /dev/null +++ b/src-py/plexcryptool/scripts/oaep_rsa.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +""" +Perform RSA-OAEP + +@author: Christoph J. Scherr +@license: MIT +@source: https://git.cscherr.de/PlexSheep/plexcryptool/src/branch/master/plexcryptool/trash-hash.py +""" + +import hashlib +from math import floor + +# the given key in the assignment +GIVEN_PUB_KEY = (0xAF5466C26A6B662AC98C06023501C9DF6036B065BD1F6804B1FC86307718DA4048211FD68A06917DE6F81DC018DCAF84B38AB77A6538BA2FE6664D3FB81E4A0886BBCDAB071AD6823FE20DF1CD67D33FB6CC5DA519F69B11F3D48534074A83F03A5A9545427720A30A27432E94970155A026572E358072023061AF65A2A18E85, 0x10001) + +SEED_LENGTH = 8 # bytes + +def mgf1(seed: bytearray, length: int, hash_func=hashlib.sha256) -> bytearray: + """ + Code stolen from wikipedia + """ + hLen = hash_func().digest_size + # https://www.ietf.org/rfc/rfc2437.txt + # 1.If l > 2^32(hLen), output "mask too long" and stop. + if length > (hLen << 32): + raise ValueError("mask too long") + # 2.Let T be the empty octet string. + T = b"" + # 3.For counter from 0 to \lceil{l / hLen}\rceil-1, do the following: + # Note: \lceil{l / hLen}\rceil-1 is the number of iterations needed, + # but it's easier to check if we have reached the desired length. + counter = 0 + while len(T) < length: + # a.Convert counter to an octet string C of length 4 with the primitive I2OSP: C = I2OSP (counter, 4) + C = int.to_bytes(counter, 4, 'big') + # b.Concatenate the hash of the seed Z and C to the octet string T: T = T || Hash (Z || C) + T += hash_func(seed + C).digest() + counter += 1 + # 4.Output the leading l octets of T as the octet string mask. + return bytearray(T[:length]) + +def byte_xor(ba1: bytearray, ba2: bytearray): + a = int(ba1.hex(), 16) + b = int(ba2.hex(), 16) + c = a^b + if c.bit_length() / 8 == floor(c.bit_length() / 8): + clen = floor(c.bit_length() / 8) + else: + clen = floor(c.bit_length() / 8) + 1 + return bytearray(c.to_bytes(clen, 'big')) + + +def rsa_oaep_inner(seed: bytearray, datablock: bytearray) -> tuple[bytearray, bytearray, bytearray]: + mgf_seed = mgf1(seed, len(seed)) + masked_db = byte_xor(mgf_seed, datablock) + print(masked_db.hex()) + +def test_rsa_oaep_inner(): + seed: bytearray = bytearray.fromhex("aa1122fe0815beef") + db: bytearray = bytearray.fromhex(""" + 00000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000001466f6f626172203132 + 33343536373839 + """) + print("seed\t%s" % seed.hex()) + print("db\t%s" % db.hex()) + + result = rsa_oaep_inner(seed, db) + + GIVEN_MASK_FOR_DB = bytearray.fromhex(""" + ea600669f6f16b3a2ad05d4b6d9b23911c8cc432fddd8d34a68d88af + 3d787b7eebf6cd1b720812086758ce56e24ab819ccd8fb5eedb1cae9 + f6f895667d7f89d0454b828777ecabc040a649c8956e78ec1c721370 + 663065cbc343deabad9eb6f2aceab6bfed5beb232cc55413bfffa06e + 68627d7ec3ded5 + """) + GIVEN_MASKED_DB = bytearray.fromhex(""" + ea600669f6f16b3a2ad05d4b6d9b23911c8cc432fddd8d34a68d88af + 3d787b7eebf6cd1b720812086758ce56e24ab819ccd8fb5eedb1cae9 + f6f895667d7f89d0454b828777ecabc040a649c8956e78ec1c721370 + 663065cbc343deabad9eb6f2aceab6bfed5bea6543aa3672cddf915c + 5b564848f4e6ec + """) + GIVEN_MASK_FOR_SEED = bytearray.fromhex("713162084a4e0e6d ") + GIVEN_MASKED_SEED = bytearray.fromhex("db2040f6425bb082") + assert result[0] == GIVEN_MASKED_SEED, "is %s" % result[0].hex() + assert result[1] == GIVEN_MASKED_DB, "is %s" % result[1].hex() + +if __name__ == "__main__": + test_rsa_oaep_inner() diff --git a/src/math/ecc.rs b/src/math/ecc.rs index f10f703..0245ef1 100644 --- a/src/math/ecc.rs +++ b/src/math/ecc.rs @@ -68,7 +68,7 @@ impl ElipticCurve { #[test] fn test_check_point() { - let f = GalloisField::new(1151, true); + let f = GalloisField::new(1151, true, None); let ec = ElipticCurve::new(f, 1, 679, true); // real points let p = vec![ diff --git a/src/math/gallois.rs b/src/math/gallois.rs index c42678f..7dd9a83 100644 --- a/src/math/gallois.rs +++ b/src/math/gallois.rs @@ -25,6 +25,12 @@ use pyo3::{prelude::*, exceptions::PyValueError}; use primes::is_prime; +/////////////////////////////////////////////////////////////////////////////////////////////////// + +pub const F_8_DEFAULT_RELATION: u128 = 0xb; +pub const F_16_DEFAULT_RELATION: u128 = 0x13; +pub const F_256_DEFAULT_RELATION: u128 = 0x11b; + /////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] /// used when trying to find a root for a number which does not have a root. @@ -70,11 +76,28 @@ pub struct GalloisField { /// implementations for the gallois field impl GalloisField { /// make a new gallois field - pub fn new(base: u128, verbose: bool, relation: Option) -> Self { + pub fn new(base: u128, verbose: bool, mut relation: Option) -> Self { let prime_base: bool = is_prime(base as u64); if !prime_base { - println!("Non prime bases for a field are currently very experimental.\n - Use them at your own risk! ({} is not a prime.)", base); + println!("Non prime bases for a field are currently very experimental.\nUse them at your own risk! ({} is not a prime.)", base); + if relation.is_none() { + // TODO choose common relations for known fields + + match base { + 8 => { + relation = Some(F_8_DEFAULT_RELATION); + } + 16 => { + relation = Some(F_16_DEFAULT_RELATION); + } + 256 => { + relation = Some(F_256_DEFAULT_RELATION); + } + _ => { + panic!("You did not specify a relation and none could be found."); + } + } + } } let mut field = GalloisField{ base, @@ -124,7 +147,7 @@ impl GalloisField { if n < 0 { panic!("reduction for negative numbers not implemented."); } - modred(n as u128, self.relation.unwrap(), self.verbose).expect("modular reduction didn't work") + modred(n as u128, self.relation.unwrap(), false).expect("modular reduction didn't work") } } @@ -421,6 +444,16 @@ fn test_gallois_sqrt() { assert_eq!(field.sqrt(275).expect("function says there is no root but there is"), (585, 392)); } +#[test] +fn test_gallois_reduce() { + let field = GalloisField::new(977, true, None); + for i in 0..976 { + assert_eq!(field.reduce(i), i); + } + + let field = GalloisField::new(16, true, None); +} + #[test] fn test_gallois_inverse() { let field = GalloisField::new(31, true, None); @@ -438,10 +471,10 @@ fn test_gallois_inverse() { assert_eq!(field.inverse(7).unwrap(), 10); assert!(field.inverse(0).is_err()); - // TODO i think this test does not catch all edge cases. In some cases, something seems to be - // wrong. - // TODO add a test for a field that has a non prime base + let field = GalloisField::new(16, true, None); + assert_eq!(field.inverse(0x130).unwrap(), 0); + assert!(field.inverse(0).is_err()); } #[test] @@ -450,8 +483,8 @@ fn test_calc_char() { assert_eq!(GalloisField::new(1151, true, None).calc_char(), 1151); assert_eq!(GalloisField::new(2, true, None).calc_char(), 2); - // only primes are supported right now. TODO - //assert_eq!(GalloisField::new(8, true).calc_char(), 2); - //assert_eq!(GalloisField::new(64, true).calc_char(), 2); - //assert_eq!(GalloisField::new(2u128.pow(64u32), true).calc_char(), 2); + // experimental + assert_eq!(GalloisField::new(8, true, None).calc_char(), 2); + assert_eq!(GalloisField::new(64, true, None).calc_char(), 2); + //assert_eq!(GalloisField::new(2u128.pow(64u32), true, None).calc_char(), 2); } diff --git a/src/math/modexp.rs b/src/math/modexp.rs index 60755b7..26145b4 100644 --- a/src/math/modexp.rs +++ b/src/math/modexp.rs @@ -159,5 +159,4 @@ fn dump_bin(bytes: &Vec) { print!("{:08b}", byte); } println!(); - }