mirror of
https://github.com/vlang/v.git
synced 2025-08-04 02:07:28 -04:00
242 lines
7.3 KiB
V
242 lines
7.3 KiB
V
// Copyright (c) 2023 Kim Shrier. All rights reserved.
|
|
// Use of this source code is governed by an MIT license
|
|
// that can be found in the LICENSE file.
|
|
//
|
|
// Package scrypt implements the key derivation functions as
|
|
// described in https://datatracker.ietf.org/doc/html/rfc7914
|
|
module scrypt
|
|
|
|
import crypto.pbkdf2
|
|
import crypto.sha256
|
|
import encoding.binary
|
|
import math.bits
|
|
|
|
pub const max_buffer_length = ((u64(1) << 32) - 1) * 32
|
|
pub const max_blocksize_parallal_product = u64(1 << 30)
|
|
|
|
// salsa20_8 applies the salsa20/8 core transformation to a block
|
|
// of 64 u8 bytes. The block is modified in place.
|
|
fn salsa20_8(mut block []u8) {
|
|
mut block_words := []u32{len: 16}
|
|
mut scratch := [16]u32{}
|
|
|
|
for i in 0 .. 16 {
|
|
block_words[i] = binary.little_endian_u32_at(block, i * 4)
|
|
scratch[i] = block_words[i]
|
|
}
|
|
|
|
for i := 8; i > 0; i -= 2 {
|
|
// processing columns
|
|
scratch[4] ^= bits.rotate_left_32(scratch[0] + scratch[12], 7)
|
|
scratch[8] ^= bits.rotate_left_32(scratch[4] + scratch[0], 9)
|
|
scratch[12] ^= bits.rotate_left_32(scratch[8] + scratch[4], 13)
|
|
scratch[0] ^= bits.rotate_left_32(scratch[12] + scratch[8], 18)
|
|
|
|
scratch[9] ^= bits.rotate_left_32(scratch[5] + scratch[1], 7)
|
|
scratch[13] ^= bits.rotate_left_32(scratch[9] + scratch[5], 9)
|
|
scratch[1] ^= bits.rotate_left_32(scratch[13] + scratch[9], 13)
|
|
scratch[5] ^= bits.rotate_left_32(scratch[1] + scratch[13], 18)
|
|
|
|
scratch[14] ^= bits.rotate_left_32(scratch[10] + scratch[6], 7)
|
|
scratch[2] ^= bits.rotate_left_32(scratch[14] + scratch[10], 9)
|
|
scratch[6] ^= bits.rotate_left_32(scratch[2] + scratch[14], 13)
|
|
scratch[10] ^= bits.rotate_left_32(scratch[6] + scratch[2], 18)
|
|
|
|
scratch[3] ^= bits.rotate_left_32(scratch[15] + scratch[11], 7)
|
|
scratch[7] ^= bits.rotate_left_32(scratch[3] + scratch[15], 9)
|
|
scratch[11] ^= bits.rotate_left_32(scratch[7] + scratch[3], 13)
|
|
scratch[15] ^= bits.rotate_left_32(scratch[11] + scratch[7], 18)
|
|
|
|
// processing rows
|
|
scratch[1] ^= bits.rotate_left_32(scratch[0] + scratch[3], 7)
|
|
scratch[2] ^= bits.rotate_left_32(scratch[1] + scratch[0], 9)
|
|
scratch[3] ^= bits.rotate_left_32(scratch[2] + scratch[1], 13)
|
|
scratch[0] ^= bits.rotate_left_32(scratch[3] + scratch[2], 18)
|
|
|
|
scratch[6] ^= bits.rotate_left_32(scratch[5] + scratch[4], 7)
|
|
scratch[7] ^= bits.rotate_left_32(scratch[6] + scratch[5], 9)
|
|
scratch[4] ^= bits.rotate_left_32(scratch[7] + scratch[6], 13)
|
|
scratch[5] ^= bits.rotate_left_32(scratch[4] + scratch[7], 18)
|
|
|
|
scratch[11] ^= bits.rotate_left_32(scratch[10] + scratch[9], 7)
|
|
scratch[8] ^= bits.rotate_left_32(scratch[11] + scratch[10], 9)
|
|
scratch[9] ^= bits.rotate_left_32(scratch[8] + scratch[11], 13)
|
|
scratch[10] ^= bits.rotate_left_32(scratch[9] + scratch[8], 18)
|
|
|
|
scratch[12] ^= bits.rotate_left_32(scratch[15] + scratch[14], 7)
|
|
scratch[13] ^= bits.rotate_left_32(scratch[12] + scratch[15], 9)
|
|
scratch[14] ^= bits.rotate_left_32(scratch[13] + scratch[12], 13)
|
|
scratch[15] ^= bits.rotate_left_32(scratch[14] + scratch[13], 18)
|
|
}
|
|
|
|
for i in 0 .. 16 {
|
|
scratch[i] += block_words[i]
|
|
binary.little_endian_put_u32_at(mut block, scratch[i], i * 4)
|
|
}
|
|
}
|
|
|
|
@[inline]
|
|
fn blkcpy(mut dest []u8, src []u8, len u32) {
|
|
for i in 0 .. len {
|
|
dest[i] = src[i]
|
|
}
|
|
}
|
|
|
|
@[inline]
|
|
fn blkxor(mut dest []u8, src []u8, len u32) {
|
|
for i in 0 .. len {
|
|
dest[i] ^= src[i]
|
|
}
|
|
}
|
|
|
|
// block_mix performs the block_mix operation using salsa20_8
|
|
//
|
|
// The block input must be 128 * r in length. The temp array
|
|
// has to be the same size, 128 * r. r is a positive integer
|
|
// value > 0. The block is modified in place.
|
|
fn block_mix(mut block []u8, mut temp []u8, r u32) {
|
|
mut scratch := []u8{len: 64, cap: 64}
|
|
|
|
blkcpy(mut scratch, block[(((2 * r) - 1) * 64)..], 64)
|
|
|
|
for i in 0 .. 2 * r {
|
|
start := i * 64
|
|
stop := start + 64
|
|
|
|
blkxor(mut scratch, block[start..stop], 64)
|
|
salsa20_8(mut scratch)
|
|
|
|
blkcpy(mut temp[start..stop], scratch, 64)
|
|
}
|
|
|
|
for i in 0 .. r {
|
|
start := i * 64
|
|
stop := start + 64
|
|
|
|
temp_start := (i * 2) * 64
|
|
temp_stop := temp_start + 64
|
|
|
|
blkcpy(mut block[start..stop], temp[temp_start..temp_stop], 64)
|
|
}
|
|
|
|
for i in 0 .. r {
|
|
start := (i + r) * 64
|
|
stop := start + 64
|
|
|
|
temp_start := ((i * 2) + 1) * 64
|
|
temp_stop := temp_start + 64
|
|
|
|
blkcpy(mut block[start..stop], temp[temp_start..temp_stop], 64)
|
|
}
|
|
}
|
|
|
|
fn smix(mut block []u8, r u32, n u64, mut v_block []u8, mut temp_block []u8) {
|
|
blkcpy(mut temp_block, block, 128 * r)
|
|
|
|
y_start := 128 * r
|
|
|
|
for i in 0 .. n {
|
|
v_start := i * (128 * r)
|
|
v_stop := v_start + (128 * r)
|
|
|
|
blkcpy(mut v_block[v_start..v_stop], temp_block, 128 * r)
|
|
block_mix(mut temp_block, mut temp_block[y_start..], r)
|
|
}
|
|
|
|
for _ in 0 .. n {
|
|
j := binary.little_endian_u64_at(temp_block, int(((2 * r) - 1) * 64)) & (n - 1)
|
|
|
|
v_start := j * (128 * r)
|
|
v_stop := v_start + (128 * r)
|
|
|
|
blkxor(mut temp_block, v_block[v_start..v_stop], 128 * r)
|
|
block_mix(mut temp_block, mut temp_block[y_start..], r)
|
|
}
|
|
|
|
blkcpy(mut block, temp_block, 128 * r)
|
|
}
|
|
|
|
struct OutputBufferLengthError {
|
|
Error
|
|
length u64
|
|
}
|
|
|
|
fn (err OutputBufferLengthError) msg() string {
|
|
return 'the output buffer length, ${err.length}, is greater than ${max_buffer_length}'
|
|
}
|
|
|
|
struct BlocksizeParallelProductError {
|
|
Error
|
|
blocksize u32
|
|
parallel u32
|
|
product u64
|
|
}
|
|
|
|
fn (err BlocksizeParallelProductError) msg() string {
|
|
return 'the product of blocksize ${err.blocksize} * parallel ${err.parallel} = ${err.product}, is greater than ${max_blocksize_parallal_product}'
|
|
}
|
|
|
|
struct CpuMemoryCostError {
|
|
Error
|
|
cost u64
|
|
}
|
|
|
|
fn (err CpuMemoryCostError) msg() string {
|
|
return 'the CPU/memory cost ${err.cost} must be greater than 0 and also a power of 2'
|
|
}
|
|
|
|
// scrypt performs password based key derivation using the scrypt algorithm.
|
|
//
|
|
// The input parameters are:
|
|
//
|
|
// password - a slice of bytes which is the password being used to
|
|
// derive the key. Don't leak this value to anybody.
|
|
// salt - a slice of bytes used to make it harder to crack the key.
|
|
// n - CPU/Memory cost parameter, must be larger than 0, a power of 2,
|
|
// and less than 2^(128 * r / 8).
|
|
// r - block size parameter.
|
|
// p - parallelization parameter, a positive integer less than or
|
|
// equal to ((2^32-1) * hLen) / MFLen where hLen is 32 and
|
|
// MFlen is 128 * r.
|
|
// dk_len - intended output length in octets of the derived key;
|
|
// a positive integer less than or equal to (2^32 - 1) * hLen
|
|
// where hLen is 32.
|
|
//
|
|
// Reasonable values for n, r, and p are n = 1024, r = 8, p = 16.
|
|
pub fn scrypt(password []u8, salt []u8, n u64, r u32, p u32, dk_len u64) ![]u8 {
|
|
if dk_len > max_buffer_length {
|
|
return OutputBufferLengthError{
|
|
length: dk_len
|
|
}
|
|
}
|
|
|
|
if u64(r) * u64(p) >= max_blocksize_parallal_product {
|
|
return BlocksizeParallelProductError{
|
|
blocksize: r
|
|
parallel: p
|
|
product: u64(r) * u64(p)
|
|
}
|
|
}
|
|
|
|
// the following is a sneaky way to determine if a number is a
|
|
// power of 2. Also, a value of 0 is not allowed.
|
|
if (n & (n - 1)) != 0 || n == 0 {
|
|
return CpuMemoryCostError{
|
|
cost: n
|
|
}
|
|
}
|
|
|
|
mut b := pbkdf2.key(password, salt, 1, int(128 * r * p), sha256.new())!
|
|
|
|
mut xy := []u8{len: int(256 * r), cap: int(256 * r), init: 0}
|
|
mut v := []u8{len: int(128 * r * n), cap: int(128 * r * n), init: 0}
|
|
|
|
for i in u32(0) .. p {
|
|
smix(mut b[i * 128 * r..], r, n, mut v, mut xy)
|
|
}
|
|
|
|
result := pbkdf2.key(password, b, 1, int(128 * r * p), sha256.new())!
|
|
|
|
return result[..dk_len]
|
|
}
|