From af6e4abe8458114c2d5c7e7f77a44947b546f6bc Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 31 Jul 2024 10:26:18 +0800 Subject: [PATCH] library: crypto/rand --- .../_randcrypt => _demo/randcrypt}/rand.go | 0 c/openssl/err.go | 156 ++++++++++++++++++ internal/build/build.go | 1 + internal/lib/crypto/rand/rand.go | 105 ++++++++++++ internal/lib/crypto/rand/util.go | 98 +++++++++++ 5 files changed, 360 insertions(+) rename {_cmptest/_randcrypt => _demo/randcrypt}/rand.go (100%) create mode 100644 c/openssl/err.go create mode 100644 internal/lib/crypto/rand/rand.go create mode 100644 internal/lib/crypto/rand/util.go diff --git a/_cmptest/_randcrypt/rand.go b/_demo/randcrypt/rand.go similarity index 100% rename from _cmptest/_randcrypt/rand.go rename to _demo/randcrypt/rand.go diff --git a/c/openssl/err.go b/c/openssl/err.go new file mode 100644 index 00000000..02fb7969 --- /dev/null +++ b/c/openssl/err.go @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +// ----------------------------------------------------------------------------- + +/*- + * The error code packs differently depending on if it records a system + * error or an OpenSSL error. + * + * A system error packs like this (we follow POSIX and only allow positive + * numbers that fit in an |int|): + * + * +-+-------------------------------------------------------------+ + * |1| system error number | + * +-+-------------------------------------------------------------+ + * + * An OpenSSL error packs like this: + * + * <---------------------------- 32 bits --------------------------> + * <--- 8 bits ---><------------------ 23 bits -----------------> + * +-+---------------+---------------------------------------------+ + * |0| library | reason | + * +-+---------------+---------------------------------------------+ + * + * A few of the reason bits are reserved as flags with special meaning: + * + * <5 bits-<>--------- 19 bits -----------------> + * +-------+-+-----------------------------------+ + * | rflags| | reason | + * +-------+-+-----------------------------------+ + * ^ + * | + * ERR_RFLAG_FATAL = ERR_R_FATAL + * + * The reason flags are part of the overall reason code for practical + * reasons, as they provide an easy way to place different types of + * reason codes in different numeric ranges. + * + * The currently known reason flags are: + * + * ERR_RFLAG_FATAL Flags that the reason code is considered fatal. + * For backward compatibility reasons, this flag + * is also the code for ERR_R_FATAL (that reason + * code served the dual purpose of flag and reason + * code in one in pre-3.0 OpenSSL). + * ERR_RFLAG_COMMON Flags that the reason code is common to all + * libraries. All ERR_R_ macros must use this flag, + * and no other _R_ macro is allowed to use it. + */ +type Errno c.Ulong + +// ERR_get_error returns the earliest error code from the thread's error queue and +// removes the entry. This function can be called repeatedly until there are no more +// error codes to return. +// +// unsigned long ERR_get_error(void); +// +//go:linkname ERRGetError C.ERR_get_error +func ERRGetError() Errno + +// ERR_get_error_all() is the same as ERR_get_error(), but on success it additionally +// stores the filename, line number and function where the error occurred in *file, +// *line and *func, and also extra text and flags in *data, *flags. If any of those +// parameters are NULL, it will not be changed. +// +// unsigned long ERR_get_error_all( +// const char **file, int *line, const char **func, const char **data, int *flags); +// +//go:linkname ERRGetErrorAll C.ERR_get_error_all +func ERRGetErrorAll( + file **c.Char, line *c.Int, function **c.Char, data **c.Char, flags *c.Int) Errno + +// unsigned long ERR_peek_error(void); +// +//go:linkname ERRPeekError C.ERR_peek_error +func ERRPeekError() Errno + +// unsigned long ERR_peek_error_all( +// const char **file, int *line, const char **func, const char **data, int *flags); +// +//go:linkname ERRPeekErrorAll C.ERR_peek_error_all +func ERRPeekErrorAll( + file **c.Char, line *c.Int, function **c.Char, data **c.Char, flags *c.Int) Errno + +// unsigned long ERR_peek_last_error(void); +// +//go:linkname ERRPeekLastError C.ERR_peek_last_error +func ERRPeekLastError() Errno + +// unsigned long ERR_peek_last_error_all( +// const char **file, int *line, const char **func, const char **data, int *flags); +// +//go:linkname ERRPeekLastErrorAll C.ERR_peek_last_error_all +func ERRPeekLastErrorAll( + file **c.Char, line *c.Int, function **c.Char, data **c.Char, flags *c.Int) Errno + +// void ERR_clear_error(void); +// +//go:linkname ERRClearError C.ERR_clear_error +func ERRClearError() + +// ERR_error_string() generates a human-readable string representing the error code e, +// and places it at buf. buf must be at least 256 bytes long. +// +// char *ERR_error_string(unsigned long e, char *buf); +// +//go:linkname ERRErrorString C.ERR_error_string +func ERRErrorString(e Errno, buf *c.Char) *c.Char + +// ERR_lib_error_string() and ERR_reason_error_string() return the library name and +// reason string respectively. +// +// const char *ERR_lib_error_string(unsigned long e); +// +//go:linkname ERRLibErrorString C.ERR_lib_error_string +func ERRLibErrorString(e Errno) *c.Char + +// const char *ERR_reason_error_string(unsigned long e); +// +//go:linkname ERRReasonErrorString C.ERR_reason_error_string +func ERRReasonErrorString(e Errno) *c.Char + +// void ERR_print_errors_cb(int (*cb) (const char *str, size_t len, void *u), void *u); +// +// [pid]:error:[error code]:[library name]:[function name]:[reason string]:[filename]:[line]:[optional text message] +// +//go:linkname ERRPrintErrorsCb C.ERR_print_errors_cb +func ERRPrintErrorsCb(cb func(str *c.Char, len uintptr, u unsafe.Pointer) c.Int, u unsafe.Pointer) + +// void ERR_print_errors_fp(FILE *fp); +// +//go:linkname ERRPrintErrorsFp C.ERR_print_errors_fp +func ERRPrintErrorsFp(fp c.FilePtr) + +// ----------------------------------------------------------------------------- diff --git a/internal/build/build.go b/internal/build/build.go index 8f487d8e..d32c39e8 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -758,6 +758,7 @@ type none struct{} var hasAltPkg = map[string]none{ "crypto/md5": {}, + "crypto/rand": {}, "fmt": {}, "hash/crc32": {}, "internal/abi": {}, diff --git a/internal/lib/crypto/rand/rand.go b/internal/lib/crypto/rand/rand.go new file mode 100644 index 00000000..091614ca --- /dev/null +++ b/internal/lib/crypto/rand/rand.go @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rand + +// llgo:skipall +import ( + "io" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/openssl" + "github.com/qiniu/x/errors" +) + +type rndReader struct{} + +func (rndReader) Read(p []byte) (n int, err error) { + return Read(p) +} + +// Reader is a global, shared instance of a cryptographically +// secure random number generator. +var Reader io.Reader = rndReader{} + +type opensslError struct { + file *c.Char + line c.Int + flags c.Int + function *c.Char + data *c.Char + err openssl.Errno +} + +// [error code]:[library name]:[function name]:[reason string]:[[filename:line]]: [text message] +func (p *opensslError) Error() string { + const bufsize = 1024 + buf := (*c.Char)(c.Alloca(bufsize)) + lib := openssl.ERRLibErrorString(p.err) + reason := openssl.ERRReasonErrorString(p.err) + n := uintptr(c.Snprintf( + buf, bufsize, + c.Str("%d:%s:%s:%s:[%s:%d]: "), + p.err, lib, p.function, reason, p.file, p.line)) + n += c.Strlen(openssl.ERRErrorString(p.err, c.Advance(buf, n))) + return c.GoString(buf, n) +} + +func getError() *opensslError { + ret := new(opensslError) + err := openssl.ERRGetErrorAll(&ret.file, &ret.line, &ret.function, &ret.data, &ret.flags) + if err == 0 { + return nil + } + return ret +} + +func getErrors() error { + var errs errors.List + for openssl.ERRPeekError() != 0 { + errs.Add(getError()) + } + return errs.ToError() +} + +// Read is a helper function that calls Reader.Read using io.ReadFull. +// On return, n == len(b) if and only if err == nil. +func Read(b []byte) (n int, err error) { + if openssl.RANDBytes(b) != 0 { + return len(b), nil + } + return 0, getErrors() +} + +/* TODO(xsw): +// batched returns a function that calls f to populate a []byte by chunking it +// into subslices of, at most, readMax bytes. +func batched(f func([]byte) error, readMax int) func([]byte) error { + return func(out []byte) error { + for len(out) > 0 { + read := len(out) + if read > readMax { + read = readMax + } + if err := f(out[:read]); err != nil { + return err + } + out = out[read:] + } + return nil + } +} +*/ diff --git a/internal/lib/crypto/rand/util.go b/internal/lib/crypto/rand/util.go new file mode 100644 index 00000000..51e10c8f --- /dev/null +++ b/internal/lib/crypto/rand/util.go @@ -0,0 +1,98 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +/* TODO(xsw): +import ( + "errors" + "io" + "math/big" +) + +// Prime returns a number of the given bit length that is prime with high probability. +// Prime will return error for any error returned by rand.Read or if bits < 2. +func Prime(rand io.Reader, bits int) (*big.Int, error) { + if bits < 2 { + return nil, errors.New("crypto/rand: prime size must be at least 2-bit") + } + + b := uint(bits % 8) + if b == 0 { + b = 8 + } + + bytes := make([]byte, (bits+7)/8) + p := new(big.Int) + + for { + if _, err := io.ReadFull(rand, bytes); err != nil { + return nil, err + } + + // Clear bits in the first byte to make sure the candidate has a size <= bits. + bytes[0] &= uint8(int(1<= 2 { + bytes[0] |= 3 << (b - 2) + } else { + // Here b==1, because b cannot be zero. + bytes[0] |= 1 + if len(bytes) > 1 { + bytes[1] |= 0x80 + } + } + // Make the value odd since an even number this large certainly isn't prime. + bytes[len(bytes)-1] |= 1 + + p.SetBytes(bytes) + if p.ProbablyPrime(20) { + return p, nil + } + } +} + +// Int returns a uniform random value in [0, max). It panics if max <= 0. +func Int(rand io.Reader, max *big.Int) (n *big.Int, err error) { + if max.Sign() <= 0 { + panic("crypto/rand: argument to Int is <= 0") + } + n = new(big.Int) + n.Sub(max, n.SetUint64(1)) + // bitLen is the maximum bit length needed to encode a value < max. + bitLen := n.BitLen() + if bitLen == 0 { + // the only valid result is 0 + return + } + // k is the maximum byte length needed to encode a value < max. + k := (bitLen + 7) / 8 + // b is the number of bits in the most significant byte of max-1. + b := uint(bitLen % 8) + if b == 0 { + b = 8 + } + + bytes := make([]byte, k) + + for { + _, err = io.ReadFull(rand, bytes) + if err != nil { + return nil, err + } + + // Clear bits in the first byte to increase the probability + // that the candidate is < max. + bytes[0] &= uint8(int(1<