Add back md5 support
Rust-crypto doesn't seem to have good rustpkg support so I've pulled the old MD5 implementation from libextra in for now.
This commit is contained in:
parent
2178eceeee
commit
8856aed96b
@ -1,12 +0,0 @@
|
||||
PostgreSQL Database Management System
|
||||
(formerly known as Postgres, then as Postgres95)
|
||||
|
||||
Portions Copyright (c) 1996-2013, The PostgreSQL Global Development Group
|
||||
|
||||
Portions Copyright (c) 1994, The Regents of the University of California
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies.
|
||||
|
||||
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
@ -82,8 +82,8 @@ Connect to a Postgres server using the standard URI format:
|
||||
let conn = PostgresConnection::connect("postgres://user:pass@host:port/database?arg1=val1&arg2=val2");
|
||||
```
|
||||
`pass` may be omitted if not needed. `port` defaults to `5432` and `database`
|
||||
defaults to the value of `user` if not specified. The driver supports `trust`
|
||||
and `password` authentication.
|
||||
defaults to the value of `user` if not specified. The driver supports `trust`,
|
||||
`password`, and `md5` authentication.
|
||||
|
||||
Statement Preparation
|
||||
---------------------
|
||||
|
55
THIRD_PARTY
Normal file
55
THIRD_PARTY
Normal file
@ -0,0 +1,55 @@
|
||||
Rust-Postgres contains some third-party content.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
* Some documentation has been copied from PostgreSQL
|
||||
|
||||
PostgreSQL Database Management System
|
||||
(formerly known as Postgres, then as Postgres95)
|
||||
|
||||
Portions Copyright (c) 1996-2013, The PostgreSQL Global Development Group
|
||||
|
||||
Portions Copyright (c) 1994, The Regents of the University of California
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose, without fee, and without a written agreement is
|
||||
hereby granted, provided that the above copyright notice and this paragraph and
|
||||
the following two paragraphs appear in all copies.
|
||||
|
||||
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
||||
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
|
||||
PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
|
||||
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGE.
|
||||
|
||||
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
|
||||
AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
|
||||
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
* The MD5 implementation and supporting code in the util directory has been
|
||||
copied from the Rust project
|
||||
|
||||
Copyright (c) 2006-2009 Graydon Hoare
|
||||
Copyright (c) 2009-2013 Mozilla Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
28
lib.rs
28
lib.rs
@ -69,6 +69,7 @@ fn main() {
|
||||
|
||||
extern mod extra;
|
||||
|
||||
use std;
|
||||
use extra::container::Deque;
|
||||
use extra::ringbuf::RingBuf;
|
||||
use extra::url::{UserInfo, Url};
|
||||
@ -80,7 +81,6 @@ use std::rt::io::net;
|
||||
use std::rt::io::net::ip::{Port, SocketAddr};
|
||||
use std::rt::io::net::tcp::TcpStream;
|
||||
use std::task;
|
||||
use std::util;
|
||||
|
||||
use self::error::hack::PostgresSqlState;
|
||||
use self::message::{BackendMessage,
|
||||
@ -120,11 +120,14 @@ use self::message::{FrontendMessage,
|
||||
Terminate};
|
||||
use self::message::{RowDescriptionEntry, WriteMessage, ReadMessage};
|
||||
use self::types::{PostgresType, ToSql, FromSql};
|
||||
use self::util::digest::Digest;
|
||||
use self::util::md5::Md5;
|
||||
|
||||
pub mod error;
|
||||
pub mod pool;
|
||||
mod message;
|
||||
pub mod types;
|
||||
mod util;
|
||||
|
||||
static DEFAULT_PORT: Port = 5432;
|
||||
|
||||
@ -502,8 +505,25 @@ impl InnerPostgresConnection {
|
||||
};
|
||||
self.write_messages([&PasswordMessage { password: pass }]);
|
||||
}
|
||||
AuthenticationMD5Password { _ }
|
||||
| AuthenticationKerberosV5
|
||||
AuthenticationMD5Password { salt } => {
|
||||
let UserInfo { user, pass } = user;
|
||||
let pass = match pass {
|
||||
Some(pass) => pass,
|
||||
None => return Some(MissingPassword)
|
||||
};
|
||||
let input = pass + user;
|
||||
let mut md5 = Md5::new();
|
||||
md5.input_str(input);
|
||||
let output = md5.result_str();
|
||||
md5.reset();
|
||||
md5.input_str(output);
|
||||
md5.input(salt);
|
||||
let output = "md5" + md5.result_str();
|
||||
self.write_messages([&PasswordMessage {
|
||||
password: output.as_slice()
|
||||
}]);
|
||||
}
|
||||
AuthenticationKerberosV5
|
||||
| AuthenticationSCMCredential
|
||||
| AuthenticationGSS
|
||||
| AuthenticationSSPI => return Some(UnsupportedAuthentication),
|
||||
@ -522,7 +542,7 @@ impl InnerPostgresConnection {
|
||||
|
||||
fn set_notice_handler(&mut self, handler: ~PostgresNoticeHandler)
|
||||
-> ~PostgresNoticeHandler {
|
||||
util::replace(&mut self.notice_handler, handler)
|
||||
std::util::replace(&mut self.notice_handler, handler)
|
||||
}
|
||||
|
||||
fn try_prepare<'a>(&mut self, query: &str, conn: &'a PostgresConnection)
|
||||
|
36
test.rs
36
test.rs
@ -6,6 +6,7 @@ use extra::comm::DuplexStream;
|
||||
use extra::future::Future;
|
||||
use extra::time;
|
||||
use extra::time::Timespec;
|
||||
#[cfg(not(travis))]
|
||||
use extra::json;
|
||||
use extra::uuid::Uuid;
|
||||
use std::f32;
|
||||
@ -310,7 +311,7 @@ fn test_bytea_params() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(not(travis))]
|
||||
fn test_json_params() {
|
||||
test_type("JSON", [(Some(json::from_str("[10, 11, 12]").unwrap()),
|
||||
"'[10, 11, 12]'"),
|
||||
@ -380,13 +381,13 @@ macro_rules! test_range(
|
||||
)
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(not(travis))]
|
||||
fn test_int4range_params() {
|
||||
test_range!("INT4RANGE", i32, 100i32, "100", 200i32, "200")
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(not(travis))]
|
||||
fn test_int8range_params() {
|
||||
test_range!("INT8RANGE", i64, 100i64, "100", 200i64, "200")
|
||||
}
|
||||
@ -401,13 +402,13 @@ fn test_timespec_range_params(sql_type: &str) {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(not(travis))]
|
||||
fn test_tsrange_params() {
|
||||
test_timespec_range_params("TSRANGE");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(not(travis))]
|
||||
fn test_tstzrange_params() {
|
||||
test_timespec_range_params("TSTZRANGE");
|
||||
}
|
||||
@ -600,6 +601,31 @@ fn test_plaintext_pass_wrong_pass() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_md5_pass() {
|
||||
PostgresConnection::connect("postgres://md5_user:password@localhost/postgres");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_md5_pass_no_pass() {
|
||||
let ret = PostgresConnection::try_connect("postgres://md5_user@localhost/postgres");
|
||||
match ret {
|
||||
Err(MissingPassword) => (),
|
||||
Err(err) => fail!("Unexpected error {}", err.to_str()),
|
||||
_ => fail!("Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_md5_pass_wrong_pass() {
|
||||
let ret = PostgresConnection::try_connect("postgres://md5_user:asdf@localhost/postgres");
|
||||
match ret {
|
||||
Err(DbError(PostgresDbError { code: InvalidPassword, _ })) => (),
|
||||
Err(err) => fail!("Unexpected error {}", err.to_str()),
|
||||
_ => fail!("Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dns_failure() {
|
||||
let ret = PostgresConnection::try_connect("postgres://postgres@asdfasdfasdf");
|
||||
|
428
util/cryptoutil.rs
Normal file
428
util/cryptoutil.rs
Normal file
@ -0,0 +1,428 @@
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::num::{One, Zero, CheckedAdd};
|
||||
use std::vec::bytes::{MutableByteVector, copy_memory};
|
||||
|
||||
|
||||
/// Write a u64 into a vector, which must be 8 bytes long. The value is written in big-endian
|
||||
/// format.
|
||||
pub fn write_u64_be(dst: &mut[u8], input: u64) {
|
||||
use std::cast::transmute;
|
||||
use std::unstable::intrinsics::to_be64;
|
||||
assert!(dst.len() == 8);
|
||||
unsafe {
|
||||
let x: *mut i64 = transmute(dst.unsafe_mut_ref(0));
|
||||
*x = to_be64(input as i64);
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a u32 into a vector, which must be 4 bytes long. The value is written in big-endian
|
||||
/// format.
|
||||
pub fn write_u32_be(dst: &mut[u8], input: u32) {
|
||||
use std::cast::transmute;
|
||||
use std::unstable::intrinsics::to_be32;
|
||||
assert!(dst.len() == 4);
|
||||
unsafe {
|
||||
let x: *mut i32 = transmute(dst.unsafe_mut_ref(0));
|
||||
*x = to_be32(input as i32);
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a u32 into a vector, which must be 4 bytes long. The value is written in little-endian
|
||||
/// format.
|
||||
pub fn write_u32_le(dst: &mut[u8], input: u32) {
|
||||
use std::cast::transmute;
|
||||
use std::unstable::intrinsics::to_le32;
|
||||
assert!(dst.len() == 4);
|
||||
unsafe {
|
||||
let x: *mut i32 = transmute(dst.unsafe_mut_ref(0));
|
||||
*x = to_le32(input as i32);
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a vector of bytes into a vector of u64s. The values are read in big-endian format.
|
||||
pub fn read_u64v_be(dst: &mut[u64], input: &[u8]) {
|
||||
use std::cast::transmute;
|
||||
use std::unstable::intrinsics::to_be64;
|
||||
assert!(dst.len() * 8 == input.len());
|
||||
unsafe {
|
||||
let mut x: *mut i64 = transmute(dst.unsafe_mut_ref(0));
|
||||
let mut y: *i64 = transmute(input.unsafe_ref(0));
|
||||
do dst.len().times() {
|
||||
*x = to_be64(*y);
|
||||
x = x.offset(1);
|
||||
y = y.offset(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a vector of bytes into a vector of u32s. The values are read in big-endian format.
|
||||
pub fn read_u32v_be(dst: &mut[u32], input: &[u8]) {
|
||||
use std::cast::transmute;
|
||||
use std::unstable::intrinsics::to_be32;
|
||||
assert!(dst.len() * 4 == input.len());
|
||||
unsafe {
|
||||
let mut x: *mut i32 = transmute(dst.unsafe_mut_ref(0));
|
||||
let mut y: *i32 = transmute(input.unsafe_ref(0));
|
||||
do dst.len().times() {
|
||||
*x = to_be32(*y);
|
||||
x = x.offset(1);
|
||||
y = y.offset(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a vector of bytes into a vector of u32s. The values are read in little-endian format.
|
||||
pub fn read_u32v_le(dst: &mut[u32], input: &[u8]) {
|
||||
use std::cast::transmute;
|
||||
use std::unstable::intrinsics::to_le32;
|
||||
assert!(dst.len() * 4 == input.len());
|
||||
unsafe {
|
||||
let mut x: *mut i32 = transmute(dst.unsafe_mut_ref(0));
|
||||
let mut y: *i32 = transmute(input.unsafe_ref(0));
|
||||
do dst.len().times() {
|
||||
*x = to_le32(*y);
|
||||
x = x.offset(1);
|
||||
y = y.offset(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
trait ToBits {
|
||||
/// Convert the value in bytes to the number of bits, a tuple where the 1st item is the
|
||||
/// high-order value and the 2nd item is the low order value.
|
||||
fn to_bits(self) -> (Self, Self);
|
||||
}
|
||||
|
||||
impl ToBits for u64 {
|
||||
fn to_bits(self) -> (u64, u64) {
|
||||
return (self >> 61, self << 3);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the specified number of bytes to the bit count. fail!() if this would cause numeric
|
||||
/// overflow.
|
||||
pub fn add_bytes_to_bits<T: Int + CheckedAdd + ToBits>(bits: T, bytes: T) -> T {
|
||||
let (new_high_bits, new_low_bits) = bytes.to_bits();
|
||||
|
||||
if new_high_bits > Zero::zero() {
|
||||
fail!("Numeric overflow occured.")
|
||||
}
|
||||
|
||||
match bits.checked_add(&new_low_bits) {
|
||||
Some(x) => return x,
|
||||
None => fail!("Numeric overflow occured.")
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the specified number of bytes to the bit count, which is a tuple where the first element is
|
||||
/// the high order value. fail!() if this would cause numeric overflow.
|
||||
pub fn add_bytes_to_bits_tuple
|
||||
<T: Int + Unsigned + CheckedAdd + ToBits>
|
||||
(bits: (T, T), bytes: T) -> (T, T) {
|
||||
let (new_high_bits, new_low_bits) = bytes.to_bits();
|
||||
let (hi, low) = bits;
|
||||
|
||||
// Add the low order value - if there is no overflow, then add the high order values
|
||||
// If the addition of the low order values causes overflow, add one to the high order values
|
||||
// before adding them.
|
||||
match low.checked_add(&new_low_bits) {
|
||||
Some(x) => {
|
||||
if new_high_bits == Zero::zero() {
|
||||
// This is the fast path - every other alternative will rarely occur in practice
|
||||
// considering how large an input would need to be for those paths to be used.
|
||||
return (hi, x);
|
||||
} else {
|
||||
match hi.checked_add(&new_high_bits) {
|
||||
Some(y) => return (y, x),
|
||||
None => fail!("Numeric overflow occured.")
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let one: T = One::one();
|
||||
let z = match new_high_bits.checked_add(&one) {
|
||||
Some(w) => w,
|
||||
None => fail!("Numeric overflow occured.")
|
||||
};
|
||||
match hi.checked_add(&z) {
|
||||
// This re-executes the addition that was already performed earlier when overflow
|
||||
// occured, this time allowing the overflow to happen. Technically, this could be
|
||||
// avoided by using the checked add intrinsic directly, but that involves using
|
||||
// unsafe code and is not really worthwhile considering how infrequently code will
|
||||
// run in practice. This is the reason that this function requires that the type T
|
||||
// be Unsigned - overflow is not defined for Signed types. This function could be
|
||||
// implemented for signed types as well if that were needed.
|
||||
Some(y) => return (y, low + new_low_bits),
|
||||
None => fail!("Numeric overflow occured.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A FixedBuffer, likes its name implies, is a fixed size buffer. When the buffer becomes full, it
|
||||
/// must be processed. The input() method takes care of processing and then clearing the buffer
|
||||
/// automatically. However, other methods do not and require the caller to process the buffer. Any
|
||||
/// method that modifies the buffer directory or provides the caller with bytes that can be modifies
|
||||
/// results in those bytes being marked as used by the buffer.
|
||||
pub trait FixedBuffer {
|
||||
/// Input a vector of bytes. If the buffer becomes full, process it with the provided
|
||||
/// function and then clear the buffer.
|
||||
fn input(&mut self, input: &[u8], func: &fn(&[u8]));
|
||||
|
||||
/// Reset the buffer.
|
||||
fn reset(&mut self);
|
||||
|
||||
/// Zero the buffer up until the specified index. The buffer position currently must not be
|
||||
/// greater than that index.
|
||||
fn zero_until(&mut self, idx: uint);
|
||||
|
||||
/// Get a slice of the buffer of the specified size. There must be at least that many bytes
|
||||
/// remaining in the buffer.
|
||||
fn next<'s>(&'s mut self, len: uint) -> &'s mut [u8];
|
||||
|
||||
/// Get the current buffer. The buffer must already be full. This clears the buffer as well.
|
||||
fn full_buffer<'s>(&'s mut self) -> &'s [u8];
|
||||
|
||||
/// Get the current position of the buffer.
|
||||
fn position(&self) -> uint;
|
||||
|
||||
/// Get the number of bytes remaining in the buffer until it is full.
|
||||
fn remaining(&self) -> uint;
|
||||
|
||||
/// Get the size of the buffer
|
||||
fn size(&self) -> uint;
|
||||
}
|
||||
|
||||
macro_rules! impl_fixed_buffer( ($name:ident, $size:expr) => (
|
||||
impl FixedBuffer for $name {
|
||||
fn input(&mut self, input: &[u8], func: &fn(&[u8])) {
|
||||
let mut i = 0;
|
||||
|
||||
// FIXME: #6304 - This local variable shouldn't be necessary.
|
||||
let size = $size;
|
||||
|
||||
// If there is already data in the buffer, copy as much as we can into it and process
|
||||
// the data if the buffer becomes full.
|
||||
if self.buffer_idx != 0 {
|
||||
let buffer_remaining = size - self.buffer_idx;
|
||||
if input.len() >= buffer_remaining {
|
||||
copy_memory(
|
||||
self.buffer.mut_slice(self.buffer_idx, size),
|
||||
input.slice_to(buffer_remaining),
|
||||
buffer_remaining);
|
||||
self.buffer_idx = 0;
|
||||
func(self.buffer);
|
||||
i += buffer_remaining;
|
||||
} else {
|
||||
copy_memory(
|
||||
self.buffer.mut_slice(self.buffer_idx, self.buffer_idx + input.len()),
|
||||
input,
|
||||
input.len());
|
||||
self.buffer_idx += input.len();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// While we have at least a full buffer size chunks's worth of data, process that data
|
||||
// without copying it into the buffer
|
||||
while input.len() - i >= size {
|
||||
func(input.slice(i, i + size));
|
||||
i += size;
|
||||
}
|
||||
|
||||
// Copy any input data into the buffer. At this point in the method, the ammount of
|
||||
// data left in the input vector will be less than the buffer size and the buffer will
|
||||
// be empty.
|
||||
let input_remaining = input.len() - i;
|
||||
copy_memory(
|
||||
self.buffer.mut_slice(0, input_remaining),
|
||||
input.slice_from(i),
|
||||
input.len() - i);
|
||||
self.buffer_idx += input_remaining;
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.buffer_idx = 0;
|
||||
}
|
||||
|
||||
fn zero_until(&mut self, idx: uint) {
|
||||
assert!(idx >= self.buffer_idx);
|
||||
self.buffer.mut_slice(self.buffer_idx, idx).set_memory(0);
|
||||
self.buffer_idx = idx;
|
||||
}
|
||||
|
||||
fn next<'s>(&'s mut self, len: uint) -> &'s mut [u8] {
|
||||
self.buffer_idx += len;
|
||||
return self.buffer.mut_slice(self.buffer_idx - len, self.buffer_idx);
|
||||
}
|
||||
|
||||
fn full_buffer<'s>(&'s mut self) -> &'s [u8] {
|
||||
assert!(self.buffer_idx == $size);
|
||||
self.buffer_idx = 0;
|
||||
return self.buffer.slice_to($size);
|
||||
}
|
||||
|
||||
fn position(&self) -> uint { self.buffer_idx }
|
||||
|
||||
fn remaining(&self) -> uint { $size - self.buffer_idx }
|
||||
|
||||
fn size(&self) -> uint { $size }
|
||||
}
|
||||
))
|
||||
|
||||
|
||||
/// A fixed size buffer of 64 bytes useful for cryptographic operations.
|
||||
pub struct FixedBuffer64 {
|
||||
priv buffer: [u8, ..64],
|
||||
priv buffer_idx: uint,
|
||||
}
|
||||
|
||||
impl FixedBuffer64 {
|
||||
/// Create a new buffer
|
||||
pub fn new() -> FixedBuffer64 {
|
||||
return FixedBuffer64 {
|
||||
buffer: [0u8, ..64],
|
||||
buffer_idx: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl_fixed_buffer!(FixedBuffer64, 64)
|
||||
|
||||
/// A fixed size buffer of 128 bytes useful for cryptographic operations.
|
||||
pub struct FixedBuffer128 {
|
||||
priv buffer: [u8, ..128],
|
||||
priv buffer_idx: uint,
|
||||
}
|
||||
|
||||
impl FixedBuffer128 {
|
||||
/// Create a new buffer
|
||||
pub fn new() -> FixedBuffer128 {
|
||||
return FixedBuffer128 {
|
||||
buffer: [0u8, ..128],
|
||||
buffer_idx: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl_fixed_buffer!(FixedBuffer128, 128)
|
||||
|
||||
|
||||
/// The StandardPadding trait adds a method useful for various hash algorithms to a FixedBuffer
|
||||
/// struct.
|
||||
pub trait StandardPadding {
|
||||
/// Add standard padding to the buffer. The buffer must not be full when this method is called
|
||||
/// and is guaranteed to have exactly rem remaining bytes when it returns. If there are not at
|
||||
/// least rem bytes available, the buffer will be zero padded, processed, cleared, and then
|
||||
/// filled with zeros again until only rem bytes are remaining.
|
||||
fn standard_padding(&mut self, rem: uint, func: &fn(&[u8]));
|
||||
}
|
||||
|
||||
impl <T: FixedBuffer> StandardPadding for T {
|
||||
fn standard_padding(&mut self, rem: uint, func: &fn(&[u8])) {
|
||||
let size = self.size();
|
||||
|
||||
self.next(1)[0] = 128;
|
||||
|
||||
if self.remaining() < rem {
|
||||
self.zero_until(size);
|
||||
func(self.full_buffer());
|
||||
}
|
||||
|
||||
self.zero_until(size - rem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use std::rand::{IsaacRng, Rng};
|
||||
use std::vec;
|
||||
use extra::hex::FromHex;
|
||||
|
||||
use super::{add_bytes_to_bits, add_bytes_to_bits_tuple};
|
||||
use super::super::digest::Digest;
|
||||
|
||||
/// Feed 1,000,000 'a's into the digest with varying input sizes and check that the result is
|
||||
/// correct.
|
||||
pub fn test_digest_1million_random<D: Digest>(digest: &mut D, blocksize: uint, expected: &str) {
|
||||
let total_size = 1000000;
|
||||
let buffer = vec::from_elem(blocksize * 2, 'a' as u8);
|
||||
let mut rng = IsaacRng::new_unseeded();
|
||||
let mut count = 0;
|
||||
|
||||
digest.reset();
|
||||
|
||||
while count < total_size {
|
||||
let next: uint = rng.gen_range(0, 2 * blocksize + 1);
|
||||
let remaining = total_size - count;
|
||||
let size = if next > remaining { remaining } else { next };
|
||||
digest.input(buffer.slice_to(size));
|
||||
count += size;
|
||||
}
|
||||
|
||||
let result_str = digest.result_str();
|
||||
let result_bytes = digest.result_bytes();
|
||||
|
||||
assert_eq!(expected, result_str.as_slice());
|
||||
assert_eq!(expected.from_hex().unwrap(), result_bytes);
|
||||
}
|
||||
|
||||
// A normal addition - no overflow occurs
|
||||
#[test]
|
||||
fn test_add_bytes_to_bits_ok() {
|
||||
assert!(add_bytes_to_bits::<u64>(100, 10) == 180);
|
||||
}
|
||||
|
||||
// A simple failure case - adding 1 to the max value
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_add_bytes_to_bits_overflow() {
|
||||
add_bytes_to_bits::<u64>(Bounded::max_value(), 1);
|
||||
}
|
||||
|
||||
// A normal addition - no overflow occurs (fast path)
|
||||
#[test]
|
||||
fn test_add_bytes_to_bits_tuple_ok() {
|
||||
assert!(add_bytes_to_bits_tuple::<u64>((5, 100), 10) == (5, 180));
|
||||
}
|
||||
|
||||
// The low order value overflows into the high order value
|
||||
#[test]
|
||||
fn test_add_bytes_to_bits_tuple_ok2() {
|
||||
assert!(add_bytes_to_bits_tuple::<u64>((5, Bounded::max_value()), 1) == (6, 7));
|
||||
}
|
||||
|
||||
// The value to add is too large to be converted into bits without overflowing its type
|
||||
#[test]
|
||||
fn test_add_bytes_to_bits_tuple_ok3() {
|
||||
assert!(add_bytes_to_bits_tuple::<u64>((5, 0), 0x4000000000000001) == (7, 8));
|
||||
}
|
||||
|
||||
// A simple failure case - adding 1 to the max value
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_add_bytes_to_bits_tuple_overflow() {
|
||||
add_bytes_to_bits_tuple::<u64>((Bounded::max_value(), Bounded::max_value()), 1);
|
||||
}
|
||||
|
||||
// The value to add is too large to convert to bytes without overflowing its type, but the high
|
||||
// order value from this conversion overflows when added to the existing high order value
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_add_bytes_to_bits_tuple_overflow2() {
|
||||
let value: u64 = Bounded::max_value();
|
||||
add_bytes_to_bits_tuple::<u64>((value - 1, 0), 0x8000000000000000);
|
||||
}
|
||||
}
|
81
util/digest.rs
Normal file
81
util/digest.rs
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Common functionality related to cryptographic digest functions
|
||||
|
||||
use std::vec;
|
||||
|
||||
use extra::hex::ToHex;
|
||||
|
||||
|
||||
/**
|
||||
* The Digest trait specifies an interface common to digest functions, such as SHA-1 and the SHA-2
|
||||
* family of digest functions.
|
||||
*/
|
||||
pub trait Digest {
|
||||
/**
|
||||
* Provide message data.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * input - A vector of message data
|
||||
*/
|
||||
fn input(&mut self, input: &[u8]);
|
||||
|
||||
/**
|
||||
* Retrieve the digest result. This method may be called multiple times.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * out - the vector to hold the result. Must be large enough to contain output_bits().
|
||||
*/
|
||||
fn result(&mut self, out: &mut [u8]);
|
||||
|
||||
/**
|
||||
* Reset the digest. This method must be called after result() and before supplying more
|
||||
* data.
|
||||
*/
|
||||
fn reset(&mut self);
|
||||
|
||||
/**
|
||||
* Get the output size in bits.
|
||||
*/
|
||||
fn output_bits(&self) -> uint;
|
||||
|
||||
/**
|
||||
* Convenience function that feeds a string into a digest.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `input` The string to feed into the digest
|
||||
*/
|
||||
fn input_str(&mut self, input: &str) {
|
||||
self.input(input.as_bytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function that retrieves the result of a digest as a
|
||||
* newly allocated vec of bytes.
|
||||
*/
|
||||
fn result_bytes(&mut self) -> ~[u8] {
|
||||
let mut buf = vec::from_elem((self.output_bits()+7)/8, 0u8);
|
||||
self.result(buf);
|
||||
buf
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function that retrieves the result of a digest as a
|
||||
* ~str in hexadecimal format.
|
||||
*/
|
||||
fn result_str(&mut self) -> ~str {
|
||||
self.result_bytes().to_hex()
|
||||
}
|
||||
}
|
||||
|
327
util/md5.rs
Normal file
327
util/md5.rs
Normal file
@ -0,0 +1,327 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[allow(missing_doc)];
|
||||
|
||||
use std::iter::range_step;
|
||||
|
||||
use super::cryptoutil::{write_u32_le, read_u32v_le, FixedBuffer, FixedBuffer64, StandardPadding};
|
||||
use super::digest::Digest;
|
||||
|
||||
|
||||
// A structure that represents that state of a digest computation for the MD5 digest function
|
||||
struct Md5State {
|
||||
s0: u32,
|
||||
s1: u32,
|
||||
s2: u32,
|
||||
s3: u32
|
||||
}
|
||||
|
||||
impl Md5State {
|
||||
fn new() -> Md5State {
|
||||
return Md5State {
|
||||
s0: 0x67452301,
|
||||
s1: 0xefcdab89,
|
||||
s2: 0x98badcfe,
|
||||
s3: 0x10325476
|
||||
};
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.s0 = 0x67452301;
|
||||
self.s1 = 0xefcdab89;
|
||||
self.s2 = 0x98badcfe;
|
||||
self.s3 = 0x10325476;
|
||||
}
|
||||
|
||||
fn process_block(&mut self, input: &[u8]) {
|
||||
fn f(u: u32, v: u32, w: u32) -> u32 {
|
||||
return (u & v) | (!u & w);
|
||||
}
|
||||
|
||||
fn g(u: u32, v: u32, w: u32) -> u32 {
|
||||
return (u & w) | (v & !w);
|
||||
}
|
||||
|
||||
fn h(u: u32, v: u32, w: u32) -> u32 {
|
||||
return u ^ v ^ w;
|
||||
}
|
||||
|
||||
fn i(u: u32, v: u32, w: u32) -> u32 {
|
||||
return v ^ (u | !w);
|
||||
}
|
||||
|
||||
fn rotate_left(x: u32, n: u32) -> u32 {
|
||||
return (x << n) | (x >> (32 - n));
|
||||
}
|
||||
|
||||
fn op_f(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 {
|
||||
return rotate_left(w + f(x, y, z) + m, s) + x;
|
||||
}
|
||||
|
||||
fn op_g(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 {
|
||||
return rotate_left(w + g(x, y, z) + m, s) + x;
|
||||
}
|
||||
|
||||
fn op_h(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 {
|
||||
return rotate_left(w + h(x, y, z) + m, s) + x;
|
||||
}
|
||||
|
||||
fn op_i(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 {
|
||||
return rotate_left(w + i(x, y, z) + m, s) + x;
|
||||
}
|
||||
|
||||
let mut a = self.s0;
|
||||
let mut b = self.s1;
|
||||
let mut c = self.s2;
|
||||
let mut d = self.s3;
|
||||
|
||||
let mut data = [0u32, ..16];
|
||||
|
||||
read_u32v_le(data, input);
|
||||
|
||||
// round 1
|
||||
for i in range_step(0u, 16, 4) {
|
||||
a = op_f(a, b, c, d, data[i] + C1[i], 7);
|
||||
d = op_f(d, a, b, c, data[i + 1] + C1[i + 1], 12);
|
||||
c = op_f(c, d, a, b, data[i + 2] + C1[i + 2], 17);
|
||||
b = op_f(b, c, d, a, data[i + 3] + C1[i + 3], 22);
|
||||
}
|
||||
|
||||
// round 2
|
||||
let mut t = 1;
|
||||
for i in range_step(0u, 16, 4) {
|
||||
a = op_g(a, b, c, d, data[t & 0x0f] + C2[i], 5);
|
||||
d = op_g(d, a, b, c, data[(t + 5) & 0x0f] + C2[i + 1], 9);
|
||||
c = op_g(c, d, a, b, data[(t + 10) & 0x0f] + C2[i + 2], 14);
|
||||
b = op_g(b, c, d, a, data[(t + 15) & 0x0f] + C2[i + 3], 20);
|
||||
t += 20;
|
||||
}
|
||||
|
||||
// round 3
|
||||
t = 5;
|
||||
for i in range_step(0u, 16, 4) {
|
||||
a = op_h(a, b, c, d, data[t & 0x0f] + C3[i], 4);
|
||||
d = op_h(d, a, b, c, data[(t + 3) & 0x0f] + C3[i + 1], 11);
|
||||
c = op_h(c, d, a, b, data[(t + 6) & 0x0f] + C3[i + 2], 16);
|
||||
b = op_h(b, c, d, a, data[(t + 9) & 0x0f] + C3[i + 3], 23);
|
||||
t += 12;
|
||||
}
|
||||
|
||||
// round 4
|
||||
t = 0;
|
||||
for i in range_step(0u, 16, 4) {
|
||||
a = op_i(a, b, c, d, data[t & 0x0f] + C4[i], 6);
|
||||
d = op_i(d, a, b, c, data[(t + 7) & 0x0f] + C4[i + 1], 10);
|
||||
c = op_i(c, d, a, b, data[(t + 14) & 0x0f] + C4[i + 2], 15);
|
||||
b = op_i(b, c, d, a, data[(t + 21) & 0x0f] + C4[i + 3], 21);
|
||||
t += 28;
|
||||
}
|
||||
|
||||
self.s0 += a;
|
||||
self.s1 += b;
|
||||
self.s2 += c;
|
||||
self.s3 += d;
|
||||
}
|
||||
}
|
||||
|
||||
// Round 1 constants
|
||||
static C1: [u32, ..16] = [
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821
|
||||
];
|
||||
|
||||
// Round 2 constants
|
||||
static C2: [u32, ..16] = [
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a
|
||||
];
|
||||
|
||||
// Round 3 constants
|
||||
static C3: [u32, ..16] = [
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665
|
||||
];
|
||||
|
||||
// Round 4 constants
|
||||
static C4: [u32, ..16] = [
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
||||
];
|
||||
|
||||
|
||||
/// The MD5 Digest algorithm
|
||||
pub struct Md5 {
|
||||
priv length_bytes: u64,
|
||||
priv buffer: FixedBuffer64,
|
||||
priv state: Md5State,
|
||||
priv finished: bool,
|
||||
}
|
||||
|
||||
impl Md5 {
|
||||
/// Construct a new instance of the MD5 Digest.
|
||||
pub fn new() -> Md5 {
|
||||
return Md5 {
|
||||
length_bytes: 0,
|
||||
buffer: FixedBuffer64::new(),
|
||||
state: Md5State::new(),
|
||||
finished: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Digest for Md5 {
|
||||
fn input(&mut self, input: &[u8]) {
|
||||
assert!(!self.finished);
|
||||
// Unlike Sha1 and Sha2, the length value in MD5 is defined as the length of the message mod
|
||||
// 2^64 - ie: integer overflow is OK.
|
||||
self.length_bytes += input.len() as u64;
|
||||
self.buffer.input(input, |d: &[u8]| { self.state.process_block(d); });
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.length_bytes = 0;
|
||||
self.buffer.reset();
|
||||
self.state.reset();
|
||||
self.finished = false;
|
||||
}
|
||||
|
||||
fn result(&mut self, out: &mut [u8]) {
|
||||
if !self.finished {
|
||||
self.buffer.standard_padding(8, |d: &[u8]| { self.state.process_block(d); });
|
||||
write_u32_le(self.buffer.next(4), (self.length_bytes << 3) as u32);
|
||||
write_u32_le(self.buffer.next(4), (self.length_bytes >> 29) as u32);
|
||||
self.state.process_block(self.buffer.full_buffer());
|
||||
self.finished = true;
|
||||
}
|
||||
|
||||
write_u32_le(out.mut_slice(0, 4), self.state.s0);
|
||||
write_u32_le(out.mut_slice(4, 8), self.state.s1);
|
||||
write_u32_le(out.mut_slice(8, 12), self.state.s2);
|
||||
write_u32_le(out.mut_slice(12, 16), self.state.s3);
|
||||
}
|
||||
|
||||
fn output_bits(&self) -> uint { 128 }
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::cryptoutil::test::test_digest_1million_random;
|
||||
use super::super::digest::Digest;
|
||||
use super::Md5;
|
||||
|
||||
|
||||
struct Test {
|
||||
input: ~str,
|
||||
output_str: ~str,
|
||||
}
|
||||
|
||||
fn test_hash<D: Digest>(sh: &mut D, tests: &[Test]) {
|
||||
// Test that it works when accepting the message all at once
|
||||
for t in tests.iter() {
|
||||
sh.input_str(t.input);
|
||||
|
||||
let out_str = sh.result_str();
|
||||
assert!(out_str == t.output_str);
|
||||
|
||||
sh.reset();
|
||||
}
|
||||
|
||||
// Test that it works when accepting the message in pieces
|
||||
for t in tests.iter() {
|
||||
let len = t.input.len();
|
||||
let mut left = len;
|
||||
while left > 0u {
|
||||
let take = (left + 1u) / 2u;
|
||||
sh.input_str(t.input.slice(len - left, take + len - left));
|
||||
left = left - take;
|
||||
}
|
||||
|
||||
let out_str = sh.result_str();
|
||||
assert!(out_str == t.output_str);
|
||||
|
||||
sh.reset();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_md5() {
|
||||
// Examples from wikipedia
|
||||
let wikipedia_tests = ~[
|
||||
Test {
|
||||
input: ~"",
|
||||
output_str: ~"d41d8cd98f00b204e9800998ecf8427e"
|
||||
},
|
||||
Test {
|
||||
input: ~"The quick brown fox jumps over the lazy dog",
|
||||
output_str: ~"9e107d9d372bb6826bd81d3542a419d6"
|
||||
},
|
||||
Test {
|
||||
input: ~"The quick brown fox jumps over the lazy dog.",
|
||||
output_str: ~"e4d909c290d0fb1ca068ffaddf22cbd0"
|
||||
},
|
||||
];
|
||||
|
||||
let tests = wikipedia_tests;
|
||||
|
||||
let mut sh = Md5::new();
|
||||
|
||||
test_hash(&mut sh, tests);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1million_random_md5() {
|
||||
let mut sh = Md5::new();
|
||||
test_digest_1million_random(
|
||||
&mut sh,
|
||||
64,
|
||||
"7707d6ae4e027c70eea2a935c2296f21");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod bench {
|
||||
use extra::test::BenchHarness;
|
||||
|
||||
use super::Md5;
|
||||
|
||||
|
||||
#[bench]
|
||||
pub fn md5_10(bh: & mut BenchHarness) {
|
||||
let mut sh = Md5::new();
|
||||
let bytes = [1u8, ..10];
|
||||
do bh.iter {
|
||||
sh.input(bytes);
|
||||
}
|
||||
bh.bytes = bytes.len() as u64;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
pub fn md5_1k(bh: & mut BenchHarness) {
|
||||
let mut sh = Md5::new();
|
||||
let bytes = [1u8, ..1024];
|
||||
do bh.iter {
|
||||
sh.input(bytes);
|
||||
}
|
||||
bh.bytes = bytes.len() as u64;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
pub fn md5_64k(bh: & mut BenchHarness) {
|
||||
let mut sh = Md5::new();
|
||||
let bytes = [1u8, ..65536];
|
||||
do bh.iter {
|
||||
sh.input(bytes);
|
||||
}
|
||||
bh.bytes = bytes.len() as u64;
|
||||
}
|
||||
}
|
3
util/mod.rs
Normal file
3
util/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod cryptoutil;
|
||||
pub mod digest;
|
||||
pub mod md5;
|
Loading…
Reference in New Issue
Block a user