如何在Perl 6中实现Python的os.urandom方法?

che*_*nyf 7 perl6 raku

我正在将用Python编写的模块转换为Perl 6.在模块中,有一个名为create_key的方法,它使用os.urandom进行加密:

def create_key(size):
    return binascii.hexlify(os.urandom(size))[:16]
Run Code Online (Sandbox Code Playgroud)

该文档描述os.urandom为:

返回一串适合加密用途的大小随机字节.

在Perl 6中,有一个名为Buf的类,但没有随机方法.那么如何os.urandom(size)使用Perl 6 实现呢?

Bra*_*ert 10

你总是可以使用Python urandom

sub py-urandom( UInt $size ){
  use Inline::Python;

  state $py = Inline::Python.new; # reuse the same instance
  once $py.import('os');          # load the Python os library only once

  $py.call('os','urandom', $size);
}

say py-urandom(10)».fmt('%02X').join;  # 1473A7D5455F15D3726B
Run Code Online (Sandbox Code Playgroud)

要完成上述工作,需要安装python-dev操作系统软件包.然后安装Inline::Pythonzef.


您可以使用binascii.hexlify,以及

sub create-key ( UInt $size ) {
  use Inline::Python;

  state $py = Inline::Python.new;
  once $py.import('os');
  once $py.import('binascii');

  $py.call('binascii','hexlify', $py.call('os','urandom',$size)).decode('ascii');
}
Run Code Online (Sandbox Code Playgroud)

我确信有更好的方法来做到这一点,但这是我第一次使用Inline::Python.(这应该是显而易见的,因为我必须安装python-dev来回答这个问题)


另一种方式,这可能是从长远来看更是刚刚打电话getrandom,getentropy或者CryptGenRandom取决于如果它在Linux,OpenBSD,或Windows上运行.基本上复制执行os.urandom.

下面是一个快速编写的例子.

sub urandom ( UInt $size ){
  use NativeCall;

  my constant is-win = $*DISTRO.is-win;
  my constant is-openbsd = $*DISTRO.name eq 'openbsd';

  if is-win {
    fail "urandom doesn't handle Windows yet";
    # It is more involved on Windows, and I don't use Windows

  } elsif is-openbsd {
    # note that this is untested as I don't use OpenBSD
    if $size > 256 {
      fail "urandom doesn't handle more than 256 on OpenBSD"
      # note that this could be changed to load it in 256 byte chunks
    }

    sub getentropy( Buf \buf, size_t \buflen --> int32 ) is native {}

    my Buf $buf .= allocate($size);
    my $result = getentropy( $buf, $size );

    fail if $result !== 0;
    $buf

  } else { # presumably Linux or other UNIX-like

    sub getrandom (Buf \buf, size_t \buflen, uint32 \flags --> ssize_t) is native {}

    my Buf $buf .= allocate($size);
    my $total = getrandom( $buf, $size, 0 );

    fail unless $total == $size; # could be changed to call it for the rest
    $buf;
  }
}
Run Code Online (Sandbox Code Playgroud)
say urandom(10)».fmt('%02X').join; # 0EF9EDB3EBC724C0E9CE
Run Code Online (Sandbox Code Playgroud)

如果您使用的是系统/dev/urandom,则可以从中读取.

sub urandom ( UInt $size ){
  my $urandom will leave {.close}
    = '/dev/urandom'.IO.open(:bin,:ro);
  $urandom.read( $size )
}

say urandom(10)».fmt('%02X').join; # 01B6C41AD0A77732C328
Run Code Online (Sandbox Code Playgroud)

最好的途径是使用已经完成上述操作的模块,如Crypt :: Random.
它实现了我不需要的Windows所需的代码,但它使用/dev/urandom*NIX系统上的文件.

# alias &Crypt::Random::crypt_random_buf as &urandom
my &urandom = do {
  use Crypt::Random;
  &crypt_random_buf
}

say urandom(10)».fmt('%02X').join; # 841720513678B1811E2D
Run Code Online (Sandbox Code Playgroud)


tim*_*imo 9

使用方法或子roll将不会为您提供适合加密使用的随机字节.他们只使用perl6提供的内置伪随机数生成器,这是rakudo在moarvm上的mersenne twister.

你需要的是一个像你这样的库Crypt::Random,它模仿arc4random并描述/dev/urandom在unices和CryptGenRandomwindows上使用.您可以在github上找到它或直接安装它zef install Crypt::Random.

使用该crypt_random_buf模块中的sub将为您提供所需大小的buf,然后您可以将其转换为十六进制数字的字符串,就像在另一个答案中一样.list.fmt("%x","").


Eli*_*sen 6

sub urandom(Int:D \size) { Buf.new: (^256).roll(size) }
say urandom(16);  # Buf:0x<98 43 10 A7 5A FD 62 4B AB 1E 42 6D 24 70 E6 89>
Run Code Online (Sandbox Code Playgroud)

或者,作为字符串:

say urandom(16).list.fmt("%x","");  # bfa1c6fef9784ba31b17cdb135ce6622
Run Code Online (Sandbox Code Playgroud)

或者把它放在urandom子里面:

sub urandom(Int:D \size) { Buf.new((^256).roll(size)).list.fmt("%x","") }
say urandom(16);  # bfa1c6fef9784ba31b17cdb135ce6622
Run Code Online (Sandbox Code Playgroud)